Ian Goodfellow:生成对抗网络 GAN 的公式是怎样推导出来的
昨天,谷歌大脑研究科学家、《深度学习》的作者之一Ian Goodfellow在Twitter推荐了他最喜欢的两个机器学习“黑魔法”(Theory Hack)。Ian Goodfellow还是生成对抗网络GAN的提出者,利用这两个技巧,他在著名的GAN论文中推导了一个公式。
很多时候,我们想用代数/微积分来分析神经网络的最优行为。神经网络模型通常非常复杂,用代数方法来实现权重衰减或许可行,但想用代数方法来解决神经网络中大多数函数的参数优化问题就会太过复杂。
为了得到一个不那么复杂的模型,一个常见的直觉方法是使用线性模型。线性模型很好,因为它能很好的解决凸优化问题。但线性模型也有缺点:它过于简单,很多神经网络能做的事情线性模型不能做。这样,解决方法就简化了。
Theory Hack#1:将神经网络建模为一个任意函数(因此可以优化所有函数f的空间,而不是特定神经网络架构的参数theta)。与使用参数和特定的架构相比,这种方法非常简洁。
将神经网络视为一个函数,保留了线性模型的主要优点:多种凸函数问题。例如,分类器的交叉熵损失在函数空间中是凸的。
这个假设不是太准确,特别是与线性模型假设相比。但根据万能逼近定理(universal approximator theorem),神经网络可以较好地近似任意函数。
Theory Hack#2:如果你在同一空间优化所有函数时遇到困难,可以将函数想象成一个包含很多项(entries)的向量。评估函数f(x),其中x在R ^ n中,设想成在一个向量中查找f_x,其中x是一个整数索引。
有了Theory Hack#2,现在对函数进行优化就变成了一个常规的微积分问题。这种方法很直观,但不是100%准确。有关更多正式版本和关于何时可以使用的限制信息,请参阅《深度学习》书的19.4.2部分:http://www.deeplearningbook.org/contents/inference.html
利用这两个 theory hack,我和共同作者推导了GAN论文(Generative Adversarial Nets)中的第2个公式:https://papers.nips.cc/paper/5423-generative-adversarial-nets.pdf ...
最后,Convex Optimization 这本书的3.2节有更多这样的theory hacks
原文发布时间为:2018-05-16
本文作者:肖琴
本文来自云栖社区合作伙伴新智元,了解相关信息可以关注“AI_era”。
原文链接:Ian Goodfellow:生成对抗网络 GAN 的公式是怎样推导出来的
GAN生成对抗网络学习笔记
一、简介GAN是Generative Adversarial Nets的缩写,是由Ian Goodfellow14年提出的,这个东西和深度学习里其他很多东西一样,看表面原理其实很好理解,就是两个网络不断对抗互相学习,最终得到一个比较好的结果,但是想搞清楚原理并且能从头推一遍公式其实还是要花不少时间的,尤其是原作里很多细节并没有展开讲,因此我觉得还是比较有必要把推导的过程记录下来的。当然,原始的GAN存在很多问题,之后再仔细写写原始GAN的缺点以及原始GAN的一些变种。
我们先来建立一个感性的认识:GAN就是一个相互博弈相互提高,最终实现生成的数据的分布和真实的数据分布重合的过程。首先要牢记,我们提出GAN的目的是人工生成数据,因此,GAN的一个网络为生成器generator,其次我们为了能生成以假乱真的数据,需要把一个啥也不会的生成器不断提升,此时用到的就是判别器discriminator,它的功能就是去鉴定生成器生成的数据。对于一个判别器,当它无法判断生成器生成的数据是真是假时,我们就得到了一个最优的生成器,然后再用生成器生成数据去训练判别器,再用判别器去鉴定生成的数据以提高判别器,循环往复直至都无法提高,有点像武侠小说中左右互搏一样,自己和自己打。
二、理论分析我之前看过其他的博客或者一些教程,一般上来都会介绍一些会用到的知识,但是我个人畏难情绪比较严重,上来讲好多没见过的概念会让我觉得比较害怕,所以自己写的话我会直接上公式:这个公式看上去很复杂,但是不要慌,问题不大,一步步来。首先,这个公式我们只需要理解就可以了,很多时候先理解了含义再想怎么来的会比直接想怎么来的更简单。根据概念,我们先要有最优的判别器和最优的生成器,那么我们就可以定义一个D(x)和G(x),其中D(x)是判别器,得到的是x属于真实数据的概率,G(x)是生成器根据输入x生成的数据。有了这两个之后我们回头来看式子,D(x)就是x属于真实数据的概率,G(z)就是根据输入z得到的生成的数据,D(G(z))得到的就是生成数据是真实数据的概率,这项越大说明判别器越挫,竟然把生成的数据判断为是真实数据,因此对于最优判别器,logD(x)的期望要最大,D(G(z))的期望要最小,1-D(G(z))的期望要最大,V要最大;对于最优生成器,D(G(z))的期望要最大(生成器想骗过判别器),1-D(G(z))的期望要最小,V要最小,这样我们就得到了这个函数的意义,然后再对其取对数得到GAN的损失函数。
了解了损失函数的含义,我们直接来看具体的优化过程:其实很简单,就三步:1、优化判别器。2、优化生成器。3、循环至此算是建立起了对GAN的基础认识,对于非专业的人来讲我觉得了解到这一步已经可以把不了解的人骗的一愣一楞的了,接下来的都是一些证明和公式推导的细节,其中对于为什么pg(x)可以收敛到pdata(x),我看了很多讲解的博客都是一笔带过,我简单讲一下我的理解。。这张是最优判别器的值的推导,只需要把期望化成积分的形式代入,然后就都是一些高中数学的推导。这张是在得到了最优的判别器后,把损失函数改写成-log4与JS散度的和的形式,其中JS散度是用来衡量两个概率分布的距离的一个量,大于等于0,当且仅当两个概率分布相等时为0(具体可以看wiki),因此此时的损失函数最小为0,也就是我们的生成器的任务也完成了。顺带说一句,这个地方我试过令pdata(x) = a , pg(x)=b,想上面一样去用初等数学的内容去进行函数分析,但是却失败了,我后来想了想,上面之所以能对D(x)进行求导因为那是一个判别函数,其余两项关于它的导数都为0,下面的式子中无论是关于pdata还是关于pg亦或是关于x求导都会出现你中有我我中有你的情况,从而无法化简。到这里为止我们就证明了GAN的最优解的存在,也就是真实数据的分布等于生成数据的分布,符合我们的感性认识,接下来就是对于该函数收敛的证明,也就是对于我们的优化过程合理性的证明。
原文中对于收敛的证明叙述了很多,提到了次倒数啊上确界啊之类的,我开始没看明白,后来也看了很多博客,全部都是一笔带过或者直接翻译,我尝试理解了一下这段话,个人感觉核心就是一句话:这个凸函数每个点的导数都属于这个凸函数的导数。原因在于只要证明了函数每个点的导数都属于函数的导数,当G和D有了足够的量,并且每次的更新量足够小,也就可以保证了梯度下降了有效性,并且最终函数可以收敛。
以上算是我对于GAN学习的一些记录,写的很简陋(可以说是很原始了,还有手写的,,,),网上写的好的有很多,比如这篇还有b站上台大李宏毅老师的视频课等等。
【GAN货】生成式对抗网络资料荟萃(原理/教程/报告/论文/实战/资料库)
简介
生成式对抗网络,是近些年来最火的无监督学习方法之一,模型由Goodfellow等人在2014年首次提出,将博弈论中非零和博弈思想与生成模型结合在一起,巧妙避开了传统生成模型中概率密度估计困难等问题,是生成模型达到良好的效果。
基本思想
囚徒困境
1950年,由就职于兰德公司的梅里尔·弗勒德和梅尔文·德雷希尔拟定出相关困境的理论,后来由顾问艾伯特·塔克以囚徒方式阐述,并命名为“囚徒困境”。经典的囚徒困境如下:
警方逮捕甲、乙两名嫌疑犯,但没有足够证据指控二人有罪。于是警方分开囚禁嫌疑犯,分别和二人见面,并向双方提供以下相同的选择:
若一人认罪并作证检控对方(相关术语称“背叛”对方),而对方保持沉默,此人将即时获释,沉默者将判监10年。
若二人都保持沉默(相关术语称互相“合作”),则二人同样判监半年。
若二人都互相检举(互相“背叛”),则二人同样判监5年。
纳什均衡
如同博弈论的其他例证,囚徒困境假定每个参与者(即“囚徒”)都是利己的,即都寻求最大自身利益,而不关心另一参与者的利益。那么囚徒到底应该选择哪一项策略,才能将自己个人的刑期缩至最短?两名囚徒由于隔绝监禁,并不知道对方选择;而即使他们能交谈,还是未必能够尽信对方不会反口。就个人的理性选择而言,检举背叛对方所得刑期,总比沉默要来得低。试设想困境中两名理性囚徒会如何作出选择:
若对方沉默、我背叛会让我获释,所以会选择背叛。
若对方背叛指控我,我也要指控对方才能得到较低的刑期,所以也是会选择背叛。
二人面对的情况一样,所以二人的理性思考都会得出相同的结论——选择背叛。背叛是两种策略之中的支配性策略。因此,这场博弈中唯一可能达到的纳什均衡,就是双方参与者都背叛对方,结果二人同样服刑5年。 这场博弈的纳什均衡,显然不是顾及团体利益的帕累托最优解决方案。以全体利益而言,如果两个参与者都合作保持沉默,两人都只会被判刑半年,总体利益更高,结果也比两人背叛对方、判刑5年的情况较佳。但根据以上假设,二人均为理性的个人,且只追求自己个人利益。均衡状况会是两个囚徒都选择背叛,结果二人判监均比合作为高,总体利益较合作为低。这就是“困境”所在。例子有效地证明了:非零和博弈中,帕累托最优和纳什均衡是互相冲突的。
生成模型与判别模型
机器学习的任务就是学习一个模型,应用这个模型,对给定的输入预测相应的输出。这个模型的一般形式为决策函数Y=f(x),或者条件概率分布:Y=argmaxYP(Y|X)。 机器学习方法又可以分为生成方法和判别方法,所学到的模型分别称为生成模型(Generative Model)和判别模型(Discriminative Model)。
判别方法由数据直接学习决策函数 f(X),或者条件概率分布P(Y|X)作为预测模型,即判别模型。 生成方法由数据学习联合分布P(X,Y),然后求出条件概率分布 P(Y|X)做预测的模型,即为生成模型,具体公式如下:P(Y|X)=P(X,Y)P(X)
相比于判别方法,生成模型更关注数据之间的内在联系,需要学习联合分布;而判别模型更关注于给定输入 X,模型应该预测怎么样的输出 Y。由生成模型可以推导出判别模型,反之则不能。
生成式对抗网络
什么是对抗生成网络?用Ian Goodfellow自己的话来说:
生成对抗网络是一种生成模型(Generative Model),其背后基本思想是从训练库里获取很多训练样本,从而学习这些训练案例生成的概率分布。而实现的方法,是让两个网络相互竞争,‘玩一个游戏’。其中一个叫做生成器网络( Generator Network),它不断捕捉训练库里真实图片的概率分布,将输入的随机噪声(Random Noise)转变成新的样本(也就是假数据)。另一个叫做判别器网络(Discriminator Network),它可以同时观察真实和假造的数据,判断这个数据到底是不是真的。”基本原理
生成对抗网络是一个强大的基于博弈论的生成模型学习框架。该模型由GoodFellow在2014年首次提出,结合了生成模型和对抗学习思想。生成对抗网络的目的是训练一个生成模型G,给定随机噪声向量noise,生成符合真实数据分布的样本。 G训练信号来自于判别器 D(x)。 D(x)的学习目标目是准确区分输入样本的来源(真实数据或生成数据), 而生成器 D的学习目标是生成尽可能真实的数据,使得判别器 G认为生成数据是真实的。整个模型使用梯度下降法进行训练,生成器和判别器可以根据特定的任务选择具体的模型,包括但不限于全连接神经网络(FCN)、卷积神经网络(CNN)、回归神经网络(RNN)、长短期记忆模型(LSTM)等。
基础知识台大李弘毅老师gan课程。参考链接:
#youtube#:
https://www.youtube.com/watch?v=DQNNMiAP5lw&index=1&list=PLJV_el3uVTsMq6JEFPW35BCiOQTsoqwNw]
#bilibili#
https://www.bilibili.com/video/av24011528?from=search&seid=11459671583323410876]
成对抗网络初学入门:一文读懂GAN的基本原理
[http://www.xtecher.com/Xfeature/view?aid=7496]
深入浅出:GAN原理与应用入门介绍
[https://zhuanlan.zhihu.com/p/28731033]
港理工在读博士李嫣然深入浅出GAN之应用篇 [https://pan.baidu.com/s/1o8n4UDk] 密码: 78wt
萌物生成器:如何使用四种GAN制造猫图[https://zhuanlan.zhihu.com/p/27769807]
GAN学习指南:从原理入门到制作生成Demo [https://zhuanlan.zhihu.com/p/24767059x]
生成式对抗网络GAN研究进展 [http://blog.csdn.net/solomon1558/article/details/52537114]
报告教程
微软剑桥研究院153页最新GAN教程(附代码)
【CVPR2018】Google GAN之父Ian Goodfellow 最新演讲:生成对抗网络介绍
【干货】Google GAN之父Ian Goodfellow ICCV2017演讲:解读生成对抗网络的原理与应用
[论文笔记] GAN开山之作及最新综述
NIPS 2016教程:生成对抗网络
[https://arxiv.org/pdf/1701.00160.pdf]
训练GANs的技巧和窍门[https://github.com/soumith/ganhacks]
OpenAI生成模型[https://blog.openai.com/generative-models/]
论文前沿
对抗实例的解释和利用(Explaining and Harnessing Adversarial Examples)2014[https://arxiv.org/pdf/1412.6572.pdf]
基于深度生成模型的半监督学习( Semi-Supervised Learning with Deep Generative Models )2014[https://arxiv.org/pdf/1406.5298v2.pdf]
条件生成对抗网络(Conditional Generative Adversarial Nets)2014[https://arxiv.org/pdf/1411.1784v1.pdf]
基于深度卷积生成对抗网络的无监督学习(Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks (DCGANs))2015[https://arxiv.org/pdf/1511.06434v2.pdf]
基于拉普拉斯金字塔生成式对抗网络的深度图像生成模型(Deep Generative Image Models using a Laplacian Pyramid of Adversarial Networks)2015[http://papers.nips.cc/paper/5773-deep-generative-image-models-using-a-5. laplacian-pyramid-of-adversarial-networks.pdf]
生成式矩匹配网络(Generative Moment Matching Networks)2015[http://proceedings.mlr.press/v37/li15.pdf]
超越均方误差的深度多尺度视频预测(Deep multi-scale video prediction beyond mean square error)2015[https://arxiv.org/pdf/1511.05440.pdf]
通过学习相似性度量的超像素自编码(Autoencoding beyond pixels using a learned similarity metric)2015[https://arxiv.org/pdf/1512.09300.pdf]
对抗自编码(Adversarial Autoencoders)2015[https://arxiv.org/pdf/1511.05644.pdf]
基于像素卷积神经网络的条件生成图片(Conditional Image Generation with PixelCNN Decoders)2015[https://arxiv.org/pdf/1606.05328.pdf]
通过平均差异最大优化训练生成神经网络(Training generative neural networks via Maximum Mean Discrepancy optimization)2015[https://arxiv.org/pdf/1505.03906.pdf]
训练GANs的一些技巧(Improved Techniques for Training GANs)2016
[https://arxiv.org/pdf/1606.03498v1.pdf]
InfoGAN:基于信息最大化GANs的可解释表达学习(InfoGAN:Interpretable Representation Learning by Information Maximizing Generative Adversarial Nets)2016[https://arxiv.org/pdf/1606.03657v1.pdf]
上下文像素编码:通过修复进行特征学习(Context Encoders: Feature Learning by Inpainting)2016
[http://www.cvfoundation.org/openaccess/content_cvpr_2016/papers/Pathak_Context_Encoders_Feature_CVPR_2016_paper.pdf]
生成对抗网络实现文本合成图像(Generative Adversarial Text to Image Synthesis)2016[http://proceedings.mlr.press/v48/reed16.pdf]
对抗特征学习(Adversarial Feature Learning)2016[https://arxiv.org/pdf/1605.09782.pdf]
结合逆自回归流的变分推理(Improving Variational Inference with Inverse Autoregressive Flow )2016[https://papers.nips.cc/paper/6581-improving-variational-autoencoders-with-inverse-autoregressive-flow.pdf]
深度学习系统对抗样本黑盒攻击(Practical Black-Box Attacks against Deep Learning Systems using Adversarial Examples)2016[https://arxiv.org/pdf/1602.02697.pdf]
参加,推断,重复:基于生成模型的快速场景理解(Attend, infer, repeat: Fast scene understanding with generative models)2016[https://arxiv.org/pdf/1603.08575.pdf]
f-GAN: 使用变分散度最小化训练生成神经采样器(f-GAN: Training Generative Neural Samplers using Variational Divergence Minimization )2016[http://papers.nips.cc/paper/6066-tagger-deep-unsupervised-perceptual-grouping.pdf]
在自然图像流形上的生成视觉操作(Generative Visual Manipulation on the Natural Image Manifold)2016[https://arxiv.org/pdf/1609.03552.pdf]
对抗性推断学习(Adversarially Learned Inference)2016[https://arxiv.org/pdf/1606.00704.pdf]
基于循环对抗网络的图像生成(Generating images with recurrent adversarial networks)2016[https://arxiv.org/pdf/1602.05110.pdf]
生成对抗模仿学习(Generative Adversarial Imitation Learning)2016[http://papers.nips.cc/paper/6391-generative-adversarial-imitation-learning.pdf]
基于3D生成对抗模型学习物体形状的概率隐空间(Learning a Probabilistic Latent Space of Object Shapes via 3D Generative-Adversarial Modeling)2016[https://arxiv.org/pdf/1610.07584.pdf]
学习画画(Learning What and Where to Draw)2016[https://arxiv.org/pdf/1610.02454v1.pdf]
基于辅助分类器GANs的条件图像合成(Conditional Image Synthesis with Auxiliary Classifier GANs)2016[https://arxiv.org/pdf/1610.09585.pdf]
隐生成模型的学习(Learning in Implicit Generative Models)2016[https://arxiv.org/pdf/1610.03483.pdf]
VIME: 变分信息最大化探索(VIME: Variational Information Maximizing Exploration)2016[http://papers.nips.cc/paper/6591-vime-variational-information-maximizing-exploration.pdf]
生成对抗网络的展开(Unrolled Generative Adversarial Networks)2016[https://arxiv.org/pdf/1611.02163.pdf]
基于内省对抗网络的神经图像编辑(Neural Photo Editing with Introspective Adversarial Networks)2016,原文链接:
[https://arxiv.org/pdf/1609.07093.pdf]
基于解码器的生成模型的定量分析(On the Quantitative Analysis of Decoder-Based Generative Models )2016,原文链接:
[https://arxiv.org/pdf/1611.04273.pdf]
结合生成对抗网络和Actor-Critic 方法(Connecting Generative Adversarial Networks and Actor-Critic Methods)2016,原文链接:
[https://arxiv.org/pdf/1610.01945.pdf]
通过对抗网络使用模拟和非监督图像训练( Learning from Simulated and Unsupervised Images through Adversarial Training)2016,原文链接:
[https://arxiv.org/pdf/1612.07828.pdf]
基于上下文RNN-GANs的抽象推理图的生成(Contextual RNN-GANs for Abstract Reasoning Diagram Generation)2016,原文链接:
[https://arxiv.org/pdf/1609.09444.pdf]
生成多对抗网络(Generative Multi-Adversarial Networks)2016,原文链接:
[https://arxiv.org/pdf/1611.01673.pdf]
生成对抗网络组合(Ensembles of Generative Adversarial Network)2016,原文链接:
[https://arxiv.org/pdf/1612.00991.pdf]
改进生成器目标的GANs(Improved generator objectives for GANs) 2016,原文链接:
[https://arxiv.org/pdf/1612.02780.pdf]
训练生成对抗网络的基本方法(Towards Principled Methods for Training Generative Adversarial Networks)2017,原文链接:
[https://arxiv.org/pdf/1701.04862.pdf]
生成对抗模型的隐向量精准修复(Precise Recovery of Latent Vectors from Generative Adversarial Networks)2017,原文链接:
[https://openreview.NET/pdf?id=HJC88BzFl]
生成混合模型(Generative Mixture of Networks)2017,原文链接:
[https://arxiv.org/pdf/1702.03307.pdf]
记忆生成时空模型(Generative Temporal Models with Memory)2017,原文链接:
[https://arxiv.org/pdf/1702.04649.pdf]
AdaGAN: Boosting Generative Models". AdaGAN。原文链接[https://arxiv.org/abs/1701.04862]
Loss-Sensitive Generative Adversarial Networks on Lipschitz Densities。原文链接:[https://arxiv.org/abs/1701.06264];代码:[https://github.com/guojunq/glsgan/]
Wasserstein GAN, WGAN。原文链接:[https://arxiv.org/abs/1701.07875];代码:
[https://github.com/martinarjovsky/WassersteinGAN]
Boundary-Seeking Generative Adversarial Networks,BSGAN。原文链接:[https://arxiv.org/abs/1702.08431];代码地址:
[https://github.com/wiseodd/generative-models]
Generative Adversarial Nets with Labeled Data by Activation Maximization,AMGAN。原文链接:
[https://arxiv.org/abs/1703.02000]
Triple Generative Adversarial Nets,Triple-GAN。原文链接
[https://arxiv.org/abs/1703.02291]
BEGAN: Boundary Equilibrium Generative Adversarial Networks。原文链接:[https://arxiv.org/abs/1703.10717];代码:
[https://github.com/wiseodd/generative-models]
Improved Training of Wasserstein GANs。原文链接:[https://arxiv.org/abs/1704.00028];代码:
[https://github.com/wiseodd/generative-models]
MAGAN: Margin Adaptation for Generative Adversarial Networks。原文链接[https://arxiv.org/abs/1704.03817],
代码:
[https://github.com/wiseodd/generative-models]
Gang of GANs: Generative Adversarial Networks with Maximum Margin Ranking。原文链接:
[https://arxiv.org/abs/1704.04865]
Softmax GAN。原文链接:
[https://arxiv.org/abs/1704.06191]
Generative Adversarial Trainer: Defense to Adversarial Perturbations with GAN。原文链接:
[https://arxiv.org/abs/1705.03387]
Flow-GAN: Bridging implicit and prescribed learning in generative models。原文链接:
[https://arxiv.org/abs/1705.08868]
Approximation and Convergence Properties of Generative Adversarial Learning。原文链接:
[https://arxiv.org/abs/1705.08991]
Towards Consistency of Adversarial Training for Generative Models。原文链接:
[https://arxiv.org/abs/1705.09199]
Good Semi-supervised Learning that Requires a Bad GAN。原文链接:[https://arxiv.org/abs/1705.09783]
On Unifying Deep Generative Models。原文链接:
[https://arxiv.org/abs/1706.00550]
DeLiGAN:Generative Adversarial Networks for Diverse and Limited Data。原文链接:
[http://10.254.1.82/cache/6/03/openaccess.thecvf.com/e029768353404049dbcac9187a363d5a/Gurumurthy_DeLiGAN__Generative_CVPR_2017_paper.pdf];代码:[https://github.com/val-iisc/deligan]
Temporal Generative Adversarial Nets With Singular Value Clipping。原始链接:
[http://openaccess.thecvf.com/content_ICCV_2017/papers/Saito_Temporal_Generative_Adversarial_ICCV_2017_paper.pdf]
Least Squares Generative Adversarial Networks. LSGAN。原始链接:
[http://openaccess.thecvf.com/content_ICCV_2017/papers/Mao_Least_Squares_Generative_ICCV_2017_paper.pdf]
工程实践
【GAN货】用神经网络生成音乐
【代码资源】GAN | 七份最热GAN文章及代码分享(Github 1000+Stars)
【干货】基于GAN实现图像锐化应用(附代码)
【ACL2018】什么都能GAN,无监督神经网络翻译新方法
【干货】基于GAN实现图像锐化应用(附代码)
深度卷积生成对抗模型(DCGAN)参考链接
[https://github.com/Newmu/dcgan_code]
用Keras实现MNIST生成对抗模型,参考链接:
[https://oshearesearch.com/index.PHP/2016/07/01/mnist-generative-adversarial-model-in-keras/]
用深度学习TensorFlow实现图像修复,参考链接:
[http://bamos.github.io/2016/08/09/deep-completion/]
TensorFlow实现深度卷积生成对抗模型(DCGAN),参考链接:
[https://github.com/carpedm20/DCGAN-tensorflow]
Torch实现深度卷积生成对抗模型(DCGAN),参考链接:
[https://github.com/soumith/dcgan.torch]
Keras实现深度卷积生成对抗模型(DCGAN),参考链接:
[https://github.com/jacobgil/keras-dcgan]
使用神经网络生成自然图像(Facebook的Eyescream项目),参考链接:
[https://github.com/facebook/eyescream]
对抗自编码(AdversarialAutoEncoder),参考链接:
[https://github.com/musyoku/adversarial-autoencoder]
利用ThoughtVectors 实现文本到图像的合成,参考链接:
[https://github.com/paarthneekhara/text-to-image]
对抗样本生成器(Adversarialexample generator),参考链接:
[https://github.com/e-lab/torch-toolbox/tree/master/Adversarial]
深度生成模型的半监督学习,参考链接:
[https://github.com/dpkingma/nips14-ssl]
GANs的训练方法,参考链接:
[https://github.com/openai/improved-gan]
生成式矩匹配网络(Generative Moment Matching Networks, GMMNs),参考链接:
[https://github.com/yujiali/gmmn]
对抗视频生成,参考链接:
[https://github.com/dyelax/Adversarial_Video_Generation]
基于条件对抗网络的图像到图像翻译(pix2pix)参考链接:
[https://github.com/phillipi/pix2pix]
对抗机器学习库Cleverhans, 参考链接:
[https://github.com/openai/cleverhans]
资料收集
生成对抗网络(GAN)专知荟萃。参考资料:
[http://www.zhuanzhi.ai/topic/2001150162715950/awesome]
The GAN Zoo千奇百怪的生成对抗网络,都在这里了。你没看错,里面已经有有近百个了。参考链接:
[https://github.com/hindupuravinash/the-gan-zoo]
gan资料集锦。参考链接:
[https://github.com/nightrome/really-awesome-gan]
gan在医学上的案例集锦:
[https://github.com/xinario/awesome-gan-for-medical-imaging]
gan应用集锦:
[https://github.com/nashory/gans-awesome-applications]
生成对抗网络(GAN)的前沿进展(论文、报告、框架和Github资源)汇总,参考链接:
[http://blog.csdn.net/love666666shen/article/details/74953970]
原文发布时间为:2018-09-19本文作者:专知内容组本文来自云栖社区合作伙伴“专知”,了解相关信息可以关注“专知”。
融合 MF 和 RNN 的电影推荐系统
随着互联网技术飞速发展,在线数据越来越庞大,如何帮助用户从海量数据中找到所需信息是急需解决的问题。
个性化推荐系统能够有效的解决信息过载问题,推荐系统根据用户的历史偏好和约束为用户提供排序的个性化物品(item)推荐列表,更精准的推荐系统可以提升和改善用户体验。所推荐的物品可以包括电影、书籍、餐厅、新闻条目等等。
本文主要针对电影推荐做了深入研究,但所提出的方法可以很方便地迁移到其他物品推荐中。
目前,协同过滤技术已成为最广泛采用的推荐技术,已经被广泛应用到了很多商业系统中,比较著名的有 Amazon、Netflix、淘宝等。
传统的基于协同过滤的推荐系统是认为用户偏好和电影属性都是静态的,但他们实质是随着用时间的推移而缓慢变化的。例如,一个电影的受欢迎程度可能由外部事件(如获得奥斯卡奖)所改变。
除了对时间演化进行建模的需求外,协同过滤方法使用了未来的评分来评估当前的喜好,这一定程度违背了统计分析中的因果关系。
另一方面,随着深度学习应用的爆发式发展,特别是在计算机视觉、自然语言处理和语音方面的进展,基于深度学习的推荐系统越来越引发大家的关注。循环神经网络(RNN)理论上能够有效地对用户偏好和物品属性的动态性进行建模,基于当前的趋势,预测未来的行为。
为了有效地利用传统协同过滤推荐技术(i.e., 矩阵分解)和深度学习方法(i.e., 循环神经网络)各自的优点,捕获用户和电影之间的长期(全局)和短期(局部)关联,本文主要研究和探索矩阵分解(Matrix Factorization, MF)和循环神经网络(Recurrent Neural Network, RNN)在推荐系统上的互补性。
该项工作已经发表在 arXiv 上,更多细节可以点击本文底部的“阅读原文”查看原论文,我们会在论文发表后放出代码。
我们提出一种 LSIC 模型(Leveraging Long and Short-term Information in Context-aware movie recommendation),具体框架如下:
LSIC 模型采用了生成对抗网络(GAN)框架将基于 MF 和 RNN 的模型融合,同时捕获用户长期偏好和短期会话信息,从而最大限度地提高推荐系统的最终性能,达到 state-of-the-art 的效果。我们介绍了 4 种方法来融合 MF 模型和 RNN 模型,具体如下:
LSIC-V1: Hard 机制
采用简单的求和方法混合 MF 和 RNN 预测的分数,如图 Figure2(a),公式如下:
LSIC-V2
我们通过预训练 MF 得到用户和视频的 latent factors,再初始化用户 LSTM 和视频 LSTM 的隐状态,如图 Figure2(b)。
LSIC-V3
我们对 LSIC-V2 进展扩展,采样 MF 得到的 latent factors 作为两个 LSTM 的 static context vectors 加到每个时刻 t 的输入中,如图 Figure2(c)。
LSIC-V4
我们采用 attention 机制动态调整 MF 和 RNN 的融合方式,如图 Figure2(d),公式如下:
生成对抗网络:判别器尝试区别视频的真假,它是来自训练集中的高分视频还是生成器生成出来的伪高分视频。生成器尝试去生成真高分视频来欺骗判别器。具体细节请参考【5】,我们以这篇工作为基础做了许多改进,比如通过 GAN 有效结合用户长期偏好的短期会话的模型等。
生成器:(Figure1 的左边)类似于条件 GAN,我们的生成器 G 输入用户偏好数据和时刻 t,给用户 i 生成推荐列表,具体公式如下:
其中,M 是视频集合,m_(g, t) 是在 t 时刻生成的视频 index。
判别器(Figure1 的右边)我们采用 Siamese 网络构建判别器 D,并且以 pair-wise 的方法融合长短时模型。具体来说,判别器 D 有两个对称的 point-wise 网络,她们共享参数并且采用 pair-wise 的损失函数来更新。具体的目标函数如下:
U 是用户集合,u_i 是用户 i,m_+ 是高分视频,m__ 是随机从视频集合中采样出的低分视频,最后我们采用 hinge 损失函数来优化判别器 D,具体公式如下:
强化学习:由于视频采样的过程是离散的,不能采用标准的 GAN 公式来优化。因此,我们采用 policy gradient 来优化生成器 G,使得 G 能生成高收益的推荐列表来欺骗判别器 D。具体来说,推导如下:
数据集:为了验证我们模型的有效性,我们在两个广泛使用的数据集进行测试 Movielens100K 和 Netflix,为了评估模型的鲁棒性,我们分别进行了 3 个月 Netflix 和全集 Netflix 的实验,数据集细节如下:
对比算法: 在实验中,我们和一些 baseline 和 state-of-art 进行对比:BPR [1],PRFM [2],LambdaFM [3],RRN [4],IRGAN [5]。
实验结果:
此外,我们还进行了 case study 的分析。我们从 Netflix 数据集中随机选择两个用户并为其生成推荐列表。LSIC 模型可以更有效的进行推荐。例如,用户“8003”的电影“9 Souls”从排名5(LambdaFM)增加到排名 1(LSIC-V4)。
总结:我们提出了一种新颖的基于生成对抗网络的推荐系统,采用强化学习动态调整历史长期偏好和短期会话的模型,此外,我们加入了封面图片特征进一步提升系统性能,最后在两个数据集上做到 state-of-art 的性能。
参考文献
1. Steffen Rendle, Christoph Freudenthaler, Zeno Gantner, and Lars SchmidtThieme. 2009. BPR: Bayesian personalized ranking from implicit feedback. InProceedings of the twenty- fth conference on uncertainty in articial intelligence. AUAI Press, 452–461.
2. Runwei Qiang, Feng Liang, and Jianwu Yang. 2013. Exploiting ranking factorization machines for microblog retrieval. In Proceedings of the 22nd ACM international conference on Conference on information & knowledge management. ACM, 1783–1788.
3. Fajie Yuan, Guibing Guo, Joemon M Jose, Long Chen, Haitao Yu, and Weinan Zhang. 2016. Lambdafm: learning optimal ranking with factorization machines using lambda surrogates. In Proceedings of the 25th ACM International on Conference on Information and Knowledge Management. ACM, 227–236.
4. Chao-YuanWu, Amr Ahmed, Alex Beutel, Alexander J Smola, and How Jing. 2017. Recurrent recommender networks. In Proceedings of the Tenth ACM International Conference on Web Search and Data Mining. ACM, 495–503.
5. Jun Wang, Lantao Yu, Weinan Zhang, Yu Gong, Yinghui Xu, Benyou Wang, Peng Zhang, and Dell Zhang. 2017. IRGAN: A Minimax Game for Unifying Generative and Discriminative Information Retrieval Models. In Proceedings of the 40th International ACM SIGIR Conference on Research and Development in Information Retrieval. 515–524.
原文发布时间为:2017-12-28
本文作者:杨敏
本文来自云栖社区合作伙伴“PaperWeekly”,了解相关信息可以关注“PaperWeekly”微信公众号
强化学习在生成对抗网络文本生成中扮演的角色(Role of RL in Text Generation by GAN)(下)
5. 一些细节 + 一些延伸
上文所述的,只是 RL + GAN 进行文本生成的基本原理,大家知道,GAN在实际运行过程中任然存在诸多不确定因素,为了尽可能优化 GAN 文本生成的效果,而后发掘更多GAN在NLP领域的潜力,还有一些值得一提的细节。
5.1. Reward Baseline:奖励值上的 Bias
在4.2节中提到,我们采用鉴别器D给予生成样本 的概率得分( 属于真实样本的概率)作为奖励 ,既然是概率值,应该意识到这些概率得分都是非负的,如此一来即便生成出再差的结果,鉴别器D也不会给出负 进行惩罚。从理论上来讲,生成器的训练会趋向于降低较小奖励值样本 出现的概率而提高较大奖励值样本 出现的概率,然而在实做时,由于采样不全等不可控因素的存在,这样不够分明的奖惩区别将有可能使得生成器G的训练变得偏颇。
实际上,在强化学习的对话生成模型当中,就已经出现了此类问题。解决的方法很简单,我们设置一个奖励值 的基准值Baseline,每次计算奖励值的时候,在后面减去这个基准值作为最终的 奖励 or 惩罚 值,使得生成器G的生成结果每次得到的奖惩有正有负,显得更加分明。记奖惩基准值为 ,则4.1节中优化梯度的计算公式修改为:
对应地,在 RL + GAN 的文本生成任务中,同样在鉴别器D对各个生成样本打出的概率得分上减去奖惩基准值 ,则4.2节中 SeqGAN 与 Conditional SeqGAN 期望奖励值的优化梯度计算公式也分别修改为如下:
5.2. REGS:一人犯错一人当
细心的读者可以发现,在SeqGAN的奖励优化梯度计算公式的推导中,由鉴别器D给予的生成样本奖励得分其实是顺应序列文本的生成过程,逐词产生的,可以看到之前的推导公式中显示了对于Partly文本序列的阶段性奖励值求和再求平均。然而在起初的实验中,根据最终推导的奖励值优化梯度计算公式,鉴别器D被训练为用于对整句生成结果进行评估打分,这样的话,鉴别器D的打分对于生成序列中的每一个token都是同等的存在,要奖励就一起奖励(奖励值可视为相同),要惩罚就一起惩罚,这种做法会导致一个后果,看下面的例子。
比如有这样一个对话组(包含真实回答和生成回答):
question = ['你', '叫', '什么', '名字', '?']
real_answer = ['我', '叫', '张三', '。']
fake_answer = ['我', '不', '知道', '。']
很显然,鉴别器D能够轻易辨识后者回答是假的,必然会给出极低的奖励值得分,但是仔细对比真/假两个回答可以发现,第一个词 “我 ” 其实和真实样本的第一个词是一样的,而最后一个字符 “。”其实也并无大碍,它们其实并没有错,真正错误的是 “不 ” 和 “知道 ” 这两个词,但很不幸,鉴别器判定 fake_answer 的整体回答是假的,原本无辜的词项 “我 ” 和 “。” 也要跟着一起接受低分判定的惩罚。
让我们回到 GAN + RL 对文本生成模型的优化原理,假设 是面对输入上文 时生成对话下文 的概率,我们将它拆分成逐个单词拼接的形式,每一个出现的词汇都将收到之前context的影响。
在4.1,4.2节中提到,如果生成样本 被鉴别器D打出低分(受到惩罚),生成器G将被训练于降低产出此结果的概率。结合上面这条公式,倘若单独将生成序列中的一部分前缀 拿出来与真实样本中完全相同,岂不是也要接受整体低分而带来的惩罚?
解决这一缺陷的直接方法就是把奖惩的判定粒度进一步细化到 word 或 character 级别,在文本逐词生成的过程中对partly的生成结果进行打分。这种处理其实在SeqGAN的论文中[17]就已经实施了,拓展到Conditional SeqGAN中,优化梯度的计算公式应改写为如下:
公式中, 是计算的关键,它代表鉴别器D在文本逐词生成过程中获得部分文本的情况下对于最终reward的估计,简而言之就是每得到一个新的生成词,就结合此前生成的前序文本估计最终reward,并作为该生成词单独的reward,SeqGAN的论文中使用蒙特卡洛搜索[21](Monte Carlo Search,MC search)的方法计算部分生成序列对于整体reward的估计值。而在Conditional SeqGAN的论文中,赋予了这种处理一个名字 —— Reward for Every Generation Step(REGS)。
5.3. MC Search & Discriminator for Partially Decoded Sequences:准度与速度的抉择
上一节说到SeqGAN中使用MC search进行部分序列奖励估计值 的计算,作为REGS操作的关键计算,其难处在于,我们并不能预知部分生成序列能给我们带来的最终结果,就好像一场篮球比赛,可能半场结束比分领先,却也不能妄言最终的比赛结果一样。
既然如此,在只得到部分序列的情况下, 只得估计获得,Monte Carlo Search[21]就是其中一种估计方法,Monte Carlo Search的思想极其简单,假设我们已经拥有了部分生成的前缀 ,我们使用当前的Generator,强制固定这个前缀,并重复生成出$M$个完整的序列(有点采样实验的意思),分别交给鉴别器D进行打分,这 个模拟样本的平均奖励得分即为部分序列 的奖励估计值 。
当然,使用MC search的缺点也很明显:每生成一个词,就要进行 次生成采样,非常耗时;还有一小点,每当我们计算较为后期的一些部分序列奖励估计值的时候,总是会无法避免地再一次计算前面早期生成的项,这样计算出来的 可能导致对于较前子序列(比如第一个词)的过拟合。
另外一种方法提出于Conditional SeqGAN的论文,干脆训练一个可以对部分已生成前缀进行打分的new鉴别器D。将某真实样本的 的全部前缀子序列(必须从第一个词开始)集合记作 ,同样将某生成样本$X^-$的全部前缀子序列集合记作 ,我们每次从这两者中随机挑选一个或若干个标定为 或 (与原序列相同),与原序列一同加入鉴别器D的训练中,这样训练得到的Discriminator便增添了给前缀子序列打分的能力,直接使用这样的Discriminator给前缀子序列打分即可获得 。这种方法的耗时比起使用MC search要少很多,但得损失一定的准度。
一句话总结两种 的计算方法:一种是利用部分序列YY出完整序列来给鉴别器打分,而另一种则直接将部分序列加入鉴别器的训练过程,得到可以为部分序列打分的鉴别器,一个较慢,另一个快却损失准度,如何选择就看大家了。
5.4. Teacher Forcing:给Generator一个榜样
在开始讲解SeqGAN中的Teacher Forcing之前,先帮助大家简单了结一下RNN运行的两种mode:(1). Free-running mode;(2). Teacher-Forcing mode[22]。前者就是正常的RNN运行方式:上一个state的输出就做为下一个state的输入,这样做时有风险的,因为在RNN训练的早期,靠前的state中如果出现了极差的结果,那么后面的全部state都会受牵连,以至于最终结果非常不好也很难溯源到发生错误的源头,而后者Teacher-Forcing mode的做法就是,每次不使用上一个state的输出作为下一个state的输入,而是直接使用ground truth的对应上一项作为下一个state的输入。
就拿Seq2Seq模型来举例,我们假设正输出到第三项,准备生成第四项:
input = ['a', 'b', 'c', 'e', 'f', 'g', 'h']
output = ['o', 'p', 's', ...]
label = ['o', 'p', 'q', 'r', 's', 't', 'u']
Free-running mode下的decoder会将第三项错误的输出 output[2] = 's'(下标从0开始)作为下一个state的输入,而在Teacher-forcing mode下,decoder则会将正确样本的第三项 label[2] = 'q' 作为下一个state的输入。 当然这么做也有它的缺点,因为依赖标签数据,在training的时候会有较好的效果,但是在testing的时候就不能得到ground truth的支持了。最好的结果是将Free-running mode的behavior训练得尽可能接近于Teacher-forcing mode,Professor Forcing[23]使用GAN尝试实现了这一目标。
当然,这些都是题外话,我们要回到Teacher-Forcing mode最初的motivation:训练(迭代)早期的RNN非常弱,几乎不能给出好的生成结果(以至于破灌破摔,产生垃圾的output影响后面的state),必须依靠ground truth强行扶着走,才能慢慢进入正轨。
SeqGAN也存在这样的问题,一开始的生成器G非常弱,即便是经过一定量的预训练,也几乎生成不出好的Result,然后这些bad result给到鉴别器D必然只能返回很低的 (惩罚),生成器G的训练只能根据鉴别器的打分来优化而无法得到good example的指导,永远不知道什么是好的结果,结果必然是恶性循环。于是,有必要在SeqGAN训练中给到生成器G真实样本的指导,也就是告诉生成器:“什么样的样本才配得到高分 ?”
4.2节中提到,生成器G 和 判别器D的训练时交替进行的,由于鉴别器返回的打分是判定输入样本为真的概率,我们可以随机取出一部分真实的样本对话组 ,然后直接设置他们的鉴别器奖励值为 (或者其他任意定义的最高分),将它们加入生成器G的训练过程中,这样生成器就能知道何种样本能得到最高的奖励,从而一定程度上避免了SeqGAN的训练过程由于一方的弱势而发生崩塌。
或者也可以这样:用训练好的鉴别器D也为随机抽样的真实样本打分,然后加入到生成器G的训练过程中,不过,一定要确保鉴别器D已经得到充分训练,至少给予任意真实样本 的打分要高于baseline才行(奖励值经过偏置处理后也必须为正)。
5.5. Actor-Critic:更广义上的GAN?
在DeepMind的一篇半综述式的文章[24]中,谈到了强化学习中的另一个特殊的模型——Actor-Critic,并分析了这个模型与GAN之间的联系。
首先我们回顾一下GAN中鉴别器D和生成器G优化时的目标函数:
再说说强化学习,在基于策略迭代的强化学习中,通过尝试当前策略的action,从环境获得 ,然后更新策略。这种操作在游戏实验环境中非常有效,因为游戏系统有封闭且清晰的环境,能够稳定地根据各种接收到的action客观地给出对应 ,而在现实生活中,很多时候并没有封闭清晰的环境,给定action应该得到什么样的 本身也不准确,只能通过设定DIY的打分器来实现,显然这么做很难完美model真实世界千变万化的情况。
那么,能不能先学习出一个能够准确评估出奖励值的值函数 ,尽可能地描述环境,对各种action返回较为公正的预期奖励呢?也就是说 的估计模型本身也是被学习的,这就是Actor-Critic,Actor部分采用传统的Policy Gradient优化策略 ,Critic部分借助“Q-Learning”学习出最优的action-value值函数,听起来有没有点像GAN的模式?来看看它的目标函数,其中 指任意一中Divergence,值域非负当且仅当两个分布相同时取值为零即可(比如,KL-divergence, JS-divergence 等等):
文中将GANs模型比作一种特殊形式的Actor-Critic,并比较了两者各自的特点以及后续的改进技术在两者上的适配情况。试想一下,既然强化学习技术帮助GAN解决了在离散型数据上的梯度传播问题,那么同为强化学习的Actor-Critic也为对抗式文本生成提供了另外一种可能。
5.6. IRGAN:两个检索模型的对抗
IRGAN[25]这篇工作发表于2017年的SIGIR,从作者的阵容来看就注定不是一篇平凡的作品,其中就包含SeqGAN的原班人马,作者将生成对抗网络的思想应用于信息检索领域,却又不拘泥于传统GAN的经典Framework,而是利用了IR领域原本就存在的两种不同路数的model:生成式IR模型 和 判别式IR模型。
生成式IR模型目标是产生一个query document的关联度分布,利用这个分布对每个输入的query返回相关的检索结果;而判别式IR模型看上去更像是一个二类分类器,它的目标是尽可能地区分有关联查询对,>和无关联查询对,>,对于给定的查询对,>,判别式IR模型给出该查询对中的两项的关联程度。
光从两个模型简单的介绍来看就能丝丝感觉到它们之间特殊的联系,两种风格迥异的IR模型在GAN的思想中“有缘地”走到了对立面,我们将生成式IR模型记作: ,将判别式IR模型记作: ,于是整个IRGAN的目标函数为:
在IRGAN中,鉴别器D定义为判别式IR模型的逻辑回归:
于是鉴别器D的目标函数进一步写为:
相对地,生成器G就直接输出以query为condition答案池中所有document与该query的关联分布,不幸地,我们必须将通过这个关联分布,过滤出当前认为最相关的document答案,才能作为鉴别器D的输入来判定此时此刻检索结果的质量,原本连续型的分布经过这一步的折腾又变成离散型的数据了,还好,我们有强化学习,设 ,则生成器G的目标函数被写成:
也就是最大化鉴别器D给出的奖励,而这个奖励值主要来源于检索结果形成的查询对 在判别式IR模型中被认为确实有关联的概率之和。将求和符号内的项记作: ,按照Policy Gradient的方式进行梯度优化,并使用4.1节中的推导方法描述 的优化梯度,在实做时为了方便,采样 个当前生成式IR模型给出的查询结果求近似。
当然,也不能忘了我们的baseline—— ,文中设置baseline为当前查询结果的平均期望 。
上述是针对Pointwise情形的IR任务,不同于Pointwise情形着重于得到直接的检索结果,Pairwise情形的IR把更多精力放在了ranking上,其返回结果 中全是非对称二元对,其中 比 与当前的查询项关联性更高。IRGAN也可以扩展到Pairwise的情形,原则是:“一切从减”。 鉴别器函数将改写为:
而假设生成器G是一个softmax函数,则Pairwise情形下的变形和简化推导如下:
IRGAN在Pairwise情形下的总目标函数如下,其中, 表示真实的非对称二元组,而 则表示生成式IR模型生成的二元组:
IRGAN的一大特点是,对抗model中的两个组件各自都是一种IR模型,所以经过对抗训练之后,不管拿出来哪个,都有希望突破原先的瓶颈。作者还关于IRGAN的训练目标是否符合纳什均衡做了一些讨论,尽管在真实检索的应用中很难获得所谓的真实关联分布,但作者认为不管是观察到的关联样本还是未观察到的关联样本,判别IR模型的输出总是和生成IR模型的对应输出存在着正相关的作用力,于是也孕育而生了文中那个关于浮力和拖拽重物最终达到漂浮平衡状态的略显晦涩的比喻。
结语
这一领域的发展之迅速,也许在我完成这篇Blog的时候,又有一批工作争先恐后的冒出来了,但最终的结局肯定不止于此,我也不怎么擅长结尾,也许要等待GAN来为我,为我们带来一个奇妙的结局。
Acknowledgement
要特别感谢台湾大学李宏毅老师生动的授课[26],这为我在多个知识点上的理解带来了重要的帮助。
本文作者:Non
本文转自雷锋网禁止二次转载,原文链接
VAE、GAN、Info-GAN:全解深度学习三大生成模型
在深度学习之前已经有很多生成模型,但苦于生成模型难以描述难以建模,科研人员遇到了很多挑战,而深度学习的出现帮助他们解决了不少问题。本章介绍基于深度学习思想的生成模型——VAE和GAN,以及GAN的变种模型。
VAE
本节将为读者介绍基于变分思想的深度学习的生成模型——Variational autoencoder,简称VAE。
1.1 生成式模型
前面的章节里读者已经看过很多判别式模型。这些模型大多有下面的规律:已知观察变量X,和隐含变量z,判别式模型对p(z|X)进行建模,它根据输入的观察变量x得到隐含变量z出现的可能性。生成式模型则是将两者的顺序反过来,它要对p(X|z)进行建模,输入是隐含变量,输出是观察变量的概率。
可以想象,不同的模型结构自然有不同的用途。判别模型在判别工作上更适合,生成模型在分布估计等问题上更有优势。如果想用生成式模型去解决判别问题,就需要利用贝叶斯公式把这个问题转换成适合自己处理的样子:
对于一些简单的问题,上面的公式还是比较容易解出的,但对于一些复杂的问题,找出从隐含变量到观察变量之间的关系是一件很困难的事情,生成式模型的建模过程会非常困难,所以对于判别类问题,判别式模型一般更适合。
但对于“随机生成满足某些隐含变量特点的数据”这样的问题来说,判别式模型就会显得力不从心。如果用判别式模型生成数据,就要通过类似于下面这种方式的方法进行。
第一步,利用简单随机一个X。
第二步,用判别式模型计算p(z|X)概率,如果概率满足,则找到了这个观察数据,如果不满足,返回第一步。
这样用判别式模型生成数据的效率可能会十分低下。而生成式模型解决这个问题就十分简单,首先确定好z的取值,然后根据p(X|z)的分布进行随机采样就行了。
了解了两种模型的不同,下面就来看看生成式模型的建模方法。
1.2 Variational Lower bound
虽然生成模型和判别模型的形式不同,但两者建模的方法总体来说相近,生成模型一般也通过最大化后验概率的形式进行建模优化。也就是利用贝叶斯公式:
这个公式在复杂的模型和大规模数据面前极难求解。为了解决这个问题,这里将继续采用变分的方法用一个变分函数q(z)代替p(z|X)。第9章在介绍Dense CRF时已经详细介绍了变分推导的过程,而这一次的推导并不需要做完整的变分推导,只需要利用变分方法的下界将问题进行转换即可。
既然希望用q(z)这个新函数代替后验概率p(z|X),那么两个概率分布需要尽可能地相近,这里依然选择KL散度衡量两者的相近程度。根据KL公式就有:
根据贝叶斯公式进行变换,就得到了:
由于积分的目标是z,这里再将和z无关的项目从积分符号中拿出来,就得到了:
将等式左右项目交换,就得到了下面的公式:
虽然这个公式还是很复杂,因为KL散度的性质,这个公式中还是令人看到了一丝曙光。
首先看等号左边,虽然p(X)的概率分布不容易求出,但在训练过程中当X已经给定,p(X)已经是个固定值不需要考虑。如果训练的目标是希望KL(q(z)||p(z|X))尽可能小,就相当于让等号右边的那部分尽可能变大。等号右边的第一项实际上是基于q(z)概率的对数似然期望,第二项又是一个负的KL散度,所以我们可以认为,为了找到一个好的q(z),使得它和p(z|X)尽可能相近,实现最终的优化目标,优化的目标将变为:
右边第一项的log似然的期望最大化:
右边第二项的KL散度最小化:
右边两个项目的优化难度相对变小了一些,下面就来看看如何基于它们做进一步的计算。
1.3 Reparameterization Trick
为了更方便地求解上面的公式,这里需要做一点小小的trick工作。上面提到了q(z)这个变分函数,为了近似后验概率,它实际上代表了给定某个X的情况下z的分布情况,如果将它的概率形式写完整,那么它应该是q(z|X)。这个结构实际上对后面的运算产生了一些障碍,那么能不能想办法把X抽离出来呢?
例如,有一个随机变量a服从均值为1,方差为1的高斯分布,那么根据高斯分布的性质,随机变量b=a-1将服从均值为0,方差为1的高斯分布,换句话说,我们可以用一个均值为0,方差为1的随机变量加上一个常量1来表示现在的随机变量a。这样一个随机变量就被分成了两部分——一部分是确定的,一部分是随机的。
实际上,q(z|X)也可以采用上面的方法完成。这个条件概率可以拆分成两部分,一部分是一个观察变量gϕ(X),它代表了条件概率的确定部分,它的值和一个随机变量的期望值类似;另一部分是随机变量ε,它负责随机的部分,基于这样的表示方法,条件概率中的随机性将主要来自这里。
这样做有什么好处呢?经过变换,如果z条件概率值完全取决于ε的概率。也就是说如果z(i)=gϕ(X+ε(i)),那么q(z(i))=p(ε(i)),那么上面关于变分推导的公式就变成了下面的公式:
这就是替换的一小步,求解的一大步!这个公式已经很接近问题最终的答案了,既然ϵ完全决定了z的分布,那么假设一个ϵ服从某个分布,这个变分函数的建模就完成了。如果ϵ服从某个分布,那么z的条件概率是不是也服从这个分布呢?不一定。z的条件分布会根据训练数据进行学习,由于经过了函数gϕ()的计算,z的分布有可能产生了很大的变化。而这个函数,就可以用深度学习模型表示。前面的章节读者已经了解到深层模型的强大威力,那么从一个简单常见的随机变量映射到复杂分布的变量,对深层模型来说是一件很平常的事情,它可以做得很好。
于是这个假设ϵ服从多维且各维度独立高斯分布。同时,z的先验和后验也被假设成一个多维且各维度独立的高斯分布。下面就来看看两个优化目标的最终形式。
1.4 Encoder和Decoder的计算公式
回顾一下10.1.2的两个优化目标,下面就来想办法求解这两个目标。首先来看看第二个优化目标,也就是让公式右边第二项KL(q(z)||p(z))最小化。刚才z的先验被假设成一个多维且各维度独立的高斯分布,这里可以给出一个更强的假设,那就是这个高斯分布各维度的均值为0,协方差为单位矩阵,那么前面提到的KL散度公式就从:
瞬间简化成为:
前面提到了一个用深层网络实现的模型gϕ(X,ϵ),它的输入是一批图像,输出是z,因此这里需要它通过X生成z,并将这一个批次的数据汇总计算得到它们的均值和方差。这样利用上面的公式,KL散度最小化的模型就建立好了。
实际计算过程中不需要将协方差表示成矩阵的形状,只需要一个向量σ1来表示协方差矩阵的主对角线即可,公式将被进一步简化:
由于函数gϕ()实现了从观测数据到隐含数据的转变,因此这个模型被称为Encoder模型。
接下来是第一个优化目标,也就是让公式左边第一项的似然期望最大化。这一部分的内容相对简单,由于前面的Encoder模型已经计算出了一批观察变量X对应的隐含变量z,那么这里就可以再建立一个深层模型,根据似然进行建模,输入为隐含变量z,输出为观察变量X。如果输出的图像和前面生成的图像相近,那么就可以认为似然得到了最大化。这个模型被称为Decoder,也就是本章的主题——生成模型。
到这里VAE的核心计算推导就结束了。由于模型推导的过程有些复杂,下面就来看看VAE实现的代码,同时来看看VAE模型生成的图像是什么样子。
1.5 实现
本节要介绍VAE模型的一个比较不错的实现——GitHub - cdoersch/vae_tutorial: Caffe code to accompany my Tutorial on Variational Autoencoders,这个工程还配有一个介绍VAE的文章[2],感兴趣的读者可以阅读,读后会有更多启发。这个实现使用的目标数据集依然是MNIST,模型的架构如图10-1所示。为了更好地了解模型的架构,这里将模型中的一些细节隐去,只留下核心的数据流动和Loss计算部分。
图10-1 VAE模型结构图
图中粗框表示求解Loss的部分。虚线展现了两个模块之间数据共享的情况。可以看出图的上半部分是优化Encoder的部分,下面是优化Decoder的部分,除了Encoder和Decoder,图中还有三个主要部分。
Encoder的Loss计算:KL散度。
z的重采样生成。
Decoder的Loss计算:最大似然。
这其中最复杂的就是第一项,Encoder的Loss计算。由于Caffe在实际计算过程中只能采用向量的计算方式,没有广播计算的机制,所以前面的公式需要进行一定的变换:
在完成了前面的向量级别计算后,最后一步就是完成汇总加和的过程。这样Loss计算就顺利完成了。
经过上面对VAE理论和实验的介绍,相信读者对VAE模型有了更清晰的认识。经过训练后VAE的解码器在MNIST数据库上生成的字符如图10-2所示。
图10-2 VAE生成的数字图
1.6 MNIST生成模型可视化
除了直接观察最终生成的数字结果,实际上还有另一种观察数据的方式,那就是站在隐变量空间的角度观察分布的生成情况。实现这个效果需要完成以下两个工作:
隐变量的维度为2,相当于把生成的数字图片投影到2维平面上,这样更方便可视化观察分析。
由于隐变量的维度为2,就可以从二维平面上等间距地采样一批隐变量,这样这批隐变量可以代表整个二维平面上隐变量的分布,然后这批隐变量经过解码器处理后展示,这样就可以看到图像的分布情况了。
上面描述的算法的流程如图10-3所示。
图10-3 模型可视化流程图。上图主要标识了模型的修改部分,下图介绍隐变量采样和生成的形式
图10-4 模型可视化结果
图10-4所示的模型很好地完成了隐变量的建模,绝大多数数字出现在了这个平面分布中,数字与数字一些过渡区域,这些过渡区域的图像拥有多个数字的特征,而这些数字的外形确实存在着相似之处。可以明显地感受到,图像随着隐变量变换产生了变换。
VAE的内容就介绍到这里,下面来看看另一个生成模型。
GAN
前面我们介绍了VAE,下面来看看GAN(Generative Adversarial Network)[3],这个网络组是站在对抗博弈的角度展现生成模型和判别模型各自的威力的,在效果上比VAE还要好些。
2.1 GAN的概念
同VAE模型类似,GAN模型也包含了一对子模型。GAN的名字中包含一个对抗的概念,为了体现对抗这个概念,除了生成模型,其中还有另外一个模型帮助生成模型更好地学习观测数据的条件分布。这个模型可以称作判别模型D,它的输入是数据空间内的任意一张图像x,输出是一个概率值,表示这张图像属于真实数据的概率。对于生成模型G来说,它的输入是一个随机变量z,z服从某种分布,输出是一张图像G(z),如果它生成的图像经过模型D后的概率值很高,就说明生成模型已经比较好地掌握了数据的分布模式,可以产生符合要求的样本;反之则没有达到要求,还需要继续训练。
两个模型的目标如下所示:
判别模型的目标是最大化这个公式:Ex[D(x)],也就是甄别出哪些图是真实数据分布中的。
生成模型的目标是最大化这个公式:Ez[D(G(z))],也就是让自己生成的图被判别模型判断为来自真实数据分布。
看上去两个模型目标联系并不大,下面就要增加两个模型的联系,如果生成模型生成的图像和真实的图像有区别,判别模型要给它判定比较低的概率。这里可以举个形象的例子,x好比是一种商品,D是商品的检验方,负责检验商品是否是正品;G是一家山寨公司,希望根据拿到手的一批产品x研究出生产山寨商品x的方式。对于D来说,不管G生产出来的商品多像正品,都应该被判定为赝品,更何况一开始G的技术水品不高,生产出来的产品必然是漏洞百出,所以被判定为赝品也不算冤枉,只有不断地提高技术,才有可能迷惑检验方。
基于上面的例子,两个模型的目标就可以统一成一个充满硝烟味的目标函数。
上面这个公式对应的模型架构如图10-5所示。
图10-5 GAN的基本形式
对应的模型学习算法伪代码如下所示:
def GAN(G,D,X):
# G 表示生成模型
# D 表示判别模型
# X 表示训练数据
for iter in range(MAX_ITER):
for step in range(K):
x = data_sample(X)
z = noise_sample()
optimize_D(G, D, x, z)
z = noise_sample()
optimize_G(G, D, z)
上面的代码只是从宏观的层面介绍了模型的优化方法,其中K表示了判别模型D的迭代次数,K一般大于等于1。从上面的公式可以看出,两个模型的目标是对立的。生成模型希望最大化自己生成图像的似然,判别模型希望最大化原始数据的似然的同时,能够最小化G生成的图像的似然。既然是对立的,那么两个模型经过训练产生的能力就可能有很多种情况。它们既可能上演“魔高一尺,道高一尺”,“道高一丈,魔高十丈”的竞争戏码,在竞争中共同成长,最终产生两个强大的模型;也可能产生一个强大的模型,将另一方完全压倒。
如果判别模型太过强大,那么生成模型会产生两种情况:一种情况是发现自己完全被针对,模型参数无法优化;另外一种情况是发现判别模型的一些漏洞后,它的模型将退化,不管输入是什么样子,输出统一变成之前突破了判别模型防线的那几类结果。这种情况被称为“Mode Collapse”,有点像一个复杂强大的模型崩塌成一个简单弱小的模型,这样的模型即使优化结果很好,也不能拿去使用。
如果判别模型不够强大,它的判别不够精准,而生成模型又是按照它的判别结果生产,那么生产出的产品不会很稳定,这同样不是我们想看到的结果。
总而言之,对抗是GAN这个模型要面对的一个大问题。虽然论文中作者试图将两个模型共同优化的问题转换成类似Coordinate Ascent那样的优化问题,并证明像Coordinate Ascent这样的算法可以收敛,那么GAN这个模型也可以。不过作者在完成证明后立刻翻脸,说证明结果和实验结果不符。所以这个问题在当时也就变成了一个悬案。
2.2 GAN的训练分析
关于GAN训练求解的过程,作者用了十分数学化的方式进行了推演。我们首先来证明第一步:当生成模型固定时,判别模型的最优形式。
首先将目标函数做变换:
由于组成式子的两部分积分的区域不同,会对后面的计算造成困难,我们首先将两个积分区域统一。我们将生成图像G(z)的分布与真实图像x的分布做一个投射,只要判别式能够在真实数据出现的地方保证判别正确最大化即可,于是公式就变成了:
只要让积分内部的公式最大化,整个公式就可以实现最大化。这样问题就转变为最大化下面的公式:
对它进行求导取极值,可以得到:
令上面的式子为0,我们可以得到结果:
这就是理论上判别式的预测结果,如果一张图像在真实分布中出现的概率大而在生成分布中出现的概率小,那么最优的判别模型会认为它是真实图像,反之则认为不是真实图
像。如果生成模型已经达到了完美的状态,也就是说对每一幅图像都有:
接下来就可以利用上面的结果,计算当生成模型达到完美状态时,损失函数的值。我们将D*(x)=1/2的结果代入,可以得到:
也就是说生成模型损失函数的理论最小值为-2log2。那么,一般情况下它的损失函数是什么样子呢?我们假设在某一时刻判别式经过优化已经达到最优,所以
我们将这个公式代入之前的公式,可以得到:
后面的两个KL散度的计算公式可以转化为Jenson-Shannon散度,也就是:
这其实是生成模型真正的优化目标函数。在介绍VAE时,读者已经了解了KL散度,也了解了它的一些基本知识,那么这个JS散度又是什么?它又有什么特性和优势?从最直观的角度,读者可以发现一个KL散度不具备的性质——JS散度是对称的:
对称又能带来什么好处呢?它能让散度度量更准确。接下来将用一段代码展示这其中的道理。首先给出两个离散随机变量的KL散度和JS散度的计算方法:
import numpy as np
import math
def KL(p, q):
# p,q为两个list,里面存着对应的取值的概率,整个list相加为1
if 0 in q:
raise ValueError
return sum(_p * math.log(_p/_q) for (_p,_q) in zip(p, q) if _p != 0)
def JS(p, q):
M = [0.5 * (_p + _q) for (_p, _q) in zip(p, q)]
return 0.5 * (KL(p, M) + KL(q, M))
下面将用3组实验看看两个散度的计算结果。首先选定一个简单的离散分布,然后求出它的KL散度和JS散度。在此基础上,把两个分布分别做一定的调整。首先是基础的分布:
def exp(a, b):
a = np.array(a, dtype=np.float32)
b = np.array(b, dtype=np.float32)
a /= a.sum()
b /= b.sum()
print a
print b
print KL(a,b)
print JS(a,b)
# exp 1
exp([1,2,3,4,5],[5,4,3,2,1])
#以下为运行结果显示
[ 0.066 0.133 0.2 0.266 0.333]
[ 0.333 0.266 0.2 0.133 0.066]
0.521
0.119
接下来把公式中第二个分布做修改,假设这个分布中有某个值的取值非常小,就有可能增加两个分布的散度值,它的代码如下所示:
# exp 2
exp([1,2,3,4,5],[1e-12,4,3,2,1])
exp([1,2,3,4,5],[5,4,3,2,1e-12])
#以下为运行结果显示
[ 0.066 0.133 0.2 0.266 0.333]
[ 9.999e-14 4.000e-01 3.000e-01 2.000e-01 1.000e-01]
2.06550201846
0.0985487692551
[ 0.066 0.133 0.2 0.266 0.333]
[ 3.571e-01 2.857e-01 2.142e-01 1.428e-01 7.142e-14]
9.662
0.193
可以看出KL散度的波动比较大,而JS的波动相对小。
最后修改前面的分布,代码如下所示:
# exp 3
exp([1e-12,2,3,4,5],[5,4,3,2,1])
exp([1,2,3,4,1e-12],[5,4,3,2,1])
这回得到的结果是这样的:
[ 7.142e-14 1.428e-01 2.142e-01 2.857e-01 3.571e-01]
[ 0.333 0.266 0.2 0.133 0.0666]
0.742
0.193
[ 1.000e-01 2.000e-01 3.000e-01 4.000e-01 9.999e-14]
[ 0.333 0.266 0.2 0.133 0.066]
0.383
0.098
如果将第二个实验和第三个实验做对比,就可以发现KL散度在衡量两个分布的差异时具有很大的不对称性。如果后面的分布在某一个值上缺失,就会得到很大的散度值;但是如果前面的分布在某一个值上缺失,最终的KL散度并没有太大的波动。这个例子可以很清楚地看出KL不对称性带来的一些小问题。而JS具有对称性,所以第二个实验和第三个实验的JS散度实际上是距离相等的分布组。
从这个小例子我们可以看出,有时KL散度下降的程度和两个分布靠近的程度不成比例,而JS散度靠近的程度更令人满意,这也是GAN模型的一大优势。
2.3 GAN实战
看完了前面关于GAN的理论分析,下面我们开始实战。在实战之前目标函数还要做一点改动。从前面的公式中可以看出这个模型和VAE一样都是有嵌套关系的模型,那么生成模型G要想完成前向后向的计算,要先将计算结果传递到判别模型计算损失函数,然后将梯度反向传播回来。那么不可避免地我们会遇到一个问题,如果梯度在判别模型那边消失了,生成模型岂不是没法更新了?生成模型的目标函数如下所示:
如果判别模型非常厉害,成功地让D(G(z))等于一个接近0的数字,那么这个损失函数的梯度就消失了。其实从理论上分析这个结果很正常,生成模型的梯度只能从判别模型这边传过来,这个结果接近0对于判别模型来说是满意的,所以它不需要更新,梯度就没有了,于是生成模型就没法训练了。所以作者又设计了新的函数目标:
这样一来梯度又有了,生成模型也可以继续训练。当然,这个目标函数也有不足的地方。
下面来看一个具体的基于深层模型的实现——DC-GAN。全称是Deep Convolution GAN。也就是用深度卷积网络进行对抗生成网络的建模。在此之前,也有一些基于卷积神经网络的GAN实现,但是相对来说,DC-GAN的最终表现与同期的模型相比更优秀,在介绍它的论文中,作者也详细介绍了模型的一些改进细节。
将Pooling层替换成带有stride的卷积层
使用Batch Normalization
放弃使用全连接层
将卷积层的非线性部分换成ReLU或者Leaky ReLU
下面将使用DC-GAN的模型进行实验,这个实验使用的数据集还是MNIST。由于Caffe并不是十分适合构建GAN这样的模型,因此这里使用另外一个十分流行且简单易懂的框架——Keras来展示DC-GAN的一些细节。代码来自https://github.com/jacobgil/keras-dcgan。由于Keras的代码十分直观,这里就直接给出源码。首先是生成模型:
def generator_model():
model = Sequential()
model.add(Dense(input_dim=100, output_dim=1024))
model.add(Activation('tanh'))
model.add(Dense(out_dim=128*7*7))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Reshape((128, 7, 7), input_shape=(128*7*7,)))
model.add(UpSampling2D(size=(2, 2)))
model.add(Convolution2D(out_channel=64, kernel_height=5, kernel_width=5, border_mode='same'))
model.add(Activation('tanh'))
model.add(UpSampling2D(size=(2, 2)))
model.add(Convolution2D(out_channel=1, kernel_height=5, kernel_width=5, border_mode='same'))
model.add(Activation('tanh'))
return model
这里需要说明的一点是,这个实现和论文中的描述有些不同,不过对于MNIST这样的小数据集,这样的模型差异不影响效果。
判别模型的结构如下所示,仔细地读一遍就可以理解,这里不再赘述。
def discriminator_model():
model = Sequential()
model.add(Convolution2D(64, 5, 5, border_mode='same', input_shape=(1, 28, 28)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(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
完成训练后,生成模型生成的手写数字如图10-6所示。
图10-6 GAN生成的图像
除了个别数字外,大多数数字生成得和真实数据很像。将图10-6和图10-2进行对比,我们可以发现,GAN模型生成的数字相对而言更为“清晰”,而VAE模型的数字略显模糊,这和两个模型的目标函数有很大的关系。另外,两个模型在训练过程中的Loss曲线如图10-7所示。
图10-7 GAN中生成模型和判别模型的损失函数
其中上面的曲线表示生成模型的Loss,下面的曲线是判别模型的Loss,虽然这两个Loss的绝对数值看上去不能说明什么问题,但是相信读者还是可以看出两个模型的Loss存在着强相关的关系,这也算是对抗过程中的此消彼长。
最终生成的数据还算令人满意,我们还很好奇,在模型优化过程中生成模型生成的图像都是什么样的呢?接下来就来观察生成图像的演变过程。在优化开始时,随机生成的图像如图10-8所示。
图10-8 GAN生成模型的初始图像
其实就是噪声图片,一点都不像数字。经过400轮的迭代,生成模型可以生成的图像如图10-9所示。
图10-9 GAN生成模型400轮迭代训练后的图像
可以看出数字的大体结构已经形成,但是能够表征数字细节的特征还没有出现。
经过10个Epoch后,生成模型的作品如图10-10所示。
图10-10 GAN生成模型经过10个Epoch迭代训练后的图像
这时有些数字已经成形,但是还有一些数字仍然存在欠缺。
20轮Epoch后的结果如图10-11所示。
图10-11 GAN生成模型经过20个Epoch迭代训练后的图像
这时的数字已经具有很强的辨识度,但与此同时,我们发现生成的数字中有大量的“1”。
当完成了所有的训练,取出生成模型在最后一轮生成的图像,如图10-12所示。
图10-12 GAN生成模型最终生成的图像
可以看出这里面的数字质量更高一些,但是里面的“1”更多了。
从模型的训练过程中可以看出,一开始生成的数字质量都很差,但生成数字的多样性比较好,后来的数字质量比较高但数字的多样性逐渐变差,模型的特性在不断发生变化。这个现象和两个模型的对抗有关系,也和增强学习中的“探索—利用”困境很类似。
站在生成模型的角度思考,一开始生成模型会尽可能地生成各种各样形状的数字,而判别模型会识别出一些形状较差的模型,而放过一些形状较好的模型,随着学习的进程不断推进,判别模型的能力也在不断地加强,生成模型慢慢发现有一些固定的模式比较容易通过,而其他的模式不那么容易通过,于是它就会尽可能地增大这些正确模式出现的概率,让自己的Loss变小。这样,一个从探索为主的模型变成了一个以利用为主的模型,因此它的数据分布已经不像刚开始那么均匀了。
如果这个模型继续训练下去,生成模型有可能进一步地利用这个模式,这和机器学习中的过拟合也有很相近的地方。
Info-GAN
本节将要介绍GAN模型的一个变种——InfoGAN,它要解决隐变量可解释性的问题。前面提到GAN的隐变量服从某种分布,但是这个分布背后的含义却不得而知。虽然经过训练的GAN可以生成新的图像,但是它却无法解决一个问题——生成具有某种特征的图像。例如,对于MNIST的数据,生成某个具体数字的图像,生成笔画较粗、方向倾斜的图像等,这时就会发现经典的GAN已经无法解决这样的问题,想要解决就需要想点别的办法。
首先想到的方法就是生成模型建模的方法:挑出几个隐变量,强制指定它们用来表示这些特性的属性,例如数字名称和方向。这样看上去似乎没有解决问题,但这种方法需要提前知道可以建模的隐变量内容,还要为这些隐变量设置好独立的分布假设,实际上有些麻烦又不够灵活。本节的主角——InfoGAN,将从信息论角度,尝试解决GAN隐变量可解释性问题。
3.1 互信息
介绍算法前要简单回顾机器学习中的信息论基本知识。第2章已经介绍了熵和“惊喜度”这些概念,熵衡量了一个随机变量带来的“惊喜度”。本节要介绍的概念叫做互信息,它衡量了随机变量之间的关联关系。假设随机事件A的结果已经知道,现在要猜测某个事件B的结果,那么知道A的取值对猜测B有多大帮助?这就是互信息要表达的东西。
我们以掷骰子为例,如果我们知道手中的骰子是不是“韦小宝特制”骰子这件事,那么它会对我们猜测最终投掷的点数有帮助吗?当然有帮助,因为一旦确定这个骰子是“韦小宝特制”,那么骰子点数是几这个信息就变得没有“惊喜”了。同理,“美国第45届总统是谁”这个消息对我们手中骰子投掷出的点数这个事情就没那么多帮助了,所以这两件事情的互信息就低,甚至可以说这两个事件是相互独立的。
了解了上面比较直观的例子,下面就可以给出连续随机变量X,Y互信息的计算公式:
上面的公式可以做如下变换:
就可以发现互信息的进一步解释:它可以变为熵和条件熵的差。同样地,这个公式还可以转变为:I(X;Y)=H(X)-H(X|Y)
最终表示为熵和条件熵的差距。用通俗的话解释,两个随机变量的互信息就是在知道和不知道一个随机变量取值的情况下,另一个随机变量“惊喜度”的变化。互信息的计算方法的代码如下所示:
import numpy as np
import math
def mutual_info(x_var, y_var):
sum = 0.0
x_set = set(x_var)
y_set = set(y_var)
for x_val in x_set:
px = float(np.sum(x_var == x_val)) / x_var.size
x_idx = np.where(x_var == x_val)[0]
for y_val in y_set:
py = float(np.sum(y_var == y_val)) / y_var.size
y_idx = np.where(y_var == y_val)[0]
pxy = float(np.intersect1d(x_idx, y_idx).size) / x_var.size
if pxy > 0.0:
sum += pxy * math.log((pxy / (px * py)), 10)
return sum
下面随意给出一对随机变量和它们的概率分布,并用上面的代码分析这对变量的互信息:
a = np.array([0,0,5,6,0,4,4,3,1,2])
b = np.array([3,4,5,5,3,7,7,6,5,1])
print mutual_info(a,b)
# 0.653
a = np.array([0,0,5,6,0,4,4,3,1,2])
b = np.array([3,3,5,6,3,7,7,9,4,8])
print mutual_info(a,b)
# 0.796
很明显,下面一组数据的相关性更强,知道其中一个随机变量的取值,就会非常容易猜出同一时刻另外一个随机变量的采样值。如果我们进一步观察第二组数据,会发现任意一组数据的熵都是0.796,也就是说当知道其中一个随机变量的值后,它们的条件熵就变成了0,另一个随机变量变得完全“惊喜”了。虽然条件熵为0这个信息并没有展现在互信息的数值中,但互信息实际上就是在衡量一个相对的信息差距,并不像熵那样衡量信息绝对量。
其实数学包含了很多人生哲理和智慧。人的一生实际上一直在和熵作斗争,每个人的人生轨迹的熵意味着什么?一个人未来的不确定性?一个人未来的“惊喜”程度?有的人说自己“一辈子也就这样了”的时候,是不是表示这个人的未来已经从一个随机变量变成了常量,它的熵变成了0?为什么人们总是向往青春,是不是因为那些年华充满了各种不确定性与精彩,可以理解为熵很大?
“身体和灵魂,总有一个在路上”,是不是标榜追求最大熵的一个口号?“公务员这种稳定工作才是好工作”是不是一种追求最小化熵的行为呢?那么对于一个人来说,究竟是熵越大越好,还是熵越小越好?
回到问题,互信息在这个问题中有什么用?如果说隐变量的确定对确定生成图像的样子有帮助,那么隐变量和最终的图像之间的互信息就应该很大:当知道了隐变量这个信息,图像的信息对变得更确定了。所以InfoGAN这个算法就是要通过约束互信息使隐变量“更有价值”。
3.2 InfoGAN模型
那么,InfoGAN模型的具体形式是什么样的呢?如果把互信息定义为损失函数的一部分,这部分损失函数就是InfoGAN中基于经典GAN修改的部分。前面的小节已经推导出了互信息的公式,那么在具体计算时要使用哪个公式计算呢?
I(X;Z)=H(X)-H(X|Z)
I(X;Z)=H(Z)-H(Z|X)
最终的选择是后者,因为图像X的分布太难确定,求解它的熵肯定相当困难,所以前者的第一项非常难计算。当然,即使选择了第二项,这个公式也不是很好优化,因为其中还有一个后验项P(Z|X)需要求解,这也是个大麻烦,不过这里可以使用本书多次提到的方法——Variational Inference求解这个后验项。
在介绍VAE时我们曾经运用过Reparameterization Trick这个方法,这里将再次采用类似的方法。在VAE中,Trick公式是z(i)=gϕ(X+ε(i)),在Encoder的过程中,输入部分被分解成确定部分和不确定部分,然后利用一个高维非线性模型拟合输入到输出的映射。这里要求出的X,和VAE正好相反,需要的是这样的一个公式:X=gϕ([c,z]+ϵ)
其中c表示与图像有相关关系的隐变量,z表示与图像无关的隐变量。于是互信息计算公式就变成了:
从实践上讲,ϵ项可以忽略,于是公式可以做进一步简化:
接下来将期望用蒙特卡罗方法代替,训练时可以通过计算大量样本求平均来代替期望值,于是公式又变成了:
这个方程变简单了很多。当然,我们也看出上面的公式中我们有一个Q,这个Q函数可以理解为一个Encoder,这部分模型在经典GAN中并不存在,但是在实际建模过程中,由于Encoder和判别模型的输入相同,且模型目标比较相近,因此二者部分网络结构可以共享。论文的作者提供了InfoGAN的源码,代码的链接在https://github.com/openai/InfoGAN,代码使用的框架为TensorFlow,感兴趣的读者可以自行阅读。模型实现的结构如图10-13所示。
图10-13 InfoGAN模型结构图
虚线部分表示的就是计算互信息的目标函数,这部分内容看似比较复杂,实则不然。由于InfoGAN模型中定义了两种类型的随机变量——服从Categorical分布、用于表示数字内容的离散类型变量,和服从均匀分布用于表示其他连续特征的连续型变量,而两种类型的变量在计算熵的方法不同,因此上面的计算图对它们进行分情况处理。
互信息计算起始于如下两个变量。
reg_z:表示了模型开始随机生成的隐变量。
fake_ref_z_dist_info:表示了经过Encoder计算后的隐变量分布信息。
接下来,根据连续型和离散型的分类,两个变量分成了以下四个变量。
cont_reg_z:reg_z的连续变量部分
cont_reg_dist_info:fake_ref_z_dist_info的连续变量部分
disc_reg_z:reg_z的离散变量部分
disc_reg_dist_info:fake_ref_z_dist_info的连续变量部分
接下来,四个变量两两组队完成了后验公式P(c)logQ(c|gϕ([c,z])的计算:
cont_log_q_c_given_x:连续变量的后验
disc_log_q_c_given_x:离散变量的后验
同时,输入的隐变量也各自完成先验P(c)logP(c)的计算:
cont_log_q_c:连续变量的先验
disc_log_q_c:离散变量的后验
由于上面的运算全部是元素级的计算,还要把向量求出的内容汇总,得到∑P(c)logQ(c|gϕ([c,z])和∑P(c)logP(c) 。
cont_cross_ent:连续变量的交叉熵
cont_ent:连续变量的熵
disc_cross_ent:离散变量的交叉熵
disc_ent:离散变量的熵
接下来,根据互信息公式两两相减,得到各自的互信息损失。
cont_mi_est:连续变量的互信息
disc_mi_est:离散变量的互信息
最后将两者相加就得到了最终的互信息损失。
模型在训练前定义了12个和图像有强烈互信息的随机变量,其中10个变量表示显示的数字,它们组成一个Categorical的离散随机向量;另外2个是服从范围为[-1,1]的连续随机变量。训练完成后,调整离散随机变量输入并生成图像,得到如图10-14所示的数字图像。
图10-14 10个离散随机变量对生成数字的影响
可以看出模型很好地识别了这些数字。调整另外两个连续随机变量,可以生成如图10-15所示的数字图像。
图10-15 2个连续随机变量对生成数字的影响
可以看出,这两个连续随机变量学到了数字粗细和倾斜的特征,而且这是在完全没有暗示的情况下完成的。可见InfoGAN模型的能力。
到此InfoGAN的介绍就结束了。从这个模型可以看出,在经典GAN模型基础上添加更多的内容会产生更多意想不到的效果。
总结
本章主要介绍了基于深度学习的生成模型,它们在生成图像上有着很强的能力。
VAE:基于变分下界约束得到的Encoder-Decoder模型对。
GAN:基于对抗的Generator-Discriminator模型对。
InfoGAN:挖掘GAN模型隐变量特点的模型。
原文发布时间为:2017-09-23
本文来自云栖社区合作伙伴“数据派THU”,了解相关信息可以关注“数据派THU”微信公众号
深度学习与CV教程(16) | 生成模型(PixelRNN,PixelCNN,VAE,GAN)
作者:韩信子@ShowMeAI教程地址:http://www.showmeai.tech/tutorials/37本文地址:http://www.showmeai.tech/article-detail/275声明:版权所有,转载请联系平台与作者并注明出处收藏ShowMeAI查看更多精彩内容本系列为 斯坦福CS231n 《深度学习与计算机视觉(Deep Learning for Computer Vision)》的全套学习笔记,对应的课程视频可以在 这里 查看。更多资料获取方式见文末。引言之前了解到的都是监督学习(Supervised Learning):我们有数据x和标签y,目标是学习到一个函数可以将数据x映射到标签y,标签可以有很多形式。典型的有监督学习有:分类问题中输入一张图片,输出图片的分类;目标检测中输入一张图片,输出目标物体的边框;语义分割中,给每个像素都打上标签。CS231n第13讲给大家介绍的是无监督学习(Unsupervised Learning)以及生成模型的一些知识。本篇重点无监督学习生成模型Pixel RNN/CNN变分自编码器(VAE)生成对抗网络(GAN)1.无监督学习无监督学习在我们只有一些没有标签的训练数据的情况下,学习数据中隐含的结构。无监督学习由于没有标签,数据获取也很容易。典型的无监督学习包括下述算法:1.1 聚类(k-Means)关于聚类算法的详细知识也可以参考ShowMeAI的下述文章图解机器学习教程 中的文章详解 聚类算法详解聚类(Clustering)是找到数据的分组,组内数据在某种度量方式下是相似的。随机初始k个中心位置,将每个样本分配到最近的中心位置,然后根据分配的样本更新中心位置。重复这个过程直至收敛(中心位置不再变化)。1.2 PCA(主成分分析)关于PCA降维算法的详细知识也可以参考ShowMeAI的下述文章图解机器学习教程 中的文章详解 降维算法详解数据降维(Dimensionality reduction):找出一些投影方向(轴),在这些轴上训练数据投影的方差最大。这些轴就是数据内潜在的结构。我们可以用这些轴来减少数据维度,数据在每个保留下来的维度上都有很大的方差。1.3 特征学习(Feature Learning)我们还有一些特征学习的方法,比如自编码(Autoencoders):1.4 密度估计( Density Estimation)密度估计( Density Estimation)也是一种无监督算法,我们会估计数据的内在分布情况,比如下图上方有一些一维和二维的点,我们用高斯函数来拟合这一密度分布,如下图所示:2.生成模型(Generative Models)生成模型是一种无监督学习方法。它对应的任务是:根据一批由真实分布p-data(x) 产生的训练数据,通过训练学习,得到一个可以以近似于真实的分布p-model(x) 来产生新样本的模型。为什么生成模型重要,因为其可以支撑一系列问题的解决:生成样本,着色问题,强化学习应用,隐式表征推断等。下图左边为生成的图片,中间生成的人脸,还可以做超分辨率或者着色之类的任务。生成模型分为「显式」和「隐式」的生成模型,往下分又可以分成很多子类。如下图所示。我们在本篇内容中主要讨论3种模型:PixelRNN / CNN,变分自动编码器属于显示密度模型,生成对抗网络(GAN)属于隐式密度估计模型。2.1 PixelRNN 和 PixelCNNPixelRNN 和 PixelCNN 使用概率链式法则来计算一张图片出现的概率。其中每一项为给定前 个像素点后第 个像素点的条件概率分布。这个分布通过神经网络 RNN 或 CNN 来建模,再通过最大化图片 的似然概率来学习出 RNN 或 CNN 的参数。条件概率公式为:其中:: 图像x的似然概率: 条件概率PixelRNN 中,从左上角开始定义「之前的像素」。由于 RNN 每个时间步的输出概率都依赖于之前所有输入,因此能够用来表示上面的条件概率分布。我们训练这个 RNN 模型时,一次前向传播需要从左上到右下串行走一遍,然后根据上面的公式求出似然,并最大化似然以对参数做一轮更新。因此训练非常耗时。PixelCNN中,使用一个CNN来接收之前的所有像素,并预测下一个像素的出现概率:对比 PixelRNN 和 PixelCNN,后者在训练时可以并行计算公式中的每一项,然后进行参数更新,因此训练速度远快于 PixelRNN。不过,在测试阶段,我们会发现PixelRNN和PixelCNN都要从左上角开始逐个像素点地生成图片,实际应用阶段生成图像的速度是很慢的。PixelRNN 和 PixelCNN 能显式地计算似然 ,是一种可优化的显式密度模型,该方法给出了一个很好的评估度量,可以通过计算的数据的似然来度量出生成样本有多好。2.2 变分自编码器(VAE)PixelCNN定义了一个易于处理的密度函数,我们可以直接优化训练数据的似然;而我们下面介绍到的变分自编码器方法中,密度函数就不易处理了,我们要通过附加的隐变量 对密度函数进行建模:我们数据的似然 是等式右边的积分形式,即对所有可能的 值取期望,但它是无法直接优化的,我们只能找出一个似然函数的下界然后再对该下界进行优化。1) 自编码器自编码器是为了无监督地学习出样本的特征表示,原理如下:如上图,自编码器由编码器和解码器组成,编码器将样本 映射到特征 ,解码器再 将特征 映射到重构样本。我们设定损失函数为 与重构样本之间的 L2 损失,训练出编码器和解码器的参数,希望能够使 解码后恢复出原来的 。#### 编码器编码器可以有多种形式,常用的是神经网络。最先提出的是非线性层的线性组合,然后有了深层的全连接网络(MLP),后来又使用 CNN,我们通过神经网络对输入数据 计算和映射,得到特征 , 的维度通常比 更小。这种降维压缩可以压缩保留 中最重要的特征。#### 解码器解码器主要是为了重构数据,它输出一些跟 有相同维度的结果并尽量拟合 。解码器一般使用和编码器相同类型的网络(与编码器对称)。训练好完整的网络后,我们会把解码器的部分去掉,使用训练好的编码器实现特征映射。通过编码器得到输入数据的特征,编码器顶部有一个分类器,如果是分类问题我们可以用它来输出一个类标签,在这里使用了外部标签和标准的损失函数如 Softmax。无标签数据得到的模型,可以帮助我们得到普适特征(比如上述自编码器映射得到的特征),它们作为监督学习的输入是非常有效的(有些场景下监督学习可能只有很少的带标签的训练数据,少量的数据很难训练模型,可能会出现过拟合等其他一些问题),通过上述方式得到的特征可以很好地初始化下游监督学习任务的网络。自编码器具有重构数据、学习数据特征、初始化一个监督模型的能力。这些学习到的特征具有能捕捉训练数据中蕴含的变化因素的能力。我们获得了一个含有训练数据中变化因子的隐变量 。2) VAE的思想VAE模型的思路是,如果我们无法直接获得样本 的分布,那么我们可以假设存在一个 对应的隐式表征 , 的分布是一个先验分布(比如高斯分布或其他简单的分布)。举例来说,如果我们想要生成微笑的人脸, 代表的是眉毛的位置,嘴角上扬的弧度,它经过解码网络后,能够映射得到 的近似真实分布。那在样本生成阶段,我们可以通过标准正态分布采样得到 ,然后解码得到样本近似分布,再在此分布上采样来生成样本。对于这个采样过程,真实的参数是 ,是有关于先验假设和条件概率分布的参数,我们的目的在于获得一个样本生成式模型,从而利用它来生成新的数据,真实参数是我们想要估计并得出的。我们表示这个生成式模型的方法是:选一个简单的关于 的先验分布,例如高斯分布,对于给定 的 的条件概率分布 很复杂,我们会使用神经网络来对 进行建模。3) 如何训练VAE我们的目标是:从一堆样本中学习出解码网络的参数,使得在标准高斯分布上采样得到的 ,经过解码后得到的 的分布,刚好近似于 的真实分布。我们通过「最大化样本 的似然 」来达到上述目标。 在已经给定隐变量 的情况下,写出 的分布 并对所有可能的 值取期望,因为 值是连续的所以表达式是一个积分:问题是利用求导来直接求最大化的似然,很不好解。第一项是 的分布 ,这里将它简单地设定为高斯分布,所以很容易求; 是一个指定的神经网络解码器,也容易得到。但是计算所有的 对应的 很困难,所以无法计算该积分。这样也导致 是难解的。解决方法是,在使用神经网络解码器来定义一个对 建模神经网络的同时,额外定义一个编码器 ,将输入 编码为 ,从而得到似然 。也就是说我们定义该网络来估计出 ,这个后验密度分布项仍然是难解的,我们用该附加网络来估计该后验分布,这将使我们得到一个数据似然的下界,该下界易解也能优化。在变分自编码器中我们想得到一个生成数据的概率模型,将输入数据 送入编码器得到一些特征 ,然后通过解码器网络把 映射到图像 。我们这里有编码器网络和解码器网络,将一切参数随机化。参数是 的编码器网络 输出一个均值和一个对角协方差矩阵;解码器网络输入 ,输出均值和关于 的对角协方差矩阵。为了得到给定 下的 和给定 下的 ,我们会从这些分布(和)中采样,现在我们的编码器和解码器网络所给出的分别是 和 的条件概率分布,并从这些分布中采样从而获得值。下面是推导过程:这里引入了一个分布 ,就是编码网络。这里我们暂时只把它当作一个符号,继续推导即可:对第一项,我们有:这样我们就得到了 VAE 的核心等式:注意到这个式子的第三项中,含有 ,而由于这个积分无法求解出来,因此我们没办法求第三项的梯度。幸运的是,由于第三项是一个KL散度,其恒大于等于 ,因此前两项的和是似然的一个下界。因此我们退而求其次,来最大化似然的下界,间接达到最大化似然的目的。现在我们引入编码器网络来对 建模,我们的训练框架如下:如何得到下界:① 第1项是对所有采样的 取期望, 是 经过编码器网络采样得到,对 采样然后再求所有 对应的 。让 变大,就是最大限度地重构数据。② 第2项是让KL的散度变小,让我们的近似后验分布和先验分布变得相似,意味着我们想让隐变量z遵循我们期望的分布类型。这个框架就非常类似于自编码器。其中最大化下界的第一项表示我们要能从解码器最大概率地重构出 ,这一步等价于去最小化与样本 的均方误差。最小化下界的第二项则限定了 要遵循我们事先给它指定的分布。公式是我们要优化及最大化的下界,前向传播按如上流程处理,对输入数据 ,让小批量的数据传递经过编码器网络的到 ,通过 来计算 KL 项,然后根据给定 的 分布对 进行采样,由此获得了隐变量的样本,这些样本可以根据 推断获得;然后把 传递给第二个解码器网络,通过解码器网络 在给定 的条件下的两个参数,均值和协方差,最终可以在给定 的条件下从这个分布中采样得到 。训练时需要获得该分布,损失项是给定 条件下对训练像素值取对数,损失函数要做的是最大化被重构的原始输入数据的似然;对于每一个小批量的输入我们都计算这一个前向传播过程,取得所有我们需要的项,他们都是可微分的,接下来把他们全部反向传播回去并获得梯度,不断更新我们的参数,包括生成器和解码器网络的参数 和 从而最大化训练数据的似然。训练好变分自编码器,当生成数据时只需要用解码器网络,我们在训练阶段就对 采样,而不用从后验分布中采样,在生成阶段会从真实的生成过程中采样。先从设定好的先验分布中采样,接下来对数据 采样。需要注意的是,这个框架里面,梯度无法通过「采样」这个算子反向传播到编码器网络,因此我们使用一种叫做重采样的 trick。即将 采样的算子分解为:这样梯度不需要经过采样算子就能回流到编码器网络中。4) VAE的优缺点总结一下,VAE 是在原来的自编码器上加了随机成分,我们使用VAE不是直接取得确定的输入 然后获得特征 最后再重构 ,而是采用随机分布和采样的思想,这样我们就能生成数据。 为了训练模型 VAEs,我们定义了一个难解的密度分布,我们推导出一个下界然后优化下界,下界是变化的,「变分」指的是用近似来解决这些难解的表达式,这是模型被称为变分自动编码器的原因。VAEs优点VAEs 就生成式模型来说是一种有据可循的方法,它使得查询推断称为可能,如此一来便能够推断出像 这样的分布,这些东西对其他任务来说会是很有用的特征表征。VAEs缺点最大化似然下界思想是OK的,但是不像 PixelRNN 和 PixelCNN 那样精准评估。而 VAE 相对后续会讲到的GAN等方法,生成的图像结果更模糊。2.3 生成对抗网络(Generative Adversarial Nets, GAN)1) GAN的核心思路我们之前的 PixelCNN 和 PixelRNN 定义了一个易于处理的密度函数,通过密度函数优化训练数据的似然;VAEs有一个额外定义的隐变量 ,有了 以后获得了很多的有利性质但是我们也有了一个难解的密度函数,对于该函数我们不能直接优化,我们推到了一个似然函数的下界,然后对它进行优化。现在我们放弃显式地对密度函数建模,我们想要得到的是从分布中采样并获得质量良好的样本。GANs 中不再在显式的密度函数上花费精力,而是采用一个博弈论的方法,并且模型将会习得从训练分布中生成数据,具体的实现是基于「生成器」和「判别器」这一对博弈玩家。相比变分自编码器,GAN 的核心思路非常简单。在 GAN 中我们定义了两个网络:「生成器」和「判别器」。判别器负责辨别哪些样本是生成器生成的假样本,哪些是从真实训练集中抽出来的真样本。生成器负责利用随机噪声 生成假样本,它的职责是生成尽可能真的样本以骗过判别器。这种对抗形式的目标可以写成如下形式:现在我们有两个玩家,通过一个 博弈公式联合训练这两个网络,该 目标函数就是如图所示的公式,我们的目标是:让目标函数在 上取得最小值,同时要在 上取得最大值。其中: 是生成器网络g的参数,指的是判别器网络的参数。公式中各项的含义:第1项是在训练数据的分布上 的期望, 是判别器网络在输入为真实数据(训练数据)时的输出,该输出是真实数据从分布 p-data 中采样的似然概率;第2项是对 取期望, 是从 中采样获得的,这意味着从生成器网络中采样,同时 这一项代表了以生成的伪数据为输入判别器网路的输出,也就是判别器网络对于生成网络生成的数据给出的判定结果。对该过程的解释:我们的判别器的目的是最大化目标函数也就是在 上取最大值,这样一来 就会接近1,也就是使判别结果接近真,因而该值对于真实数据应该相当高,这样一来 的值也就是判别器对伪造数据输出就会相应减小,我们希望这一值接近于 。如果我们能最大化这一结果,就意味着判别器能够很好的区别真实数据和伪造数据。对于生成器来说,我们希望它最小化该目标函数,也就是让 接近 ,如果 接近 ,那么用 减去它就会很小,判别器网络就会把伪造数据视为真实数据,也就意味着我们的生成器在生成真实样本。从数据准备上看,整个过程是一个无监督学习,我们无需人工给每个图片打上标签。具体网络学习时候,我们会把生成器生成的图片标记为 (对应假图片),训练集标记为 (都是真图片)。判别器的损失函数会使用上述信息,判别器是一个分类器,我们希望它能经过训练获得分辨能力:对生成器生成的图片输出 ,而对真实图片输出 。#### 训练方法对于GAN,我们最初能想到的训练方式如下:① 对判别器进行梯度上升,学习到 来最大化该目标函数;② 对生成器进行梯度下降, 进行梯度下降最小化目标函数(此时目标函数如下的部分,因为只有它与 有关)不断在上述 ① 和 ② 之间重复。这里有个trick:我们观察生成器的损失函数形状如下:发现当生成器效果不好( 接近 )时,梯度非常平缓;当生成器效果好(接近)时,梯度很陡峭。这就与我们期望的相反了,我们希望在生成器效果不好的时候梯度更陡峭,这样能学到更多。因此我们使用下面的目标函数来替代原来的生成器损失:这样就使得在生成器效果不好时具有较大的梯度。此外,联合训练两个网络很有挑战,交替训练的方式不可能一次训练两个网络,还有损失函数的函数空间会影响训练的动态过程。在每一个训练迭代期都先训练判别器网络,然后训练生成器网络,GAN 的总体训练过程如下:训练判别器对于判别器网络的k个训练步,先从噪声先验分布 中采样得到一个小批量样本,接着从训练数据 中采样获得小批量的真实样本,下面要做的将噪声样本传给生成器网络,并在生成器的输出端获得生成的图像。此时我们有了一个小批量伪造图像和小批量真实图像,我们有这些小批量数据在判别器生进行一次梯度计算,接下来利用梯度信息更新判别器参数,按照以上步骤迭代几次来训练判别器。训练生成器在这一步采样获得一个小批量噪声样本,将它传入生成器,对生成器进行反向传播,来优化目标函数。训练 GAN 的过程会交替进行上述两个步骤。训练完毕后,就可以用生成器来生成比较逼真的样本了。2) GAN的探索传统的GAN生成的样本还不是很好,这篇论文在GAN中使用了CNN架构,取得了惊艳的生成效果:[Radford et al, “Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks”, ICLR 2016]Wasserstein GAN 一定程度解决了GAN训练中两个网络如何平衡的问题。用GAN来做text -> image3) GAN的优缺点以及热门研究方向GAN 的优点GAN通过一种博弈的方法来训练,通过两个玩家的博弈从训练数据的分布中学会生成数据。GAN可以生成目前最好的样本,还可以做很多其他的事情。GAN 的缺点GAN没有显式的密度函数(它是利用样本来隐式表达该函数)GAN不好训练且不稳定,我们并不是直接优化目标函数,我们要努力地平衡两个网络。GAN 的热门研究方向更好的损失函数设计,更稳定的训练方式(例如Wasserstein GAN, LSGAN及其他)条件GAN,GAN的各种应用领域探索3.拓展学习可以点击 B站 查看视频的【双语字幕】版本frameLabelStart--frameLabelEnd【课程学习指南】斯坦福CS231n | 深度学习与计算机视觉【字幕+资料下载】斯坦福CS231n | 深度学习与计算机视觉 (2017·全16讲)【CS231n进阶课】密歇根EECS498 | 深度学习与计算机视觉【深度学习教程】吴恩达专项课程 · 全套笔记解读【Stanford官网】CS231n: Deep Learning for Computer Vision4.要点总结本篇讲了三种目前最常用生成模型:PixelCNN 和 PixelRNN 他们是显式密度模型,该模型优化的是一个显式的似然函数并产生良好的样本,但是效率很低,它是一个顺序的生成过程。VAE 优化的是一个似然函数的下界,它会产生一个有用的隐式表征,可以用它来进行查询推断,生成的样本也不是特别好。GAN 是目前能生成最好样本的模型,但是训练需要技巧且不稳定,查询推断上也有一些问题。还有一些将模型的优点结合起来做的研究。斯坦福 CS231n 全套解读深度学习与CV教程(1) | CV引言与基础深度学习与CV教程(2) | 图像分类与机器学习基础深度学习与CV教程(3) | 损失函数与最优化深度学习与CV教程(4) | 神经网络与反向传播深度学习与CV教程(5) | 卷积神经网络深度学习与CV教程(6) | 神经网络训练技巧 (上)深度学习与CV教程(7) | 神经网络训练技巧 (下)深度学习与CV教程(8) | 常见深度学习框架介绍深度学习与CV教程(9) | 典型CNN架构 (Alexnet, VGG, Googlenet, Restnet等)深度学习与CV教程(10) | 轻量化CNN架构 (SqueezeNet, ShuffleNet, MobileNet等)深度学习与CV教程(11) | 循环神经网络及视觉应用深度学习与CV教程(12) | 目标检测 (两阶段, R-CNN系列)深度学习与CV教程(13) | 目标检测 (SSD, YOLO系列)深度学习与CV教程(14) | 图像分割 (FCN, SegNet, U-Net, PSPNet, DeepLab, RefineNet)深度学习与CV教程(15) | 视觉模型可视化与可解释性深度学习与CV教程(16) | 生成模型 (PixelRNN, PixelCNN, VAE, GAN)深度学习与CV教程(17) | 深度强化学习 (马尔可夫决策过程, Q-Learning, DQN)深度学习与CV教程(18) | 深度强化学习 (梯度策略, Actor-Critic, DDPG, A3C)ShowMeAI 系列教程推荐大厂技术实现:推荐与广告计算解决方案大厂技术实现:计算机视觉解决方案大厂技术实现:自然语言处理行业解决方案图解Python编程:从入门到精通系列教程图解数据分析:从入门到精通系列教程图解AI数学基础:从入门到精通系列教程图解大数据技术:从入门到精通系列教程图解机器学习算法:从入门到精通系列教程机器学习实战:手把手教你玩转机器学习系列深度学习教程:吴恩达专项课程 · 全套笔记解读自然语言处理教程:斯坦福CS224n课程 · 课程带学与全套笔记解读深度学习与计算机视觉教程:斯坦福CS231n · 全套笔记解读
GAN完整理论推导、证明与实现(附代码)
本文是机器之心第二个 GitHub 实现项目,上一个 GitHub 实现项目为从头开始构建卷积神经网络。
本文主要分四部分:
第一部分描述 GAN 的直观概念
第二部分描述概念与优化的形式化表达
第三部分将对 GAN 进行详细的理论推导与分析
最后我们将实现前面的理论分析。
GitHub项目地址:
https://github.com/jiqizhixin/ML-Tutorial-Experiment
生成对抗网络基本概念
要理解生成对抗模型(GAN),首先要了解生成对抗模型可以拆分为两个模块:一个是判别模型,另一个是生成模型。简单来说就是:两个人比赛,看是 A 的矛厉害,还是 B 的盾厉害。比如,我们有一些真实数据,同时也有一把随机生成的假数据。A 拼命地把随手拿过来的假数据模仿成真实数据,并揉进真实数据里。B 则拼命地想把真实数据和假数据区分开。
这里,A 就是一个生成模型,类似于造假币的,一个劲地学习如何骗过 B。而 B 则是一个判别模型,类似于稽查警察,一个劲地学习如何分辨出 A 的造假技巧。
如此这般,随着 B 的鉴别技巧越来越厉害,A 的造假技巧也是越来越纯熟,而一个一流的假币制造者就是我们所需要的。虽然 GAN 背后的思想十分直观与朴素,但我们需要更进一步了解该理论背后的证明与推导。
总的来说,Goodfellow 等人提出来的 GAN 是通过对抗过程估计生成模型的新框架。在这种框架下,我们需要同时训练两个模型,即一个能捕获数据分布的生成模型 G 和一个能估计数据来源于真实样本概率的判别模型 D。生成器 G 的训练过程是最大化判别器犯错误的概率,即判别器误以为数据是真实样本而不是生成器生成的假样本。因此,这一框架就对应于两个参与者的极小极大博弈(minimax game)。在所有可能的函数 G 和 D 中,我们可以求出唯一均衡解,即 G 可以生成与训练样本相同的分布,而 D 判断的概率处处为 1/2,这一过程的推导与证明将在后文详细解释。
当模型都为多层感知机时,对抗性建模框架可以最直接地应用。为了学习到生成器在数据 x 上的分布 P_g,我们先定义一个先验的输入噪声变量 P_z(z),然后根据 G(z;θ_g) 将其映射到数据空间中,其中 G 为多层感知机所表征的可微函数。我们同样需要定义第二个多层感知机 D(s;θ_d),它的输出为单个标量。D(x) 表示 x 来源于真实数据而不是 P_g 的概率。我们训练 D 以最大化正确分配真实样本和生成样本的概率,因此我们就可以通过最小化 log(1-D(G(z))) 而同时训练 G。也就是说判别器 D 和生成器对价值函数 V(G,D) 进行了极小极大化博弈:
我们后一部分会对对抗网络进行理论上的分析,该理论分析本质上可以表明如果 G 和 D 的模型复杂度足够(即在非参数限制下),那么对抗网络就能生成数据分布。此外,Goodfellow 等人在论文中使用如下案例为我们简要介绍了基本概念。
如上图所示,生成对抗网络会训练并更新判别分布(即 D,蓝色的虚线),更新判别器后就能将数据真实分布(黑点组成的线)从生成分布 P_g(G)(绿色实线)中判别出来。下方的水平线代表采样域 Z,其中等距线表示 Z 中的样本为均匀分布,上方的水平线代表真实数据 X 中的一部分。向上的箭头表示映射 x=G(z) 如何对噪声样本(均匀采样)施加一个不均匀的分布 P_g。(a)考虑在收敛点附近的对抗训练:P_g 和 P_data 已经十分相似,D 是一个局部准确的分类器。(b)在算法内部循环中训练 D 以从数据中判别出真实样本,该循环最终会收敛到 D(x)=p_data(x)/(p_data(x)+p_g(x))。(c)随后固定判别器并训练生成器,在更新 G 之后,D 的梯度会引导 G(z)流向更可能被 D 分类为真实数据的方向。(d)经过若干次训练后,如果 G 和 D 有足够的复杂度,那么它们就会到达一个均衡点。这个时候 p_g=p_data,即生成器的概率密度函数等于真实数据的概率密度函数,也即生成的数据和真实数据是一样的。在均衡点上 D 和 G 都不能得到进一步提升,并且判别器无法判断数据到底是来自真实样本还是伪造的数据,即 D(x)= 1/2。
上面比较精简地介绍了生成对抗网络的基本概念,下一节将会把这些概念形式化,并描述优化的大致过程。
概念与过程的形式化
理论完美的生成器
该算法的目标是令生成器生成与真实数据几乎没有区别的样本,即一个造假一流的 A,就是我们想要的生成模型。数学上,即将随机变量生成为某一种概率分布,也可以说概率密度函数为相等的:P_G(x)=P_data(x)。这正是数学上证明生成器高效性的策略:即定义一个最优化问题,其中最优生成器 G 满足 P_G(x)=P_data(x)。如果我们知道求解的 G 最后会满足该关系,那么我们就可以合理地期望神经网络通过典型的 SGD 训练就能得到最优的 G。
最优化问题
正如最开始我们了解的警察与造假者案例,定义最优化问题的方法就可以由以下两部分组成。首先我们需要定义一个判别器 D 以判别样本是不是从 P_data(x) 分布中取出来的,因此有:
其中 E 指代取期望。这一项是根据「正类」(即辨别出 x 属于真实数据 data)的对数损失函数而构建的。最大化这一项相当于令判别器 D 在 x 服从于 data 的概率密度时能准确地预测 D(x)=1,即:
另外一项是企图欺骗判别器的生成器 G。该项根据「负类」的对数损失函数而构建,即:
因为 x<1 的对数为负,那么如果最大化该项的值,则需要令均值 D(G(z))≈0,因此 G 并没有欺骗 D。为了结合这两个概念,判别器的目标为最大化:
给定生成器 G,其代表了判别器 D 正确地识别了真实和伪造数据点。给定一个生成器 G,上式所得出来的最优判别器可以表示为 (下文用 D_G*表示)。定义价值函数为:
然后我们可以将最优化问题表述为:
现在 G 的目标已经相反了,当 D=D_G*时,最优的 G 为最小化前面的等式。在论文中,作者更喜欢求解最优化价值函的 G 和 D 以求解极小极大博弈:
对于 D 而言要尽量使公式最大化(识别能力强),而对于 G 又想使之最小(生成的数据接近实际数据)。整个训练是一个迭代过程。其实极小极大化博弈可以分开理解,即在给定 G 的情况下先最大化 V(D,G) 而取 D,然后固定 D,并最小化 V(D,G) 而得到 G。其中,给定 G,最大化 V(D,G) 评估了 P_G 和 P_data 之间的差异或距离。
最后,我们可以将最优化问题表达为:
上文给出了 GAN 概念和优化过程的形式化表达。通过这些表达,我们可以理解整个生成对抗网络的基本过程与优化方法。当然,有了这些概念我们完全可以直接在 GitHub 上找一段 GAN 代码稍加修改并很好地运行它。但如果我们希望更加透彻地理解 GAN,更加全面地理解实现代码,那么我们还需要知道很多推导过程。比如什么时候 D 能令价值函数 V(D,G) 取最大值、G 能令 V(D,G) 取最小值,而 D 和 G 该用什么样的神经网络(或函数),它们的损失函数又需要用什么等等。总之,还有很多理论细节与推导过程需要我们进一步挖掘。
理论推导
在原 GAN 论文中,度量生成分布与真实分布之间差异或距离的方法是 JS 散度,而 JS 散度是我们在推导训练过程中使用 KL 散度所构建出来的。所以这一部分将从理论基础出发再进一步推导最优判别器和生成器所需要满足的条件,最后我们将利用推导结果在数学上重述训练过程。这一部分为我们下一部分理解具体实现提供了强大的理论支持。
KL 散度
在信息论中,我们可以使用香农熵(Shannon entropy)来对整个概率分布中的不确定性总量进行量化:
如果我们对于同一个随机变量 x 有两个单独的概率分布 P(x) 和 Q(x),我们可以使用 KL 散度(Kullback-Leibler divergence)来衡量这两个分布的差异:
在离散型变量的情况下,KL 散度衡量的是,当我们使用一种被设计成能够使得概率分布 Q 产生的消息的长度最小的编码,发送包含由概率分布 P 产生的符号的消息时,所需要的额外信息量。
KL 散度有很多有用的性质,最重要的是它是非负的。KL 散度为 0 当且仅当 P 和 Q 在离散型变量的情况下是相同的分布,或者在连续型变量的情况下是 『几乎处处』 相同的。因为 KL 散度是非负的并且衡量的是两个分布之间的差异,它经常 被用作分布之间的某种距离。然而,它并不是真的距离因为它不是对称的:对于某些 P 和 Q,D_KL(P||Q) 不等于 D_KL(Q||P)。这种非对称性意味着选择 D_KL(P||Q) 还是 D_KL(Q||P) 影响很大。
在李弘毅的讲解中,KL 散度可以从极大似然估计中推导而出。若给定一个样本数据的分布 P_data(x) 和生成的数据分布 P_G(x;θ),那么 GAN 希望能找到一组参数θ使分布 P_g(x;θ) 和 P_data(x) 之间的距离最短,也就是找到一组生成器参数而使得生成器能生成十分逼真的图片。
现在我们可以从训练集抽取一组真实图片来训练 P_G(x;θ) 分布中的参数 θ 使其能逼近于真实分布。因此,现在从 P_data(x) 中抽取 m 个真实样本 {x^1,x^2,…,x^},其中符号「^」代表上标,即 x 中的第 i 个样本。对于每一个真实样本,我们可以计算 P_G(x^i;θ),即在由 θ 确定的生成分布中,x^i 样本所出现的概率。因此,我们就可以构建似然函数:
其中「∏」代表累乘、P_G(x^i;θ) 代表第 i 个样本在生成分布出现的概率。从该似然函数可知,我们抽取的 m 个真实样本在 P_G(x;θ) 分布中全部出现的概率值可以表达为 L。又因为若 P_G(x;θ) 分布和 P_data(x) 分布相似,那么真实数据很可能就会出现在 P_G(x;θ) 分布中,因此 m 个样本都出现在 P_G(x;θ) 分布中的概率就会十分大。
下面我们就可以最大化似然函数 L 而求得离真实分布最近的生成分布(即最优的参数θ):
在上面的推导中,我们希望最大化似然函数 L。若对似然函数取对数,那么累乘 ∏ 就能转化为累加 ∑,并且这一过程并不会改变最优化的结果。因此我们可以将极大似然估计化为求令 log[P_G(x;θ)] 期望最大化的 θ,而期望 E[logP_G(x;θ)] 可以展开为在 x 上的积分形式:∫P_data(x)logP_G(x;θ)dx。又因为该最优化过程是针对 θ 的,所以我们添加一项不含 θ 的积分并不影响最优化效果,即可添加 -∫P_data(x)logP_data(x)dx。添加该积分后,我们可以合并这两个积分并构建类似 KL 散度的形式。该过程如下:
这一个积分就是 KL 散度的积分形式,因此,如果我们需要求令生成分布 P_G(x;θ) 尽可能靠近真实分布 P_data(x) 的参数 θ,那么我们只需要求令 KL 散度最小的参数 θ。若取得最优参数 θ,那么生成器生成的图像将显得非常真实。
推导存在的问题
下面,我们必须证明该最优化问题有唯一解 G*,并且该唯一解满足 P_G=P_data。不过在开始推导最优判别器和最优生成器之前,我们需要了解 Scott Rome 对原论文推导的观点,他认为原论文忽略了可逆条件,因此最优解的推导不够完美。
在 GAN 原论文中,有一个思想和其它很多方法都不同,即生成器 G 不需要满足可逆条件。Scott Rome 认为这一点非常重要,因为实践中 G 就是不可逆的。而很多证明笔记都忽略了这一点,他们在证明时错误地使用了积分换元公式,而积分换元却又恰好基于 G 的可逆条件。Scott 认为证明只能基于以下等式的成立性:
该等式来源于测度论中的 Radon-Nikodym 定理,它展示在原论文的命题 1 中,并且表达为以下等式:
我们看到该讲义使用了积分换元公式,但进行积分换元就必须计算 G^(-1),而 G 的逆却并没有假定为存在。并且在神经网络的实践中,它也并不存在。可能这个方法在机器学习和统计学文献中太常见了,因此我们忽略了它。
最优判别器
在极小极大博弈的第一步中,给定生成器 G,最大化 V(D,G) 而得出最优判别器 D。其中,最大化 V(D,G) 评估了 P_G 和 P_data 之间的差异或距离。因为在原论文中价值函数可写为在 x 上的积分,即将数学期望展开为积分形式:
其实求积分的最大值可以转化为求被积函数的最大值。而求被积函数的最大值是为了求得最优判别器 D,因此不涉及判别器的项都可以看作为常数项。如下所示,P_data(x) 和 P_G(x) 都为标量,因此被积函数可表示为 a*D(x)+b*log(1-D(x))。
若令判别器 D(x) 等于 y,那么被积函数可以写为:
为了找到最优的极值点,如果 a+b≠0,我们可以用以下一阶导求解:
如果我们继续求表达式 f(y) 在驻点的二阶导:
其中 a,b∈(0,1)。因为一阶导等于零、二阶导小于零,所以我们知道 a/(a+b) 为极大值。若将 a=P_data(x)、b=P_G(x) 代入该极值,那么最优判别器 D(x)=P_data(x)/(P_data(x)+P_G(x))。
最后我们可以将价值函数表达式写为:
如果我们令 D(x)=P_data/(P_data+P_G),那么我们就可以令价值函数 V(G,D) 取极大值。因为 f(y) 在定义域内有唯一的极大值,最优 D 也是唯一的,并且没有其它的 D 能实现极大值。
其实该最优的 D 在实践中并不是可计算的,但在数学上十分重要。我们并不知道先验的 P_data(x),所以我们在训练中永远不会用到它。另一方面,它的存在令我们可以证明最优的 G 是存在的,并且在训练中我们只需要逼近 D。
最优生成器
当然 GAN 过程的目标是令 P_G=P_data。这对最优的 D 意味着什么呢?我们可以将这一等式代入 D_G*的表达式中:
这意味着判别器已经完全困惑了,它完全分辨不出 P_data 和 P_G 的区别,即判断样本来自 P_data 和 P_G 的概率都为 1/2。基于这一观点,GAN 作者证明了 G 就是极小极大博弈的解。该定理如下:
「当且仅当 P_G=P_data,训练标准 C(G)=maxV(G,D) 的全局最小点可以达到。」
以上定理即极大极小博弈的第二步,求令 V(G,D*) 最小的生成器 G(其中 G*代表最优的判别器)。之所以当 P_G(x)=P_data(x) 可以令价值函数最小化,是因为这时候两个分布的 JS 散度 [JSD(P_data(x) || P_G(x))] 等于零,这一过程的详细解释如下。
原论文中的这一定理是「当且仅当」声明,所以我们需要从两个方向证明。首先我们先从反向逼近并证明 C(G) 的取值,然后再利用由反向获得的新知识从正向证明。设 P_G=P_data(反向指预先知道最优条件并做推导),我们可以反向推出:
该值是全局最小值的候选,因为它只有在 P_G=P_data 的时候才出现。我们现在需要从正向证明这一个值常常为最小值,也就是同时满足「当」和「仅当」的条件。现在放弃 P_G=P_data 的假设,对任意一个 G,我们可以将上一步求出的最优判别器 D* 代入到 C(G)=maxV(G,D) 中:
因为已知 -log4 为全局最小候选值,所以我们希望构造某个值以使方程式中出现 log2。因此我们可以在每个积分中加上或减去 log2,并乘上概率密度。这是一个十分常见并且不会改变等式的数学证明技巧,因为本质上我们只是在方程加上了 0。
采用该技巧主要是希望能够构建成含 log2 和 JS 散度的形式,上式化简后可以得到以下表达式:
因为概率密度的定义,P_G 和 P_data 在它们积分域上的积分等于 1,即:
此外,根据对数的定义,我们有:
因此代入该等式,我们可以写为:
现在,如果读者阅读了前文的 KL 散度(Kullback-Leibler divergence),那么我们就会发现每一个积分正好就是它。具体来说:
KL 散度是非负的,所以我们马上就能看出来 -log4 为 C(G) 的全局最小值。
如果我们进一步证明只有一个 G 能达到这一个值,因为 P_G=P_data 将会成为令 C(G)=−log4 的唯一点,所以整个证明就能完成了。
从前文可知 KL 散度是非对称的,所以 C(G) 中的 KL(P_data || (P_data+P_G)/2) 左右两项是不能交换的,但如果同时加上另一项 KL(P_G || (P_data+P_G)/2),它们的和就能变成对称项。这两项 KL 散度的和即可以表示为 JS 散度(Jenson-Shannon divergence):
假设存在两个分布 P 和 Q,且这两个分布的平均分布 M=(P+Q)/2,那么这两个分布之间的 JS 散度为 P 与 M 之间的 KL 散度加上 Q 与 M 之间的 KL 散度再除以 2。
JS 散度的取值为 0 到 log2。若两个分布完全没有交集,那么 JS 散度取最大值 log2;若两个分布完全一样,那么 JS 散度取最小值 0。
因此 C(G) 可以根据 JS 散度的定义改写为:
这一散度其实就是 Jenson-Shannon 距离度量的平方。根据它的属性:当 P_G=P_data 时,JSD(P_data||P_G) 为 0。综上所述,生成分布当且仅当等于真实数据分布式时,我们可以取得最优生成器。
收敛
现在,该论文的主要部分已经得到了证明:即 P_G=P_data 为 maxV(G,D) 的最优点。此外,原论文还有额外的证明白表示:给定足够的训练数据和正确的环境,训练过程将收敛到最优 G,我们并不详细讨论这一块。
重述训练过程
下面是推导的最后一步,我们会重述整个参数优化过程,并简要介绍实际训练中涉及的各个过程。
参数优化过程:
若我们需要寻找最优的生成器,那么给定一个判别器 D,我们可以将 maxV(G,D) 看作训练生成器的损失函数 L(G)。既然设定了损失函数,那么我们就能使用 SGD、Adam 等优化算法更新生成器 G 的参数,梯度下降的参数优化过程如下:
其中求 L(G) 对θ_G 的偏导数涉及到求 max{V(G,D)} 的偏导数,这种对 max 函数求微分的方式是存在且可用的。
现在给定一个初始 G_0,我们需要找到令 V(G_0,D) 最大的 D_0*,因此判别器更新的过程也就可以看作损失函数为-V(G,D) 的训练过程。并且由前面的推导可知,V(G,D) 实际上与分布 P_data(x) 和 P_G(x) 之间的 JS 散度只差了一个常数项。因此这样一个循环对抗的过程就能表述为:
给定 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;
。。。
实际训练过程:
根据前面价值函数 V(G,D) 的定义,我们需要求两个数学期望,即 E[log(D(x))] 和 E[log(1-D(G(z)))],其中 x 服从真实数据分布,z 服从初始化分布。但在实践中,我们是没有办法利用积分求这两个数学期望的,所以一般我们能从无穷的真实数据和无穷的生成器中做采样以逼近真实的数学期望。
若现在给定生成器 G,并希望计算 maxV(G,D) 以求得判别器 D,那么我们首先需要从 P_data(x) 采样 m 个样本 {x^1,x^2,…,x^m},从生成器 P_G(x) 采样 m 个样本。因此最大化价值函数 V(G,D) 就可以使用以下表达式近似替代:
若我们需要计算上述的极大化过程,可以采用等价形式的训练方法。若我们有一个二元分类器 D(参数为 θ_d),当然该分类器可以是深度神经网络,那么极大化过程的输出就为该分类器 D(x)。现在我们从 P_data(x) 抽取样本作为正样本,从 P_G(x) 抽取样本作为负样本,同时将逼近负 V(G,D) 的函数作为损失函数,因此我们就将其表述为一个标准的二元分类器的训练过程:
在实践中,我们必须使用迭代和数值计算的方法实现极小极大化博弈过程。在训练的内部循环中完整地优化 D 在计算上是不允许的,并且有限的数据集也会导致过拟合。因此我们可以在 k 个优化 D 的步骤和一个优化 G 的步骤间交替进行。那么我们只需慢慢地更新 G,D 就会一直处于最优解的附近,这种策略类似于 SML/PCD 训练的方式。
综上,我们可以描述整个训练过程,对于每一次迭代:
从真实数据分布 P_data 抽取 m 个样本
从先验分布 P_prior(z) 抽取 m 个噪声样本
将噪声样本投入 G 而生成数据,通过最大化 V 的近似而更新判别器参数θ_d,即极大化,且判别器参数的更新迭代式为
以上是学习判别器 D 的过程。因为学习 D 的过程是计算 JS 散度的过程,并且我们希望能最大化价值函数,所以该步骤会重复 k 次。
从先验分布 P_prior(z) 中抽取另外 m 个噪声样本 {z^1,...,z^m}
通过极小化 V^tilde 而更新生成器参数θ_g,即极大化 ,且生成器参数的更新迭代式为
以上是学习生成器参数的过程,这一过程在一次迭代中只会进行一次,因此可以避免更新太多而令 JS 散度上升。
实现
在上一期机器之心 GitHub 项目中,我们从零开始使用 TensorFlow 实现了简单的 CNN,我们不仅介绍了 TensorFlow 基本的操作,并从全连接神经网络开始简单地实现了 LeNet-5。在第一期 GitHub 实现中,我们陆续上传了三段实现代码,第二次上传补充的是全连接网络进行 MNIST 图像识别,我们逐行注释了该模型的所有代码。第三次上传补充的是使用 Keras 构建简单的 CNN,我们同样添加了大量注释。本文是第二期 GitHub 实现,首先提供的是 GAN 实现代码与注释,随后我们会将以上的理论分析与实现代码相结合并展示在 Jupyter Notebook 中。虽然首次实现使用的是比较简单的高级 API(Keras),但后面我们会补充使用 TensorFlow 构建 GAN 的代码与注释。
GitHub 实现地址:
https://github.com/jiqizhixin/ML-Tutorial-Experiment
机器之心首先使用基于 TensorFlow 后端的 Keras 实现了该生成对抗网络,并且我们在 MNIST 数据集上对模型进行训练并生成了一系列手写字体。这一章节只简要解释部分实现代码,更完整与详细的注释请查看 GitHub 项目地址。
生成模型
首先需要定义一个生成器 G,该生成器需要将输入的随机噪声变换为图像。以下是定义的生成模型,该模型首先输入有 100 个元素的向量,该向量随机生成于某分布。随后利用两个全连接层接连将该输入向量扩展到 1024 维和 128*7*7 维,后面就开始将全连接层所产生的一维张量重新塑造成二维张量,即 MNIST 中的灰度图。我们注意到该模型采用的激活函数为 tanh,所以也尝试过将其转换为 relu 函数,但发现生成模型如果转化为 relu 函数,那么它的输出就会成为一片灰色。
由全连接传递的数据会经过几个上采样层和卷积层,我们注意到最后一个卷积层所采用的卷积核为 1,所以经过最后卷积层所生成的图像是一张二维灰度图像,更详细的分析请查看机器之心 GitHub 项目。
def generator_model():
#下面搭建生成器的架构,首先导入序贯模型(sequential),即多个网络层的线性堆叠
model = Sequential()
#添加一个全连接层,输入为100维向量,输出为1024维
model.add(Dense(input_dim=100, output_dim=1024))
#添加一个激活函数tanh
model.add(Activation('tanh'))
#添加一个全连接层,输出为128×7×7维度
model.add(Dense(128*7*7))
#添加一个批量归一化层,该层在每个batch上将前一层的激活值重新规范化,即使得其输出数据的均值接近0,其标准差接近1
model.add(BatchNormalization())
model.add(Activation('tanh'))
#Reshape层用来将输入shape转换为特定的shape,将含有128*7*7个元素的向量转化为7×7×128张量
model.add(Reshape((7, 7, 128), input_shape=(128*7*7,)))
#2维上采样层,即将数据的行和列分别重复2次
model.add(UpSampling2D(size=(2, 2)))
#添加一个2维卷积层,卷积核大小为5×5,激活函数为tanh,共64个卷积核,并采用padding以保持图像尺寸不变
model.add(Conv2D(64, (5, 5), padding='same'))
model.add(Activation('tanh'))
model.add(UpSampling2D(size=(2, 2)))
#卷积核设为1即输出图像的维度
model.add(Conv2D(1, (5, 5), padding='same'))
model.add(Activation('tanh'))
return model
拼接
前面定义的是可生成图像的模型 G(z;θ_g),而我们在训练生成模型时,需要固定判别模型 D 以极小化价值函数而寻求更好的生成模型,这就意味着我们需要将生成模型与判别模型拼接在一起,并固定 D 的权重以训练 G 的权重。下面就定义了这一过程,我们先添加前面定义的生成模型,再将定义的判别模型拼接在生成模型下方,并且我们将判别模型设置为不可训练。因此,训练这个组合模型才能真正更新生成模型的参数。
def generator_containing_discriminator(g, d):
#将前面定义的生成器架构和判别器架构组拼接成一个大的神经网络,用于判别生成的图片
model = Sequential()
#先添加生成器架构,再令d不可训练,即固定d
#因此在给定d的情况下训练生成器,即通过将生成的结果投入到判别器进行辨别而优化生成器
model.add(g)
d.trainable = False
model.add(d)
return model
判别模型
判别模型相对来说就是比较传统的图像识别模型,前面我们可以按照经典的方法采用几个卷积层与最大池化层,而后再展开为一维张量并采用几个全连接层作为架构。我们尝试了将 tanh 激活函数改为 relu 激活函数,在前两个 epoch 基本上没有什么明显的变化。
def discriminator_model():
#下面搭建判别器架构,同样采用序贯模型
model = Sequential()
#添加2维卷积层,卷积核大小为5×5,激活函数为tanh,输入shape在‘channels_first’模式下为(samples,channels,rows,cols)
#在‘channels_last’模式下为(samples,rows,cols,channels),输出为64维
model.add(
Conv2D(64, (5, 5),
padding='same',
input_shape=(28, 28, 1))
)
model.add(Activation('tanh'))
#为空域信号施加最大值池化,pool_size取(2,2)代表使图片在两个维度上均变为原长的一半
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, (5, 5)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
#Flatten层把多维输入一维化,常用在从卷积层到全连接层的过渡
model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('tanh'))
#一个结点进行二值分类,并采用sigmoid函数的输出作为概念
model.add(Dense(1))
model.add(Activation('sigmoid'))
return model
训练
训练这一部分比较长,也值得我们进行详细的探讨。总的来说,以下训练过程可简述为:
加载 MNIST 数据
将数据分割为训练与测试集,并赋值给变量
设置训练模型的超参数
编译模型的训练过程
在每一次迭代内,抽取生成图像与真实图像,并打上标注
随后将数据投入到判别模型中,并进行训练与计算损失
固定判别模型,训练生成模型并计算损失,结束这一次迭代
以上是下面训练过程的简要介绍,我们将结合上文的理论推导在 GitHub 中展示更详细的分析。
def train(BATCH_SIZE):
# 国内好像不能直接导入数据集,我们试了几次都不行,后来将数据集下载到本地'~/.keras/datasets/',也就是当前目录(我的是用户文件夹下)下的.keras文件夹中。
#下载的地址为:https://s3.amazonaws.com/img-datasets/mnist.npz
(X_train, y_train), (X_test, y_test) = mnist.load_data()
#iamge_data_format选择"channels_last"或"channels_first",该选项指定了Keras将要使用的维度顺序。
#"channels_first"假定2D数据的维度顺序为(channels, rows, cols),3D数据的维度顺序为(channels, conv_dim1, conv_dim2, conv_dim3)
#转换字段类型,并将数据导入变量中
X_train = (X_train.astype(np.float32) - 127.5)/127.5
X_train = X_train[:, :, :, None]
X_test = X_test[:, :, :, None]
# X_train = X_train.reshape((X_train.shape, 1) + X_train.shape[1:])
#将定义好的模型架构赋值给特定的变量
d = discriminator_model()
g = generator_model()
d_on_g = generator_containing_discriminator(g, d)
#定义生成器模型判别器模型更新所使用的优化算法及超参数
d_optim = SGD(lr=0.001, momentum=0.9, nesterov=True)
g_optim = SGD(lr=0.001, momentum=0.9, nesterov=True)
#编译三个神经网络并设置损失函数和优化算法,其中损失函数都是用的是二元分类交叉熵函数。编译是用来配置模型学习过程的
g.compile(loss='binary_crossentropy', optimizer="SGD")
d_on_g.compile(loss='binary_crossentropy', optimizer=g_optim)
#前一个架构在固定判别器的情况下训练了生成器,所以在训练判别器之前先要设定其为可训练。
d.trainable = True
d.compile(loss='binary_crossentropy', optimizer=d_optim)
#下面在满足epoch条件下进行训练
for epoch in range(30):
print("Epoch is", epoch)
#计算一个epoch所需要的迭代数量,即训练样本数除批量大小数的值取整;其中shape[0]就是读取矩阵第一维度的长度
print("Number of batches", int(X_train.shape[0]/BATCH_SIZE))
#在一个epoch内进行迭代训练
for index in range(int(X_train.shape[0]/BATCH_SIZE)):
#随机生成的噪声服从均匀分布,且采样下界为-1、采样上界为1,输出BATCH_SIZE×100个样本;即抽取一个批量的随机样本
noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 100))
#抽取一个批量的真实图片
image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]
#生成的图片使用生成器对随机噪声进行推断;verbose为日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录
generated_images = g.predict(noise, verbose=0)
#每经过100次迭代输出一张生成的图片
if index % 100 == 0:
image = combine_images(generated_images)
image = image*127.5+127.5
Image.fromarray(image.astype(np.uint8)).save(
"./GAN/"+str(epoch)+"_"+str(index)+".png")
#将真实的图片和生成的图片以多维数组的形式拼接在一起,真实图片在上,生成图片在下
X = np.concatenate((image_batch, generated_images))
#生成图片真假标签,即一个包含两倍批量大小的列表;前一个批量大小都是1,代表真实图片,后一个批量大小都是0,代表伪造图片
y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
#判别器的损失;在一个batch的数据上进行一次参数更新
d_loss = d.train_on_batch(X, y)
print("batch %d d_loss : %f" % (index, d_loss))
#随机生成的噪声服从均匀分布
noise = np.random.uniform(-1, 1, (BATCH_SIZE, 100))
#固定判别器
d.trainable = False
#计算生成器损失;在一个batch的数据上进行一次参数更新
g_loss = d_on_g.train_on_batch(noise, [1] * BATCH_SIZE)
#令判别器可训练
d.trainable = True
print("batch %d g_loss : %f" % (index, g_loss))
#每100次迭代保存一次生成器和判别器的权重
if index % 100 == 9:
g.save_weights('generator', True)
d.save_weights('discriminator', True)
试验
在实践中,我们训练 30 个 epoch 后能得到如下不错的生成结果:
当然,中间我们还发现很多训练上的问题,比如说学习率、批量大小、激活函数等。学习率一般我们设置为 0.001 到 0.0005,其它的学习率还有很多没有测试。批量大小我们使用的比较小,例如 16、32、64 等,较小的学习率可能训练的 epoch 数就不需要那么多。我们发现若将生成模型的激活函数修改为 relu,那么生成的图像很可能会显示为一片灰色,生成模型和判别模型的训练损失可能会表现为:
#batch_size=32
batch 2000 d_loss : 0.000318
batch 2000 g_loss : 7.618911
以上是在迭代 2000 次后所出现的情况,判别模型的损失一直在下降,而生成模型的损失一直在上升。而正常情况下,我发现生成模型的损失和判别模型的损失会在一定范围内交替上升与下降,而迭代 2000 次后训练损失情况为:
#batch_size=36
batch 2000 g_loss : 1.663281
batch 2000 d_loss : 0.483616
此外,我们还发现很多出现问题的生成模式,比如说如下生成结果更多是倾向于 0 与 1:
最后,附上我们结束训练的标志。
原文发布时间为:2017-10-06
本文作者:蒋思源
本文来自云栖社区合作伙伴“数据派THU”,了解相关信息可以关注“数据派THU”微信公众号
MMGeneration | PyTorch 零基础入门 GAN 模型
快乐搬砖的周一又如期而至度过了幸福洋溢的周末(狗头)想必大家一定干劲充沛!不知七夕大家成功检测到对象没?没有检测到也别着急小编这不是带着 MMGeneration 来了吗!那么怎么生成对象呢? 对象是什么,对象怎么生成?今天小编就来带领大家学习一下到底怎么生成对象,关于生成对象的事,想必大家都有所了解,什么一见钟情啦、寤寐思服啦,都二三十岁的人了还不知道可遇不可求,做白日梦想桃子吃呐? 以上就是生成对象的含义和方法,希望小编精心整理的这篇内容能够解决你的困惑。要想学习用 MMGeneration 生成对象,首先要了解如何使用和训练各种生成模型。近年来,各种生成模型及其应用广泛地出现在大家的视野范围内,像最近非常火爆的 Alias-Free GAN 更是从一个全新的视角,为生成模型领域中新的发展方向打下了坚实的理论基础。但是现在来看,无论是之前的 StyleGAN2还是现在的 Alias-Free GAN,模型细节还有训练过程都是非常繁杂的。同时,如果再结合 PyTorch 的话,又需要考虑各种分布式训练的问题。在这种内卷和快速迭代的时代,如何快速上手,把握住机会呢?在这一系列的教程里面呢,我们将以 MMGeneration 为基础,来帮助大家快速入门 GAN 这个庞大的领域。选择 MMGeneration 是因为,在前期,你可以不需要任何 PyTorch 的基础,到后期熟练之后,你也只需要在对应模型上进行一些修改就可以轻松地上手做一些实验啦。这么好用的工具包,还不来 Star 一波?本文内容1. 从 GAN 到 DCGAN2. 模型结构和代码分析3. DCGAN CelebA 实验1. 入门第一步:从 GAN 到 DCGANGAN 的全称是 Generative Adversarial Networks,翻译过来就是生成对抗网络。这里我们需要重点理解的是对抗的含义:GAN 的基本想法是两个网络,生成器(G)和判别器(D),在训练过程中相互对抗。看起来就像是两人练武,虽然一开始大家都很菜,但是判别器进步一点,然后生成器就迎头赶上,然后慢慢地两个人携手成为一代宗师。这是一种很抽象的理解,具体到数学理论上,GAN 的思想是由 Ian J. Goodfellow 在 Generative Adversarial Nets 中完整提出并证明的。整个 GAN 的对抗思想就体现在上图所示的损失函数公式当中,下面我们一点一点解析这个公式。简析损失函数公式在公式中 G(z) 描述了生成器的工作方式:输入一个噪声信号,然后输出一个尽可能逼真的图片(样本)。D(x) 和 D(G(z)) 表示判别器输入的分别是真实数据集中的图片和生成的图片,判别器的任务就是需要尽可能将两者区分开(在这里可以看作一个简单的二分类问题),这也就是让 D 去最大化这个损失函数的意义。而 G 就需要尽可能让 D 无法分辨出真实图片和虚假图片的差异,换句话说期望 G 生成的图片可以以假乱真。上图展示具体的训练流程,可以看到,在训练过程中 G 和 D 的优化是交替进行的,而且一般情况下我们往往希望 D 学习的稍微快一点,这样能够带动 G 更好地朝着全局最优解方向优化。具体的理论推导,大家可以去参考Generative Adversarial Networks 一文,在这里通过 KL 散度可以很非常直观地看到我们的优化目标最优解就是生成器能够完全拟合真实样本的分布。在 Ian J. Goodfellow 的文章中,主要是提出了 GAN 的思想。可是面对图像,我们常用的算子是卷积层。在 DCGAN 一文中,作者将 transposed convolution 用到了生成器中,成为了一个里程碑式的工作。从此之后,以卷积神经网络为主体的 GAN 模型就不断地涌现了出来。那下面我们将从这个基础模型入手,来看一下 GAN 网络的构造。2. 模型结构和代码分析上图就展示了一个典型的生成器和判别器的结构。在生成器当中,首先我们需要一个将噪声向量转换成为二维特征的模块,也就是 noise2feat block。那接下来需要连续经过几个上采样块将低分辨率的特征转换成高分辨的特征,在 DCGAN 中,我们使用的是 transposed convolution 来实现。最后,需要一个 to_rgb 块来将特征图的通道数映射为3通道,从而生成图片。那判别器其实就是生成器的一个反转,我们需要通过 img2feat 和大量的下采样块将特征图不断降低分辨率,最后输送给 decision head,来对当前的输入图片进行评判。对应到 MMGeneration 当中,模型的具体代码都存放在 models/architectures/dcgan 文件夹下面,下图展示了对应的生成器和判别器的代码逻辑,在 mmgen 中我们也是严格按照这样的设计来构建代码,相信大家能够更容易上手。如果关心具体实现的同学,可以到文件中查看,如果你暂时还是 PyTorch 初学者,那你大可不必关心具体的实现,我们接下来告诉大家怎么用 mmgen 训练一个 DCGAN。3. DCGAN CelebA 实验接下来,将通过 MMGeneration 来一步一步详细地教大家如何训练第一个 DCGAN 模型。这里不需要大家有太多的 PyTorch 基础知识,只需要跟着我们一步一步来就可以了。Step 1 安装使用 MMGeneration,你只需要克隆一下 github 上面的仓库到本地,然后按照安装手册配置一下环境即可,如果安装遇到什么问题,可以给 MMGeneration 提 issue,或是加入 OpenMMLab 社区微信群提问(入群方式见文末),我们会尽快为小伙伴们解答。下面是具体的安装步骤:
# we assume that you have installed pytorch and mmcv-full in your env.
# clone repo
git clone https://github.com/open-mmlab/mmgeneration mmgen
cd mmgen
# install mmgen
pip install -e .Step 2 数据假设大家已经安装好了 MMGeneration,回到训练上来,首先我们要做的是准备训练数据,CelebA 的数据可以通过其官方网站下载,我们选用其中的 Align&Cropped 数据来进行训练。下载解压完了之后,我们需要回到 MMGeneration 仓库的文件夹,通过软链的方式将数据链接到仓库的 data 目录下面:
mkdir data
ln -s absolute_path_to_CelebA ./data/celeba这样我们的数据准备工作就基本完成了。不过我们需要再更新一下我们的 config 文件的中的 img_root 字段,将我们现在的数据路径更新上去。具体要做的就是修改 dcgan-celeba config 文件的第 11 行:# define dataset
# you must set `samples_per_gpu` and `imgs_root`
data = dict(
samples_per_gpu=128,
train=dict(imgs_root='data/celeba')) # set img_root这个 config 文件其实就能帮我们定义整个训练的过程,包括数据集的构造,模型的定义以及训练流程的定义等等,详细的介绍后续会带给大家。大家现在可以先通过我们提供的 config 来实现快速的上手训练和采样生成图片。Step 3 训练训练的指令其实非常简单,通过我们之前修改的 config 文件,我们就可以通过如下命令进行训练了:
bash tools/dist_train.sh ./configs/dcgan/dcgan_celeba-cropped_64_b128x1_300k.py 1 --work-dir ./work_dirs/dcgan-celeba在训练过程中,我们会自动保存不同阶段模型生成的样本到 work_dirs/dcgan-celeba 文件夹下面:这样我们就可以随时观测到模型的收敛情况了,当然后续的教程里面,我们还会介绍如何通过一些客观的评价指标来检测我们的训练过程。训练完成之后,我们就可以通过随机采样来看看模型能带给我们什么样的样本啦。在 MMGeneration 当中,我们可以轻松得通过使用demo/unconditional_demo.py 来实现:
python demo/unconditional_demo.py ./configs/dcgan/dcgan_celeba-cropped_64_b128x1_300k.py work_dirs/dcgan-celeba/ckpt/iter_290000.pth其实在 MMGeneration 当中已经支持了非常多模型的采样,并且提供了公共的 checkpoint 供大家把玩,在我们的快速上手教程中有更详细地介绍(可联系小运营获取快速上手教程),欢迎大家来试用并且提出你们宝贵的意见。总之,活用 MMGeneration不仅仅能生成对象哦!心动不如行动,快一起试一下吧!下 *N 期预告# MMGeneration # 真 · 生成对象MMGeneration 开发者说如果 Github 上 OpenMMLab 的 Star 增加超过 1314 就安排兄弟们,机会来了靠自己了! 文章来源:公众号【OpenMMLab】2021-08-16 19:00
AI文案通过图灵测试!一秒生成2万条广告神器问世
广告界的“奥斯卡”上,出现了中国的AI。
6月20日,戛纳国际创意节上出现了一款AI智能文案产品,可以根据商品自动生成文案,并且文案品质与人写文案已经并无二致。
质量有多高?
下图是AI文案与人工文案的对比:
对比发现,AI在词语押韵、联想延伸、意境创造等方面更胜一筹。
怎么做到的?
这款文案生成神器是阿里妈妈海棠创意中心在戛纳国际创意节上发布的一款AI智能文案产品,结合淘宝、天猫的海量优质内容与自然语言算法,主要聚焦于商品文案,已经实现了三项核心能力——高度模拟人写文案、自由定义字数、实时在线样本学习。
高度模拟人写文案:顺利通过图灵测试
阿里妈妈AI智能文案早已突破傻瓜式的机器模式,提供多种文案写作风格:描述型、特价型、实功效型、逗趣型、古诗词型等八种类型,风格类型也在不断扩充中。
生产出来的文案品质与人写文案已经并无二致,顺利通过了图灵测试。
模拟人写文案最重要是数据训练的问题,训练数据的质量直接决定了模型效果的好坏。目前阿里妈妈可以拿到的文案规模大约上亿,这些数据质量参差不齐。通过人工+机器半自动的清洗,得到高质量的训练数据达到数百万级,几乎覆盖了淘内所有类目和商品。
自由定义字数:对同一商品产出不同长度文案
由于阿里妈妈AI智能文案的主要聚焦点是商品文案,因此在功能上也做了针对性的突破,解决日常商家针对同一商品产出不同长度文案的问题。
通过对位置信息进行编码,改变每个词生成时的概率分布,实现长度可控。
无论是几个字的短标题,还是60字左右的商品描述,都可以一键生成。以最基础的短标题文案来举例,阿里妈妈AI智能文案的生产能力已达到1秒20000条。
实时在线样本学习:优质内容成为“养分”
作为一项深度学习逻辑下的产品,阿里妈妈AI智能文案保持实时在线学习,通过不断增加的优秀文案样本量,来提升自身的文案产出能力,满足用户的需求。
在这一点上,阿里妈妈AI智能文案优势尽显,其学习的样本来自于淘宝、天猫的多个优质内容渠道,同时,整个集团内容化的趋势已十分明显,来自达人和商家的海量优质内容成为源源不断的养分,经过机器学习,再转化成为优秀的文案产出,赋能商家,形成良性循环。
智能文案服务于商家/运营,同时商家/运营的选择结果会回流。目前该数据被用来训练一个二分类的质量控制模型,类似对抗生成网络里的判别器,来对智能文案生成网络的结果进行二分类判别。这个质量控制模型本质上是在拟合人对生成结果的判断。随着数据的不断积累,质量控制模型对结果的判断会更加准确,同时可以更好的指导生成网络的迭代。
另外,对于硬件要求,基于深度神经网络,目前智能文案服务部署在一个上百块卡的GPU集群上,提供离/在线一体化的文案解决方案。同时,软件方面,我们的服务建立在阿里妈妈创意中心之上,由智能创意服务SCS提供统一的在线文案生成服务。
把创意像公式一样“推导”出来,让“创新”稳定的发生。今后,一部分的文案工作可以交给AI来完成了。
原文发布时间为:2018-06-21
本文来自云栖社区合作伙伴新智元,了解相关信息可以关注“AI_era”。
原文链接:AI文案通过图灵测试!一秒生成2万条广告神器问世