前面的文章介绍了gan的原理,但是可以看到gan有一个很有意思的结果,就是gan可以生成,但是他是在指定学习的一堆原始图片基础上,随机生成的。那么如何才能够按照我们指定的意愿去生成内容呢?比如,MNIST数据集的生产,我希望我能指定生成一些我希望的数字,例如1,或者2等。
这就是条件gan了,即CGan和CDCGAN。
我们先来看看原理:
我们知道,对于原来的GAN网络(包括DCGAN)实际上是包含了两个网络G(z)生成网络,还有D(X)判别网络,然后由此两个网络进行相互的博弈,产生对应的结果,最后讲训练好的G(z)网络拿出来,生成图片。
而对于条件网络,实际上就是在两个网络的基础上都要增加新的条件标签:y。而两个网络G(z)和D(X)在构造的时候,都带上条件:G(z,y),D(X,y),这样训练出来的结果,带上y条件,就可以生成条件对应的图片了。
我们直接看例子:
如果,我们要将一个GAN网络改造成一个cGan怎么做呢?
首先要把y定义出来
y = tf.placeholder(tf.float32, shape=[None, y_dim])
然后,改造生成网络
def generator(z, y):
# Concatenate z and y
inputs = tf.concat(concat_dim=1, values=[z, y])
G_h1 = tf.nn.relu(tf.matmul(inputs, G_W1) + G_b1)
G_log_prob = tf.matmul(G_h1, G_W2) + G_b2
G_prob = tf.nn.sigmoid(G_log_prob)
return G_prob
可见,生成网络是在维度上直接把条件拼接上去的,就是数组变“长”了。
再看看判别网络:
def discriminator(x, y):
# Concatenate x and y
inputs = tf.concat(concat_dim=1, values=[x, y])
D_h1 = tf.nn.relu(tf.matmul(inputs, D_W1) + D_b1)
D_logit = tf.matmul(D_h1, D_W2) + D_b2
D_prob = tf.nn.sigmoid(D_logit)
return D_prob, D_logit
由此可见,判别网络和生成网络一样,是将输入部分和条件标签拼接在一起,进行统一的学习工作。
中间的计算lost和进行梯度下降这些和原来一样,不再给出例子了。最后看看训练部分的伪代码:
X_mb, y_mb = mnist.train.next_batch(mb_size)
Z_sample = sample_Z(mb_size, Z_dim)
_, D_loss_curr = sess.run([D_solver, D_loss], feed_dict={X: X_mb, Z: Z_sample, y:y_mb})
_, G_loss_curr = sess.run([G_solver, G_loss], feed_dict={Z: Z_sample, y:y_mb})
每次训练,gan网络,都是公用一个y标签进行训练。
当然使用的时候很简单,把对应的条件y带上,就可以获取到训练的成果。
到这里,cGAN的设计就结束了,大家可以拿著名的MNIST数据集进行测试一下。
下面讲讲,如果是面对的是DCGAN,那么cDCGAN是怎么处理的呢?
只讲核心的,其实也就是在生成网络和判别网络构造的时候有所区别,其他都是一样的。
先看看生成网络:
def discriminator(x, y_fill, isTrain=True, reuse=False):
with tf.variable_scope('discriminator', reuse=reuse):
# initializer
w_init = tf.truncated_normal_initializer(mean=0.0, stddev=0.02)
b_init = tf.constant_initializer(0.0)
# concat layer
cat1 = tf.concat([x, y_fill], 3)
# 1st hidden layer
conv1 = tf.layers.conv2d(cat1, 128, [5, 5], strides=(2, 2), padding='same', kernel_initializer=w_init, bias_initializer=b_init)
lrelu1 = lrelu(conv1, 0.2)
# 2nd hidden layer
conv2 = tf.layers.conv2d(lrelu1, 256, [5, 5], strides=(2, 2), padding='same', kernel_initializer=w_init, bias_initializer=b_init)
lrelu2 = lrelu(tf.layers.batch_normalization(conv2, training=isTrain), 0.2)
# output layer
conv3 = tf.layers.conv2d(lrelu2, 1, [7, 7], strides=(1, 1), padding='valid', kernel_initializer=w_init)
o = tf.nn.sigmoid(conv3)
return o, conv3
如上,黑体字部分很关键,实际就是将标签合并到判别网络的原始图像的“深度”上。如此就可以利用y标签了。
再看看生成网络:
def generator(x, y_label, isTrain=True, reuse=False):
with tf.variable_scope('generator', reuse=reuse):
# initializer
w_init = tf.truncated_normal_initializer(mean=0.0, stddev=0.02)
b_init = tf.constant_initializer(0.0)
# concat layer
cat1 = tf.concat([x, y_label], 3)
# 1st hidden layer
deconv1 = tf.layers.conv2d_transpose(cat1, 256, [7, 7], strides=(1, 1), padding='valid', kernel_initializer=w_init, bias_initializer=b_init)
lrelu1 = lrelu(tf.layers.batch_normalization(deconv1, training=isTrain), 0.2)
# 2nd hidden layer
deconv2 = tf.layers.conv2d_transpose(lrelu1, 128, [5, 5], strides=(2, 2), padding='same', kernel_initializer=w_init, bias_initializer=b_init)
lrelu2 = lrelu(tf.layers.batch_normalization(deconv2, training=isTrain), 0.2)
# output layer
deconv3 = tf.layers.conv2d_transpose(lrelu2, 1, [5, 5], strides=(2, 2), padding='same', kernel_initializer=w_init, bias_initializer=b_init)
o = tf.nn.tanh(deconv3)
return o
其实和CGAN的生成网络是一样的,将y标签加入到最后一个维度上,但例子中给了一个多维度的,其实这个维度定义如下:
z = tf.placeholder(tf.float32, shape=(None, 1, 1, 100))
G_z = generator(z, y_label, isTrain)
好,其他就和普通的cGAN没有什么区别了,大家试试吧。
这里给出关于CGAN的参考:
https://wiseodd.github.io/techblog/2016/12/24/conditional-gan-tensorflow/
这篇是原理的介绍
代码在下面的链接里面:
https://github.com/wiseodd/generative-models/tree/master/GAN/conditional_gan
最后,cDCGAN的例子和资料相对较少,参见如下,有一个样例代码:
https://github.com/znxlwm/tensorflow-MNIST-cGAN-cDCGAN
为了实践,之前也说过,折腾阿里云的平台很长时间了,下一篇内容,将最近在个个平台上的实践,都写一遍。
不过,我先给出结论,机器学习目前平台还是推荐在AWS上,这个是最好的,没有之一!