首页 > 编程知识 正文

pytorch中文手册,hiperface编码器

时间:2023-05-04 19:24:35 阅读:10611 作者:1197

Pytorch Note44变分自动编码器(VAE )文章目录Pytorch Note44变分自动编码器(VAE )变分自动编码器重新参数定义VAE培训

所有笔记的总结粘贴: Pytorch Note快乐之星

变分自动编码器

可变编码器是自动编码器的版本升级版,其结构与自动编码器相似,由编码器和解码器构成。

请回想一下。 自动编码器有问题。 就是说不能任意生成图像。 因为我们不能自己制作隐藏向量,所以不知道必须用一张图像输入编码才能得到的默认向量是什么。 此时,我们可以用变分自动编码器解决这个问题。

其实原理特别简单,只要在编码过程中加以限制,生成的隐式向量就会被迫大致遵循标准正态分布。 这是与一般自动编码器的最大区别。

这样很容易生成新图像。 只需给出标准正态分布的随机隐式向量。 这样,解码器就可以生成所需的图像,而无需先对原始图像进行编码。

一般来说,我们通过encoder得到的隐式向量不是标准的正态分布,而是使用KL divergence来测量两个分布的相似度,使用表示隐式向量和标准正态分布之间差异的loss,另一个loss使用生成图像和原始图像的均方误差来表示

KL divergence公式如下

dL(p ) ) q )=p ) x ) q ) dxd ) KL ) p||q )=(int_{-(infty}^{infty}p ) x

为了避免在重参数KL divergence下进行积分计算,我们使用重参数技巧来生成两个向量,而不是每次生成一个隐式向量。 一个表示平均值,另一个表示标准差。 现在,如果默认编码的隐式向量遵循正态分布,则可以用标准正态分布将标准偏差乘以标准偏差,然后将平均值相加以合成正态分布。 最后loss希望这个生成的正态分布符合标准

因此,标准的变量自动编码器如下

最后,将我们的loss定义为下一个函数,可以从均方误差和KL divergence之和得到综合loss

defloss_function(recon_x,x,mu, logvar ) 3360 ' ' recon _ x : generatingimagesx : originimagesmu : latentmeanlogvar : latentlogvariance ' ' ' MSE=re core

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 数据集来简单说明一下变分自动编码器

定义VAE 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 = eps.cuda() else: eps = 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()_, mu, var = net(x)print(mu) tensor([[-1.7714e-01, -3.3226e-02, 1.8475e-04, -2.1119e-01, -3.0027e-01, -9.8018e-02, 5.1128e-02, -7.5215e-02, -1.6336e-01, 6.2949e-02, 3.7830e-03, 2.7366e-01, -2.6736e-01, -1.7473e-01, -3.8985e-01, 5.1227e-02, 7.3497e-02, 1.4625e-01, -7.1520e-02, 3.5298e-01]], device='cuda:0', grad_fn=<AddmmBackward>)

可以看到,对于输入,网络可以输出隐含变量的均值和方差,这里的均值方差还没有训练

下面开始训练

训练 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 + KLDoptimizer = 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 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 + KLDoptimizer = 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) 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)) 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: 68.4358epoch: 40, Loss: 63.8153epoch: 60, Loss: 59.8225epoch: 80, Loss: 64.8400epoch: 100, Loss: 64.3905

可以看看使用变分自动编码器得到的结果,可以发现效果比一般的编码器要好很多

我们可以输出其中的均值看看

x, _ = train_set[0]x = x.view(x.shape[0], -1)if torch.cuda.is_available(): x = x.cuda()_, mu, _ = net(x)print(mu) tensor([[ 0.3334, 0.0050, 0.0564, 1.2526, -0.8657, 0.1395, -0.2028, -1.6824, 0.6442, 0.9594, -0.6583, -0.4416, -0.3341, 3.4309, -0.0213, -2.1715, -1.2688, 0.4125, 0.0148, 1.3052]], device='cuda:0', grad_fn=<AddmmBackward>)

变分自动编码器虽然比一般的自动编码器效果要好,而且也限制了其输出的编码 (code) 的概率分布,但是它仍然是通过直接计算生成图片和原始图片的均方误差来生成 loss,这个方式并不好,在下面生成对抗网络中,我们会讲一讲这种方式计算 loss 的局限性,然后会介绍一种新的训练办法,就是通过生成对抗的训练方式来训练网络而不是直接比较两张图片的每个像素点的均方误差

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。