GAN 生成对抗网络入门

1. 简介

神经网络分很多种,有普通的前向传播网络,有分析图片的CNN卷积神经网络,有分析序列化数据的RNN循环神经网络,这些都是根据数据去预测结果。而对于生成网络而言,则是”捏造结果”了。GAN(Generative Adversarial Networks,生成对抗网络)就是其中一种生成网络,是根据随机数来生成结果,提供了一种不需要大量的标注训练数据就能学习深度表征的方式。

相比于其他的生成模型,GAN有两大特点:

  • 不依赖任何的先验假设:传统的许多方法会假设数据服从某一分布,然后使用极大似然去估计数据分布。
  • 生成 real-like 样本的方式非常简单:通过生成器(Generator)的前向传播。

1.1 生成模型

生成模型是指能够随机生成观测数据的模型,尤其是在给定某些隐含参数的条件下。

1.2 形象描述

生成对抗网络的过程可以用一个新手画家和一个新手鉴赏家的例子来说明。新手画家要画一幅达芬奇的画,但是他刚开始学,不知道达芬奇的画风格要怎么呈现,于是他用新手的灵感画画,画得一团糟。这时候有一个新手鉴赏家,他接受到了一些画作,但是他不知道哪些是新手画家画的,哪些是达芬奇画的。新手鉴赏家说出了自己的判断,你来纠正他的判断。于是新手鉴赏家就一边判断,一边告诉新手画家要怎么作画才能更加接近达芬奇的画作。新手画家也就一步步学会怎么画的更像达芬奇的风格了。如下,包含一个生成网络和一个判别网络。

image-20190804225740236

图片来自 莫烦Python 的B站频道 [1]

1.3 原理

有两个网络,G(Generator)和D(Descriminator)。G是生成图片的网络,接收一个隐变量z(通常为服从高斯分布的随机噪声),并生成图片G(z);D是判别网络,判定一张图片是不是真实的,输入x为一张图片,输出的D(x)代表x为真实图片的概率,输出为1表示百分百是真实图片,输出为0表示不可能为真实的图片。

image-20190805165445205

训练的过程,就是G生成尽量真实的图片去欺骗D,而D则是尽量把G生成的图片和真实的图片区分开,二者就形成了一个博弈的过程。而在最理想的情况下,博弈的最终结果,就是D完全无法区分开G的作品和真实的图片,也就是D(G(z)) = 0.5。

  • 寻找生成模型和判别模型之间的一个纳什均衡!

数学语言描述:(优化的目标函数)

image-20190805111737882

  • 对判别器D来说,这是一个二分类问题。V(D,G) 为二分类问题中常见的交叉熵损失。

  • 对于生成器 G 来说,为了尽可能欺骗 D,所以需要最大化生成样本的判别概率 D(G(z))(最小化 log(1-D(G(z))) )。生成器优化的目标是:最小化 V(D,G) 的最大值。

  • D和G采取交替训练的方法,先训练D,目标是希望V(G, D)越大越好,所以采用梯度上升;之后训练G,目标是希望V(G, D)越小越好,所以采用梯度下降

image-20190805113902723

  • 生成器G固定时,可以对V(D, G)求导,求出最佳判别器(已经被作者证明该D*(x)存在且唯一)

image-20190807163018201

  • 判别函数代入上面的目标函数,可以求出在最佳判别器下,生成器的目标函数等价于优化 Pdata(x) , Pg(x) 的 JS 散度 (JS-Divergence)。
  • 全局的优化目标为Pg = Pdata。经过若干次训练之后,如果G和D有一定的复杂度,那么二者会达到Pg = Pdata这个均衡点,即生成器的密度概率函数等于真实数据的密度概率函数,即生成数据和密度数据一样,D(x) = 1/2。

训练总结起来有以下步骤:

  1. 参数优化过程

要寻找最优的生成器,那给定一个判别器,可以将 max V(G,D) 看作训练生成器的损失函数 L(G)。设定了损失函数之后,就可以利用Adam等优化算法通过梯度下降来更新生成器G的参数。

  • 给定 G_0,最大化 V(G_0,D),求得 D_0*,即 max[JSD(P_data(x)||P_G0(x)];
  • 固定 D_0*,计算θ_G1 ← θ_G0 −η(dV(G,D_0*) /dθ_G) 以求得更新后的 G_1;
  • 固定 G_1,最大化 V(G_1,D_0*) 以求得 D_1*,即 max[JSD(P_data(x)||P_G1(x)];
  • 固定 D_1*,计算θ_G2 ← θ_G1 −η(dV(G,D_0*) /dθ_G) 以求得更新后的 G_2;
  • 。。。
  1. 实际训练过程

根据价值函数 V(G,D) 的定义,要求两个数学期望, E[log(D(x))] 和 E[log(1-D(G(z)))], x 服从真实数据分布,z 服从初始化分布。实践中没法利用积分求解,所以一般是从无穷的真实数据和无穷的生成器中做采样以逼近真实的数学期望。

最大化价值函数:P_data(x) 采样 m 个样本, P_G(x) 采样 m 个样本

image-20190812100303119

最小化损失函数:P_data(x) 抽取样本作为正样本,从 P_G(x) 抽取样本作为负样本,同时将逼近 -V(G,D) 的函数作为损失函数。

image-20190812100351590

image-20190812100433912

——用迭代和数值计算的方式实现极小极大化博弈过程。

  • 从真实数据分布 P_data 抽取 m 个样本

  • 从先验分布 P_prior(z) 抽取 m 个噪声样本

  • 将噪声样本投入 G 而生成数据,通过最大化 V 的近似而更新判别器参数θ_d,即极大化,判别器参数的更新迭代式:image-20190812102447202

    —— 以上是学习判别器D的过程。学习D的过程是计算JS散度的过程,我们希望最大化价值函数,所以会进行K次迭代。实践中好像一般K取1也足够。

  • 从先验分布 P_prior(z) 中抽取另外 m 个噪声样本 {z^1,…,z^m}

  • 生成器参数的更新迭代式:image-20190812102556437

    ——以上是学习生成器G的过程。为避免更新太多使得JS散度上升,在一次迭代中只进行一次。

1.4 优缺点

优点:无需马尔可夫链,仅仅使用反向传播来获得梯度,学习间无需推理,且模型中可以融合入多种函数。

缺点:需要同步D和G;p(x)的隐式表示(???不太懂)

  • 生成对抗网络模型和其他生成模型之间的对比:

image-20190812134440513

2. 其他常见生成式模型

2.1 PixelCNN 和 PixelRNN

对图像数据的概率分布Pdata(x)进行显式建模,并利用极大似然估计优化模型。给定 x1,x2,…,xi-1 条件下,所有 p(xi) 的概率乘起来就是图像数据的分布。如果使用 RNN 对上述依然关系建模,就是 pixelRNN。如果使用 CNN,则是 pixelCNN。

优点:定义了一个易于处理的密度函数,可以直接优化训练数据的似然。

缺点:像素值是从图像的一个角落开始,一个个生成的,所以速度会很慢。

image-20190811235648428

2.2 VAE: Variational Auto-Encode 变分自编码器

真实样本X通过神经网络计算出均值方差,假设隐变量服从正态分布。然后通过采样得到采样变量Z进行重构。VAE和GAN均学习了隐变量 z 到真实数据分布的映射,但VAE的不同之处在于:

  • GAN思路直接,使用一个判别器去度量分布转换模块(即生成器)生成分布与真实数据分布的距离。
  • VAE委婉,通过约束隐变量 z 服从标准正态分布以及重构数据实现了分布转换映射 X=G(z)。

image-20190811234857262

3. GAN常见模型结构

3.1 DCGAN

提出使用CNN结构来稳定GAN的训练,这允许了生成器和判别器学习优秀的上采样和下采样操作,这些操作可能提升图像合成的质量。

3.2 层级结构

GAN 对于高分辨率图像生成一直存在许多问题,层级结构的 GAN 通过逐层次,分阶段生成,一步步提生图像的分辨率。

  • 使用多对GAN:StackGAN、GoGAN
  • 单一GAN,多阶段生成:ProgressiveGAN

image-20190812001612728

3.3 自编码结构

BEGAN,EBGAN,MAGAN ……

4. GAN存在的问题

  • 不收敛问题:由于GAN是采用极小极大博弈,D 在进行梯度下降时,使得在损失流形上下降,而G使其上升,可能造成两者的梯度相互抵消,最终在最优点附近徘徊。因此,不收敛问题也是GAN所面临的最大的问题。目前采用的优化方法都是采用的启发式的方法。WGAN也在一定程度上解决了收敛不稳定的问题。

  • 模式崩溃:生成器”崩溃”,即用不同的输入生成相似的样本。可以理解成多样性问题,当G生成了一张比较真实的图片之后,就不再学习其他的分布,而仅靠这一张图片来欺骗,仅仅收敛到一种模式。这样即便训练时间再长也不会有好的结果。

  • 梯度消失:判别器的损失很快收敛为零,从而没有足够强的梯度路径可以继续更新生成器

  • 离散输出问题:GAN对生成器的唯一要求就是——生成器表示的函数必须可导,因此,GAN似乎无法用于离散输出,如文本。目前,仍未有将GAN应用于NLP领域。目前可能解决该问题三个可能的方向:采用强化学习、采用具体的分布、训练生成器产生连续的输出值,并将其编码为离散值。

5. GAN的发展

5.1 GAN: Generative Adversarial Networks, 2014

论文地址: arxiv.org/abs/1406.26…

“GAN之父” Ian Goodfellow 发表的第一篇提出 GAN 的论文,提出了 GAN 这个模型框架,讨论了非饱和的损失函数,然后对于最佳判别器(optimal discriminator)给出其导数,然后进行证明;最后是在 Mnist、TFD、CIFAR-10 数据集上进行了实验。

结论和未来的研究方向:

  • 条件生成模型p(x∣c)可以通过将c作为G和D的输入来获得。
  • 半监督学习:提供适量的带标签数据,以提高判别网络或推理网络的特征分类效果。
  • 效率改善:设计更好的方法来协调D和G,或确定更好的分布来对 z 进行采样,以提高训练速率。
  • ……

5.2 Conditional GAN, 2014

论文地址:arxiv.org/abs/1411.17…

之前的GAN是无监督模型,但给生成器提供随机噪声的话效果往往没有那么好。cGAN的提出将其拉回监督学习领域,缓和了GAN的训练不稳定的问题。

image-20190812104750739

5.3 DCGAN

Deep Convolutional GAN

第一次采用CNN结构实现GAN。将G和D换成两个CNN,但对CNN的结构做出了一些改变来提高样本质量和收敛速度:

  • 取消池化层,G网络中使用转置卷积层(transposed convolutional layer)进行上采样,D网络中用加入stride的卷积代替pooling。
  • D和G中均使用batch normalization(???)
  • 去掉全连接层,使网络变为全卷积网络
  • G网络中使用ReLU作为激活函数,最后一层使用tanh
  • D网络中使用LeakyReLU作为激活函数
  • 采用Adam优化算法,学习率是0.0002,beta1=0.5

DCGAN中的G网络:

image-20190812142834368

这篇论文介绍了如何使用卷积层,并给出一些额外的结构上的指导建议来实现。另外,它还讨论如何可视化 GAN 的特征、隐空间的插值、利用判别器特征训练分类器以及评估结果。

5.4 Improved Techniques for Training GANs

论文地址:arxiv.org/abs/1606.03…

作者之一是 Ian Goodfellow。论文介绍了很多如何构建一个 GAN 结构的建议,可以帮助理解 GAN 不稳定的原因,给出稳定训练 DCGANs 的建议,比如特征匹配(feature matching)、最小批次判别(minibatch discrimination)、单边标签平滑(one-sided label smoothing)、虚拟批归一化(virtual batch normalization)等等。

5.5 Pix2Pix

Image-to-Image Translation with Conditional Adversarial Networks

论文地址:arxiv.org/abs/1611.07…

目标是实现图像转换:语义图转街景,黑白图片上色,素描图变真实照片等。

在训练时候需要采用成对的训练数据,并对 GAN 模型采用了不同的配置。其中它应用到了 PatchGAN 这个模型,PatchGAN 对图片的一块 70*70 大小的区域进行观察来判断该图片是真是假,而不需要观察整张图片。生成器部分使用 U-Net 结构,即结合了 ResNet 网络中的 skip connections 技术,编码器和解码器对应层之间有相互连接。

image-20190813095316532

5.6 CycleGAN

Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks

论文地址:arxiv.org/abs/1703.10…

和上面的Pix2Pix不同,不需要原图和转换后的图来训练,仅仅需要准备两个领域的数据集即可,比如说普通马的图片和斑马的图片,但不需要一一对应。这篇论文提出了一个非常好的方法–循环一致性(Cycle-Consistency)损失函数。

image-20190813095734307

image-20190813095719444

5.7 Progressive Growing of GANs

Progressive Growing of GANs for Improved Quality, Stability, and Variation

论文地址:arxiv.org/abs/1710.10…

利用一个多尺度结构,从 4*48*8 一直提升到 1024*1024 的分辨率,如下图所示的结构,这篇论文提出了一些如何解决由于目标图片尺寸导致的不稳定问题。

image-20190813100008687

5.8 StackGAN

StackGAN: Text to Photo-realistic Image Synthesis with Stacked Generative Adversarial Networks

论文地址:arxiv.org/abs/1612.03…

StackGAN 和 cGAN、Progressively GANs 两篇论文比较相似,它同样采用了先验知识,以及多尺度方法。整个网络结构如下图所示,第一阶段根据给定文本描述和随机噪声,然后输出 64*64的图片,接着将其作为先验知识,再次生成 256*256大小的图片。相比前面 7 篇论文,StackGAN 通过一个文本向量来引入文本信息,并提取一些视觉特征。

image-20190813100146022

5.9 BigGAN

Large Scale GAN Training for High Fidelity Natural Image Synthesis

论文地址:arxiv.org/abs/1809.11…

当前 ImageNet 上图片生成最好的模型,但这篇论文比较难在本地电脑上进行复现。它同时结合了很多结构和技术,包括自注意机制(Self-Attention)、谱归一化(Spectral Normalization)等。

image-20190813100410787

6. GAN的实现

6.1 MNIST数据集上的应用

  • 进行GAN实验时候,只是将二维的数据拉伸成一维,没有用到卷积,只是多层神经网络的叠加。
  • 生成器和判别器使用不同的激活函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import...
from skimage.io import imsave #保存影像
#from tensorflow.examples.tutorials.mnist import input_data #第一次下载数据时解注释
#data = input_data.read_data_sets('MNIST_data/') #第一次下载数据时解注释

#设置超参数
image_height = ...
...

#定义load_data()函数以读取数据
def load_data(data_path):...

#定义GAN的生成器
def generator(z_prior):...

#定义GAN的判别器
def discriminator(x_data, x_generated, keep_prob):...

#显示结果的函数
def show_result(batch_res, fname, grid_size=(8, 8), grid_pad=5):...

#定义训练过程
def train():...

if __name__ == '__main__':
if train:
train()

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#mnist数据集下载,下载之后的文件在MNIST_data文件夹下
from tensorflow.examples.tutorials.mnist import input_data
data = input_data.read_data_sets('MNIST_data/', one_hot = True)

# 定义load_data()函数以读取数据
def load_data(data_path):
'''
函数功能:导出MNIST数据
输入: data_path 传入数据所在路径(解压后的数据)
输出: train_data 输出data,形状为(60000, 28, 28, 1)
train_label 输出label,形状为(60000, 1)
'''
f_data = open(os.path.join(data_path, 'train-images.idx3-ubyte'))
loaded_data = np.fromfile(file=f_data, dtype=np.uint8)
#前16个字符为说明符,需要跳过
train_data = loaded_data[16:].reshape((-1, 784)).astype(np.float)

f_label = open(os.path.join(data_path, 'train-labels.idx1-ubyte'))
loaded_label = np.fromfile(file=f_label, dtype=np.uint8)
#前8个字符为说明符,需要跳过
train_label = loaded_label[8:].reshape((-1)).astype(np.float)

return train_data, train_label


# 导入需要的包
import os #读取路径下文件
import shutil #递归删除文件
import tensorflow as tf #编写神经网络
import numpy as np #矩阵运算操作
from skimage.io import imsave #保存影像
from tensorflow.examples.tutorials.mnist import input_data #第一次下载数据时用

# 图像的size为(28, 28, 1)
image_height = 28
image_width = 28
image_size = image_height * image_width

# 是否训练和存储设置
train = True
restore = False #是否存储训练结果
output_path = "./output/" #存储文件的路径

# 实验所需的超参数
max_epoch = 500
batch_size = 256
h1_size = 256 #第一隐藏层的size,即特征数
h2_size = 512 #第二隐藏层的size,即特征数
z_size = 128 #生成器的传入参数


# 定义GAN的生成器
def generator(z_prior):
'''
函数功能:生成影像,参与训练过程
输入:z_prior, #输入tf格式,size为(batch_size, z_size)的数据
输出:x_generate, #生成图像
g_params, #生成图像的所有参数
'''
# 第一个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[z_size, h1_size]的随机值,权值weight初始化。
w1 = tf.Variable(tf.truncated_normal([z_size, h1_size], stddev=0.1), name="g_w1", dtype=tf.float32)
#生成大小为[h1_size]的0值矩阵,偏置bias初始化
b1 = tf.Variable(tf.zeros([h1_size]), name="g_b1", dtype=tf.float32)
#通过矩阵运算,将输入z_prior传入隐含层h1。激活函数为relu
h1 = tf.nn.relu(tf.matmul(z_prior, w1) + b1)

# 第二个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[h1_size, h2_size]的随机值,权值weight初始化。
w2 = tf.Variable(tf.truncated_normal([h1_size, h2_size], stddev=0.1), name="g_w2", dtype=tf.float32)
#生成大小为[h2_size]的0值矩阵,偏置bias初始化
b2 = tf.Variable(tf.zeros([h2_size]), name="g_b2", dtype=tf.float32)
#通过矩阵运算,将h1传入隐含层h2。激活函数为relu
h2 = tf.nn.relu(tf.matmul(h1, w2) + b2)

# 第三个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[h2_size, image_size]的随机值,权值weight初始化。
w3 = tf.Variable(tf.truncated_normal([h2_size, image_size], stddev=0.1), name="g_w3", dtype=tf.float32)
#生成大小为[image_size]的0值矩阵,偏置bias初始化
b3 = tf.Variable(tf.zeros([image_size]), name="g_b3", dtype=tf.float32)
#通过矩阵运算,将h2传入隐含层h3。
h3 = tf.matmul(h2, w3) + b3
#利用tanh激活函数,将h3传入输出层
x_generate = tf.nn.tanh(h3)

#将所有参数合并到一起
g_params = [w1, b1, w2, b2, w3, b3]

return x_generate, g_params

# 定义GAN的判别器
def discriminator(x_data, x_generated, keep_prob):
'''
函数功能:对输入数据进行判断,并保存其参数
输入:x_data, #输入的真实数据
x_generated, #生成器生成的虚假数据
keep_prob, #dropout率,防止过拟合
输出:y_data, #判别器对batch个数据的处理结果
y_generated, #判别器对余下数据的处理结果
d_params, #判别器的参数
'''
# 合并输入数据,包括真实数据x_data和生成器生成的假数据x_generated
x_in = tf.concat([x_data, x_generated], 0)

# 第一个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[image_size, h2_size]的随机值,权值weight初始化。
w1 = tf.Variable(tf.truncated_normal([image_size, h2_size], stddev=0.1), name="d_w1", dtype=tf.float32)
#生成大小为[h2_size]的0值矩阵,偏置bias初始化
b1 = tf.Variable(tf.zeros([h2_size]), name="d_b1", dtype=tf.float32)
#通过矩阵运算,将输入x_in传入隐含层h1.同时以一定的dropout率舍弃节点,防止过拟合
h1 = tf.nn.dropout(tf.nn.relu(tf.matmul(x_in, w1) + b1), keep_prob)

# 第二个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[h2_size, h1_size]的随机值,权值weight初始化。
w2 = tf.Variable(tf.truncated_normal([h2_size, h1_size], stddev=0.1), name="d_w2", dtype=tf.float32)
#生成大小为[h1_size]的0值矩阵,偏置bias初始化
b2 = tf.Variable(tf.zeros([h1_size]), name="d_b2", dtype=tf.float32)
#通过矩阵运算,将h1传入隐含层h2.同时以一定的dropout率舍弃节点,防止过拟合
h2 = tf.nn.dropout(tf.nn.relu(tf.matmul(h1, w2) + b2), keep_prob)

# 第三个链接层
#以2倍标准差stddev的截断的正态分布中生成大小为[h1_size, 1]的随机值,权值weight初始化。
w3 = tf.Variable(tf.truncated_normal([h1_size, 1], stddev=0.1), name="d_w3", dtype=tf.float32)
#生成0值,偏置bias初始化
b3 = tf.Variable(tf.zeros([1]), name="d_b3", dtype=tf.float32)
#通过矩阵运算,将h2传入隐含层h3
h3 = tf.matmul(h2, w3) + b3

#从h3中切出batch_size张图像
y_data = tf.nn.sigmoid(tf.slice(h3, [0, 0], [batch_size, -1], name=None))
#从h3中切除余下的图像
y_generated = tf.nn.sigmoid(tf.slice(h3, [batch_size, 0], [-1, -1], name=None))

#判别器的所有参数
d_params = [w1, b1, w2, b2, w3, b3]

return y_data, y_generated, d_params

#显示结果的函数
def show_result(batch_res, fname, grid_size=(8, 8), grid_pad=5):
'''
函数功能:输入相关参数,将运行结果以图片的形式保存到当前路径下
输入:batch_res, #输入数据
fname, #输入路径
grid_size=(8, 8), #默认输出图像为8*8张
grid_pad=5, #默认图像的边缘留白为5像素
输出:无
'''
#将batch_res进行值[0, 1]归一化,同时将其reshape成(batch_size, image_height, image_width)
batch_res = 0.5 * batch_res.reshape((batch_res.shape[0], image_height, image_width)) + 0.5
#重构显示图像格网的参数
img_h, img_w = batch_res.shape[1], batch_res.shape[2]
grid_h = img_h * grid_size[0] + grid_pad * (grid_size[0] - 1)
grid_w = img_w * grid_size[1] + grid_pad * (grid_size[1] - 1)
img_grid = np.zeros((grid_h, grid_w), dtype=np.uint8)
for i, res in enumerate(batch_res):
if i >= grid_size[0] * grid_size[1]:
break
img = (res) * 255.
img = img.astype(np.uint8)
row = (i // grid_size[0]) * (img_h + grid_pad)
col = (i % grid_size[1]) * (img_w + grid_pad)
img_grid[row:row + img_h, col:col + img_w] = img
#保存图像
imsave(fname, img_grid)


# 定义训练过程
def train():
'''
函数功能:训练整个GAN网络,并随机生成手写数字
输入:无
输出:sess.saver()
'''
# 加载数据
train_data, train_label = load_data("MNIST_data")
size = train_data.shape[0]

# 构建模型---------------------------------------------------------------------
# 定义GAN网络的输入,其中x_data为[batch_size, image_size], z_prior为[batch_size, z_size]
x_data = tf.placeholder(tf.float32, [batch_size, image_size], name="x_data") # (batch_size, image_size)
z_prior = tf.placeholder(tf.float32, [batch_size, z_size], name="z_prior") # (batch_size, z_size)
# 定义dropout率
keep_prob = tf.placeholder(tf.float32, name="keep_prob")
rate = 1 - keep_prob
global_step = tf.Variable(0, name="global_step", trainable=False)

# 利用生成器生成数据x_generated和参数g_params
x_generated, g_params = generator(z_prior)
# 利用判别器判别生成器的结果
y_data, y_generated, d_params = discriminator(x_data, x_generated, rate)

# 定义判别器和生成器的loss函数
d_loss = - (tf.log(y_data) + tf.log(1 - y_generated))
g_loss = - tf.log(y_generated)

# 设置学习率为0.0001,用AdamOptimizer进行优化
optimizer = tf.train.AdamOptimizer(0.0001)

# 判别器discriminator 和生成器 generator 对损失函数进行最小化处理
d_trainer = optimizer.minimize(d_loss, var_list=d_params)
g_trainer = optimizer.minimize(g_loss, var_list=g_params)
# 模型构建完毕--------------------------------------------------------------------

# 全局变量初始化
init = tf.global_variables_initializer()

# 启动会话sess
saver = tf.train.Saver()
sess = tf.Session()
sess.run(init)

# 判断是否需要存储
if restore:
#若是,将最近一次的checkpoint点存到outpath下
chkpt_fname = tf.train.latest_checkpoint(output_path)
saver.restore(sess, chkpt_fname)
else:
#若否,判断目录是存在,如果目录存在,则递归的删除目录下的所有内容,并重新建立目录
if os.path.exists(output_path):
shutil.rmtree(output_path)
os.mkdir(output_path)

# 利用随机正态分布产生噪声影像,尺寸为(batch_size, z_size)
z_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)

# 逐个epoch内训练
for i in range(sess.run(global_step), max_epoch):
# 图像每个epoch内可以放(size // batch_size)个size
for j in range(size // batch_size):
if j%20 == 0:
print("epoch:%s, iter:%s" % (i, j))

# 训练一个batch的数据
batch_end = j * batch_size + batch_size
if batch_end >= size:
batch_end = size - 1
x_value = train_data[ j * batch_size : batch_end ]
# 将数据归一化到[-1, 1]
x_value = x_value / 255.
x_value = 2 * x_value - 1

# 以正太分布的形式产生随机噪声
z_value = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
# 每个batch下,输入数据运行GAN,训练判别器
sess.run(d_trainer,
feed_dict={x_data: x_value, z_prior: z_value, rate: np.sum(0.7).astype(np.float32)})
# 每个batch下,输入数据运行GAN,训练生成器
if j % 1 == 0:
sess.run(g_trainer,
feed_dict={x_data: x_value, z_prior: z_value, rate: np.sum(0.7).astype(np.float32)})
# 每一个epoch中的所有batch训练完后,利用z_sample测试训练后的生成器
x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_sample_val})
# 每一个epoch中的所有batch训练完后,显示生成器的结果,并打印生成结果的值
show_result(x_gen_val, os.path.join(output_path, "sample%s.jpg" % i))
print(x_gen_val)
# 每一个epoch中,生成随机分布以重置z_random_sample_val
z_random_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
# 每一个epoch中,利用z_random_sample_val生成手写数字图像,并显示结果
x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_random_sample_val})
show_result(x_gen_val, os.path.join(output_path, "random_sample%s.jpg" % i))
# 保存会话
sess.run(tf.assign(global_step, i + 1))
saver.save(sess, os.path.join(output_path, "model"), global_step=global_step)


1
2
3
if __name__ == '__main__':
if train:
train()

运行结果:

Epoch 1: image-20190813092847343

Epoch 10:image-20190813093021600

Epoch 50:image-20190813093052998

Epoch 100:image-20190813093115056

Epoch 300:image-20190813093130880

6.2 在图网络中的应用

详见:综述 | 生成对抗网络(GAN)在图网络中的应用

GraphGAN采用GAN网络中常见的对抗机制:生成器G尽可能的逼近Ptrue(V|Vc)以找到与Vc的相邻节点极其相似的节点来欺骗判别器D,而判别器D则会反过来检测给定的节点V是Vc的真实邻居还是由生成器生成的。

image-20190813101644848

Reference

  1. 什么是 GAN 生成对抗网络 (深度学习)? - 莫烦Python
  2. GAN学习指南:从原理入门到制作生成Demo
  3. 万字综述之生成对抗网络 (GAN)
  4. 生成式对抗网络GAN的研究进展与展望
  5. 必读的10篇关于GAN的论文
  6. GAN笔记——理论与实现

一些相关资料

  1. GAN论文汇总
  2. tensorflow-GANs
  3. 机器之心GitHub项目:GAN完整理论推导与实现,Perfect!
  4. 花式解释AutoEncoder与VAE
  5. 变分自编码器VAE:原来是这么一回事 | 附开源代码
  6. 十个生成模型(GANs)的最佳案例和原理 | 代码+论文
  7. 对抗生成网络学习(一)——GAN实现mnist手写数字生成(tensorflow实现)
  8. 全部梭哈迟早暴富 的CSDN博客

GAN 生成对抗网络入门
http://bingcs.com/2019/08/02/GAN-Learning/
Author
Bing
Posted on
August 2, 2019
Licensed under