基于minist数据集用VAE训练生成图片(VAE基础入门学习)

2023-09-19 11:06:25

参考的代码

复现的代码

VAE介绍

 VAE是变分自编码器(Variational Auto-Encoder)的缩写。它是一种深度生成模型,由 Kingma 等人于 2014 年提出的基于变分贝叶斯(Variational Bayes,VB)推断的生成式网络结构1. VAE 模型有两部分,分别是一个编码器和一个解码器,常用于 AI 图像生成。

训练模型的网络的结构

#所以整个VAE的图像形状的变化流程就是,
	#输入:batchsize*28*28 
	#编码器:
		#28*28 - 100
		#100 - 100
	#_sample_latent
		#100 - 8
	#解码器:
		#8 -100
		#100 - 28*28

损失函数loss包括两部分:重构误差和KL散度
 重构误差
  重构误差(reconstruction error)计算得出的,它反映了解码器解码得到的向量与输入向量之间的差异.一般用均方差来计算
 KL散度
  KL 散度项用于衡量潜在空间中的分布与标准正态分布之间的差异
VAE的作用
 通过神经网络学习到分布的转换过程,那么可以实现图像的压缩,只记录中间过程,然后通过中间过程根据模型生成重建后的图像VAE的作用是生成新的图像,而不是还原原图像。
 通过学习数据的潜在分布,VAE可以从该分布中生成与训练数据相似但不完全相同的新样本。

代码实现与解读

代码块

import torch
import torch.nn.functional as F
from torch.autograd import Variable
import torchvision
from torchvision import transforms
import torch.utils.data as Data
import torch.optim as optim
from torch import nn
import numpy as np
import matplotlib.pyplot as plt
import os
import torchsummary
from torchsummary import summary
#summary(your_model, input_size=(channels, H, W))

os.environ["CUDA_VISIBLE_DEVICES"] = '3'


class Normal(object):
    def __init__(self, mu, sigma, log_sigma, v=None, r=None):
        self.mu = mu
        self.sigma = sigma
        self.logsigma = log_sigma
        dim = mu.get_shape()
        if v is None:
            v = torch.FloatTensor(*dim) # create a tensor
        if r is None:
            r = torch.FloatTensor(*dim)
        self.v = v
        self.r = r


class Encoder(torch.nn.Module):
    def __init__(self, D_in, H, D_out): # input_dim, hidden_dim, hidden_dim
        super().__init__()
        self.liner1 = torch.nn.Linear(D_in, H) # dimension(D_in, H)
        self.liner2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        x = F.relu(self.liner1(x))
        return F.relu(self.liner2(x))


class Decoder(torch.nn.Module):
    def __init__(self, D_in, H, D_out): # latent_dim, hidden_dim, input_dim
        super().__init__()
        self.liner1 = torch.nn.Linear(D_in, H)
        self.liner2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        x = F.relu(self.liner1(x))
        return F.relu(self.liner2(x))


class VAE(torch.nn.Module):

    def __init__(self, encoder, decoder, latent_dim, hidden_dim): # 定义构造方法
        super().__init__() #调用父类方法
        self.encoder = encoder
        self.decoder = decoder
        self._enc_mu = torch.nn.Linear(hidden_dim, latent_dim)  # 神经网络线性模块
        self._enc_log_sigma = torch.nn.Linear(hidden_dim, latent_dim)
    #这里对μ和σ的学习,只是利用神经网络得到两个向量,然后加入的正太分布的大小也和μ和σ的维度是一样的
    #sample——latent加在了encoder和decoder之间,进行一个维度的转化,以及参数的重构,增加我们模型的参数,让我们的模型更有泛化能力

    def _sample_latent(self, h_enc):
        """
        :param h_enc:
        :return: the latent normal sample z ~ N(mu, sigma^2)
        """
        mu = self._enc_mu(h_enc)
        log_sigma = self._enc_log_sigma(h_enc)
        sigma = torch.exp(log_sigma)  # y = e^x
        std_z = torch.from_numpy(np.random.normal(0, 1, size=sigma.size())).float() # 讲numpy转成tensor

        self.z_mean = mu
        self.z_sigma = sigma

        return mu + sigma * Variable(std_z, requires_grad=False)  # Reparametization trick

    def forward(self, state):
        #前向传播,将图像数据转化为size为  batch_size*8 的形式
        h_enc = self.encoder(state)
        #然后利用重参数化技巧,将encoder的结果转化为decoder可以处理的结果
        z = self._sample_latent(h_enc)
        #最后用decoder对图像进行还原
        return self.decoder(z)


def latent_loss(z_mean, z_stddev):
    mean_sq = z_mean * z_mean
    stddev_sq = z_stddev * z_stddev
    return 0.5 * torch.mean(mean_sq + stddev_sq - torch.log(stddev_sq) - 1)




latent_dim = 8
hidden_dim = 100
input_dim = 28 * 28
batch_size = 32
transform = transforms.Compose([transforms.ToTensor()])  # pytorch 的一个图形库
mnist = torchvision.datasets.MNIST('./', download=True, transform=transform)
dataloader = Data.DataLoader(mnist, batch_size=batch_size, shuffle=True, num_workers=2)  # 用来把训练数据分成多个小组,此函数每次抛出一组数据。直至把所有的数据都抛出

encoder = Encoder(input_dim, hidden_dim, hidden_dim)
decoder = Decoder(latent_dim, hidden_dim, input_dim)
vae = VAE(encoder, decoder, latent_dim, hidden_dim)


#所以整个VAE的图像形状的变化流程就是,
	#输入:batchsize*28*28 
	#编码器:
		#28*28 - 100
		#100 - 100
	#_sample_latent
		#100 - 8
	#解码器:
		#8 -100
		#100 - 28*28
criterion = nn.MSELoss()  # 均方损失函数: loss(x_i,y_i) = (x_i, y_i)^2

optimizer = optim.Adam(vae.parameters(), lr=0.0001)  # 为了使用torch.optim,需先构造一个优化器对象Optimizer,用来保存当前的状态,并能够根据计算得到的梯度来更新参数,lr学习率
# Adam 自适应 SGD随机梯度下降
l = 0



for epoch in range(100):
    for i, data in enumerate(dataloader, 0):
        inputs, classes = data
        #Variable的作用是将 tensor 转换为可以进行自动求导的对象
        inputs, classes = Variable(inputs.resize_(batch_size, input_dim)), Variable(classes)
        optimizer.zero_grad()  # 梯度置0
        #vae返回的是解码器生成的图像
        dec = vae(inputs)
        #计算在sample过程中的潜在损失
        la_loss = latent_loss(vae.z_mean, vae.z_sigma)
        loss = criterion(dec, inputs) + la_loss
        loss.backward()  # 反向传播,计算当前梯度;
        optimizer.step()  # 根据梯度更新网络参数
        l += loss.item()
    #这里是损失函数的累加,所以损失的值是不断增大
    print(epoch, l)

#最后我们得到的VAE能给定一个图像,然后其对图像进行压缩,然后解码为另一个图像(实现图像的转换,风格的变化)
#VAE的作用是生成新的图像,而不是还原原图像。通过学习数据的潜在分布,VAE可以从该分布中生成与训练数据相似但不完全相同的新样本。
plt.imshow(vae(inputs).data[0].numpy().reshape(28, 28), cmap='gray')
plt.show(block=True)

累计损失函数的变化

0 139.16818779706955
1 267.87674168124795
2 395.3811474405229
3 522.4189930967987
4 649.2481354251504
5 775.955449514091
6 902.5857038684189
7 1029.162303648889
8 1155.701839581132
9 1282.207650154829
10 1408.6873084045947
11 1535.1423131600022
12 1661.581350132823
13 1788.0235249735415
14 1914.4417337104678
15 2040.8419712409377
16 2167.2386958152056
17 2293.6268618367612
18 2420.0079036839306
19 2546.382174734026
20 2672.755078855902
21 2799.1218701675534
22 2925.488541547209
23 3051.846152242273
24 3178.199055157602
25 3304.5493181720376
26 3430.8967041336
27 3557.2448443993926
28 3683.589498732239
29 3809.9357734806836
30 3936.2771221250296
31 4062.6171908825636
32 4188.954324498773
33 4315.289867065847
34 4441.629120387137
35 4567.959967684001
36 4694.293099652976
37 4820.619683727622
38 4946.952780801803
39 5073.282864153385
40 5199.609850440174
41 5325.9397988170385
42 5452.265446525067
43 5578.591994319111
44 5704.914690893143
45 5831.23753689602
46 5957.559496037662
47 6083.884328097105
48 6210.208755288273
49 6336.530883956701
50 6462.851982541382
51 6589.169302016497
52 6715.490865979344
53 6841.8104959875345
54 6968.132151730359
55 7094.45059369877
56 7220.767149258405
57 7347.088673260063
58 7473.406335223466
59 7599.724608790129
60 7726.037183042616
61 7852.353367693722
62 7978.6651857718825
63 8104.976332779974
64 8231.28970463574
65 8357.603039838374
66 8483.91544116661
67 8610.230692859739
68 8736.543450322002
69 8862.851587157696
70 8989.160839147866
71 9115.472040515393
72 9241.782772105187
73 9368.089835889637
74 9494.398323338479
75 9620.7102698721
76 9747.017931006849
77 9873.32592940703
78 9999.633243571967
79 10125.944087844342
80 10252.24886193499
81 10378.557406943291
82 10504.867665823549
83 10631.173416335136
84 10757.483445655555
85 10883.788483023643
86 11010.096190895885
87 11136.400267038494
88 11262.704502705485
89 11389.009268335998
90 11515.318977285177
91 11641.624094836414
92 11767.9290404059
93 11894.233354754746
94 12020.536377009004
95 12146.839713525027
96 12273.143506359309
97 12399.446860846132
98 12525.751429088414
99 12652.052845072001

迭代100次后生成的图像

在这里插入图片描述

更多推荐

学习记忆——宫殿篇——记忆宫殿——记忆桩——单间+客厅+厨房+厕所+书房+院子

文章目录单间客厅厨房厕所书房院子单间水壶水龙头香皂果汁机电视门空间花红酒葡萄不锈钢白毛沙发彩色垫子吉他皮椅挂画风扇糖抱枕盒子花土水晶腿衣柜笔三环相框水壶壁挂台灯被网球拍足球抽屉闹钟蝴蝶心斑马三轮车音响椅子碗玩偶烟灰缸电视窗帘玻璃上铺镜子壁灯枕头电话纸盘鱼长方形镜子垃圾桶电视柜地板砖折叠凳窗帘挂坠毯子竹节式台灯台灯床头床

如何利用好Twitter的功能进行营销

虽然Twitter不是最复杂的社交网络,但您需要了解其中的一些特性和功能。这些是我们进行基本操作的地方。您进行探索并想出更多有创意的方式来使用这些功能。推文。推文是您可以分享的帖子和更新,限制在140个字符内。每一条推文都有存档,您可以查看自己和其他用户的推文。通过浏览其他用户的近期推文,您可以更好地了解他们关注的话题

SpringCLoud——docker中的数据卷

数据卷容器与数据耦合的问题不便于修改当我们要改Nginx的HTML内容时,需要进入容器内部修改,很不方便。数据不可复用在容器内的修改对外是不可见的。所有修改对新的容器是不可复用的。升级维护困难数据在容器内,如果要升级容器必然删除旧容器,所有数据都跟着删除了。数据卷(Volume)是一个虚拟目录,指向宿主机文件系统中的某

MQ和分布式事务

MQmq通知时,消费者没消费到怎么办简单聊聊消息中间件?你了解那些具体的消息中间件产品?mq的消费端是怎么处理的?整理一下你的消费端的整个处理逻辑流程,然后说说你的ack是在哪里返回的。按照你这样画的话,如果数据库突然宕机,你的消息该怎么确认已经接收?那如果发送端的服务是多台部署呢?你保存消息的时候数据库就一直报唯一性

Vue中如何进行表格排序与过滤

Vue中如何进行表格排序与过滤在Vue.js中,表格是一个常见的数据展示方式。很多时候,我们需要对表格中的数据进行排序和过滤,以提供更好的用户体验。本文将介绍如何在Vue中实现表格的排序和过滤功能,并提供相关的代码示例。准备工作在开始之前,我们需要准备一些基本的工作。首先,确保你已经安装了Vue.js,并且创建了一个V

Service 层异常抛到 Controller 层处理还是直接处理?

0前言一般初学者学习编码和[错误处理]时,先知道[编程语言]有一种处理错误的形式或约定(如Java就抛异常),然后就开始用这些工具。但却忽视这问题本质:处理错误是为了写正确程序。可是1啥叫“正确”?由解决的问题决定的。问题不同,解决方案不同。如一个web接口接受用户请求,参数age,也许业务要求字段是0~150之间整数

JMeter:断言之响应断言

一、断言的定义断言用于验证取样器请求或对应的响应数据是否返回了期望的结果。可以是看成验证测试是否预期的方法。对于接口测试来说,就是测试Request/Response,断言即可以针对Request进行,也可以针对Response进行。但大部分是对Response做断言。JMeter常见的断言元件如下:1.响应断言2.J

【MySQL系列】如何在MySQL中使用触发器?MySQL触发器详解

MySQL可以通过触发器来实现自动化业务逻辑和操作。触发器是一种在数据库表发生特定操作时自动执行的存储过程,能够响应特定事件,如INSERT、UPDATE和DELETE语句。本文将详细介绍MySQL中的触发器概念、创建和使用方法,以及一些注意事项。一、概念触发器是一种与表相关联的一段代码,它会在特定事件(INSERT、

利用 SOAR 加快事件响应并加强网络安全

随着攻击面的扩大和攻击变得越来越复杂,与网络攻击者的斗争重担落在了安全运营中心(SOC)身上。SOC可以通过利用安全编排、自动化和响应(SOAR)平台来加强组织的安全态势。这一系列兼容的以安全为中心的软件可加快事件调查和响应速度。SOAR平台提高了对所有安全数据的可见性,简化了IT流程,自动执行了与安全相关的手动任务,

[EI复现】基于主从博弈的新型城镇配电系统产消者竞价策略(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。⛳️座右铭:行百里者,半于九十。📋📋📋本文目录如下:🎁🎁🎁目录💥1概述📚2运行结果🎉3参考文献🌈4Matlab代码实现💥1概述新型城镇的发展需要实现能源结构清洁化和能源利用高效

{案例分析}**市 SA-接通率CCE参数优化报告

【问题描述】SA-无线接通率主要包含了SA-RRC连接建立成功率,SA-NG接口UE相关逻辑信令连接建立成功率和SA-QosFlow建立成功率;现网接通率99.27%,【问题分析】接通率主要问题在于QosFlow建立成功率相对较差,RRC建立成功率基本处于99.6%以上,NG接通率接近百分之百;【问题根因】针对于Qos

热文推荐