生成性对抗网络(GAN)是一种神经网络,可以生成与人类生成的内容类似的材料,如图像、音乐、语音或文本。
近几年来,GANS一直是一个活跃的研究课题。Facebook的AI研究总监Yann LeCun称对抗性训练是机器学习领域“过去10年来最有趣的想法”。下面,在实现您自己的两个生成式模型之前,您将了解GAN是如何工作的。
免费奖励:关于Python掌握的5点想法,这是一门面向Python开发人员的免费课程,它向您展示了将Python技能提升到更高水平所需的路线图和心态。
生成性对抗性网络是一种机器学习系统,可以学习模仿给定的数据分布。它们最早是由深度学习专家伊恩·古德费罗和他的同事在2014年发表的一篇NeurIPS论文中提出的。
GANS由两个神经网络组成,一个被训练成生成数据,另一个被训练成区分真实数据和假数据(因此该模型具有“对抗性”性质)。尽管结构生成数据的想法并不新鲜,但当涉及到图像和视频生成时,Gans已经提供了令人印象深刻的成果,例如:
使用CycleGAN进行样式转换,可以对图像执行大量令人信服的样式转换。
使用StyleGan生成人脸,就像网站上演示的那样,这个人不存在。
生成数据的结构,包括GAN,被认为是生成模型,而不是更广泛研究的判别模型。在深入了解Gans之前,您将了解这两种模型之间的差异。
如果您研究过神经网络,那么您遇到的大多数应用程序可能都是使用区别性模型实现的。另一方面,生成性对抗性网络是称为生成性模型的另一类模型的一部分。
判别模型是用于大多数监督分类或回归问题的模型。举个分类问题的例子,假设您想训练一个模型,将手写数字的图像从0分类到9。为此,您可以使用一个带标签的数据集,其中包含手写数字的图像及其关联的标签,这些标签指示每个图像代表哪个数字。
在训练过程中,您将使用一种算法来调整模型的参数。目标是最小化损失函数,以便模型在给定输入的情况下学习输出的概率分布。训练阶段结束后,您可以使用该模型通过估计输入对应的最可能的数字来对新的手写数字图像进行分类,如下图所示:
您可以将分类问题的区别性模型描述为块,这些块使用训练数据来学习类之间的边界。然后,他们使用这些边界来区分输入并预测其类别。在数学术语中,判别模型学习给定输入x的输出y的条件概率P(y|x)。
除神经网络外,其他结构也可用作判别模型,如Logistic回归模型和支持向量机(SVMs)。
然而,像Gans这样的生成性模型被训练成根据概率模型来描述数据集是如何生成的。通过从生成模型中采样,您能够生成新数据。虽然判别模型用于监督学习,但生成模型通常用于未标记的数据集,可以被视为一种非监督学习形式。
使用手写数字的数据集,您可以训练生成模型来生成新的数字。在训练阶段,使用某种算法来调整模型的参数,使损失函数最小化,并学习训练集的概率分布。然后,通过训练模型,您可以生成新的样本,如下图所示:
为了输出新的样本,生成模型通常考虑影响模型生成的样本的随机因素。用于驱动发生器的随机样本是从潜在空间中获得的,在该潜在空间中,向量表示所生成样本的一种压缩形式。
与判别性模型不同,生成模型学习输入数据x的概率P(X),并且通过获得输入数据的分布,它们能够生成新的数据实例。
注意:生成性模型也可以与带标签的数据集一起使用。当它们是这样的时候,它们被训练来学习给定输出y的输入x的概率P(x|y)。它们也可以用于分类任务,但一般来说,判别模型在分类时表现得更好。
您可以在关于区分量词与生成量词的文章:Logistic回归和朴素贝叶斯的比较中找到有关区别量词和生成量词的相对优势和劣势的更多信息。
尽管Gans在最近几年受到了很多关注,但它们并不是唯一可以用作生成性模型的建筑。除了Gans之外,还有各种其他生成性模型体系结构,例如:
然而,由于在图像和视频生成方面取得的令人兴奋的结果,甘斯最近吸引了最多的公众兴趣。
现在您已经了解了生成式模型的基础知识,您将了解GAN是如何工作的,以及如何训练它们。
生成性对抗性网络是由两个神经网络组成的整体结构,一个称为生成器,另一个称为鉴别器。
生成器的作用是估计真实样本的概率分布,以便提供与真实数据相似的生成样本。鉴别器又被训练来估计给定样本来自真实数据而不是由生成器提供的概率。
这些结构被称为生成性对抗网络,因为生成器和鉴别器被训练成相互竞争:生成器试图更好地欺骗鉴别器,而鉴别器试图更好地识别生成的样本。
要了解GAN训练的工作原理,请考虑一个玩具示例,其中的数据集由二维样本(x₁,x₂)组成,其中x₁在0到2π之间,x₂=Sin(x₁),如下图所示:
如您所见,此数据集由位于正弦曲线上的点(x₁,x₂)组成,具有非常特殊的分布。用于生成与数据集样本相似的对(x̃₁,x̃₂)的GaN的整体结构如下图所示:
生成器G被馈送来自潜在空间的随机数据,其作用是生成与真实样本相似的数据。在本例中,您有一个二维潜在空间,因此生成器由随机的(z₁,z₂)对馈送,并且需要对其进行变换,使其与实际样本相似。
神经网络G的结构可以是任意的,允许您将神经网络用作多层感知器(MLP)、卷积神经网络(CNN)或任何其他结构,只要输入和输出的维度与潜在空间的维度和真实数据的维度相匹配。
向鉴别器D馈送来自训练数据集的真实样本或由G提供的生成样本。它的作用是估计输入属于真实数据集的概率。执行训练,以便D在馈送真实样本时输出1,在馈送生成样本时输出0。
与G一样,您可以为D选择任意的神经网络结构,只要它尊重必要的输入和输出维度。在本例中,输入是二维的。对于二进制鉴别器,输出可以是从0到1的标量。
GAN训练过程由两人极小极大博弈组成,其中D用于最小化真实样本和生成样本之间的区分误差,G用于最大化D出错的概率。
虽然包含真实数据的数据集没有标记,但D和G的训练过程是以有监督的方式执行的。在训练的每一步,D和G的参数都会更新。实际上,在最初的GAN方案中,D的参数更新k次,而G的参数每训练一步只更新一次。但是,为了使训练更简单,您可以认为k等于1。
为了训练D,在每次迭代中,您将从训练数据中提取的一些实际样本标记为1,将G提供的一些生成样本标记为0。这样,您可以使用常规的监督训练框架来更新D的参数,以便最小化损失函数,如以下方案所示:
对于包含带标签的实数样本和生成的样本的每批训练数据,您可以更新D的参数以最小化损失函数。在更新D的参数之后,您可以训练G以生成更好的生成样本。G的输出连接到D,D的参数保持冻结,如下图所示:
您可以将由G和D组成的系统想象为单个分类系统,该系统接收随机样本作为输入并输出分类,在这种情况下,分类可以解释为概率。
当G做得足够好来愚弄D时,输出概率应该接近于1。在这里,您也可以使用传统的监督训练框架:用于训练由G和D组成的分类系统的数据集将由随机输入样本提供,并且与每个输入样本相关联的标签将为1。
在训练过程中,随着D和G参数的更新,预计G给出的生成样本将更接近真实数据,D将更难区分真实数据和生成数据。
现在您已经了解了GAN的工作方式,可以使用PyTorch实现您自己的GAN了。
作为生成性对抗性网络的第一次实验,您将实现上一节中描述的示例。
要运行该示例,您将使用PyTorch库,您可以使用Anaconda Python发行版以及Conda包和环境管理系统安装该库。要了解更多关于蟒蛇和Conda的信息,请查看有关在Windows上为机器学习设置Python的教程。
激活CONDA环境后,提示符将显示其名称GAN。然后,您可以在环境中安装必要的软件包:
由于PyTorch是一个非常活跃的开发框架,因此API可能会在新版本中发生变化。为了确保示例代码能够运行,您需要安装特定的版本1.4.0。
除了PyTorch之外,您还将使用Matplotlib处理绘图,并使用Jupyter Notebook在交互式环境中运行代码。这样做不是强制性的,但它促进了机器学习项目的工作。
有关使用Matplotlib和Jupyter笔记本的更新,请参阅Python Ploting with Matplotlib(Guide)和Jupyter Notebook:an Introduction。
在打开Jupyter Notebook之前,您需要注册Conda gan环境,以便可以使用它作为内核创建笔记本。为此,在激活GAN环境的情况下,运行以下命令:
现在您可以通过运行Jupyter Notebook打开Jupyter Notebook。通过单击新建,然后选择GAN来创建新笔记本。
在这里,您将使用TORCH导入PyTorch库。导入nn也只是为了能够以一种不太冗长的方式设置神经网络。然后导入数学以获得pi常量的值,并像往常一样将Matplotlib绘图工具作为PLT导入。
设置随机生成器种子是一种很好的做法,这样实验就可以在任何机器上完全相同地复制。要在PyTorch中执行此操作,请运行以下代码:
数字111表示用于初始化随机数生成器的随机种子,该随机种子用于初始化神经网络的权重。尽管实验的性质是随机的,但只要使用相同的种子,它就必须提供相同的结果。
训练数据由对(x₁,x₂)组成,使得x₂由x₁对于x₁在0到2π的区间内的正弦值组成。您可以按如下方式实现:
1 TRAIN_DATA_LENGTH=1024 2 TRAIN_DATA=手电筒。零((TRAIN_DATA_LENGTH,2))3 TRAIN_DATA[:,0]=2*数学。PI*火炬。RAND(TRAIN_DATA_LENGTH)4 TRAIN_DATA[:,1]=手电筒。SIN(TRAIN_DATA[:,0])5 TRAIN_LABEGS=手电筒。零(TRAIN_DATA_LENGTH)6 TRAIN_SET=[7(TRAIN_DATA[i],TRAIN_LABESTS[i]),i在范围内(TRAIN_DATA_LENGTH)8]。
在这里,您将使用1024对(x₁,x₂)组成一个训练集。在第2行中,您初始化了Train_Data,这是一个维度为1024行和2列的张量,全部包含零。张量是类似于NumPy数组的多维数组。
在第3行中,使用第一列TRAIND_DATA存储0到2π间隔内的随机值。然后,在第4行中,将张量的第二列计算为第一列的正弦。
接下来,您将需要一个标签张量,这是PyTorch的数据加载器所需的。由于Gans使用无监督学习技术,标签可以是任何东西。毕竟,它们不会被使用。
在第5行中,您创建了Train_Labels,这是一个用零填充的张量。最后,在第6行到第8行,您将Train_Set创建为一个元组列表,每个元组中的每一行Train_Data和Train_Labels都按照PyTorch的数据加载器的预期表示。
您可以通过绘制每个点(x₁,x₂)来检查培训数据:
在这里,您将创建一个名为trainloader的数据加载器,它将混洗TrainSet中的数据,并返回一批32个样本,您将使用这些样本训练神经网络。
在设置训练数据之后,您需要为构成GaN的鉴别器和生成器创建神经网络。在下一节中,您将实现鉴别器。
在PyTorch中,神经网络模型由继承自nn.Module的类表示,因此您必须定义一个类来创建鉴别器。有关定义类的更多信息,请看一下Python3中的面向对象编程(OOP)。
该鉴别器是具有二维输入和一维输出的模型。它将从真实数据或生成器接收样本,并将提供样本属于真实训练数据的概率。下面的代码显示了如何创建鉴别器:
1级鉴别器(nn.。模块):2 def__init__(Self):3 Super()。__init__()4自身。型号=nn。顺序(5 nn.。线性(2,256),6nn.。Relu(),7nn.。辍学(0.3),8 nn。线性(256,128),9 nn。Relu(),10nn。辍学(0.3),11 nn。线性(128,64),12nn.。Reu(),13 nn.。辍学(0.3),14 nn。线性(64,1),15nn.。Sigmoid(),16)17 18 def ward(self,x):19 output=self。型号(X)20返回输出。
您可以使用.__init__()来构建模型。首先,您需要调用Super().__init__()来从nn.Module运行.__init__()。您正在使用的鉴别器是使用nn.Sequential()以顺序方式定义的MLP神经网络。它具有以下特点:
第5行和第6行:输入是二维的,第一个隐含层由256个具有RELU激活的神经元组成。
第8、9、11和12行:第二和第三隐藏层分别由128个和64个神经元组成,具有RELU激活。
第14行和第15行:输出由单个神经元组成,该神经元具有S形激活以表示概率。
第7、10和13行:在第一个、第二个和第三个隐藏层之后,使用退出以避免过度拟合。
最后,使用.ward()描述如何计算模型的输出。这里,x表示模型的输入,即二维张量。在此实现中,输出是通过将输入x提供给您定义的模型而获得的,而无需任何其他处理。
鉴别器代表您已经定义并准备好训练的神经网络的一个实例。但是,在实施训练循环之前,您的GAN还需要一个发电机。您将在下一节中实现一个。
在生成性对抗性网络中,生成器是从潜在空间中获取样本作为其输入,并生成与训练集中的数据相似的数据的模型。在本例中,它是一个具有二维输入的模型,它将接收随机点(z₁,z₂),而二维输出必须提供与训练数据中的点相似的(x̃₁,x̃₂)点。
该实现类似于您对鉴别器所做的操作。首先,您必须创建一个继承自nn.Module的Generator类,定义神经网络体系结构,然后需要实例化一个Generator对象:
1类生成器(nn。模块):2 def__init__(Self):3 Super()。__init__()4自身。型号=nn。顺序(5 nn.。线性(2,16),6nn.。Relu(),7nn.。线性(16,32),8nn.。Relu(),9 nn.。线性(32,2),10)11 12向前定义(SELF,x):13 OUTPUT=SELF。型号(X)14返回输出15 16发生器=发电机()
这里,生成器代表生成器神经网络。它由两个隐含层组成,分别有16个和32个神经元,都有RELU激活,还有一个线性激活层,在输出端有2个神经元。这样,输出将由具有两个元素的向量组成,这两个元素可以是从负无穷大到无穷大的任何值,这将表示(x̃₁,x̃₂)。
既然您已经定义了鉴别器和生成器的模型,您就可以执行培训了!
在训练模型之前,您需要设置一些参数以在训练期间使用:
第1行设置学习率(LR),您将使用它来调整网络权重。
第2行设置纪元数(Num_Pechs),它定义将使用整个训练集执行多少次重复训练。
第3行将变量Loss_Function赋给二进制交叉熵函数BCELoss(),它是您将用来训练模型的损失函数。
二进制交叉熵函数是用于训练鉴别器的合适的损失函数,因为它考虑了二进制分类任务。它还适用于训练生成器,因为它将其输出馈送到鉴别器,鉴别器提供二进制可观测输出。
PyTorch在torch.optim中为模型训练实现了各种权重更新规则。您将使用ADAM算法训练鉴别器和生成器模型。要使用torch.optim创建优化器,请运行以下行:
最后,您需要实现一个训练循环,其中将训练样本提供给模型,并更新它们的权重以最小化损失函数:
1对于范围内的纪元(Num_Epochs):2对于n,枚举(Train_Loader)中的(REAL_SAMPLES,_):3#用于训练鉴别器的数据4 REAL_SAMPLES_LABEL=TORCH。1((Batch_Size,1))5潜伏期空间样本=火炬。RANDN((Batch_Size,2))6 Generated_Samples=Generator(潜在空间_Samples)7 Generated_Samples_Labels=火炬。零((Batch_Size,1))8 ALL_SAMPLES=手电筒。CAT((real_Samples,Generated_Samples))9 ALL_SAMPLES_Labels=手电筒。CAT(10(Real_Samples_Labels,Generated_Samples_Labels)11)12 13#训练鉴别器14鉴别器。Zero_grad()15输出鉴别器=鉴别器(所有样本)16损耗鉴别器=损失函数(17输出鉴别器,所有样本标签)18 LO。
.