什么是Autoencoder?自编码器(AutoEncoder)最初是作为一种数据压缩方法使用的。它的特点是:与数据高度相关,也就是说Autoencoder只能压缩与训练数据相似的Data,这个其实比较明显,因为神经网络提取的特征一般都和原始训练集高度相关,而autoencoder人脸训练在压缩自然界动物图片时表现不佳,因为它只学习人脸特征,不学习自然图片特征;压缩后的数据是有损的,因为在降维过程中不可避免地会丢失信息;到2012年,人们发现在卷积网络中使用自编码器进行逐层预训练可以训练出更深的网络,但很快人们发现良好的初始化策略比费力的逐层预训练更有效训练。2014年出现的BatchNormalization技术也是一种可以有效训练的更深层次的网络。到2015年底,我们基本可以通过残差(ResNet)训练出任意深度的神经网络。所以现在自编码器主要有两个应用,第一个是数据去噪,第二个是视觉降维。然而,自动编码器也具有生成数据的功能。我们之前讲过GAN,它和GAN相比有一些优点,但也有一些缺点。先说说它和GAN相比的优势。第一点,我们用GAN生成图片有一个很不好的缺点就是我们生成图片的时候有随机的高斯噪声,也就是说我们不能生成任何我们指定类型的图片,也就是说我们没有办法决定是哪一个使用随机噪声可以产生我们想要的图片,除非我们可以尝试所有的初始分布。但是使用autoencoder,我们可以通过输出图片的编码过程得到这类图片的编码分布,相当于知道了每张图片对应的噪声分布,我们可以通过选择特定的来产生我们想要的噪声噪音。要生成的图像。第二点,这既是生成网络的优势,也有一定的局限性,即生成网络通过对抗过程来区分“真”图片和“假”图片,但是这样得到的图片只是作为尽可能真实,但这并不能保证图片的内容就是我们想要的。也就是说,可以尽可能多地生成一些背景图案,使它们尽可能真实,但其中并没有实际的物体。自编码器的结构首先,我们给出自编码器的一般结构。从上图我们可以看到两部分,第一部分是编码器(Encoder),第二部分是解码器(Decoder),编码器和解码器都可以是任意模型,通常我们使用神经网络模型作为编码器和解码器。输入数据通过神经网络还原为一个编码,再由另一个神经网络解码得到一个与输入原始数据完全相同的生成数据,然后将两个数据进行比较,使它们之间的差异最小化为在该网络中训练编码器和解码器的参数。这个过程训练好之后,我们可以把这个解码器拿出来,随机传入一个代码(code),希望通过解码器生成一个和原始数据相似的数据。上图的例子就是生成一张差不多的图片。这件事能实现吗?事实上,这是可能的。下面我们将使用PyTorch来简单实现一个自编码器。首先,让我们构建一个简单的多层感知器来实现它。classautoencoder(nn.Module):def__init__(self):super(autoencoder,self).__init__()self.encoder=nn.Sequential(nn.Linear(28*28,128),nn.ReLU(True),nn.Linear(128,64),nn.ReLU(True),nn.Linear(64,12),nn.ReLU(True),nn.Linear(12,3))self.decoder=nn.Sequential(nn.Linear(3),12),nn.ReLU(真),nn.Linear(12,64),nn.ReLU(真),nn.Linear(64,128),nn.ReLU(真),nn.Linear(128,28*28)),nn.Tanh())defforward(self,x):x=self.encoder(x)x=self.decoder(x)returnx这里我们定义一个简单的4层网络作为编码器,在中间函数,最终输出维度为3D,定义解码器,输入3D编码,输出一个28x28的图像数据,特别注意最后的激活函数是Tanh,这个激活函数可以将最终输出转换为-1和1之间,这是因为我们输入的图像已经变换到-1和1之间了,这里的输出必须对应。训练过程也比较简单。我们使用最小均方误差作为损失函数来比较生成图像的每个像素与原始图像之间的差异。同时,我们也可以用卷积神经网络代替多层感知器,对图片的特征提取有更好的效果。classautoencoder(nn.Module):def__init__(self):super(autoencoder,self).__init__()self.encoder=nn.Sequential(nn.Conv2d(1,16,3,stride=3,padding=1),#b,16,10,10nn.ReLU(真),nn.MaxPool2d(2,stride=2),#b,16,5,5nn.Conv2d(16,8,3,stride=2,padding=1),#b,8,3,3nn.ReLU(True),nn.MaxPool2d(2,stride=1)#b,8,2,2)self.decoder=nn.Sequential(nn.ConvTranspose2d(8,16,3),stride=2),#b,16,5,5nn.ReLU(真),nn.ConvTranspose2d(16,8,5,stride=3,padding=1),#b,8,15,15nn.ReLU(真),nn.ConvTranspose2d(8,1,2,stride=2,padding=1),#b,1,28,28nn.Tanh())defforward(self,x):x=self.encoder(x)x=self.decoder(x)returnx这里用的是nn.ConvTranspose2d(),可以看成是卷积的逆运算,某种意义上可以看做反卷积。我们使用卷积网络得到最终生成的图片效果会更好,具体的图片效果我这里就不放了,大家可以看我们github上的图片展示。github地址:http://t.cn/RK5gxpMVariationalAutoEncoder(变分自编码器)VariationalAutoEncoder是自编码器的升级版,其结构与自编码器类似,也是由编码器和解码器组成装置组成。回想一下我们在autoencoder中所做的,我们需要输入一个图像,然后对图像进行编码得到一个隐藏向量,这比取一个随机随机噪声要好,因为这包含了原始图像信息,然后我们解码隐藏向量vector得到原图对应的照片。但是这样一来,我们实际上并不能任意生成图片,因为我们没有办法自己构造隐向量。我们需要通过一张图片输入代码,才能知道隐藏向量是什么。这时候,我们可以使用变分自编码器来解决这个问题。其实原理很简单。你只需要在编码过程中给它加上一些限制,迫使它生成的隐向量大致服从标准的正态分布。这是它和一般自编码器最大的区别。这样,我们生成一张新图片就非常简单了。我们只需要给它一个标准正态分布的随机隐向量,这样解码器就可以在不给它原始图片的情况下生成我们想要的图片。先写代码。在实际情况下,我们需要在模型的准确性和隐藏向量服从标准正态分布之间进行权衡。所谓模型的准确率是指解码器生成的图片与原始图片的相似度。我们可以让网络自己做这个决定,很简单,我们只需要让两者都损失,然后把它们加起来作为总损失,这样网络就可以选择如何让总损失下降。此外,我们需要衡量两个分布之间的相似性。前面关于GAN的数学推导怎么看?你会知道会有一个叫做KL散度的东西来衡量两个分布之间的相似性。这里我们用KL散度来表示隐含的向量与标准正态分布之间的差异的损失,另一个损失仍然是用生成图像和原始图像之间的均方误差来表示。我们可以给出KL散度的公式这里变分编码器使用了一个技巧“重新参数化”来解决KL散度的计算问题。此时不再每次生成一个隐向量,而是生成两个向量,一个代表均值,一个代表标准差,然后通过这两个统计量合成隐向量,也很简单,用一个标准正态分布先乘以标准差,然后加上均值。这里我们默认编码后的隐向量服从正态分布。这时候我们希望均值尽可能接近0,标准差尽可能接近1。论文中对于如何得到这个loss的计算公式有详细的推导。有兴趣的同学可以去看看具体的推送过程:https://arxiv.org/pdf/1606.05908.pdf下面是PyTorch的实现:reconstruction_function=nn.BCELoss(size_average=False)#mselossdefloss_function(recon_x,x,mu,logvar):"""recon_x:generatingimagesx:originimagesmu:latentmeanlogvar:latentlogvariance"""BCE=reconstruction_function(recon_x,x)#loss=0.5*sum(1+log(sigma^2)-mu^2-sigma^2)KLD_element=mu.pow(2).add_(logvar.exp()).mul_(-1).add_(1).add_(logvar)KLD=torch.sum(KLD_element).mul_(-0.5)#KLdivergencereturnBCE+KLD除了可以让我们随机生成隐变量外,变分编码器还可以提高网络的泛化能力。最后是VAE的代码实现:classVAE(nn.Module):def__init__(self):super(VAE,self).__init__()self.fc1=nn.Linear(784,400)self.fc21=nn.Linear(400,20)self.fc22=nn.Linear(400,20)self.fc3=nn.Linear(20,400)self.fc4=nn.Linear(400,784)defencode(self,x):h1=F.relu(self.fc1(x))返回self.fc21(h1),self.fc22(h1)defreparametrize(self,mu,logvar):std=logvar.mul(0.5).exp_()iftorch.cuda.is_available():eps=torch.cuda.FloatTensor(std.size()).normal_()else:eps=torch.FloatTensor(std.size()).normal_()eps=变量(eps)returneps.mul(std).add_(mu)defdecode(self,z):h3=F.relu(self.fc3(z))returnF.sigmoid(self.fc4(h3))defforward(self,x):mu,logvar=self.encode(x)z=自己。reparametrize(mu,logvar)returnsself.decode(z),mu,logvarVAE的结果比普通的自动编码器好很多。下面是结果:VAE的缺点也很明显。他直接计算生成图像和原始图像的均方。error不是像GAN那样通过对抗学习,导致生成的图片有点模糊。已经有一些作品结合了VAE和GAN,使用VAE的结构,但是使用对抗网络进行训练。详情请参考本文:https://arxiv.org/pdf/1512.09300.pdf文中相关代码链接:http://t.cn/RK5gxpM英文参考:http://t.cn/RtoJRAa
