GAN系列:论文阅读 & 代码阅读——DCGAN(Deep Convolutional Generative Adversarial Networks)

本文介绍了DCGAN(Deep Convolutional Generative Adversarial Networks)的基本原理,结合代码详细探讨了如何将CNN应用于GAN网络中。通过分析论文《Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks》,讨论了网络结构的改进,如全卷积网络、批量归一化以及激活函数的选择。同时,文章提供了针对MNIST和celebA数据集的代码实现,展示了DCGAN在有条件和无条件生成任务中的应用,并解释了不同数据集的网络结构差异。此外,文章还探讨了训练过程和可视化技术,帮助理解每个维度的z向量如何影响生成图像的特定属性。
摘要由CSDN通过智能技术生成

DCGAN也是GAN领域内比较经典的内容,涉及到了GAN网络结构上的很多改变,所以结合代码看了看论文。

论文:UNSUPERVISED REPRESENTATION LEARNING WITH DEEP CONVOLUTIONAL GENERATIVE ADVERSARIAL NETWORKS

在上一篇论文Generative Adversarial Networks中,主要学习的是GAN的原理和对抗思想,对于生成器和判别器的网络结构没有仔细研究。因此借这篇论文看一看CNN最开始是如何融入到GAN中的(其实是因为看了天池最近的一个比赛,发现给的示例中判别器用了Inceptionv1等网络,但自己没什么改进的方向,还是对GAN的具体内容了解不多)。

代码来自:https://github.com/carpedm20/DCGAN-tensorflow

这里有两个数据集:MNIST和celebA,分别对应有无condition的情况。

该论文最重要的就是对GAN的网络结构进行了修改:生成器和判别器中都以卷积为主(生成器中要升维,所以是反卷积;判别器中降维,所以是普通卷积),卷积的特性使网络提取到很好的特征。带步长的卷积还可以取代池化实现下采样,池化是一个非参数的过程,但卷积是一个带参数的可学习的过程。因为卷积层的存在,生成器中必须把z向量变成具有图像空间结构的矩阵,这里用reshape实现。

注:论文中说舍弃了全连接层,判别器和生成器都是全卷积网络,但个人觉得linear的操作和全连接层没什么区别啊,不太明白。

除此之外,生成器和判别器中每一层都加入了BN(除了判别器的输入层和生成器的输出层),该方法一定程度上避免了梯度消失。同时,激活函数的选择也能够改善结果,生成器中除了输出层全部用relu激活,判别器中则用leaky relu。接下来对不同的数据集所对应的网络进行分析:

若是输入数据集为MNIST,用函数load_mnist获取并处理图像数据,返回值为像素值介于0-1的图像矩阵(每张图像为一行,共70000行)和经过one-hot编码的标签(也是70000行,每行10个元素分别表示10个类别),load_mnist函数代码如下:

 def load_mnist(self):
    # 获取数据保存的文件夹目录
    data_dir = os.path.join(self.data_dir, self.dataset_name)
    
    # 获取训练集图像
    fd = open(os.path.join(data_dir,'train-images-idx3-ubyte'))
    loaded = np.fromfile(file=fd,dtype=np.uint8)
    # 从第16个数字开始才是图片?loaded中是一维向量
    # 60000张图片,28*28*1的尺寸
    trX = loaded[16:].reshape((60000,28,28,1)).astype(np.float)

    # 获取训练集标签
    fd = open(os.path.join(data_dir,'train-labels-idx1-ubyte'))
    loaded = np.fromfile(file=fd,dtype=np.uint8)
    # 标签从第八位开始
    trY = loaded[8:].reshape((60000)).astype(np.float)

    # 测试集图片
    fd = open(os.path.join(data_dir,'t10k-images-idx3-ubyte'))
    loaded = np.fromfile(file=fd,dtype=np.uint8)
    teX = loaded[16:].reshape((10000,28,28,1)).astype(np.float)

    # 测试集标签
    fd = open(os.path.join(data_dir,'t10k-labels-idx1-ubyte'))
    loaded = np.fromfile(file=fd,dtype=np.uint8)
    teY = loaded[8:].reshape((10000)).astype(np.float)

    # 把标签转换为ndarray形式
    trY = np.asarray(trY)
    teY = np.asarray(teY)
    
    # 把训练集和测试集串联
    X = np.concatenate((trX, teX), axis=0)
    # 标签转换为int
    y = np.concatenate((trY, teY), axis=0).astype(np.int)
    
    # 将图像和标签按同样的随机性打乱,仍是一一对应的
    seed = 547
    np.random.seed(seed)
    np.random.shuffle(X)
    np.random.seed(seed)
    np.random.shuffle(y)
    
    # y_dim = 10
    y_vec = np.zeros((len(y), self.y_dim), dtype=np.float)
    # y_vec是标签one-hot编码结果
    for i, label in enumerate(y):
      y_vec[i,y[i]] = 1.0
    
    # 把图像像素值归一到0-1
    return X/255.,y_vec

标签作为condition共同输入生成器中,相当于监督学习,生成器具体结构如代码所示:

        s_h, s_w = self.output_height, self.output_width
        s_h2, s_h4 = int(s_h/2), int(s_h/4)
        s_w2, s_w4 = int(s_w/2), int(s_w/4)

        # yb = tf.expand_dims(tf.expand_dims(y, 1),2)
        yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
        z = concat([z, y], 1)

        # gfc_dim:fc dimension for generator
        # 把z扩展到全连接层的长度
        h0 = tf.nn.relu(
            self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin')))
        h0 = concat([h0, y], 1)

        h1 = tf.nn.relu(self.g_bn1(
            linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin')))
        h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])

        h1 = conv_cond_concat(h1, yb)

        h2 = tf.nn.relu(self.g_bn2(deconv2d(h1,
            [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2')))
        h2 = conv_cond_concat(h2, yb)

        return tf.nn.sigmoid(
            deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))

根据代码画出了下图,其中s_h=s_w=28,c_dim=1因为是灰度图像。根据下图一行行分析:生成器的输入是100维的随机向量z与10维的condition进行concatenate的结果(因此有110列),首先通过矩阵进行线性变换升维至gfc_dim=1024(这个1024就是一般的全连接层的神经元个数),然后和condition进行concatenate变成1034维,再次通过矩阵线性变换升维至(gf_dim*2*s_h4*s_w4),这个维度是根据最后的输出维度s_h和s_w决定的。这时将二维矩阵进行reshape变成符合图像矩阵的四维形式[batch_num, s_h4, s_w4, gf_dim*2],并再次与condition进行concatenate变成(gf_dim*2+10)。接下来通过两次反卷积扩大特征图,达到最后的图像尺寸[batch_num, s_h, s_w, c_dim],并最后利用sigmoid函数激活。

有一点疑问是,为什么condition每次都要进行concatenate?看了一下Conditional GAN的论文,只是在输入层进行了concatenate呀,不过打算实验看一下每一层都concatenate和只在输入concatenate的差距。

还要注意的是,MNIST的图片不经过裁剪(crop),保持本身28*28的大小。

对应的判别器代码为:

        yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
        x = conv_cond_concat(image, yb)

        h0 = lrelu(conv2d(x, self.c_dim + self.y_dim, name='d_h0_conv'))
        h0 = conv_cond_concat(h0, yb)

        h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim + self.y_dim, name='d_h1_conv')))
        h1 = tf.reshape(h1, [self.batch_size, -1])      
        h1 = concat([h1, y], 1)
        
        h2 = lrelu(self.d_bn2(linear(h1, self.dfc_dim, 'd_h2_lin')))
        h2 = concat([h2, y], 1)

        h3 = linear(h2, 1, 'd_h3_lin')
        
        return tf.nn.sigmoid(h3), h3

判别器的输出有两个,一个是未经过sigmoid分类的结果h3,一个是

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值