前面无监督学习主要针对的是一种降维的学习任务,将数据降维到另一个能够表达数据含义的某种空间中,本节主要是无监督学习中的另一个任务生成进行介绍。生成模型0。生成模型介绍 通常生成模型是指学习样本数据的分布,可以生成一些新的数据,是相对于判别模型而言的,并不特指有监督学习和无监督学习,比如朴素贝叶斯模型就是一种生成模型。 在这里生成模型主要指的是无监督学习中的生成模型,在无监督学习中的主要任务是让机器学习给定的样本,然后生成一些新的东西出来。比如: 给机器看一些图片,能够生成一些新的图片出来,给机器读一些诗,然后能够自己写诗出来。 在前面所学习的无监督学习主要是针对降维的,成为化繁为简,那么在这里的生成模型则称之为无中生有。 有三种常见的生成模型: 1、ComponentbyComponent 2、AutoEncoder 3、GenerativeAdversarialNetWork(GAN) 下面就对这三种方法进行简单的介绍,这里还是主要介绍其大致概念,后面深度学习会具体展开讨论。1。ComponentbyComponent 这种方法类似于前面说的Predictedbased的方法,即根据前面的来预测后面的。比如一张33大小的图片: 我们希望有一个网络,输入相邻的两个像素,然后输出下一个像素。通过大量的图片来训练网络,然后给定一个初始的像素,就可以生成一张新的图片出来。 又或者通过阅读大量的文章,然后输出一张新的文章出来。 这个任务也称作Seq2Seq的学习任务,其网络主要用的就是RNN。后面到深度学习部分会对RNN再进行了解。这里先举个简单的例子:让机器自己创造一些宝可梦出来。 通过大量的宝可梦的图片训练一个网络,然后让这个网络生成一些图片出来,如图: 在测试时,首先拿一些真实的宝可梦的图片,然后盖住一部分,比如盖住50,让机器生成这50的图片,可以得到如右图所示的结果(并非对应关系)。2。AutoEncoder2。1ReviewAutoEncoder 在前面说过AutoEncoder的基本概念,即通过encoder对数据的降维,而在生成模型中,decoder则可以用来生成新的数据出来。 当把AutoEncoder的网络层数增加时,就变成了deepAutoEncoder。通过给定一个code,然后输入到decoder中,就会产生新的image出来。 比如在手写识别中,数据降到2维后,给定一个二维code,则可以生成一张手写数字出来。 然而在实际中,通常对于未知的code,我们并不能保证所给的code与产生的图片属于同一个分布,这也就可能会导致当给一个code时,所生成的图片是一个四不像,与预期不符。 比如对于月球图片的学习,将图片降到1维(中间红色的线),然后再decode回去,如图: 假设两边的状态一个是满月一个是新月,我们想要中间找个点,得到弦月的图片,然而当我们在中间的位置任意找一个code输入到decoder进去时,并不一定能保证得到的是弦月。 也就是说我们无法真正的构造出code,我们并不知道code来自于哪个分布,因此为解决这一问题,需要变分自编码器(VAE)。2。2VAE简介 VAE在进行图片还原时,要保证code与decoder的输出服从一定的分布,所以V的意思代表Variational。换句话说,就是在生成code的时候我们限制这些code服从一定的分布。 VAE的直观理解是,在生成的code上加上一些噪音,如图: 在生成的code上面加上噪音,例如满月的图片生成code之后,在其周围加上噪音之后,那么在噪音范围内所生成的图片都是满月的图片,同理新月也是。 当我们在满月和新月的code的中间取一点时(红色的箭头),此时相当于对新月和满月进行一个加权,从而生成了弦月。 上面就是VAE的直观的解释,那么通常这个code的噪声是如何加呢?又为什么这么加,下面就是VAE的网络结构和原理: 可以看到code的部分变成了ci的样子,其中m是原来的code,是噪音分布的方差,是自己学出来的,取exp是为了保证学习的时候是一个正值,e是一个正态分布,从而得到新的code。 然而仅仅在code上加noise是不够的,在训练时,我们希望recontructionerror越小越好,那么在训练时,会偏向于将学成0,因为当0时,损失就越小。 因此在训练时要加上一项: exp()为蓝色的线,(1)为红色的线,二者相减则为绿色的线,最小化这一项,则使得i在0附近,再取exp,那么varaice则趋向于1,最后一项m的平方则可以看做是L2正则化。 因此VAE在训练时是重构误差加上上边那一项。 上边是VAE的做法和直观的理解,对于VAE的原理和推导稍微有点复杂,这里简单总结一下: 首先在高斯混合模型中,样本x的分布可以用有限个高斯分布组合而成的: 假设样本由M个混合高斯模型所组成的,x从其中一个高斯分布m而来,那么产生x的概率p(x)p(m)p(xm),总体的分布则为: 那么现在我们在训练时限制住x降维后所产生的code服从一定的标准正态分布zN(0,I),那么: 这里z就是降维后的code,其每一维代表一个属性,不同的是这里的高斯混合模型相当于有无限个高斯模型,因此是积分的形式。 随机出来一个z,得到z的均值和方差,就可以得到一个x,我们希望有这样一个function,输入z,输出为z的均值和方差: 这也就是decoder,同样,需要借助一个分布q(zx),其含义是给一个x,其在z这个空间中的分布,也就是给一个x,在z空间中的均值和方差,从而sample出一个z。其做的事刚好是跟上面的相反的。 这就是encoder。 根据所给定的样本x,根据极大似然估计: 我们需要最大化上面的式子L,接下来就是一系列的推导: 然后就变成了最大化Lowerbound,进一步: 至此训练变成了最大化这两项,其中前一项可以表示为P(z)和q(zx)的散度的负数,最大化这一项即是最小化二者的KL散度。 这一项的含义就是保证z(也就是降维的code)的分布要尽量与x在z空间中的分布保持一致。这也就限制了在降维后要保持一定的分布,那么在sample时我们可以从该分布中来生成一些样本。这也是VAE的精神所在。 而后一项就是使得z生成的x要与原来的x越接近越好,这与原来的autoencoder是一致的。2。3VAE的问题 VAE虽然能够比较容易地产生一些数据,但其实VAE并没有学会如何真正的去生成一些新的事物,而是一直在模仿,希望尽可能地接近已知样本。比如: 对于生成的两张图片7,第一张显然对于我们来说是可以接受的,而第二种是不可接受的,然而对于VAE来说,二者具有相同的损失(重构误差)。同时通过实验可以看出,VAE生成的图片一般比较糊,这是因为autoencoder在生成图片时,每一个pixel是独立的,它并没有考虑相互之间的关系(大局观)。 这时就需要另一个生成模型登场了GAN。2。4VAE的实现 在介绍GAN之前,先来做个VAE的demo,前面简单对autoencoder进行了简单的实现,这里顺便就做一下VAE,代码没有封装,只是VAE的实现过程,便于理解本部分内容,以手写数字识别为例。importtensorflowastfimportnumpyasnpimportmatplotlib。pyplotaspltfromtensorflow。examples。tutorials。mnistimportinputdata读取数据mnistinputdata。readdatasets(MNISTdata,onehotTrue)输入占位符,因为是无监督学习,所以只需要xxtf。placeholder(tf。float32,〔None,784〕)定义变量从输入层到隐藏层的w,b,隐藏层假设有100个节点encodewtf。Variable(tf。truncatednormal(〔784,100〕,stddev0。01))encodebtf。Variable(tf。zeros(〔100〕))定义隐藏层到输出m那一层的权重w,假设降维到128维encodemeanwtf。Variable(tf。truncatednormal(〔100,128〕,stddev0。01))定义隐藏层到输出的variance的权重encodevarwtf。Variable(tf。truncatednormal(〔100,128〕,stddev0。01))code到输出层的w和bdecodewtf。Variable(tf。truncatednormal(〔128,784〕,stddev0。01))decodebtf。Variable(tf。zeros(〔784〕))隐藏层输出encodeoutputtf。nn。relu(tf。matmul(x,encodew)encodeb)求解mean和varianceencodemeantf。matmul(encodeoutput,encodemeanw)encodevartf。matmul(encodeoutput,encodevarw)加上一个randomnormalEtf。randomnormal(〔1,128〕)降维后的数据mexp(var)Ecodetf。add(tf。exp(encodevar)E,encodemean)decoder,把code解回原数据784维decodeoutputtf。nn。relu(tf。matmul(code,decodew)decodeb)loss,原来的重构误差decodelosstf。reducemean((decodeoutputx)2)加上另一项误差encodelosstf。reducemean(tf。exp(encodevar)(1encodevar)encodemean2)losstf。add(decodeloss,encodeloss)optimizertf。train。AdamOptimizer(learningrate0。001)。minimize(loss)tf。resetdefaultgraph()withtf。Session()assess:sess。run(tf。globalvariablesinitializer())forepochinrange(10000):totalnumint(mnist。train。numexamples100)foriinrange(totalnum):xs,ysmnist。train。nextbatch(100),losssess。run(〔optimizer,loss〕,feeddict{x:xs})ifepoch1000:print(epoch:,epoch,loss:,loss)testItesttf。truncatednormal(shape〔1,128〕,stddev0。00001)decodeoutputtesttf。nn。relu(tf。matmul(Itest,decodew)decodeb)decodeoutputtestdatasess。run(〔decodeoutputtest〕)testimgnp。reshape(decodeoutputtestdata,〔28,28〕)plt。imshow(testimg,cmapgray)plt。pause(0。1)折叠 可以看到随着训练的次数增加,生成的图片也越来越清晰,隐约可以看到9的形状,一方面是因为没有对模型中的参数进行调节,加上模型较为简单,特征提取不完整。另一方面也是前面说的VAE本身的问题。 接下来结合CNN,经过卷积之后再对图片进行降维,利用VAE进行降维和还原:这里首先定义一个maxpool函数,返回的经过maxpool之后的图片和所对应的索引defmaxpoolwithargmax(net,stride):,masktf。nn。maxpoolwithargmax(net,ksize〔1,stride,stride,1〕,strides〔1,stride,stride,1〕,paddingSAME)masktf。stopgradient(mask)nettf。nn。maxpool(net,ksize〔1,stride,stride,1〕,strides〔1,stride,stride,1〕,paddingSAME)returnnet,mask根据maxpool的索引进行反池化的操作,原理在前面CNN部分已经说过defunpool(net,mask,stride):ksize〔1,stride,stride,1〕inputshapenet。getshape()。aslist()outputshape(inputshape〔0〕,inputshape〔1〕ksize〔1〕,inputshape〔2〕ksize〔2〕,inputshape〔3〕)onelikemasktf。oneslike(mask)batchrangetf。reshape(tf。range(outputshape〔0〕,dtypetf。int64),shape〔inputshape〔0〕,1,1,1〕)bonelikemaskbatchrangeymask(outputshape〔2〕outputshape〔3〕)xmask(outputshape〔2〕outputshape〔3〕)outputshape〔3〕featurerangetf。range(outputshape〔3〕,dtypetf。int64)fonelikemaskfeaturerangeupdatessizetf。size(net)indicestf。transpose(tf。reshape(tf。stack(〔b,y,x,f〕),〔4,updatessize〕))valuestf。reshape(net,〔updatessize〕)rettf。scatternd(indices,values,outputshape)returnretxtf。placeholder(tf。float32,〔100,28,28,1〕)wconv1tf。Variable(tf。truncatednormal(〔3,3,1,64〕,stddev0。01))bconv1tf。constant(0。1,shape〔64〕)conv1tf。nn。relu(tf。nn。conv2d(x,wconv1,strides〔1,1,1,1〕,paddingSAME)bconv1)pool1,mask1maxpoolwithargmax(conv1,2)wconv2tf。Variable(tf。truncatednormal(〔3,3,64,10〕,stddev0。01))bconv2tf。constant(0。1,shape〔10〕)conv2tf。nn。relu(tf。nn。conv2d(pool1,wconv2,strides〔1,1,1,1〕,paddingSAME)bconv2)pool2,mask2maxpoolwithargmax(conv2,2)pool2tf。nn。maxpool2d(conv2,ksize〔1,3,3,1〕,strides〔1,3,3,1〕,paddingSAME)convouttf。reshape(pool2,〔1,490〕)encodewtf。Variable(tf。truncatednormal(〔490,100〕))encodebtf。Variable(tf。constant(0。1,shape〔100〕))encodeoutputtf。add(tf。matmul(convout,encodew),encodeb)encodemeanwtf。Variable(tf。truncatednormal(〔100,128〕,stddev0。01))encodevarwtf。Variable(tf。truncatednormal(〔100,128〕,stddev0。01))encodemeantf。matmul(encodeoutput,encodemeanw)encodevartf。matmul(encodeoutput,encodevarw)Etf。randomnormal(〔1,128〕)codetf。add(tf。exp(encodevar)E,encodemean)decoderdecodewtf。Variable(tf。truncatednormal(〔128,490〕,stddev0。01))decodebtf。Variable(tf。constant(0。1,shape〔490〕))decodeoutputtf。nn。relu(tf。add(tf。matmul(code,decodew),decodeb))decodeoutputtf。reshape(decodeoutput,〔1,7,7,10〕)tconv2unpool(decodeoutput,mask2,2)tpool1tf。nn。conv2dtranspose(tconv2bconv2,wconv2,pool1。shape,〔1,1,1,1〕)tconv1unpool(tpool1,mask1,2)preoutputtf。nn。conv2dtranspose(tconv1bconv1,wconv1,x。shape,〔1,1,1,1〕)decodelosstf。reducemean((preoutputx)2)encodelosstf。reducemean(tf。exp(encodevar)(1encodevar)encodemean2)losstf。add(decodeloss,encodeloss)optimizertf。train。AdamOptimizer(learningrate0。001)。minimize(loss)withtf。Session()assess:sess。run(tf。globalvariablesinitializer())forepochinrange(10000):totalnumint(mnist。train。numexamples100)foriinrange(totalnum):xs,ysmnist。train。nextbatch(100)xsnp。reshape(xs,〔1,28,28,1〕),losssess。run(〔optimizer,loss〕,feeddict{x:xs})ifepoch1000:print(epoch:,epoch,loss:,loss)test这里就有一个问题,在随机给定一个code时,在进行反池化操作时mask的选择不能用原来的训练的mask了。Itesttf。truncatednormal(shape〔1,128〕,stddev0。01)decodeoutputtesttf。nn。relu(tf。add(tf。matmul(Itest,decodew),decodeb))decodeoutputtesttf。reshape(decodeoutputtest,〔1,7,7,10〕)mask2testtf。reshape(mask2〔0〕,〔1,7,7,10〕)tconv2testunpool(decodeoutputtest,mask2test,2)tpool1testtf。nn。conv2dtranspose(tconv2testbconv2,wconv2,〔1,14,14,64〕,〔1,1,1,1〕)mask1testtf。reshape(mask1〔0〕,〔1,14,14,64〕)tconv1testunpool(tpool1test,mask1test,2)preoutputtesttf。nn。conv2dtranspose(tconv1testbconv1,wconv1,〔1,28,28,1〕,〔1,1,1,1〕)decodeoutputtestdatasess。run(〔preoutputtest〕,feeddict{x:xs})testimgnp。reshape(decodeoutputtestdata,〔28,28〕)plt。imshow(testimg,cmapgray)plt。pause(0。1)折叠 可以看出上面生成的一些图片相比于之前的VAE更加清晰了,也比较像数字了,但其中有个问题就是代码中注释的那样,有的一般只做卷积,不做池化,关于池化后再test时如何生成,下去再思考。3。GAN简介 GenrativeAdvesarialNetworl(GAN)是机器学习中一个耳熟能详的算法,随着时间的发展,GAN也从原始的算法进化了更多的版本,这里就先对GAN进行简要的介绍,后面会单独开一节来介绍GAN及其变种算法。 GAN全名叫做生成对抗网络,顾名思义,就是在不断地生成和对抗中进行成长学习。举一个例子: 图中是枯叶蝶,其天敌是一种鸟类,在最开始时,枯叶蝶可能就是普通的蝴蝶,而这种鸟靠捕食蝴蝶为食,这种鸟认为蝴蝶不是棕色的,因此,蝴蝶进化成棕色的骗过第一代的鸟,而鸟类也在进化,进化成第二代,可以辨别蝴蝶是没有叶脉的,因此蝴蝶进一步进化成枯叶蝶。这其实就是一种对抗生成。 那么在实际的机器学中,对抗生成网络有两个部分组成,一个是Generator,另一个是Discriminator,二者在不断进行生成与对抗,称为亦师亦友的关系。在图片生成中: 第一代的Generator所及生成一些图片,给到第一代的Discriminator识别,其认为都是假的图片, 然后Generator进化到第二代,此时第二代所产生的的图片能够骗过第一代的Discriminator,单后Discriminator进化到第二代,发现第二代Generator产生的也是假的, 如此反复不断进化和迭代,直到Discriminator无法分辨Generator所产生的图片是假的。 上面是GAN的基本概念,对于GAN的原理可以解释如下: 对于实际的样本图片的数据分布我们用Pdata(x)来表示,假设它的分布是下面这样的: 在蓝色的区域是实际的图片,区域以外则产生的图片不像是真的图片,那么我们想要通过训练得到Pdata(x)的分布,假设Generator所产生的分布为PG(x): 我们训练时希望G所产生的图片的分布于原来的图片的数据分布越接近越好。然而在实际中,我们其实并不知道PG(x)长什么样,因此这样也变得困难。 但是在GAN中,Discriminator则可以为解决这一问题提供方法,具体做法如下: 首先Generator产生一下图片数据,同时从样本中sample出一些数据 然后把这些数据Generator所产生的图片标记为0,从原数据中产生的图片标记为1,然后训练Discriminator: 那么Discriminator最终训练完成的loss则与PG(x)和Pdata(x)的JSpergence有关,也就说loss可以用来衡量两个分布有多相似。 然后Generator则可以根据这个loss进行进化成为第二代Generator。那么参数是如何更新的呢?下面举个例子: 训练完成Discriminator后,随机sample一个数据,丢进Generator中,然后产生一张图片,图片经过Discriminator,假设此时Discriminator给的分数是0。13,那么此时Generator开始调整参数(注意此时Discriminator的参数是固定不变的),使得所产生的图片丢给Discriminator让其输出值为1,然后完成进化成为GeneratorV2。 以上就是GAN的基本思想以及其算法的较为通俗的解释。下面给出文献中GAN的算法: Generator:G,Discriminator:DInitializeGg,Dd(初始化Generator和Discriminator的参数)foreachtrainingsamplemexamples{x1,x2,。。。。。,xm}(从样本集中sample出一些数据)samplemnoisesamples{z1,z2,。。。。。,zm}(从一种分布中sample出一些数据)Obtaininggenerateddata{x1,x2,。。。。。。xm},xiG(zi);(然后把z丢进G中产生一些图片)FixG,updatedtomaximize,(固定住Generator的参数,调整Discriminator的参数,这里并不一定使用这种方法更新,还有其他方法也就衍生了其他算法)samplemnoisesamples{z1,z2,。。。。。。,zm}(再另外从某种分布中sample出一些数据)FixD,(固定住Discriminator,调整Generator的参数,使得sample出来的那些数据让Discriminator的分数越高越好) 以上就是GAN的基本概念和算法,这里就暂时对这部分内容介绍到这里,后边会单独开一节GAN有关其他的衍生算法及其实现。 文章来自https:www。cnblogs。com501731wybp16455870。html