GAN系列之—Deep Convolutional GAN(DCGAN)

DCGAN 的判别器和生成器都使用了卷积神经网络(CNN)来替代GAN 中的多层感知机,同时为了使整个网络可微,拿掉了CNN 中的池化层,另外将全连接层以全局池化层替代以减轻计算量。

去卷积(反卷积,Deconvolution)

从上图中可以看到,生成器G 将一个100 维的噪音向量扩展成64 * 64 * 3 的矩阵输出,整个过程采用的是微步卷积的方式。作者在文中将其称为fractionally-strided convolutions,并特意强调不是deconvolutions。

去卷积(链接:反卷积)又包含转置卷积和微步卷积,两者的区别在于padding 的方式不同,看看下面这张图片就可以明白了:

3. 训练方法

DCGAN 的训练方法跟GAN 是一样的,分为以下三步:

(1)for k steps:训练D 让式子【logD(x) + log(1 – D(G(Z)) (G keeps still)】的值达到最大

(2)保持D 不变,训练G 使式子【logD(G(z))】的值达到最大

(3)重复step(1)和step(2)直到G 与D 达到纳什均衡

4. 相比于GAN 的改进

DCGAN 相比于GAN 或者是普通CNN 的改进包含以下几个方面:

(1)使用卷积和去卷积代替池化层

(2)在生成器和判别器中都添加了批量归一化操作

(3)去掉了全连接层,使用全局池化层替代

(4)生成器的输出层使用Tanh 激活函数,其他层使用RELU

(5)判别器的所有层都是用LeakyReLU 激活函数

5. 漫游隐空间

通过使用插值微调噪音输入z 的方式可以导致隐空间结构发生变化从而引导生成图像发生语义上的平滑过度,比如说从有窗户到没窗户,从有电视到没电视等等。

6. 语义遮罩

通过标注窗口,并判断激活神经元是否在窗口内的方式来找出影响窗户形成的神经元,将这些神经元的权重设置为0,那么就可以导致生成的图像中没有窗户。从下图可以看到,上面一行图片都是有窗户的,下面一行通过语义遮罩的方式拿掉了窗户,但是空缺的位置依然是平滑连续的,使整幅图像的语义没有发生太大的变化。

7. 矢量算法

在向量算法中有一个很经典的例子就是【vector(“King”) – vector(“Man”) + vector(“Woman”) = vector(“Queue”)】,作者将该思想引入到图像生成当中并得到了以下实验结果:【smiling woman – neutral woman + neutral man = smiling man】

leetcodeday4寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
示例 3:

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:

输入:nums1 = [], nums2 = [1]
输出:1.00000

第一次解题:

# @lc code=start
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums3=sorted(nums1+nums2)
        length= len(nums3)
        return nums3[length//2] if length%2==1 else (nums3[length//2]+nums3[length//2-1])/2

# @lc code=end

占用的内存太高了….

改进:

不需要合并两个有序数组,只要找到中位数的位置即可。由于两个数组的长度已知,因此中位数对应的两个数组的下标之和也是已知的。维护两个指针,初始时分别指向两个数组的下标 0 的位置,每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。

class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
def getKthElement(k):
“””
– 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
– 这里的 “/” 表示整除
– nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
– nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
– 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
– 这样 pivot 本身最大也只能是第 k-1 小的元素
– 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums1 数组
– 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums2 数组
– 由于我们 “删除” 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
“””

        index1, index2 = 0, 0
        while True:
            # 特殊情况
            if index1 == m:
                return nums2[index2 + k - 1]
            if index2 == n:
                return nums1[index1 + k - 1]
            if k == 1:
                return min(nums1[index1], nums2[index2])

            # 正常情况
            newIndex1 = min(index1 + k // 2 - 1, m - 1)
            newIndex2 = min(index2 + k // 2 - 1, n - 1)
            pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
            if pivot1 <= pivot2:
                k -= newIndex1 - index1 + 1
                index1 = newIndex1 + 1
            else:
                k -= newIndex2 - index2 + 1
                index2 = newIndex2 + 1

    m, n = len(nums1), len(nums2)
    totalLength = m + n
    if totalLength % 2 == 1:
        return getKthElement((totalLength + 1) // 2)
    else:
        return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2

python中的取整:

  1. 向上取整:math.ceil()
  2. 向下取整:math.floor()、整除”//”
  3. 四舍五入:round()——奇数向远离0取整,偶数去尾取整;或言之:奇数进位,偶数去尾
  4. 向0取整:int()
  • 向上取整:math.ceil()
import math

math.ceil(-0.5)
>>> 0
 
math.ceil(-0.9)
>>> 0

math.ceil(0.3)
>>> 1

  • 向下取整:math.floor()、整除”//”
  • 
    math.floor(-0.3)
    >>> -1
     
    math.floor(0.9)
    >>> 0
    
    
    (-1) // 2  # -0.5
    >>> -1
     
    (-3) // 2  # -1.5
    >>> -2
     
    1 // 2    # 0.5 
    >>> 0
     
    3 // 2    # 1.5
    

    int()

    int(-0.5)>>> 0 int(-0.9)>>> 0 int(0.5)>>> 0 int(0.9)>>> 0一句话总结:int()函数是“向0取整”,取整方向总是让结果比小数的绝对值更小

  • round()
  • 
    round(-2.5)
    >>> -2
     
    round(-1.5)
    >>> -2
     
    round(-0.5)
    >>> 0
     
    round(0.5)
    >>> 0
     
    round(1.5)
    >>> 2
     
    

    C++函数(重点)

    • 函数和数组
    • 函数和结构
    • 函数和指针
    • 函数和string
    • 函数递归

    函数定义:

    typename functionnname(parameterlist){
         statements;
         return value;
    }

    函数原型:如果函数定义在函数调用之后,必须在函数调用前进行函数原型声明,函数原型描述了函数到编译器的接口,将函数返回值类型、参数类型和数量告诉编译器。 函数原型是一条语句,就是函数定义中的函数头,此外,在原型的参数中不要求提供变量名,有类型列表就足够了。

    函数参数和按值传递:

    函数和数组:

    1、输入为数组:

    定义:
    int sum_array (int array[],int n)
    实际情况是array是一个指针,在函数内部,array可以看成是数组
    调用:
    sum_array(cooke,4) 其中cooke是数组名

    2、使用指针处理数组:

    数组名==指针
    
    
    定义:
    int sum_array (int* array,int n)
    调用:
    sum_array(cooke,4) 其中cooke是数组名

    3、使用 输入为数组区间的函数:

     int sum_array (const int* start,const int* end ) 
    调用:
    
    sum_array(cooke,cooke+3) 其中cooke是数组名

    指针和const:

    将const用于指针有很微妙的地方,第一种方法是让指针指向一个常量对象,这样可以防止该指针来修改所指向的值,第二种方法是将指针本身声明为常量,防止改变指针指向的位置。

    但pt的声明并不意味着它指向的值实际上就是一个常量,而只意味着队医pt来说,是一个常量,因此可以通过修改age来 改变age值。

    注意:c++禁止将const地址付给非const的指针,除非使用强制准换。

    函数和二维数组:

    定义函数: 注意这个[4]中的数字必不可少
    int sum_array (int array[][4],int n)
    调用:
    
    sum_array (cooke,4)
    
    or
    定义:array是一个由4个指向int的指针组成的数组
    int sum_array (int (*array)[4],int n)
    

    函数和c风格字符串

    表示字符串方法有三种:

    char数组 、用括号引起的字符串常量、被设置为字符串的地址的char指针。

    字符串作为参数来传递,实际传递的是字符串的第一个字符的地址,因此,需要在函数中将表示字符串的形参声明为char * 类型

    int 函数名(const char * str,char ch)

    函数返回c风格字符串的函数

    在函数内部 声明一个字符串指针 : char * x = char [n+1],然后return x

    函数和结构:

    定义结构:

    struct tracal{
        int x;
        char y;
    }
    声明函数原型:
    tracal sum(tracal x)
    也可以传递结构的地址:
    tracal sum(tracal *x)
    
    

    函数和string 对象 array对象

    函数递归:

    函数指针:

    函数也有地址,可以通过指针获取到函数的地址,也可以通过函数指针调用函数

    1. 获取函数的地址:获取 函数地址很简单 ,只要 使用函数名(不加参数即可),例如think()是一个函数名,那么函数地址就是think。

    如果要将函数作为参数进行传递,必须是传递函数名;

    1. 声明函数指针:
    对于函数:double pam(int)
    声明一个函数指针:
    double (*pf) (int)
    和函数声明类似,不过是将函数名改为指针(加括号),
    
    (*pf) (int)意味着是一个 指向函数的指针
    
    
    1. 使用函数指针调用函数

    double (*pf) (int) 定义之后

    令: pf = pam(函数名)

    调用:x=(*pf)(5)

    typeof 别名

    python 中的序列

    python中序列是最基本的数据结构,他是一块用于存放多个值的连续内存空间。python中内置了5个常用的序列结构,分别是列表、元组、集合、字典和字符串

    1、序列:存放多个值的连续内存空间。

    序列包括:列表[ ],元组 ( ),字典{a:b},集合set={ a,b},字符串:”asdc”

    其中:集合和字典不支持索引、切片、相加和相乘操作。

    序列索引:正序索引,从0开始到n-1,倒序索引:从-(n-1)开始,到-1结束

    切片:name[start:end:step]

    相加:name1+name2

    乘法: name*2 表示name+name

    空序列: [None]

    检测某个元素是否是序列的成员: x in sequence

    计算序列的长度、最大小值: len( )\、max( )、 min( )

    其他内置函数:

    list()将序列转换为list
    str()将序列转换为字符串
    sum()计算元素和
    sorted()排序
    reversed()将原序列元素反向
    enumerate()将序列组合为一个索引序列

    python 中查看数据类型:

    内置函数isinstance(object, (type1,type2…))
    isinstance(‘content’, str)
    返回True or False

    使用内置函数type(object)
    print(type(1))
    print(type(‘content’))

    2、列表 list

    列表中的元素可以是任意各种类型的组合。

    list(seqence ) 将序列转换为列表

    listname=[a,v,bd,d]

    访问序列元素 :

    for i in list:

    for index, item in enumerate(list):

    enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

    enumerate(sequence, [start=0])
    sequence -- 一个序列、迭代器或其他支持迭代对象。
    start -- 下标起始位置。

    添加元素: list.append(“x”)

    添加多个元素: list.extend(seqence)

    根据元素值删除元素: list.remove(value)

    统计指定元素出现次数:list.count(obj)

    获取指定元素首次出现的下标:list.index(obj)

    统计元素和:sum(iterable[,start])

    start表示统计结果需要在 加上start值。 start — 指定相加的参数,如果没有设置这个值,默认为0。

    排序: list.sort(key=None,reverse=False)

    • key — 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。如设置key=str.lower,表示在排序时候不区分字母大小写。
    • reverse — 排序规则,reverse = True 降序, reverse = False 升序(默认)。
    # 获取列表的第二个元素
    def takeSecond(elem):
        return elem[1]
     
    # 列表
    random = [(2, 2), (3, 4), (4, 1), (1, 3)]
     
    # 指定第二个元素排序
    random.sort(key=takeSecond)

    个人理解:首先将list元素送到key定义的函数中 ,然后再把返回值进行排序。

    排序:使用内置的sorted函数: sorted(iterable,key=None,reverse=False)

    列表推导式:

    list=[expression for x in range]

    range是 range ()函数生成的range对象

    从x中筛选满足codition的x

    list = [expression for x in range if condition]

    二维列表:[[ ],[ ],[ ]]

    使用for循环创建:

    for i in range(4):   
       arr.append([])
       for j in range(5):
          arr[i].append[j]
    
    

    列表推导式创建:

    arr = [[j for j in range(6)] for i in range(4)]

    3、元组:不可变的列表,定义以后不可修改,只能重新定义

    A=(1,2,3,4)

    tuple 元组:不可修改,在python中,元组使用一对小括号将所有元素括起来,但小括号并不是必须的只需要将一组值用逗号分隔开,python就可以视为元组 B=1,2,3,4,5 也是元组

    创建元组 tuple(data)

    题外话:print(“ddd”,end=” “) //表示不换行输出

    因为元组不可修改,因此需要对元组中某个元素修改时需要重新定义元组:

    A=(“W”,”Q”)

    A=(“w”,”q”)

    元组推导式:和list类似,将[ ]变为( ),并在最外面还要嵌套一层tuple( ):

    x= (i for i in range) //这句生成的结果不是一个元组,而是一个生成器对象,因此需要千年套一层tuple()

    tuple(x)就是元组

    4、字典 dict( ) == c++中的Map

    dictionary={‘key’:’value’}

    创建字典:

    dictionary = dict(zip(list1,list2))

    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

    dictionary = dict(‘key1’=’value1′,’key2’=’value2’,….)

    创建值为空的字典: dictionary = dict.fromkeys(list)

    获取指定键的值:

    dictionary.get(key[,default])

    遍历字典:

    dictionary.item() 返回“键-值”列表

    for item  in  dictionary.item(): 获得每一个键值对元组:(key,value)
    for key,value  in  dictionary.item(): 获得每一个键-值

    字典推导式:

    dictionary = {i:j for i,j in zip(list1,list2)}

    5、 集合 set():{ }集合中元素不重复,用于自动去重

    创建set :x = {1,2,3 }

    y=set(iteration)

    创建空集合 z =set( )

    集合的增删:

    setname.add(element) //添加元素

    setname.remove(element) //删除 元素

    setname.pop() //删除元素

    setname.clear() //清空set

    集合的交并差补运算: & | – ^

    实例: c = set1&set2

    6、字符串str()

    拼接字符串:+

    转换字符串 str(num)

    计算字符串长度 len(str)

    计算字符串所占字节数: len(str.encode())

    字符串编解码 str.encode() str.decode()

    截取字符串 string[start:end:step] (前闭后开)

    分割字符str.split(seq,maxsplit)

      seq — 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。

    maxsplit 分割次数。默认为 -1, 即分隔所有。

    合并字符:string.jion(iterable) // string 合并符,用于合并时候的分隔符

    检索字符串

    str.count(sub[,start,end]) //统计sub出现的次数

    str.find (sub[,start,end])// 检索是否存在sub,不存在返回-1,否则返回首次出现的位置

    str.index (sub[,start,end])// 检索是否存在sub , 返回首次出现的位置 ,如不存在会报异常

    str.startwith(prefix[,start,end]) //判断是否以prefix开头,返回true or flase

    str.endwith(prefix[,start,end]) //判断是否以prefix结尾,返回true or flase

    大小写转换: str.lower() str.upper()

    去除字符串中的空格和特殊字符

    str.strip([chars]) // chars :要去除的字符,默认是去除空格、制表符\t,回车符\r 换行符\n

    str.lstrip([chars])

    str.rstrip([chars])