机器学习中的DCGAN-Tensorflow:用于更稳定的训练
自从Ian Goodfellow的论文以来,GAN已应用于许多领域,但其不稳定性一直存在问题。GAN必须解决极小极大(鞍点)问题,因此这个问题是固有的。
马鞍点的滑稽表示
许多研究人员试图通过各种方法解决GAN的这些困境。其中DCGAN已经取得了显著的成果。DCGAN提出了稳定的GAN网络结构。如果您根据本文的指导设计模型,可以看出模型训练是稳定的。
稳定的深度卷积GANs的体系结构指南
•用 strided convolutions(判别器)和 fractional-strided convolutions (生成器)替换任何池化层 。
• 在生成器和判别器中使用 batchnorm。
• 删除完全连接的隐藏层以获得更深层次的架构。
• 除了使用Tanh之外, 对生成器中的所有层使用ReLU激活 。
• 在判别器中对所有层使用LeakyReLU激活 。
生成器
DCGAN生成器的结构
首先,生成器项目和重塑噪声分布(100-dim阵列)到4x4x1024特征映射。我们使用matmul和reshape函数来实现它。然后,我们使用一系列四个fractionally-strided函数卷积(conv2d_transpose)来逐步创建64x64图像。如上所述,batchnorm放置在每层的末尾,并且除输出之外,relu作为激活函数。我们使用tanh作为输出,所以输出的像素范围是[-1,1]。
def generator(self, input, reuse = False): with tf.variable_scope("generator") as scope: if reuse: scope.reuse_variables() G_FW1 = tf.get_variable('G_FW1', [self.n_noise, self.n_hidden], initializer = tf.random_normal_initializer(stddev=0.01)) G_Fb1 = tf.get_variable('G_Fb1', [self.n_hidden], initializer = tf.constant_initializer(0)) G_W1 = tf.get_variable('G_W1', [5,5,self.n_W2, self.n_W1], initializer = tf.truncated_normal_initializer(stddev=0.02)) G_W2 = tf.get_variable('G_W2', [5,5,self.n_W3, self.n_W2], initializer = tf.truncated_normal_initializer(stddev=0.02)) G_W3 = tf.get_variable('G_W3', [5,5,self.n_W4, self.n_W3], initializer = tf.truncated_normal_initializer(stddev=0.02)) G_W4 = tf.get_variable('G_W4', [5,5,self.image_channels, self.n_W4], initializer = tf.truncated_normal_initializer(stddev=0.02)) hidden = tf.nn.relu( tf.matmul(input, G_FW1) + G_Fb1) hidden = tf.reshape(hidden, [self.batch_size, 4,4,self.n_W1]) dconv1 = tf.nn.conv2d_transpose(hidden, G_W1, [self.batch_size, 8, 8, self.n_W2], [1, 2, 2, 1]) dconv1 = tf.nn.relu(tf.contrib.layers.batch_norm(dconv1,decay=0.9, epsilon=1e-5)) dconv2 = tf.nn.conv2d_transpose(dconv1, G_W2, [self.batch_size, 16, 16, self.n_W3], [1, 2, 2, 1]) dconv2 = tf.nn.relu(tf.contrib.layers.batch_norm(dconv2,decay=0.9, epsilon=1e-5)) dconv3 = tf.nn.conv2d_transpose(dconv2, G_W3, [self.batch_size, 32, 32, self.n_W4], [1, 2, 2, 1]) dconv3 = tf.nn.relu(tf.contrib.layers.batch_norm(dconv3,decay=0.9, epsilon=1e-5)) dconv4 = tf.nn.conv2d_transpose(dconv3, G_W4, [self.batch_size, 64, 64, self.image_channels], [1, 2, 2, 1]) #dconv4 = tf.nn.relu(tf.layers.batch_normalization(dconv3, training = 'True')) output = tf.nn.tanh(dconv4) return output
判别器
本文中使用的判别器与生成器具有对称结构。与生成器相反,它通过减小图像大小来训练特征映像。所以我们使用了步幅大小为2的conv2d。与生成器相同,batchnorm放置在每层的末尾。但是leaky relu被用作激活函数。
def discriminator(self, input, reuse = False): with tf.variable_scope("discriminator") as scope: if reuse: scope.reuse_variables() D_W1 = tf.get_variable('D_W1', [5,5,self.image_channels, self.n_W5], initializer = tf.truncated_normal_initializer(stddev=0.02)) D_W2 = tf.get_variable('D_W2', [5,5,self.n_W5, self.n_W4], initializer = tf.truncated_normal_initializer(stddev=0.02)) D_W3 = tf.get_variable('D_W3', [5,5,self.n_W4, self.n_W3], initializer = tf.truncated_normal_initializer(stddev=0.02)) D_W4 = tf.get_variable('D_W4', [5,5,self.n_W3, self.n_W2], initializer = tf.truncated_normal_initializer(stddev=0.02)) D_FW1 = tf.get_variable('D_FW1', [4*4*self.n_W2, 1], initializer = tf.random_normal_initializer(stddev=0.01)) D_Fb1 = tf.get_variable('D_Fb1', [1], initializer = tf.constant_initializer(0)) conv1 = tf.nn.conv2d(input, D_W1, strides = [1, 2, 2, 1], padding='SAME') conv1 = tf.nn.leaky_relu(conv1, alpha = 0.2) conv2 = tf.nn.conv2d(conv1, D_W2, strides = [1, 2, 2, 1], padding='SAME') conv2 = tf.nn.leaky_relu(tf.contrib.layers.batch_norm(conv2, decay=0.9, epsilon=1e-5), alpha = 0.2) conv3 = tf.nn.conv2d(conv2, D_W3, strides = [1, 2, 2, 1], padding='SAME') conv3 = tf.nn.leaky_relu(tf.contrib.layers.batch_norm(conv3, decay=0.9, epsilon=1e-5), alpha = 0.2) conv4 = tf.nn.conv2d(conv3, D_W4, strides = [1, 2, 2, 1], padding='SAME') conv4 = tf.nn.leaky_relu(tf.contrib.layers.batch_norm(conv4, decay=0.9, epsilon=1e-5), alpha = 0.2) hidden = tf.reshape(conv4, [self.batch_size, 4*4*self.n_W2]) output = tf.nn.sigmoid( tf.matmul(hidden, D_FW1) + D_Fb1) return output
损失函数和优化器
损失函数和优化器与基本GAN相同。然而,根据该DCGAN论文的(对抗性训练的细节)中,我们设置优化器的学习率是0.0002和bata1为0.5。
def loss(self, X, Z): g_out = self.generator(Z) d_fake = self.discriminator(g_out, reuse = False) d_real = self.discriminator(X, reuse = True) d_loss = tf.reduce_mean(tf.log(d_real) + tf.log(1 - d_fake)) g_loss = tf.reduce_mean(tf.log(d_fake)) return d_loss, g_loss def optimizer(self, d_loss, g_loss, learning_rate): d_var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='discriminator') g_var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='generator') #print('G_var_list:', len(G_var_list)) #print('D_var_list:', len(D_var_list)) d_opt = tf.train.AdamOptimizer(learning_rate, beta1 = 0.5).minimize(-d_loss, var_list=d_var_list) g_opt = tf.train.AdamOptimizer(learning_rate, beta1 = 0.5).minimize(-g_loss, var_list=g_var_list) return d_opt, g_opt
结果
数据集是从kaggle下载的。数据集下载地址(https://www.kaggle.com/scolianni/mnistasjpg)
DCGAN
BasicGAN
使用mnist数据集训练了DCGAN和basicGAN模型。DCGAN生成比basicGAN更干净的图像。当然,上述结果似乎是合理的,因为DCGAN具有比BasicGAN更复杂的结构并且具有更多参数。但是DCGAN中引入的方法甚至可以稳定地学习复杂的模型。这是DCGAN的贡献。
此外,我还使用Celeb_A数据集训练了两个模型,其中有大约200k肖像照片。数据集下载地址(https://www.kaggle.com/jessicali9530/celeba-dataset)
DCGAN
BasicGAN
我们为DCGAN和BasicGAN模型训练了9个周期。DCGAN不仅可以产生清晰的图像,还可以表现出各种特征,如眼镜,妆容和胡须。但它仍然看起来不自然。
这就是利用深度学习来实现人脸的创造。其他深度学习模型(如变分自编码器和自回归模型)也是生成模型的好示例。