暑期学习 DCGAN 笔记
前言:在 GAN 的基础上,把经典GAN中的 G 和 D 换成了两个卷积神经网络(CNN),并不是直接替换, DCGAN 对 CNN 的结构做了一些改变。
DCGAN 的特点:
- 判别模型:使用带步长的卷积(strided convolutions)取代了空间池化(spatial pooling),容许网络学习自己的空间下采样(spatial downsampling)。
- 生成模型:使用微步幅卷积(fractional strided),容许它学习自己的空间上采样(spatial upsampling)。
- 激活函数: Leaky-ReLU
- Batch Normalization 批标准化:解决因糟糕的初始化引起的训练问题,使得梯度能传播更深层次。 Batch Normalization证明了生成模型初始化的重要性,避免生成模型崩溃:生成的所有样本都在一个点上(样本相同),这是训练GANs经常遇到的失败现象。
接下来将对这些概念做容易理解的说明。
一、DCGAN 的变化
深度学习中对图像处理应用最好的模型是 CNN,是否可以同时结合 CNN 与 GAN 的优势?
DCGAN 就是采用深度卷积的对抗生成网络。英文全称是(deep convolutional generative adversarial networks)。
传统的 CNN 结构特点:
- (回忆CNN):
http://blog.csdn.net/qq_23947237/article/details/77045819
………………………………………………………………………………………………卷积层: 提取特征
池化层: 特征压缩(下采样)
DCGAN 结构特点:
- 所有的池化层都改为卷积层: 只有卷积层;没有池化层(pooling 都用 convolution代替);移除 全连接层;
pooling用conv代替 对于 G 和 D 的不同意义:
- 判别模型 D: 该网络本来是要完成(0、1)分类预测任务的;CNN 非常擅长图像分类,然而在改进的 D 网络中缺少了pooling下采样压缩,因此,只能
让网络自己去学习如何 空间下采样。 - 生成模型 G: 在 GAN 中一个相当重要的模块,承担的责任是从无到有生成一个完整图像;而 CNN 一般是将完整图象数据压缩为提取特征;此时发现,G 网络在这里的任务过程刚好与传统 CNN 完全颠倒过来了,(此过程称之为“反卷积”),因此,与上一段刚好对应,
该网络需要自己学习如何 空间上采样。
- 上、下采样操作需要的参数:
stride (步长)
- pooling 层不涉及权重参数
w 、b ,故采样也不需要。
- 激活函数
- G:除了输出层外的所有层采用
ReLu ;输出层采用tanh 。- D:所有层上使用
leaky−ReLu ;
二、GAN 早期的问题
- 传统的 GAN在刚提出来的时候,经常出现的问题是:生成的样本都基于一个点(即,生成的样本们基本上非常相似,基本上没有差异性),最大原因在于生成的网络把所有的样本都收敛到一个点了。
如何解决这个问题,方案是一个简单的操作:Batch Normalization (批标准化,即在每一个阶段做一个阶段性的归一化)。它可以防止 G 网络把所有样本都收敛到同一个点。可以同时解决下面的问题:
- 初始化差
- 梯度传播中的问题(帮助梯度传播到每一层)
三、DCGAN 的网络结构
1、生成网络
100
z :随机初始化(噪音向量;100维)G(z) :噪音向量经过生成器最后转化成64∗64∗3 。project and reshape:转化的过程即与 CNN 相反,将噪音转化为类似特征图(feature map)的向量;100维的噪音向量
z 借助全连接层FC 转化成16384 维更大的向量,再reshape为4∗4∗1024 的特征图形式。(16384=4*4*1024)反卷积:转化的过程即与 卷积 相反,将向量变成图像数据格式,
4∗4∗1024 →8∗8∗512 ;长、宽大小为原来的2倍,特征图个数不断缩小。最终得到图像64∗64∗3 。
2、判别网络
- 一张
64∗64∗3 的图像输入到网络通过卷积得到二分类的结果。
四、DCGAN 的实现
1、 生成网络
和之前的 GAN 网络代码稍微不同的地方就是(全卷积)。
def get_generator(noise_img, output_dim, is_train=True, alpha=0.01):
with tf.variable_scope("generator", reuse=(not is_train)):
# 100 x 1 to 4 x 4 x 512
# 全连接层
layer1 = tf.layers.dense(noise_img, 4*4*512)
layer1 = tf.reshape(layer1, [-1, 4, 4, 512])
# batch normalization
layer1 = tf.layers.batch_normalization(layer1, training=is_train)
# Leaky ReLU
layer1 = tf.maximum(alpha * layer1, layer1)
# dropout
layer1 = tf.nn.dropout(layer1, keep_prob=0.8)
# 4 x 4 x 512 to 7 x 7 x 256
# padding='valid'代表不用填充;
layer2 = tf.layers.conv2d_transpose(layer1, 256, 4, strides=1, padding='valid')
layer2 = tf.layers.batch_normalization(layer2, training=is_train)
layer2 = tf.maximum(alpha * layer2, layer2)
layer2 = tf.nn.dropout(layer2, keep_prob=0.8)
# 7 x 7 256 to 14 x 14 x 128
# tf 四维计算 strides = [batch_size, h, w, channel]
# padpadding ='SAME'采用pad
layer3 = tf.layers.conv2d_transpose(layer2, 128, 3, strides=2, padding='same')
layer3 = tf.layers.batch_normalization(layer3, training=is_train)
layer3 = tf.maximum(alpha * layer3, layer3)
layer3 = tf.nn.dropout(layer3, keep_prob=0.8)
# 14 x 14 x 128 to 28 x 28 x 1
logits = tf.layers.conv2d_transpose(layer3, output_dim, 3, strides=2, padding='same')
# MNIST原始数据集的像素范围在0-1,这里的生成图片范围为(-1,1)
# 因此在训练时,记住要把MNIST像素范围进行resize
outputs = tf.tanh(logits)
return outputs
2、 判别网络
和正常的 CNN 做分类任务一样的结构。
def get_discriminator(inputs_img, reuse=False, alpha=0.01):
with tf.variable_scope("discriminator", reuse=reuse):
# 28 x 28 x 1 to 14 x 14 x 128
# 第一层不加入BN(批标准化)
layer1 = tf.layers.conv2d(inputs_img, 128, 3, strides=2, padding='same')
layer1 = tf.maximum(alpha * layer1, layer1)
layer1 = tf.nn.dropout(layer1, keep_prob=0.8)
# 14 x 14 x 128 to 7 x 7 x 256
layer2 = tf.layers.conv2d(layer1, 256, 3, strides=2, padding='same')
layer2 = tf.layers.batch_normalization(layer2, training=True)
layer2 = tf.maximum(alpha * layer2, layer2)
layer2 = tf.nn.dropout(layer2, keep_prob=0.8)
# 7 x 7 x 256 to 4 x 4 x 512
layer3 = tf.layers.conv2d(layer2, 512, 3, strides=2, padding='same')
layer3 = tf.layers.batch_normalization(layer3, training=True)
layer3 = tf.maximum(alpha * layer3, layer3)
layer3 = tf.nn.dropout(layer3, keep_prob=0.8)
# 4 x 4 x 512 to 4*4*512 x 1
flatten = tf.reshape(layer3, (-1, 4*4*512))
# 全连接层 拉直
logits = tf.layers.dense(flatten, 1)
outputs = tf.sigmoid(logits)
# 得分值;概率值
return logits, outputs
3、 目标函数
def get_loss(inputs_real, inputs_noise, image_depth, smooth=0.1):
g_outputs = get_generator(inputs_noise, image_depth, is_train=True)
d_logits_real, d_outputs_real = get_discriminator(inputs_real)
d_logits_fake, d_outputs_fake = get_discriminator(g_outputs, reuse=True)
# 计算Loss
g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake,
labels=tf.ones_like(d_outputs_fake)*(1-smooth)))
# smooth 平滑处理
d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_real,
labels=tf.ones_like(d_outputs_real)*(1-smooth)))
d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake,
labels=tf.zeros_like(d_outputs_fake)))
d_loss = tf.add(d_loss_real, d_loss_fake)
return g_loss, d_loss
4、 优化
def get_optimizer(g_loss, d_loss, beta1=0.4, learning_rate=0.001):
train_vars = tf.trainable_variables()
g_vars = [var for var in train_vars if var.name.startswith("generator")]
d_vars = [var for var in train_vars if var.name.startswith("discriminator")]
# Optimizer
with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
g_opt = tf.train.AdamOptimizer(learning_rate).minimize(g_loss, var_list=g_vars)
d_opt = tf.train.AdamOptimizer(learning_rate).minimize(d_loss, var_list=d_vars)
return g_opt, d_opt
def plot_images(samples):
fig, axes = plt.subplots(nrows=1, ncols=25, sharex=True, sharey=True, figsize=(50,2))
for img, ax in zip(samples, axes):
ax.imshow(img.reshape((28, 28)), cmap='Greys_r')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
fig.tight_layout(pad=0)
def show_generator_output(sess, n_images, inputs_noise, output_dim):
cmap = 'Greys_r'
noise_shape = inputs_noise.get_shape().as_list()[-1]
# 生成噪声图片
examples_noise = np.random.uniform(-1, 1, size=[n_images, noise_shape])
samples = sess.run(get_generator(inputs_noise, output_dim, False),
feed_dict={inputs_noise: examples_noise})
result = np.squeeze(samples, -1)
return result
5、 训练
# 定义参数
batch_size = 64
noise_size = 100
epochs = 5
n_samples = 25
learning_rate = 0.001
这个模板可以直接套用,只需要自己写好next_batch()函数
def train(noise_size, data_shape, batch_size, n_samples):
# 存储loss
losses = []
steps = 0
inputs_real, inputs_noise = get_inputs(noise_size, data_shape[1], data_shape[2], data_shape[3])
g_loss, d_loss = get_loss(inputs_real, inputs_noise, data_shape[-1])
g_train_opt, d_train_opt = get_optimizer(g_loss, d_loss, beta1, learning_rate)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 迭代epoch
for e in range(epochs):
for batch_i in range(mnist.train.num_examples//batch_size):
steps += 1
batch = mnist.train.next_batch(batch_size)
batch_images = batch[0].reshape((batch_size, data_shape[1], data_shape[2], data_shape[3]))
# scale to -1, 1
batch_images = batch_images * 2 - 1
# noise
batch_noise = np.random.uniform(-1, 1, size=(batch_size, noise_size))
# run optimizer
_ = sess.run(g_train_opt, feed_dict={inputs_real: batch_images,
inputs_noise: batch_noise})
_ = sess.run(d_train_opt, feed_dict={inputs_real: batch_images,
inputs_noise: batch_noise})
if steps % 101 == 0:
train_loss_d = d_loss.eval({inputs_real: batch_images,
inputs_noise: batch_noise})
train_loss_g = g_loss.eval({inputs_real: batch_images,
inputs_noise: batch_noise})
losses.append((train_loss_d, train_loss_g))
# 显示图片
samples = show_generator_output(sess, n_samples, inputs_noise, data_shape[-1])
plot_images(samples)
print("Epoch {}/{}....".format(e+1, epochs),
"Discriminator Loss: {:.4f}....".format(train_loss_d),
"Generator Loss: {:.4f}....". format(train_loss_g))
结语
- DCGAN 比 GAN 生成的效果要好些。实际任务中用 DCGAN 的模型也比较多。
- 学习到现在,自己也勉勉强强算是一个蹭 Deep Learning 热度的水货了吧(可能连水货都算不上……)。
- 清·袁枚《随园诗话》第十一:“毕尚书弘奖风流;一时学士文人趋之若鹜。”
- 我不想做那个趋之若鹜的人;正如金庸老爷子的《侠客行》里,长乐帮的展飞展堂主对石破天说的:“天地行侠尔等焉独享,长乐非我却是天下人!”
- 我看到了人工智能里,机器学习的力量和潜能。坚信苦学一些皮毛,在未来的三十年里派上用场,解救江湖罢了。并不是想乘着深度学习火热一时长乐一世,长乐非我啊。
来源:CSDN
作者:icaoys
链接:https://blog.csdn.net/qq_23947237/article/details/77479151