基于分类优化的GAN(Auxiliary Classifier Generative Adversarial Network,ACGAN) [1] 它的主要改进如图13-21所示,是在生成图像和进行图像分类的环节引入了图像内容的标签,所谓图像内容的标签,在MNIST里面表现为标记数字0~9。正是由于加入了图像内容的分类标签,可以让图像的生成和训练更加有针对性。下面我们将介绍如何基于ACGAN实现生成MNIST数据集的功能,相关代码在GitHub的code/keras-acgan.py。
1.Generator
我们创建Generator,由于需要支持多输入,所以需要使用函数式生成模型的方法,架构如图13-22所示。需要指出的是使用Sequential创建的CNN结构以Sequential_2表示,参数如下:
图13-21 ACGAN原理图 [2]
·输入层一共有两个,其中一个是大小100的噪音。
·第二个输入层是大小为1标记MNIST图片内容的标记。
·将第二个输入层映射成大小为100的向量并压平。
·将两个大小均为100的输入层相加,大小依然为100。
·构建两个全连接层,结点数分别为1024和128×7×7即6272,激活函数均为relu。
·改变形状为(7,7,128)。
·使用(2,2)进行上采样。
·使用64个大小为(5,5)进行卷积处理。
·使用(2,2)进行上采样。
·使用1个(5,5)进行卷积处理,生成一个(28,28,1)的图像数据。
代码如下:
def build_generator(latent_size): cnn = Sequential() cnn.add(Dense(1024, input_dim=latent_size, activation='relu')) cnn.add(Dense(128 * 7 * 7, activation='relu')) cnn.add(Reshape((128, 7, 7))) cnn.add(UpSampling2D(size=(2, 2))) cnn.add(Conv2D(256, (5, 5), padding="same", kernel_initializer="glorot_normal", activation="relu")) cnn.add(UpSampling2D(size=(2, 2))) cnn.add(Conv2D(128, (5, 5), padding="same", kernel_initializer="glorot_normal", activation="relu")) cnn.add(Conv2D(1, (2, 2), padding="same", kernel_initializer="glorot_normal", activation="tanh")) latent = Input(shape=(latent_size, )) image_class = Input(shape=(1,), dtype='int32') cls = Flatten()(Embedding(10, 100, embeddings_initializer="glorot_normal")(image_class)) h=add([latent, cls]) fake_image = cnn(h) return Model(inputs=[latent, image_class], outputs=[fake_image])
图13-22 ACGAN的Generator
2.Discriminator
我们创建Discriminator,架构如图13-23所示,需要指出的是使用Sequential创建的CNN结构以Sequential_1表示,参数如下:
·输入层大小为(1,28,28)。
·32个大小为(3,3),采样步长大小为(2,2)的卷积处理。
·64个大小为(3,3),采样步长大小为(1,1)的卷积处理。
·128个大小为(3,3),采样步长大小为(2,2)的卷积处理。
·256个大小为(3,3),采样步长大小为(1,1)的卷积处理。
·压平为一维向量。
·输出有两个,其中一个连接结点数为1全连接,使用激活函数sigmoid输出大小为1的分类概率。
·另外一个连接结点数为10全连接,使用激活函数softmax输出大小为10的0~9数字分类概率。
代码如下:
def build_discriminator(): cnn = Sequential() cnn.add(Conv2D(32, (3, 3), padding="same", strides=(2, 2), input_shape=(1, 28, 28) )) cnn.add(LeakyReLU()) cnn.add(Dropout(0.3)) cnn.add(Conv2D(64, (3, 3), padding="same", strides=(1, 1))) cnn.add(LeakyReLU()) cnn.add(Dropout(0.3)) cnn.add(Conv2D(128, (3, 3), padding="same", strides=(2, 2))) cnn.add(LeakyReLU()) cnn.add(Dropout(0.3)) cnn.add(Conv2D(256, (3, 3), padding="same", strides=(1, 1))) cnn.add(LeakyReLU()) cnn.add(Dropout(0.3)) cnn.add(Flatten()) image = Input(shape=(1, 28, 28)) features = cnn(image) fake = Dense(1, activation='sigmoid', name='generation')(features) aux = Dense(10, activation='softmax', name='auxiliary')(features) return Model(inputs=[image], outputs=[fake, aux])
图13-23 ACGAN的Discriminator
3.对抗模型
ACGAN的对抗模型实现非常简单,把Generator和Discriminator连接即可,不过需要将Discriminator参数设置为只允许手工更新,只有当设置trainable为Ture时才根据训练结果自动更新参数。
代码如下:
discriminator.trainable = False fake, aux = discriminator(fake) combined = Model(inputs=[latent, image_class], outputs=[fake, aux]) combined.compile( optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), loss=['binary_crossentropy', 'sparse_categorical_crossentropy'] )
4.训练过程
ACGAN的训练过程也分为两步:第一步,生成一个大小为(batch_size,latent_size)的在-1~1之间平均分布的噪声,使用Generator生成图像样本,对应的图像内容标记sampled_labels随机生成,然后和同样大小的真实MNIST图像样本合并,分别标记为0和1,对Discriminator进行训练。这个过程中Discriminator的trainable状态为True,训练过程会更新其参数,代码如下:
noise = np.random.uniform(-1, 1, (batch_size, latent_size)) image_batch = X_train[index * batch_size:(index + 1) * batch_size] label_batch = y_train[index * batch_size:(index + 1) * batch_size] sampled_labels = np.random.randint(0, 10, batch_size) generated_images = generator.predict( [noise, sampled_labels.reshape((-1, 1))], verbose=0) X = np.concatenate((image_batch, generated_images)) y = np.array([1] * batch_size + [0] * batch_size) aux_y = np.concatenate((label_batch, sampled_labels), axis=0) discriminator.train_on_batch(X, [y, aux_y])
第二步,生成一个大小为(2×batch_size,latent_size)的在-1~1之间平均分布的噪声,对应的图像内容标记sampled_labels随机生成,使用Generator生成图像样本,标记为1,欺骗Discriminator,这个过程针对对抗模型进行训练,代码如下:
noise = np.random.uniform(-1, 1, (2 * batch_size, latent_size)) sampled_labels = np.random.randint(0, 10, 2 * batch_size) trick = np.ones(2 * batch_size) combined.train_on_batch( [noise, sampled_labels.reshape((-1, 1))], [trick, sampled_labels])
5.训练结果
Mehdi Mirza在他的论文 [3] 中提到了训练的效果,如图13-24所示。
图13-24 ACGAN运行结果
整个训练过程在Mac本上运行非常缓慢,一个训练周期超过1小时,所以我们使用GPU服务器。由于GPU服务器安装的字符界面的操作系统,Python的基于图形界面的图像生成库无法使用,所以我们把模型的训练和图像的生成分开,在GPU服务器上完成模型的训练,在我的Mac本上加载模型生成的图像,对应的文件在GitHub的code/acgan文件夹下。在GPU Tesla M60下,完成一轮训练花费约300秒,代码如下:
name: Tesla M60 major: 5 minor: 2 memoryClockRate (GHz) 1.1775 pciBusID 0000:00:15.0 Total memory: 7.43GiB Free memory: 7.36GiB I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0 I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0: Y
进行了1轮学习后,生成的图像如图13-25所示。
图13-25 ACGAN进行了1轮学习后生成的图像
进行了2轮学习后,生成的图像如图13-26所示。
进行了5轮学习后,生成的图像如图13-27所示,可见训练次数越多生成的图像越逼真。
图13-26 ACGAN进行了2轮学习后生成的图像
图13-27 ACGAN进行了5轮学习后生成的图像
[1] https://arxiv.org/abs/1610.09585
[2] https://arxiv.org/pdf/1411.1784.pdf
[3] https://arxiv.org/abs/1610.09585