喜欢猫吗?用这个开源工具撸一只吧!-深蓝源码网


时间: 2020-09-03 00:08:26 人气: 2301 评论: 0

【导读】我们身边总是不乏各种各样的撸猫人士,面对朋友圈一波又一波晒猫的浪潮,作为学生狗和工作狗的我们只有羡慕的份,更流传有 “吸猫穷三代,撸猫毁一生?” 的名言,今天小编就为广大爱猫人士发放一份福利,看看如何用 AI 来生成猫的图片?

用 DCGAN 生成的猫图片示例

领军研究员 Yann Lecun 称生成式对抗网络( Generative Adverserial Networks, GAN )是 “过去 20 年里机器学习中最棒的想法”。因为这种网络结构的出现,我们才能在今天搭建一个可以生成栩栩如生的猫图片的 AI 系统。这是不是很令人振奋?

DCGAN 的训练过程

完整代码(Github):

https://gist.github.com/simoninithomas/c7d1e80810ef838330d7dab068d6b26f#file-training-py

如果你使用过 Python、Tensorflow,学习过深度学习、CNNs(卷积神经网络),将对理解代码大有裨益。

▌什么是 DCGAN?

深度卷积生成对抗网络(Deep Convolutional Generative Adverserial Networks,DCGAN)是一种深度学习架构,它会生成和训练集中数据相似的结果。

这一模型用卷积层代替了生成对抗网络(GAN)模型中的全连接层。

为了解释 DCGAN 是如何运行的,我们用艺术专家和冒牌专家来做比喻。

冒牌专家( 即 “生成器” )企图模仿梵高的画作生成图片并把它当做真实的梵高作品。

而另一边,艺术专家( 即 “分类器” )试图利用它们对梵高画作的了解来识别出赝品( 即生成图片 )。

随着时间推移,艺术专家鉴别赝品的技术不断长进,冒牌专家仿作的能力也不断提高。

如我们所见,DCGANs 由两个互相对抗的深度神经网络组成。

  • 生成器是一个仿造者,生成和真实数据相似的结果。它本身不知道真实数据是什么样,但会从另一个模型的反馈信息中学习和调整。
  • 分类器是一个检测者,通过与真实数据比较来确定伪造数据(即模型生成的图片),但尽力不对真实数据报错。这一部分会为生成器的反向传播服务。

DCGAN 工作流程示例

  • 生成器会加入随机噪声向量,生成图片;
  • 这张图片被输入给分类器,和训练集进行比较;
  • 最后分类器返回一个 0(伪造图像)和 1(真实图像)之间的数字。

▌让我们来创建 DCGAN 吧!

现在,我们可以准备创建 AI 了。

在这部分,我们将关注模型的主要元素。若你想看所有代码,请点这里的 notebook(https://github.com/simoninithomas/CatDCGAN/blob/master/Cat%20DCGAN.ipynb)。

输入部分

先创建输入占位符:分类器:inputs_real,生成器:inputs_z。

注意,我们用两个学习率,一个是生成器的学习率,一个是分类器的学习率。

DCGANs 对超参数特别敏感,所以精确调参尤其重要。

def model_inputs(real_dim, z_dim):
    """    Create the model inputs
    :param real_dim: tuple containing width, height and channels
    :param z_dim: The dimension of Z
    :return: Tuple of (tensor of real input images, tensor of z data, learning rate G, learning rate D)
    """    # inputs_real for Discriminator
    inputs_real = tf.placeholder(tf.float32, (None, *real_dim), name='inputs_real')
  
    # inputs_z for Generator
    inputs_z = tf.placeholder(tf.float32, (None, z_dim), name="input_z")
    
    # Two different learning rate : one for the generator, one for the discriminator
    learning_rate_G = tf.placeholder(tf.float32, name="learning_rate_G")
    
    learning_rate_D = tf.placeholder(tf.float32, name="learning_rate_D")
    
    return inputs_real, inputs_z, learning_rate_G, learning_rate_D

分类器和生成器

我们用函数 tf.variable_scope 的原因有两个:

  • 第一,我们想要保证所有变量名称都以 generator 或 discriminator 开头,这将为我们之后训练两个网络提供帮助。
  • 第二,我们要用不同的输入重复训练网络:对于生成器,既要训练它,也要在训练后从生成图像中采样;对于分类器,我们需要在生成图像和真实图像间共用变量。

我们先来创建分类器。记住,要用真实或生成图像作为输入,然后输出分数。

需要注意的技术点:

  • 关键点是在每个卷积层加倍过滤器的尺寸;
  • 不建议进行下采样,我们只用一定步长的卷积层;
  • 每层都使用 batch 标准化(输入层除外),因为它会减小协方差转变。想了解更多信息的话请看这篇文章(https://medium.com/@ageitgey/machine-learning-is-fun-80ea3ec3c471)。
  • 我们用 Leaky ReLU 作为激活函数,因为它能帮助避免梯度消失问题。
def discriminator(x, is_reuse=False, alpha = 0.2):
    ''' Build the discriminator network.
        Arguments
        ---------
        x : Input tensor for the discriminator
        n_units: Number of units in hidden layer
        reuse : Reuse the variables with tf.variable_scope
        alpha : leak parameter for leaky ReLU
        Returns
        -------
        out, logits:
    '''
    with tf.variable_scope("discriminator", reuse = is_reuse):
        # Input layer 128*128*3 --> 64x64x64
        # Conv --> BatchNorm --> LeakyReLU  
        conv1 = tf.layers.conv2d(inputs = x,
                                filters = 64,
                                kernel_size = [5,5],
                                strides = [2,2],
                                padding = "SAME",
                                kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                name='conv1')
        batch_norm1 = tf.layers.batch_normalization(conv1,
                                                   training = True,
                                                   epsilon = 1e-5,
                                                     name = 'batch_norm1')
        conv1_out = tf.nn.leaky_relu(batch_norm1, alpha=alpha, name="conv1_out")
        # 64x64x64--> 32x32x128
        # Conv --> BatchNorm --> LeakyReLU  
        conv2 = tf.layers.conv2d(inputs = conv1_out,
                                filters = 128,
                                kernel_size = [5, 5],
                                strides = [2, 2],
                                padding = "SAME",
                                kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                name='conv2')
        batch_norm2 = tf.layers.batch_normalization(conv2,
                                                   training = True,
                                                   epsilon = 1e-5,
                                                     name = 'batch_norm2')
        conv2_out = tf.nn.leaky_relu(batch_norm2, alpha=alpha, name="conv2_out")
        # 32x32x128 --> 16x16x256
        # Conv --> BatchNorm --> LeakyReLU  
        conv3 = tf.layers.conv2d(inputs = conv2_out,
                                filters = 256,
                                kernel_size = [5, 5],
                                strides = [2, 2],
                                padding = "SAME",
                                kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                name='conv3')
        batch_norm3 = tf.layers.batch_normalization(conv3,
                                                   training = True,
                                                   epsilon = 1e-5,
                                                name = 'batch_norm3')
        conv3_out = tf.nn.leaky_relu(batch_norm3, alpha=alpha, name="conv3_out")
        # 16x16x256 --> 16x16x512
        # Conv --> BatchNorm --> LeakyReLU  
        conv4 = tf.layers.conv2d(inputs = conv3_out,
                                filters = 512,
                                kernel_size = [5, 5],
                                strides = [1, 1],
                                padding = "SAME",
                                kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                name='conv4')
        batch_norm4 = tf.layers.batch_normalization(conv4,
                                                   training = True,
                                                   epsilon = 1e-5,
                                                name = 'batch_norm4')
        conv4_out = tf.nn.leaky_relu(batch_norm4, alpha=alpha, name="conv4_out")
        # 16x16x512 --> 8x8x1024
        # Conv --> BatchNorm --> LeakyReLU  
        conv5 = tf.layers.conv2d(inputs = conv4_out,
                                filters = 1024,
                                kernel_size = [5, 5],
                                strides = [2, 2],
                                padding = "SAME",
                                kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                name='conv5')
        batch_norm5 = tf.layers.batch_normalization(conv5,
                                                   training = True,
                                                   epsilon = 1e-5,
                                                name = 'batch_norm5')
        conv5_out = tf.nn.leaky_relu(batch_norm5, alpha=alpha, name="conv5_out")
        # Flatten it
        flatten = tf.reshape(conv5_out, (-1, 8*8*1024))
        # Logits
        logits = tf.layers.dense(inputs = flatten,
                                units = 1,
                                activation = None)
        out = tf.sigmoid(logits)
        return out, logits

再来创建生成器。记住,用随机噪声向量(z)作为输入,根据转置的卷积层输出生成图像。

其主要思想是在每层将过滤器尺寸减半,而将图片尺寸加倍。研究已经发现,用 tanh 作为输出层的激活函数时,生成器的表现最好。

def generator(z, output_channel_dim, is_train=True):
    ''' Build the generator network.
        Arguments
        ---------
        z : Input tensor for the generator
        output_channel_dim : Shape of the generator output
        n_units : Number of units in hidden layer
        reuse : Reuse the variables with tf.variable_scope
        alpha : leak parameter for leaky ReLU
        Returns
        -------
        out:
    '''
    with tf.variable_scope("generator", reuse= not is_train):
        # First FC layer --> 8x8x1024
        fc1 = tf.layers.dense(z, 8*8*1024)
        # Reshape it
        fc1 = tf.reshape(fc1, (-1, 8, 8, 1024))
        # Leaky ReLU
        fc1 = tf.nn.leaky_relu(fc1, alpha=alpha)
        # Transposed conv 1 --> BatchNorm --> LeakyReLU
        # 8x8x1024 --> 16x16x512
        trans_conv1 = tf.layers.conv2d_transpose(inputs 
						 技术沙龙 教程文章 热点综合					

评论