朱茵的脸变成了杨幂
美国女歌手泰勒斯威夫特被“一键脱衣”,就是GAN干的
人工智能2016年忽然爆发,代表的概念当然是深度学习,火得不得了。深度学习的重要基本概念,其实基本是早期就出现的,多层神经网络、样本打标、反向传播训练,这都有二三十年的历史了。只有2014年提出的GAN,是个新的东西,被认为是深度学习领域十年来最有意思的思想。
英伟达推出的GAN应用:只要一张照片,就能合成出视频,让人自己“动起来”
吃瓜群众对GAN的应用多少听说过,但是背后的技术原理,就基本不了解。深度学习的常见名词,如多层神经网络、GPU、权重、训练,一般人相对熟悉。但是对GAN的技术背景与相关名词,就连许多学历较高的理工科人士都不清楚。
如果已经对深度学习训练的一般原理有所了解,那么再进一步理解GAN并不太困难。再多理解几个概念就可以了:生成器、判别器、两者博弈的纳什均衡。
笔者的经验是:不需要任何数学公式就能理解深度学习,GAN也并不难懂。
本文的目标是帮助一般人理解GAN和深度学习的原理,看相关的文章时(如又有什么新鲜的GAN应用),会明白多些。要进行深度学习或者GAN开发就并不简单,需要学的基本操作很多,此类教程有不少,但比较难懂。
下面我们用一些问答来解释,目标是说明白GAN的工作原理,也要顺带解释一些深度学习的基本常识。
一.多层神经网络识别与训练的基本常识
这一节可以大略看一下,细节不用太关注。但如果有兴趣的话,本文的一些细节是一般的深度学习科普不会写的。
1.我知道神经网络就是给它喂海量的打标了的数据,它就学会了本事。但是怎么就有那个能力了,是怎么学会的?
一般的理解,知道一个神经网络里有一大堆权重系数,用打标的数据去把这些权重训练好。然后应用时,就能用那些权重,去和输入图片数据混着计算,最后就得出了有意义的输出。这种概念的理解差不多够了。
再深入一些理解,也并不难。需要去了解一下多层神经网络的结构,以及在这个结构上的运算过程。
2.多层神经网络长什么样?怎么运行的?
早期的多层神经网络结构
现在的多层神经网络,都有很多层,比早期的多层神经网络层数要多得多,但是层与层的基本前后关系是一致的。每一层有不少数据,数据的数量(维度)是固定的。主要的模式,是从上一层数据算出下一层的。从上一层到下一层,通常数据的数量会改变,有时减少,有时还增加了。
数据又分两种,一种叫权重,这些在运行过程中是不变的,当常数写在程序里,或者预先载入;一种叫特征值,这种就是从输入数据开始,在一层层网络中不断地变换,是一种特征提取的过程。直到输出层,就呈现出非常明显的特征,最傻的电脑都能看出结果了。神经网络运算的过程,就是从计算机无法理解的图片特征,不停变换,一直变换到,傻子计算机都能给出答案的特征。
例如人看到一个128*128大小的图片,会说这个图是猫、那个图是狗、那个图是猪,但是计算机只看到128*128的数组,根本不知道是什么。如果最后经过十几层神经网络变换,“特征变换”出一个3个元素的数组(是不是猫,是不是狗,是不是猪),计算机就能理解了。例如最后得出的数组是(1,0,0),计算机就说,这图是猫;如果是(0,1,0),就说这图是狗;如果是(0,0,0)就说,这个图啥也不是;如果是(1,1,0),就说乱套了,不知道是啥。总之,计算机会给出一个说法。
当然,实际算出来的没这么规整。通常得到的是(是猫的置信度,是狗的置信度,是猪的置信度),如(0.999,0.100,0.005)。但是把这种小数处理成0和1不难。
(0,1,0)这种值也是有实际用途的,它在“打标”时就是给出精确的数据。例如我们人工对1000个图片进行标注,人工标好这是猫是狗是猪。其实就是告诉电脑,每个图片都给出(0,1,0)这样的一个三元数组。整个过程中,电脑还是不知道什么叫猫狗猪,它只知道人给它标成(1,0,0)这样的东西。
3.神经网络一层层搞特征变换,到底是怎么做的?
假如我们要从128*128的图像数据,得到三个置信度,一种简单的办法,是把128*128个数值,给出三个算式,最后得出三个数值。比如给128*128输入图片矩阵M,配三个系数矩阵,即三个权重矩阵A、B、C,每个都是128*128大小。把M和A作一个“全乘”操作,也就是对应位置上M的数值和A的权重乘起来,再把所有乘数加一起,就得到了一个数a。再把M和B、C搞同样的操作,得到数b和c。这个搞完,(a,b,c)就是我们最后的特征值。
当然,这种乘法加法搞出来的数值会很大,就进行一个“归一化”操作,弄成0-1之间的小数。
如果我们对ABC三个矩阵的系数配得巧,那指不定这么操作一下,就出来有意义的结果。这也是早期神经网络研究的意图,搞点简单的算式,就希望能完成任务。确实有些人工智能任务用这种简单的计算过程就能搞定。例如识别0-9和A-Z的字母数字,计算过程真就这么简单,它的输入可能是32*32的灰度图,最终输出是(0,0,1,0,......,0)这样的36维的数组。
但是认猫狗猪这样的任务,就比较复杂了,这种简单算式确实就是不行。最后可行的办法是多层神经网络,层数还不少。
例如我们要识别一个图片是猫狗猪,输入就处理成标准化大小(如128*128)的图片,这个容易。这就是神经网络的第0层,有128*128个“特征值”(或者128*128*3个,每个象素有RGB三个值),这时人眼看上去就是和原始图片特征差不多的。中间经过十多层或者更多层的变换,每一层都有数量不等的特征值,如有的维度是64*64,有的32*32。特征值的维度经常会是倍数关系,多数会减一半,有时会加一倍。
用上一层的特征值组合,计算下一层的特征值,基本是简单的加法和乘法,先乘再加的“卷积”操作。最常见的是一组3*3的9个权重,对上一层的特征值进行统一的操作。这一组9个权重,叫一个“核”,或者一个“filter”。
用上面这个网络结构图例子,能解释明白,每一层的特征值到底是什么形态。
在第0层,就是320*192大小的图片输入(不同大小的原始图片都归一化成这么大),有RGB三个值,所以input是320*192*3维的数据。
每个filter是3*3的,它会和320*192的矩阵里每个3*3大小的区域进行“乘了加”的卷积操作,得到一个数。最开始是从左上角开始,然后往右挪一格,3*3的区域就左边3个数退出,右边进来3个新的。还能往下挪,每挪一次,都和当前的filter搞“乘了加”。这样就挪出320*192个乘数了(其实会少两个,但是边界补齐)。这样一个3*3的filter去“卷”一个320*192的矩阵,得出的还是320*192大小的矩阵。
有RGB三个矩阵,就把得到的三个矩阵加起来,得到最终一个320*192的矩阵。R、G与B每个矩阵都配一套16个3*3的filters。总的filters的数量是16*3个。
第0层有16*3个3*3的filters,都单独地和input矩阵进行卷积运算,就得到了16个320*192的矩阵作为第0层的output结果,它也就是第1层的输入。这种层叫Convolutional Layer,卷积层。
第1层操作相对简单,叫max层。它是把矩阵里2*2的区域,取一个最大值,4个数变一个数。每操作一次,2*2的区域“跳”二格,再往下做。这样320*192的矩阵,大小就变成160*96了。矩阵的数量还是16。通常一个卷积层会跟一个max层,一次操作就是卷一下,再矩阵大小减半。
第2层又是卷积层,它有32套3*3的filters,每套16个(对应前一层16个矩阵)。每个filters都对160*96的矩阵搞卷积,最后16个矩阵加出一个矩阵。这样32套filters,得到了32个矩阵。第3层的max层又把矩阵大小减半。
这样搞来搞去,原来的320*192的图,大小就变成40*24了。但是图的数量变成了128个。人早就看不懂里面的数是些啥了,但是从概念上来说,40*24*128维的数据,包含了原始图片的信息。
有时还会upsample,1个变4个,矩阵变大一倍。还有一些其它名字的层级操作,但各类操作都是简单的加减乘除。
最后还会有关键一步,就是把这种40*24*128较多维的数据,变成(0,1,0)这样较少维的数据,就也还是乘了系数再加。十几层卷来卷去合适了,就走到最后一步。
总之,从输入矩阵开始,一层层往下算,这个过程叫“前向传播”。回头看,每一层的input就是上一层的ouput,这些就是“特征值”。而filters就是“权重”,每一层都有很多个权重。这些filters数量是很多的,每个是3*3,有16-128套,而每套又有16-128个。最终权重数据文件少则几M,多则几百M。
现在一大类多层神经网络,都是“卷积神经网络”,核心动作就是上面说的3*3的filter对一个图片搞的卷积操作。这类网络之所以在图像识别上很管用,可以这样去理解:一个3*3的filter对一个点卷一下,会把这个点和周围8个点建立联系;把图宽高减半,再用3*3的filter卷,又将更远的点和这个点联系起来了;不断地图像大小减半“降采样”,一个点就能和较远的点都有些关联了,体现在一些权重里;通过训练把这些远近关联模式找出来,就很有效了。
4.“反向传播”训练是怎么回事?
前面的前向传播,从图片数据开始,最后到傻瓜特征值,计算过程基本是乘了加。但是为什么到最后,它就能把猫狗猪认对?
其实一开始,那些filters里的权重系数是随机的,最后算出来的傻瓜特征值肯定是乱的,根本就是胡认。例如,对一个猫图,算出来是(0.44,0.25,0.66),和正确答案(1,0,0)差得很多,根本不对。
但是,深度学习厉害就在这,不怕不对,不对可以训练。我们去改那些filters的权重数值,让这个计算过程最后输出的值是(1,0,0)。怎么改?这就是训练的过程。
训练就是从“误差”开始,例如上面的例子,误差就是(-0.56,0.25,0.66)。这一层误差就出现在最后的输出层。
“反向传播”是说,识别的时候是从输入层算到中间层再到输出层,前一层传到后一层,变动的是特征值;训练时,就把误差当输入数据,后一层往前一层传。传到前面一层,和前面的输出相比,又有一个误差。根据一层的误差,去改这层filters的系数,变动的是filters的值。
这个具体的改法非常厉害,是深度学习的核心技术。要先大改再小改,不要改得失控了。把所有的打标样本,都学习一遍,随机的权重就变得有谱一些了。但是一遍不够,还会有很多认错的(其实是对所有样本总的误差值大)。
就反复对这些样本学习,要成千上万遍。每次都改进一点点,最后真能把总的误差越改越小。从最初巨大的总误差值,降成几万分之一了。总的误差值很小了,就居然发现,这个神经网络学会了认猫狗猪了,抓住了特征。这时即使把不在打标样本里的猫狗图来认,也有谱了。
其实这神经网络不知道自己在干啥,它只是想把认样本的误差降下来。
我们要强调这一点,面对众多的深度学习问题,不管外在的应用表现形式如何,神经网络的本质都是一样的:将误差降下来。
GAN中的神经网络,在结构上也就这是这个样子,训练优化目标也是为了降低误差。只不过GAN中有两个神经网络:生成器和判别器,它们进行博弈斗争,整个框架上比单一神经网络要有意思得多。
二.GAN的工作原理
1.GAN是如何生成图片的?
训练好以后,GAN管用的就是一个生成器,就是前面一节描述的多层神经网络。应用起来,和识别猫狗的神经网络一回事,也是从输入层开始不断“前向传播”,只不过最后的输出是一个图像。
有时,输入是一个图片。如照片修复,输入有残损的老照片,GAN网络就根据训练得来的“经验”,把老照片修补好。输入也要标准化,如256*256的图片大小。生成好了,得到的也是256*256的图,再拉升回原始照片尺寸。
有时,这个输入可以是随机产生的。比如模仿某大师的绘画风格的应用,开始是随机产生256*256大小的输入数值,肯定是一团混乱。但经过多层神经网络运算,出来的东西总带着大师的“特征”。
总之,这就是一个多层神经网络,输入是一个标准大小的图片数据。中间经过多层神经网络反复折腾,最后输出是同样大小的图片。这中间可以把数据的维度降一半,也可以升一倍,反复搞。每层的权重,也是一堆3*3的filters,也是卷积。其实输出和输入是一样的直观图片数据,比前一节的网络更好理解。
应用的时候,判别器是不需要的。其实应用起来,GAN和一般的深度神经网络架构没什么不同。
GAN真正的精华,是训练中,生成器与判别器博弈的过程。如果没有这个博弈,生成器训练不出来。
2.GAN的架构
GAN训练学习架构
生成器(Generator)如前面所述,是一个多层神经网络,输出的是一张给定大小的图片。判别器(Discriminator)也是一个多层神经网络,输入是同样大小的图片,输出是一个概率,一个0-1的实数。
整个GAN的架构中,有一堆“真实图片”,是准备的样本。这些样本是“无监督”的,不需要人工来打标,就是收集一堆图片放那就好。如某大师的一堆作品,如真实的无残损图片。GAN的目的,就是让生成器生成的东西和这些真实图片风格接近。前面一节描述的猫狗网络,是“有监督”的,要人工去对猫狗图片标注类别。
引入判别器很妙,它的作用就是判断,输入的图片是不是真实的。一个理想的判别器,会对真实图片输出概率1,对生成器生成的图输出概率0。但这个判别器并不是拿图片去和真实图片逐个像素对比,一模一样就是1。它还是按照一个前向传导的计算过程,从图片输入开始,每层算特征值。最后一层输出一个算出来的数值,算出多少是多少,会是0-1之间的一个概率值。
其实判别器本身并不难理解,也不难训练。如果我们准备了1万张真实图片,并胡乱生成了1000张假图,那么这个数据集就自动标记好了,真的就是1,假的就是0。一开始判别器内部的权重是随机生成的,对这些图算出来的基本不对,是一个0-1之间的小数,如0.2,0.7之类的。
那么我们就可以按前面一节的“反向传播”训练过程,对判别器进行训练。误差就是用一张图自动标的0或者1,减去判别器计算出来的0.2、0.7,就有一个“误差”产生。有了误差,就可以反向传播去调整判别器网络的系数。多张图多批次学习之后,判别器就有谱了,对真图的输出值接近1,对假图的输出接近0。只要图够多,是真能抓到样本的特征。
同一张输入图,用GAN改造成不同绘画风格:莫奈、梵高、塞尚、浮世绘
相对来说,判别器是较容易训练的,但是生成器很难。就如我们很容易判断出,哪个画是莫奈风格、梵高风格,但是自己很难画出来。
GAN让人拍案叫绝的精华,是对生成器的训练。通常神经网络的训练,是从这个网络的输出开始,根据样本的标注值与输出值,算出误差反向传播。但是GAN架构中,对生成器的训练,是把生成器与判别器连接起来当一个网络的!
根据一个随机输入,生成器会生成出一张图,它自己知道这张图是假的,但希望判别器认为它是真的。那么它怎么训练?它把生成出来的图,输入给判别器;判别器就会产生一个概率值,说这是自己的判断,比如为真概率0.3。生成器认为,目标应该是1,于是就有一个误差0.7产生。把这个误差反向通过判断器传回来到生成器的输出层,再反向传回到生成器内部的各层。这个反向传播过程中,判别器的权重不改,只修改生成器的权重。经过一次训练后,这个随机输入到判别器那的输出,就会更接近1了。反复进行这种训练,生成器生成的东西,就能更多地让判别器输出1,也就是更接近真实图片。
这个基本框架就有了,用真实图片主导,训练引导判别器的“风格品味”。再用判别器,帮助生成器更多向判别器的“风格”接近。过程中不需要进行标注,训练数量足够后,生成器生成的图片,就相当接近真实样本的风格了。人们就惊奇地发现,生成器居然学会了模仿大师绘画!
3.生成器与判别器的对抗博弈
这个GAN之所以巧妙,一是前面说的,将判别器放在生成器后面,帮助生成器训练。从整个框架来说,它更重要的思想是:对抗。
如果我们只是一开始用真实图片和随机假图,训练了一个过得去的判别器,之后再也不改它了,那么这个判别器就只能帮助生成器训练一次。生成器通过少量训练,就能学会,如何欺骗这个判别器。因为判别器的系数都定死了,欺骗它并不难。就如有些报道说,有些神经网络对于鬼扯的bug图,也认出有意义的东西。
这个欺骗就很不好了。判别器计算通过了,说生成器你生成的都是真的了。但是在人眼看来,很可能效果还很差,就象那种bug图一样。
那怎么办?很简单,再对判别器进行训练就可以了。其实跳出判别器的计算过程,人能直接断定,只有真实图片才是真的,别的生成的都是假的。应该告诉判别器,要勇敢地揭发生成器,你搞的是假图!
于是,判别器又根据“真实图片输入应该得出1、假图输入应该得出0”,对自己的系数进行调整。训练一阵子后,它终于又调整好了,对生成器的假图说不。理论上来说,判别器的能力就提升了。
生成器又可以得到“升级版判别器”的帮助,再进行一轮训练。
这个过程反复进行,生成器生成的图就越来越象真的,而判别器也越来越有分辨能力,将生成器生成得还不好的瑕疵找出来说不。
最后,整个过程的轮次足够多以后,在理想情况下,生成器的输出就和真实图片风格完全一致了。判别器就说,认输了,你生成的就是真的,我实在分辩不了。
对GAN这个过程,一个常用的比喻是造假钞。伪造假钞的,和检测假钞的两伙人博弈,造假和检测的人技术都越来越高。检测假钞的逼得造假的人提高技术,造假的人逼得检测的人增强能力。最后,还是造假的能胜利,因为真钞也是造出来的。
从博弈论意义上来说,生成器和判别器在进行一个“零和博弈”:同一张图,一个要当骗子,一个要揭发骗子,总有一个失败一个成功。对于生成器的“作品”,判别器算出来接近1,生成器就成功了,对手失败;判别器算出来接近0,自己成功,生成器就失败了。
按博弈论理论,这种零和博弈,会出现一个“纳什均衡”状态。也就是说,判别器会有一个均衡的输出策略,无论面对什么输入,都将自己的损失最小化。这个有些难懂,但是保证了这个框架是合理的,不会生成器和判别器互相扯皮,你不服我、我不服你,生成器的图片质量来回振荡上不去。
GAN人脸生成训练失败的产物
实际训练,如何实现这个“纳什均衡”就有些困难。有些训练搞得不好,就真的崩溃了,出现各种bug。如老生成一种图片(因为这种图片保证能通过判别器的检查);如生成的人脸各种瑕疵。
理想情况下训练收敛后,判别器会对真实图片输出0.5,对生成器的假图也输出0.5,它再也改进不了。这个意思是说,判别器宣布,对手已经天衣无缝了,我根本不知道给的图是生成的还是真图。这种情况下,我用“全给0.5概率”的策略,这个策略“最大损失”才能最小化,这样才能实现纳什均衡。但是这种情况下也很难训练出来,生成的图片总会有点破绽。
GAN的思想很深刻,有人说,GAN真正的核心理念是对抗,生成器判别器的形式完全可以换成别的。AlphaGo的升级版Master和AlphaGo Zero,就用了GAN的思想。下棋的网络互相对抗,都越搞越强。
GAN基本原理就是上面描述的。它有很多种改进变种,是非常活跃的深度学习领域。希望以上的基础知识,能帮助人们理解GAN的基本概念。
最后要说一下,GAN引入了对抗,但不会取代“无对抗的深度学习”。现在主要的深度学习应用还是“无对抗”的,只是有些特殊的有难度的任务,需要引入GAN。
一般的深度学习任务,有大量容易“监督学习”打标的样本,只要准备足够多的样本就可以做得不错了。这种开发难度也不算高,所以应用很普及。而GAN整个框架要搞有不小难度,一般任务如果能打标训练解决问题,并不需要强行引入GAN。
责任编辑:杨玉露
文章来源:风云之声微信公众号