CGAN概述
原始的GAN在生成高维度数据时,只能随机生成,无法生成我们给定条件的数据。这极大的限制了GAN的应用范围,因为,对于我们来说,仅仅生成足够真实的数据(如图片)没有太大的意义,因为只要架起高清照相机,我们就能够得到足够多的、真实的图片,所以,只有能够生成我们指定条件的数据,才有意义。
有条件约束的GAN,将数字类别作为约束条件与随机噪声一起输入到生成模型,使生成模型能够生成指定的手写数字。对应的,CGAN的辨别模型的输入也包含两个部分,图片和标签。
一、CGAN模型架构
CGAN的模型架构,如图1所示。
图1 CGAN模型架构
从图1中可以看出,样本数据中的图片(x)和类别标签(y)对,一起输入到辨别模型,除此之外,输入到辨别模型的还有生成模型生成的图片(G(z|y))与类别标签(y),辨别模型的目标是将他们区分开。生成模型的输入是类别标签(y)和随机噪声(z),输出的是生成的手写数字图片。需要注意的是,这里的类别标签(y)来源于样本数据,而不是随机生成的标签,只有这样生成模型才能学习到如何生成指定类别的手写数字。
二、 生成模型架构
CGAN的生成模型,如图2所示。输入的类别标签(y)和随机噪声(z),分别与包含200个和1000个神经元的全连接层连接(类别标签先被转换成one-hot张量),该全连接层之后紧接着一个批量正则化层,激活函数采用的是ReLU。
图2 CGAN原始的生成模型架构
然后,将上述两个全连接层串联起来,与一个包含512个神经元的全连接层连接起来,紧接着是带泄露的ReLU激活函数、批量正则化函数。需要说明的是,在原始的CGAN论文中,该层似乎是1200个神经元,但是,在实际模型训练过程中,笔者发现512个神经元模型更容易训练。
最后,连接到包含784个神经元的全连接层,采用sigmoid激活函数,紧接着是批量正则化层。由于sigmoid输出的取值范围是[0, 1],所以,在模型训练阶段,我们读取样本数据时,需要将样本数据的取值范围从[0,255]映射到[0, 1],这样真实的样本数据和生成的数据取值范围才有可能一致。对应的,在展示生成的图片是,需要将生成的数据取值范围映射到[-1, 1],才能正确的显示图片,否则,生成的图片会变灰、变白,看起来很模糊。
三、辨别模型架构
CGAN的辨别模型,见图3。样本数据的图片(x)首先与一个k=5、输出神经元个数为240的Maxout网络层连接。Maxout网络层是全连接层的变种,原理和实现代码见“7.1.4 Maxout网络层”。类别标签分别(y)与一个k=5、输出神经元个数为50的Maxout网络层连接。
其次,将上述两个Maxout网络层的输出结果串联起来,再与一个k=4、输出神经元个数为240的Maxout网络层连接。
最后,将上述Maxout网络层的输出结果连接到一个只包含1个神经元的全连接层,该全连接层采用sigmoid作为激活函数。
图3 CGAN原始的辨别模型架构
四、 Maxout网络层原理
Maxout网络层是原始全连接层的变种。原始的全连接层的计算过程,见图4。此图展示了输入,输出的计算过程。其中,是激活函数,是权重,是偏置项。
图4 原始全连接层示意图
我们仍然以输入,输出为例,展示Maxout网络层的计算过程,参见图5。从图5中可以看出,与原始的全连接层神经网络相比,Maxout网络层中多了一个包含k个神经元的隐藏层,输入的分别与这k个神经元连接,对应的使用k组()权重参数,输出k个y,然后,这k个y输入给求最大值(max)函数,将求出的最大值作为Maxout的最终输出。
图5 Maxout网络层的计算过程示意图
五、 Maxout网络层实现
Maxout没有内置在TensorFlow 2.0的版本中,所以,我们来实现一个简单的、自定义Maxout网络层,命名为MaxoutDense。
实现自定义的Maxout网络层,需要实现一个继承tf.keras.layers.Layer的MaxoutDense类,实现它的初始化函数、buid函数、以及call函数。其中,初始化函数,用于接收MaxoutDense的超参,比如k和output_dims。build函数用于对权重参数进行初始化,它是由父类在调用call函数之前自动调用。由于初始化参数时,需要根据输入张量的形状来计算参数的形状,因此,build的输入参数是input_shape,代表输入张量的形状。最后是call函数,输入参数是输入张量,call函数用于执行最终的计算。
Maxout网络层的实现代码如下,我们将下面的代码保存到maxout.py文件中备用。
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf # TensorFlow 2.0
class MaxoutDense(tf.keras.layers.Layer):
def __init__(self, k, output_dims, kernel_initializer=None):
"""
参数:
k : k个神经元一组
output_dims: 输出的神经元个数
kernel_initializer: 参数初始化器,GAN模型训练困难,参数初始化必须小心。
在这里我们采用均值为0.0、方差为0.02的正态分布随机数来填充
"""
super(MaxoutDense, self).__init__()
self.k = k
self.output_dims = output_dims
if kernel_initializer is None:
kernel_initializer = tf.random_normal_initializer(
mean=0.0, stddev=0.02)
self.kernel_initializer = kernel_initializer
def build(self, input_shape):
"""
在调用call之前,根据输入张量的形状,初始化变量。
在调用call函数之前,由父类自动调用。
"""
d = input_shape[-1]
self.W = tf.Variable(self.kernel_initializer(
shape=[d, self.output_dims, self.k]))
self.b = tf.Variable(self.kernel_initializer(
shape=[self.output_dims, self.k]))
def call(self, input):
"""
执行Maxout计算。
"""
z = tf.tensordot(input, self.W, axes=1) + self.b
# 对k组输出结果,求最大值
z = tf.reduce_max(z, axis=2)
return z
Maxout网络层的调用示例代码如下:
# 将图像连接到k=5、输出神经元个数为240的Maxout层
image_h0 = MaxoutDense(k=5, output_dims=240)(image)
# 将k=5、输出神经元个数为240的Maxout层增加到模型中
model = Sequential( )
model.add(MaxoutDense(k=5, output_dims=50))
本文选自----《GAN生成对抗神经网络原理与实践》一书中,经授权此公号。
文末赠书
编辑推荐
全景:囊括GAN起源、发展和演变的全貌;
插图:100余幅插图,图说GAN的原理;
实战:10余种有代表性的GAN案例代码。
大家好!我们将这本《GAN生成对抗神经网络原理与实践》赠送给大家,你只需要留言评论即可,其中集赞最多的5位读者可以免费领取这本书。
活动截止时间:2021年6月26日 18:00整
想要的读者朋友请赶紧留言,邀请你的好友为你点赞吧!
点个在看你最好看