用pytorch怎样实现自动编码器,方法是什么
Admin 2022-07-26 群英技术资讯 298 次浏览
本来以为自动编码器是很简单的东西,但是也是看了好多资料仍然不太懂它的原理。先把代码记录下来,有时间好好研究。
# -*- coding: utf-8 -*- """ Created on Fri Oct 12 11:42:19 2018 @author: www """ import os import torch from torch.autograd import Variable import torch.nn.functional as F from torch import nn from torch.utils.data import DataLoader from torchvision.datasets import MNIST from torchvision import transforms as tfs from torchvision.utils import save_image im_tfs = tfs.Compose([ tfs.ToTensor(), tfs.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 标准化 ]) train_set = MNIST('E:\data', transform=im_tfs) train_data = DataLoader(train_set, batch_size=128, shuffle=True) class VAE(nn.Module): def __init__(self): super(VAE, self).__init__() self.fc1 = nn.Linear(784, 400) self.fc21 = nn.Linear(400, 20) # mean self.fc22 = nn.Linear(400, 20) # var self.fc3 = nn.Linear(20, 400) self.fc4 = nn.Linear(400, 784) def encode(self, x): h1 = F.relu(self.fc1(x)) return self.fc21(h1), self.fc22(h1) def reparametrize(self, mu, logvar): std = logvar.mul(0.5).exp_() eps = torch.FloatTensor(std.size()).normal_() if torch.cuda.is_available(): eps = Variable(eps.cuda()) else: eps = Variable(eps) return eps.mul(std).add_(mu) def decode(self, z): h3 = F.relu(self.fc3(z)) return F.tanh(self.fc4(h3)) def forward(self, x): mu, logvar = self.encode(x) # 编码 z = self.reparametrize(mu, logvar) # 重新参数化成正态分布 return self.decode(z), mu, logvar # 解码,同时输出均值方差 net = VAE() # 实例化网络 if torch.cuda.is_available(): net = net.cuda() x, _ = train_set[0] x = x.view(x.shape[0], -1) if torch.cuda.is_available(): x = x.cuda() x = Variable(x) _, mu, var = net(x) print(mu) #可以看到,对于输入,网络可以输出隐含变量的均值和方差,这里的均值方差还没有训练 #下面开始训练 reconstruction_function = nn.MSELoss(size_average=False) def loss_function(recon_x, x, mu, logvar): """ recon_x: generating images x: origin images mu: latent mean logvar: latent log variance """ MSE = 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) # KL divergence return MSE + KLD optimizer = torch.optim.Adam(net.parameters(), lr=1e-3) def to_img(x): ''' 定义一个函数将最后的结果转换回图片 ''' x = 0.5 * (x + 1.) x = x.clamp(0, 1) x = x.view(x.shape[0], 1, 28, 28) return x for e in range(100): for im, _ in train_data: im = im.view(im.shape[0], -1) im = Variable(im) if torch.cuda.is_available(): im = im.cuda() recon_im, mu, logvar = net(im) loss = loss_function(recon_im, im, mu, logvar) / im.shape[0] # 将 loss 平均 optimizer.zero_grad() loss.backward() optimizer.step() if (e + 1) % 20 == 0: print('epoch: {}, Loss: {:.4f}'.format(e + 1, loss.item())) save = to_img(recon_im.cpu().data) if not os.path.exists('./vae_img'): os.mkdir('./vae_img') save_image(save, './vae_img/image_{}.png'.format(e + 1))
补充:PyTorch 深度学习快速入门――变分自动编码器
变分编码器是自动编码器的升级版本,其结构跟自动编码器是类似的,也由编码器和解码器构成。
回忆一下,自动编码器有个问题,就是并不能任意生成图片,因为我们没有办法自己去构造隐藏向量,需要通过一张图片输入编码我们才知道得到的隐含向量是什么,这时我们就可以通过变分自动编码器来解决这个问题。
其实原理特别简单,只需要在编码过程给它增加一些限制,迫使其生成的隐含向量能够粗略的遵循一个标准正态分布,这就是其与一般的自动编码器最大的不同。
这样我们生成一张新图片就很简单了,我们只需要给它一个标准正态分布的随机隐含向量,这样通过解码器就能够生成我们想要的图片,而不需要给它一张原始图片先编码。
一般来讲,我们通过 encoder 得到的隐含向量并不是一个标准的正态分布,为了衡量两种分布的相似程度,我们使用 KL divergence,利用其来表示隐含向量与标准正态分布之间差异的 loss,另外一个 loss 仍然使用生成图片与原图片的均方误差来表示。
KL divergence 的公式如下
重参数 为了避免计算 KL divergence 中的积分,我们使用重参数的技巧,不是每次产生一个隐含向量,而是生成两个向量,一个表示均值,一个表示标准差,这里我们默认编码之后的隐含向量服从一个正态分布的之后,就可以用一个标准正态分布先乘上标准差再加上均值来合成这个正态分布,最后 loss 就是希望这个生成的正态分布能够符合一个标准正态分布,也就是希望均值为 0,方差为 1
所以最后我们可以将我们的 loss 定义为下面的函数,由均方误差和 KL divergence 求和得到一个总的 loss
def loss_function(recon_x, x, mu, logvar): """ recon_x: generating images x: origin images mu: latent mean logvar: latent log variance """ MSE = 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) # KL divergence return MSE + KLD
用 mnist 数据集来简单说明一下变分自动编码器
import os import torch from torch.autograd import Variable import torch.nn.functional as F from torch import nn from torch.utils.data import DataLoader from torchvision.datasets import MNIST from torchvision import transforms as tfs from torchvision.utils import save_image im_tfs = tfs.Compose([ tfs.ToTensor(), tfs.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 标准化 ]) train_set = MNIST('./mnist', transform=im_tfs) train_data = DataLoader(train_set, batch_size=128, shuffle=True) class VAE(nn.Module): def __init__(self): super(VAE, self).__init__() self.fc1 = nn.Linear(784, 400) self.fc21 = nn.Linear(400, 20) # mean self.fc22 = nn.Linear(400, 20) # var self.fc3 = nn.Linear(20, 400) self.fc4 = nn.Linear(400, 784) def encode(self, x): h1 = F.relu(self.fc1(x)) return self.fc21(h1), self.fc22(h1) def reparametrize(self, mu, logvar): std = logvar.mul(0.5).exp_() eps = torch.FloatTensor(std.size()).normal_() if torch.cuda.is_available(): eps = Variable(eps.cuda()) else: eps = Variable(eps) return eps.mul(std).add_(mu) def decode(self, z): h3 = F.relu(self.fc3(z)) return F.tanh(self.fc4(h3)) def forward(self, x): mu, logvar = self.encode(x) # 编码 z = self.reparametrize(mu, logvar) # 重新参数化成正态分布 return self.decode(z), mu, logvar # 解码,同时输出均值方差 net = VAE() # 实例化网络 if torch.cuda.is_available(): net = net.cuda() x, _ = train_set[0] x = x.view(x.shape[0], -1) if torch.cuda.is_available(): x = x.cuda() x = Variable(x) _, mu, var = net(x) print(mu) Variable containing: Columns 0 to 9 -0.0307 -0.1439 -0.0435 0.3472 0.0368 -0.0339 0.0274 -0.5608 0.0280 0.2742 Columns 10 to 19 -0.6221 -0.0894 -0.0933 0.4241 0.1611 0.3267 0.5755 -0.0237 0.2714 -0.2806 [torch.cuda.FloatTensor of size 1x20 (GPU 0)]
可以看到,对于输入,网络可以输出隐含变量的均值和方差,这里的均值方差还没有训练 下面开始训练
reconstruction_function = nn.MSELoss(size_average=False) def loss_function(recon_x, x, mu, logvar): """ recon_x: generating images x: origin images mu: latent mean logvar: latent log variance """ MSE = 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) # KL divergence return MSE + KLD optimizer = torch.optim.Adam(net.parameters(), lr=1e-3) def to_img(x): ''' 定义一个函数将最后的结果转换回图片 ''' x = 0.5 * (x + 1.) x = x.clamp(0, 1) x = x.view(x.shape[0], 1, 28, 28) return x for e in range(100): for im, _ in train_data: im = im.view(im.shape[0], -1) im = Variable(im) if torch.cuda.is_available(): im = im.cuda() recon_im, mu, logvar = net(im) loss = loss_function(recon_im, im, mu, logvar) / im.shape[0] # 将 loss 平均 optimizer.zero_grad() loss.backward() optimizer.step() if (e + 1) % 20 == 0: print('epoch: {}, Loss: {:.4f}'.format(e + 1, loss.data[0])) save = to_img(recon_im.cpu().data) if not os.path.exists('./vae_img'): os.mkdir('./vae_img') save_image(save, './vae_img/image_{}.png'.format(e + 1)) epoch: 20, Loss: 61.5803 epoch: 40, Loss: 62.9573 epoch: 60, Loss: 63.4285 epoch: 80, Loss: 64.7138 epoch: 100, Loss: 63.3343
变分自动编码器虽然比一般的自动编码器效果要好,而且也限制了其输出的编码 (code) 的概率分布,但是它仍然是通过直接计算生成图片和原始图片的均方误差来生成 loss,这个方式并不好,生成对抗网络中,我们会讲一讲这种方式计算 loss 的局限性,然后会介绍一种新的训练办法,就是通过生成对抗的训练方式来训练网络而不是直接比较两张图片的每个像素点的均方误差
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
PyQt提供了一个设计良好的窗口控件集合,具有更方便的操作性。学过VB的同学会知道,相比与VB的使用,在界面设计上元素更丰富,这篇文章主要介绍了基于PyQt5完成的图转文功能,需要的朋友可以参考下
Python例子-计算阶乘 n! = 1 * 2 * 3 * ... * n。两种方法,一种是用while循环实现,另一种是用递归函数实现
本文主要介绍的是关于python中urllib库的使用,小编觉得是比较实用的,因此分享给大家作参考,感兴趣的朋友可以了解一下,下面就跟随小编一起来看看urllib库的使用吧。
这篇文章主要介绍了Python查找多个字典公共键key案例,文章主要通过案例分享展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
with从Python 2 5就有,需要from __future__ import with_statement。自python 2 6开始,成为默认关键字。也就是说with是一个控制流语
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008