简介
生成对抗网络(以下简称GAN)是通过让两个神经网络相互博弈的方式进行学习,可以根据原有的数据集生成以假乱真的新的数据,举个不是很恰当的例子,类似于造假鞋,莆田艺术家通过观察真鞋,模仿真鞋的特点造出假鞋并卖给消费者,消费者收到鞋子后将它与网上的真鞋信息进行对比找瑕疵,并给出反馈,比如标不正,气垫弹性不好,莆田艺术家根据消费者给出的反馈积极地改进工艺,经过不懈努力后最终造出了可以忽悠消费者的假鞋。
在上述情景中,莆田艺术家相当于生成器,消费者相当于辨别器,在造假的过程中,生成器和判别器一直处于对抗状态。
我们把上述情景抽象为神经网络。首先,通过对生成器输入一个分布的数据,生成器通过神经网络模仿生成出一个输出(假鞋),将假鞋与真鞋的信息共同输入到判别器中。然后,判别器通过神经网络学着分辨两者的差异,做一个分类判断出这双鞋是真鞋还是假鞋。
这样,生成器不断训练为了以假乱真,判别器不断训练为了区分二者。最终,生成器真能完全模拟出与真实的数据一模一样的输出,判别器已经无力判断。基于伊恩·古德费洛最早对 GAN 的定义,GAN 实际上是在完成这样一个优化任务:
式中, 表示生成器; 表示判别器; 是定义的价值函数,代表判别器的判别性能,该数值越大性能越好; 表示真实的数据分布; 表示生成器的输入数据分布; 表示期望。
第一项 是依据真实数据的对数函数损失而构建的。具体可以理解为,最理想的情况是,判别器 能够对基于真实数据的分布数据给出 1
的判断。所以,通过优化 最大化这一项可以使 。其中, 服从 分布。
第二项,,是相对生成器的生成数据而言的。我们希望,当喂给判别器的数据是生成器的生成数据时,判别器能输出 0
。由于 的输出是,输入数据是真实数据的概率,那么 是,输入数据是生成器生成数据的概率,通过优化 最大化这一项,则可以使 。其中, 服从 ,也就是生成器的生成数据分布。
生成器与判别器是对抗的关系,价值函数代表了判别器的判别性能。那么,通过优化 能够在第二项 上迷惑判别器,让判别器对于 这个输入,尽可能地得到 。本质上,生成器就是在最小化这一项,也就是在最小化价值函数。
散度
为了界定两个数据分布,也就是真实数据和生成器生成数据之间的差异,需要引入 散度。
散度具有非负性。
当且仅当 , 在离散型变量下是相同的分布时,即 ,。
散度衡量了两个分布差异的程度,经常被视为两种分布间的距离。
要注意的是,,即 散度没有对称性。
最优判别器
将价值函数里的生成器固定不动,将期望写成积分的形式有:
整个式子中,只有一个变量 。次数,对被积函数,令 ,,,, 均为常数。那么,被积函数变为:
为了找到最优值 ,需要对上式求一阶导数。而且,在 的情况下有:
验证 的二阶导数 ,则 这个点为极大值,这个事实给出了最优判别器的存在可能性。
尽管在实践中我们并不知道 ,也就是真实的数据的分布。但我们在利用深度学习训练判别器时,可以让 向这个目标逐渐逼近。
最优生成器
若最优的判别器为:
我们将其代入 ,此时价值函数里只有 这一个变量:
此时,通过变换,我们可以得到下面的式子:
这个变换比较复杂,大家可以检验步与步之间的恒等性判断。根据对数的一些基本变换,可以得到:
最终得到:
因为 散度的非负性,那么就可以知道 就是 的最小值,而且最小值是在当且仅当 时取得。这其实就是真实数据分布等于生成器的生成数据分布,可以从数学理论上证明了它的存在性和唯一性。
GAN的实现过程
生成器的输入:即上面的 ,我们当然不能让这个分布任意化,一般会设为常见的分布类型,如高斯分布、均匀分布等等,然后生成器基于这个分布产生的数据生成自己的伪造数据来迷惑判别器。
期望如何模拟:实践中,我们是没有办法利用积分求数学期望的,所以一般只能从无穷的真实数据和无穷的生成器中采样以逼近真实的数学期望。
近似价值函数:若给定生成器 ,并希望计算 以求得判别器 。那么,首先需要从真实的数据分布 中采样 个样本 {}。并从生成器的输入,即 中采样 个样本 {}。因此,最大化价值函数 就可以使用以下表达式近似替代:
可以把 GAN 的训练过程总结为:
- 从真实数据 采样 个样本 {};
- 从生成器的输入,即噪声数据 采样 个样本 {};
- 将噪声样本 {} 投入到生成器中生成{};
- 通过梯度上升的方法,极大化价值函数,更新判别器的参数;
- 从生成器的输入,即噪声数据 另外采样 个样本{};
- 将噪声样本 {} 投入到生成器中生成 {};
- 通过梯度下降的方法,极小化价值函数,更新生成器的参数。
利用PyTorch搭建GAN生成手写识别数据
安装GPU版本PyTorch
- 打开终端,在conda 配置中添加清华源
- 编辑~/.condarc,将- defaults整行删除
- 安装PyTouch GPU版本
使用conda安装,不用自己额外配置依赖包和版本兼容问题,conda会自动配置好,而且可以直接在jupyter中调用,非常方便。
一般需要等待很长时间,而且会经常中断,中断直接再重复运行安装命令即可,会继续安装之前没装上的
得益于国内无与伦比的网络环境,100Mb的宽带完全失灵,下载了大概一个小时,中途中断了三四次,终于装好了!!我感觉天快亮了... ...
训练GAN
为了方便可视化,直接用jupyter notebook
- 首先,导入需要用的模块
-
下载并解压mnist数据集
transform
函数允许我们把导入的数据集按照一定规则改变结构,我们在这里引入了Normalize
将会把Tensor
正则化。即:Normalized_image=(image-mean)/std
。这样做的目的是便于后续的训练。
-
接下来,搭建深度学习模型,用于构建判别器和生成器。这里通过引入
nn.Module
基类的方法来搭建
判别器构建过程,遵照 PyTorch 的 Sequential 网络搭建法。我们用4
层网络结构,并把每层都使用全连接配上LeakyReLU
激活再带上dropout
防止过拟合。最后一层,用sigmoid
保证输出值是一个0
到1
之间的概率值。设计前馈过程函数时,注意把每个样本大小 的输入矩阵先转换为784
的向量用于全连接。
-
接下来构建生成器。本模型中的设定生成器的每个输入样本是大小为
100
的向量,通过全连接层配上LeakyReLU
激活搭建,最后一层用tanh
激活,且保证每个样本输出是一个784
的向量。
-
接下来实例化生成器与判别器,设定学习率和损失函数。价值函数按照定义是:
PyTorch 中,BCELoss 表示二项 Cross Entropy,它的展开形式是:
其中y
是label
,x
是输出。那么,对于0
和1
这两种label
而言,当 ,上式第一项不存在,就剩下 的第二项。当 ,上式第二项不存在,就剩下 的第一项。那么 BCELoss 的结构就与损失函数 相同,只不过我们定义的损失函数有对真实数据与对生成器生成的数据两种情况的输出。
-
接下来,就可以定义如何训练判别器了。值得注意的是,这里需要设置
zero_grad()
来消除之前的梯度,以免造成梯度叠加。此外,我们通过将真实数据的损失和伪造数据的损失两部分相加,作为最终的损失函数。然后,通过后向传播,用之前的判定器优化器优化,通过降低 BCELoss 来增大价值函数的值。
-
同样,接下来需要定义生成器的训练方法。注意,这里的
之前已经设定好生成器的每个样本输入为一个real_labels
在之后将设为1
。因为对于所有的生成器输出,我们希望它向真实的数据分布学习,那么BCELoss
此时为 。最终,我们希望判别器的输出 接近于1
,即判别器判断该数据为真实数据的概率越大。所以,这里依旧是在减少BCELoss
,则直接调用criterion
就可以设定好生成器的损失函数。
100
大小的向量,这里就将生成器的输入产生一个100
大小,且服从标准正态分布的向量。
-
一切准备就绪,开始 GAN 的训练。
以下是刚开始产生的图片
以下是最终生成的图片,可以看到,通过生成器与判定器的不断博弈,产生的图片也越来越逼真
GAN的改进
相比起卷积神经网络之于计算机视觉,循环神经网络之于自然语言处理,GAN 尚且没有一个特别适合的应用场景。主要原因是 GAN 目前还存在诸多问题。例如:
- 不收敛问题:GAN 是两个神经网络之间的博弈。试想,如果判别器提前学到了非常强的,那么生成器很容易出现梯度消失而无法继续学习。所有 GAN 的收敛性一直是个问题,这样也导致 GAN 在实际搭建过程中对各种超参数都非常敏感,需要精心设计才能完成一次训练任务;
- 崩溃问题:GAN 模型被定义为一个极小极大问题,可以说,GAN 没有一个清晰的目标函数。这样会非常容易导致,生成器在学习的过程中开始退化,总是生成相同的样本点,而这也进一步导致判别器总是被喂给相同的样本点而无法继续学习,整个模型崩溃;
- 模型过于自由: 理论上,我们希望 GAN 能够模拟出任意的真实数据分布,但事实上,由于我们没有对模型进行事先建模,再加上「真实分布与生成分布的样本空间并不完全重合」是一个极大概率事件。那么,对于较大的图片,如果像素一旦过多,GAN 就会变得越来越不可控,训练难度非常大。
网友评论