时间: 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(卷积神经网络),将对理解代码大有裨益。
深度卷积生成对抗网络(Deep Convolutional Generative Adverserial Networks,DCGAN)是一种深度学习架构,它会生成和训练集中数据相似的结果。
这一模型用卷积层代替了生成对抗网络(GAN)模型中的全连接层。
为了解释 DCGAN 是如何运行的,我们用艺术专家和冒牌专家来做比喻。
冒牌专家( 即 “生成器” )企图模仿梵高的画作生成图片并把它当做真实的梵高作品。
而另一边,艺术专家( 即 “分类器” )试图利用它们对梵高画作的了解来识别出赝品( 即生成图片 )。
随着时间推移,艺术专家鉴别赝品的技术不断长进,冒牌专家仿作的能力也不断提高。
如我们所见,DCGANs 由两个互相对抗的深度神经网络组成。
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 的原因有两个:
我们先来创建分类器。记住,要用真实或生成图像作为输入,然后输出分数。
需要注意的技术点:
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 技术沙龙 教程文章 热点综合