一.GAN简介:
"生成模型"可以将一个输入噪音生成和真实数据相似的数据
"判别模型"能够判断出真实数据和类真实数据。
随着博弈的进行,"生成模型"能够输出越来越接近真实数据的数据
总之GAN 网络的目标是:使得生成的数据和真实数据更接近。
(1)G(X)(生成模型网络)要学到优秀的模型参数,使得判别模型网络无法区别真实数据和类真实数据
(2)D(X)(判别模型网络)的判别能力要足够强,使得完成对数据的真实性做出很好的二分类任务。
x 代表真实数据,z 代表噪音,G(z)代表一个输入噪音通过生成网络后的输出。
一方面,要求判别网络能够准确判断出数据的真实性,即 D(x)尽可能接近 1,D(G(z))尽可能接近于 0;
另一方面,要求生成网络产生的数据非常接近真实数据,即 D(G(z))尽可能接近于 1。
损失函数
损失函数目的是:最大化 D 的区分度,最小化 G 输出和真实数据的区别。
将公式拆分
(1)判别模型:log(D1(x))+log(1-D2(G(z)))
(2)生成模型:log(D2(G(z)))
当判别模型能力强时,D1(x)->1, D2(G(z))->0,(1)式趋近于 0
当生成模型能力强时,D2(G(z))->1,(2)式趋近于 0
训练过程
其基本算法如图
解释:
GAN 的训练 在同一轮梯度反传的过程分为两步既是先训练 D 再训练 G:
注意:不是等D训练完成后开始训练G, D 的训练也需要上一轮梯度反传中 G 的输出值作为输入。
(1)训练D:上一轮G产生的图片,和真实图片,直接拼接在一起,作为 x。根据上图,按顺序摆放 0 和 1,假图对应 0,真图对应 1。x 输入生成一个 score(从 0 到 1 之间的数),通过 score 和 y 组成的损失函数,就可以进行梯度反传了。
(注意:图片上举的例子是 batch = 1,len(y)=2*batch,训练时通常可以取较大的 batch)
(2)训练G:将D和G看成整体(以下简称DG系统)DG系统输出仍是score。
DG前向过程:输入一组随机向量,在G处生成图,在D处对图打分。
score=1是优化目标,故而根据score和y=1的差异组成损失函数,从而进行反向传播梯度。
注意:此处D不可训练,保证G的训练符合D的打分标准
注意:GAN的过程都是无监督
二.数学推导:
数据集的分布 Pdata(x),其中是一个真实的图片。在GAN中,生成器G可以生成一个分布,这个分布的参数是 θ事实上,生成器可以是任何模型,如果它是一个高斯混合模型,就是其中的平均值和方差。在现在的处理中,我们更倾向于用神经网络来行使生成器的功能,因为它能形成比高斯模型复杂地多的分布。
在 Pdata(x)中进行采样,得到{x1,x2,..xm},此时在生成器中得到一个似然L=Ⅱ(m,i=1)PG(xi,θ) θ*这里需要找到一个 来最大化这个似然。
我们可以看到在寻找θ*来进行最大化似然的过程中,可以在外面套上 ㏒函数。这样做可以把相乘转化为相加,这样就近似于㏒PG在Pdata分布中的期望。期望又可以等价于求概率积分,因为这是在求 θ,可以将与其无关的项去掉,最后转化为使用KL divergence衡量的概率分布差异。最小化KL的过程就是寻找参数使得PG更接近于Pdata的过程。这里注意,PG 可以是任何模型的,这里我们就用神经网络来模拟。因为激活函数的存在,它可以你和任意的分布,我们可以用神经网络学习很复杂的分布。GAN的基本公式是:
在这里,给定一个G,最大似然V就表示了生成器输出和样本之前的差异,在这个过程中调整的是D的参数,然后找到一个最好的G使得这个最大值最小。第一项表示真实数据,此时D要让这一项的D(x)尽量大,后一项是G产生的数据,D(x)则要尽量小。
首先,对于给定的G,求解最优的D:
所得的D*代入:
JS Divergence也是衡量两个分布差异的指标,不同于KL,它是对称的。在上式中,找到 D后,就需要找到G来将maxDV(G,D)最小化,D0有
然后更新G0为G1,可能会有V(G1,D1)>V(G0,D0)这样更新G就没达到它原来应该要的效果,如下图所示:
这时候可以控制更新的量,保证一次只做微小的改动。
三.样例介绍:
线性数据样例:
目标:
实现下图,绿线(生成网络输出数据)接近蓝线(真实数据)
(1)定义模型
def main(args):
model = GAN(
DataDistribution(), //真实数据真实分布
GeneratorDistribution(range=8), //生成数据分布
args.num_steps, //迭代次数,取 1200
args.batch_size, //一次迭代更新 12个点的数据
args.log_every, //每隔多少次迭代打印一次 loss 值
)
model.train()
(2)初始化参数
def __init__(self, data, gen, num_steps, batch_size, log_every):
self.data = data
self.gen = gen
self.num_steps = num_steps
self.batch_size = batch_size
self.log_every = log_every
self.mlp_hidden_size = 4 //神经网络中隐藏层个数设置为 4
self.learning_rate = 0.03 //学习率
self._create_model()
(3) D_pre 网络
注意:判别网络D的参数不能随机初始化,必须具有一定的判别能力,所以预先使用 D_pre 网络对 D 的参数进行训练。
with tf.variable_scope('D_pre'):
self.pre_input = tf.placeholder(tf.float32,
shape=(self.batch_size, 1))
self.pre_labels = tf.placeholder(tf.float32,
shape=(self.batch_size, 1))
D_pre = discriminator(self.pre_input, self.mlp_hidden_size)
self.pre_loss = tf.reduce_mean(tf.square(D_pre -
self.pre_labels))
self.pre_opt = optimizer(self.pre_loss, None,
self.learning_rate)
(4)G,D网络设置
G网络:
with tf.variable_scope('Gen'):
self.z = tf.placeholder(tf.float32,
shape=(self.batch_size, 1)) //输入,为一个随机输入
self.G = generator(self.z, self.mlp_hidden_size)//产生输出数据
D网络:
注意:D网络有两个输入,一个是真实数据x,另一个是生成网络的输出数据G(z)
with tf.variable_scope('Disc') as scope:
self.x = tf.placeholder(tf.float32,
shape=(self.batch_size, 1))
self.D1 = discriminator(self.x, self.mlp_hidden_size)
scope.reuse_variables()
self.D2 = discriminator(self.G, self.mlp_hidden_size)
(5)损失函数
self.loss_d = tf.reduce_mean(-tf.log(self.D1) - tf.log(1 - self.D2))
//见(1)式,对(1)式取反
self.loss_g = tf.reduce_mean(-tf.log(self.D2))
//见(2)式
self.opt_d = optimizer(self.loss_d, self.d_params, self.learning_rate)
//使用优化器对两者的损失函数进行优化
self.opt_g = optimizer(self.loss_g, self.g_params,
self.learning_rate)
(6)训练模型
def train(self):
with tf.Session() as session:
tf.global_variables_initializer().run()
# pretraining discriminator
num_pretrain_steps = 1000
for step in range(num_pretrain_steps):
d = (np.random.random(self.batch_size) - 0.5) * 10.0
labels = norm.pdf(d, loc=self.data.mu,
scale=self.data.sigma)
pretrain_loss, _ = session.run([self.pre_loss,
self.pre_opt], {
self.pre_input: np.reshape(d, (self.batch_size,1)),
self.pre_labels: np.reshape(labels,
(self.batch_size, 1))
})
self.weightsD = session.run(self.d_pre_params) //将d_pre 网络的训练结果赋值
# copy weights from pre-training over to new D network
for i, v in enumerate(self.d_params): //将 d_pre 网
络的训练结果拷贝给 self.d_params
session.run(v.assign(self.weightsD[i]))
for step in range(self.num_steps):
# update discriminator
x = self.data.sample(self.batch_size)
//定义 x 真实数据
z = self.gen.sample(self.batch_size)
//定义 z 噪音输入
loss_d, _ = session.run([self.loss_d, self.opt_d], {
self.x: np.reshape(x, (self.batch_size, 1)),
self.z: np.reshape(z, (self.batch_size, 1))
})
# update generator
z = self.gen.sample(self.batch_size)
loss_g, _ = session.run([self.loss_g, self.opt_g], {
self.z: np.reshape(z, (self.batch_size, 1))
})
if step % self.log_every == 0:
print('{}: {}\t{}'.format(step, loss_d, loss_g))
if step % 100 == 0 or step==0 or step == self.num_steps
-1 :
self._plot_distributions(session)
(以上代码仍需修改)
训练结果
1200次迭代,可知下列结果
四.小狗图片训练样例:
算法图了解结构
(1)初始化
生成器接受一个 1000 维的随机生成的数组,然后输出一个 64×64×3
通道的图片数据
生成器就是一个神经网络,或看成一个函数,低维度的向量生成一个高维度的向量。训练完成后的生成器,输入向量每一个元素可以对应图片的一个象征。
def generator_model():
model = Sequential()
model.add(Dense(input_dim=1000, output_dim=1024))
model.add(Activation('tanh'))
model.add(Dense(128 * 8 * 8))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Reshape((8, 8, 128), input_shape=(8 * 8 * 128,)))
model.add(UpSampling2D(size=(4, 4)))
model.add(Conv2D(64, (5, 5), padding='same'))
model.add(Activation('tanh'))
model.add(UpSampling2D(size=(2, 2)))
model.add(Conv2D(3, (5, 5), padding='same'))
model.add(Activation('tanh'))
return model
(2)判别器代码与结构:
def discriminator_model():
model = Sequential()
model.add(Conv2D(64, (5, 5), padding='same', input_shape=(64, 64, 3)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, (5, 5)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('tanh'))
model.add(Dense(1))
model.add(Activation('sigmoid'))
return model
输入是 64,64,3 的图片,输出是一个数 1 或者 0
(3)训练D
# 随机生成的 1000 维的噪声
noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 1000))
# X_train 是训练的图片数据,这里取出一个 batchsize 的图片用于训练,这个是真图(64张)
image_batch = X_train[index * BATCH_SIZE:(index + 1) * BATCH_SIZE]
# 这里是经过生成器生成的假图
generated_images = generator_model.predict(noise, verbose=0)
# 将真图与假图进行拼接
X = np.concatenate((image_batch, generated_images))
# 与 X 对应的标签,前 64 张图为真,标签是 1,后 64 张图是假图,标签为 0
y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
# 把真图与假图的拼接训练数据 1 送入判别器进行训练判别器的准确度
d_loss = discriminator_model.train_on_batch(X, y)
(4)训练G
def generator_containing_discriminator(g, d):
model = Sequential()
model.add(g)
# 判别器参数不进行修改
d.trainable = False
model.add(d)
return model
这个模型上半部分是生成网络,下半部分是判别网络,生成网络首先生成假图,然后送入判别网络中进行判断,这里有一个 d.trainable=False,意思是,只调整生成器,判别的的参数不做更改。
(5)训练G
# 训练一个 batchsize 里面的数据
for index in range(int(X_train.shape[0]/BATCH_SIZE)):
# 产生随机噪声
noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 1000))
# 这里面都是真图片
image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]
# 这里产生假图片
generated_images = g.predict(noise, verbose=0)
# 将真图片与假图片拼接在一起
X = np.concatenate((image_batch, generated_images))
# 前 64 张图片标签为 1,即真图,后 64 张照片为假图
y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
# 对于判别器进行训练,不断提高判别器的识别精度
d_loss = d.train_on_batch(X, y)
# 再次产生随机噪声
noise = np.random.uniform(-1, 1, (BATCH_SIZE, 1000))
# 设置判别器的参数不可调整
d.trainable = False
# ××××××××××××××××××××××××××××××××××××××××××××××
# 在此输入噪声,并认为这些噪声是真实的标签
g_loss = generator_containing_discriminator.train_on_batch(noise, [1] *
BATCH_SIZE)
# ××××××××××××××××××××××××××××××××××××××××××××××
# 此时设置判别器可以被训练,参数可以被修改
d.trainable = True
# 打印损失值
print("batch %d d_loss : %s, g_loss : %f" % (index, d_loss, g_loss))
(此处在前文已经介绍过工作过程,如果已经了解过程可以不看)
先输入生成器中,然后生成器生成图片之后,把图片输入判别器中,标签此刻输入的是 1,真实的图片,但实际上是假图,此刻判别器就会判断为假图,然后模型就会不断调整生成器参数,此时判别器的参数被设置为为不可调整,
d.trainable=False所以为了不断降低 loss 值,模型就会一直调整生成器的参数,直到判别器认为这是真图。此时判别器与生成器达到了一个平衡。也就是说生成器产生的假图,判别器已经分辨不出来了。所以继续迭代,提高判别器精度,如此往复循环,直到个人满意。
训练结果
1.参考https://www.pianshen.com/article/2495690365/;jsessionid=A929EB195A785EF5D377012CF21FB7FF
2.参考:
https://www.pianshen.com/article/3628327017/
3.参考:
https://github.com/jensleeGit/Kaggle_self_use/tree/master/Generative Dog Images
4.参考:
https://zhuanlan.zhihu.com/p/44218749
5.参考百度百科
网友评论