PyTorch-11 自编码器AutoEncoders 、 Variational AutoEncoders
这里我们主要涉及如何利用大量Unlabeled数据?
因此Unsupervised Learning这个领域就需要解决这个问题。
下图是主要的三个领域:
Why needed为什么需要非监督学习
▪ Dimension reduction 从杂乱的数据中找到有用的信息
▪ Preprocessing: Huge dimension, say 224x224, is hard to process
▪ Visualization: https://projector.tensorflow.org/
这里我们查看一下Mnist数据集并用T-SNE方法:
我们再查看一下Mnist数据集并用PCA方法:
很明显,PCA方法数字都混在一起,而T-SNE数字之间较为分散且不同类型间相互聚集。
▪ Taking advantages of unsupervised data 利用非监督数据的优势
▪ Compression, denoising, super-resolution … 压缩、去噪、超分辨率……
unsupervised learning最简单的形态:Auto-Encoders
Auto-Encoders就是重建过程:
特殊点1:输入和输出的维度是一样的,这样才能保证其能够重建。
特殊点2:在网络中有一个特殊的latenet code,这部分称为neck。这个neck可以用于升维,也可以用于降维。下图是用于降维,从784=>2。
PCA V.S. Auto-Encoders
▪ PCA, which finds the directions of maximal variance in high-dimensional data, select only those axes that have the largest variance.
▪ The linearity of PCA, however, places significant limitations
on the kinds of feature dimensions that can be extracted.
mnist数据集:
文本类型:
降噪 Denoising AutoEncoders
我们添加一些噪声对原来的图片,之后通过重建,依然能发掘正确的语义内容。
Dropout AutoEncoders
Dropout是帮助神经网络提升的一个小技巧:
比如说我们在到下一层时,有20%的可能会断掉之前的连接,这样会使得网络更加robust,因为这断掉的20%有可能是断掉了噪声部分,因此在做test时,效果可能会好一些。但是如果随着断掉的概率不断提高,到达某一dropout时,会比没有dropout时test效果还要差,所以这个dropout是需要选取一个最好的概率,从而更好的使test得到提升。
对抗Adversarial AutoEncoders
▪ Distribution of hidden code
可以发现其分布是偏向一侧的,如果我们重建后,其分布依然保持偏向一侧,那我们应该如何解决呢?因此我们这里就提出了Adversarial AutoEncoders。
▪ Give more details after GAN
给与类似GAN的一个辨别器,Discriminator鉴别器,这样可以用于判别真实值Fake(即经过Encode的值)与我们期望按照某一种正太分布的期望值Real都送于Discriminator这个网络来鉴别,如果这两个之间的差距大,则为0,不属于这个分布,输出Fake,差距小则为1,符合这个分布,输出Real。
这样处理能够完成重建工作以外,还能符合我们期望的某种分布状态,这样就解决了重建后分布依然偏向一侧的问题。
由Adversarial GAN引出另一个重点Variational autoencoders
Variational Autoencoders 的初衷
Variational Autoencoders (VAEs) 变分自动编码器
Kullback-Leibler散度 (通常简称为KL散度) = KL divergence
Another Approach: 𝒒 𝒛 → 𝒑(𝒛)
▪ Explicitly enforce
直观地理解KL (p◎q) Intuitively comprehend KL(p◎q)
当两个曲线越接近的时候,即重合区域越多,其p=q;
p和q的差异越来越大时,KL divergence就越来越大。
即两个曲线完全重叠时,KL divergence就是谷底,即最右侧曲线的最小值。
中间图的曲线面积越小(即积分值越小),两条曲线重合度越大,其KL divergence值就越小。
Autoencoders V.S. Variational Autoencoders
对于Fashion-Mnist数据集,VAE重建效果要好于AE。
VAE在重建效果上好于AE以外,更重要的是VAE是作为一个stochastic随机模型,即不同的sample可以重建出不同的图片,其中sample是随机的,因此可以重建大量不同的图片。
VAE V.S. GAN
GAN效果好于VAE。
其中下图GAN(右侧)的图片笑脸明显好于左侧的VAE。
总结:
AE不是stochastic随机模型,因为AE没有随机性。
VAE是有distribution,这个distribution可以sample,sample是随机的,因此是stochastic随机模型。
通过这单元我们将引出下个单元GAN的内容。
实践:
两部分:
1、Auto-Encoders
2、Variational AutoEncoders
都是在mnist数据集
1、Auto-Encoders
(1)首先加载mnist数据集:
main.py文件名:
import torch
from torch.utils.data import DataLoader
from torchvision import transforms,datasets
def main():
#加载mnist数据集
mnist_train=datasets.MNIST('mnist',True,transform=transforms.Compose({
transforms.ToTensor()
}),download=True)
mnist_train = DataLoader(mnist_train,batch_size=32, shuffle=True)
mnist_test= datasets.MNIST('mnist',False,transform=transforms.Compose({
transforms.ToTensor()
}),download=True)
mnist_test = DataLoader(mnist_test,batch_size=32, shuffle=True)
#测试:定义一个迭代器
#这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
x, _ = iter(mnist_train).next()
print('x:',x.shape)
print(1*28*28)
if __name__ == '__main__':
main()
(2)编写AutoEncoders
ae.py文件:
import torch
from torch import nn
class AE(nn.Module):
def __init__(self):
super(AE, self).__init__()
#这里假设已经经过reshape了
#x: torch.Size([32, 1, 28, 28])
#=>[b,784]
self.encoder = nn.Sequential(
nn.Linear(784,256), #降维操作
nn.ReLU(),#激活函数
nn.Linear(256,64), #降维操作
nn.ReLU(),
nn.Linear(64, 20), #再降维操作
nn.ReLU()
) #=>[b,20]
#将hidden layer decode出来
#=>[b,20]
self.decoder = nn.Sequential(
nn.Linear(20,64),
nn.ReLU(),
nn.Linear(64,256),
nn.ReLU(),
nn.Linear(256, 784),
nn.Sigmoid() #这里是用来将像素压缩到0-1
) #=>[b,784]
def forward(self,x):
"""
:param x: [b,1,28,28]
:return:
"""
batchsz = x.size(0)
#进行flatten打平操作
x = x.view(batchsz,784)
#进行encoder
x = self.encoder(x) #=>[b,20]
#进行decoder
x = self.decoder(x) #=>[b,784]
#因为是打平过的,所有我们需要reshape一下,变成图片
x = x.view(batchsz,1,28,28)
return x
(3)继续编写main.py文件的代码
将mnist数据集输入到AE中进行重建
main.py
import torch
from torch.utils.data import DataLoader
from torchvision import transforms,datasets
from torch import nn, optim
from ae import AE
#利用visdom可视化生成图片
import visdom
def main():
#加载mnist数据集
mnist_train=datasets.MNIST('mnist',True,transform=transforms.Compose({
transforms.ToTensor()
}),download=True)
mnist_train = DataLoader(mnist_train,batch_size=32, shuffle=True)
mnist_test= datasets.MNIST('mnist',False,transform=transforms.Compose({
transforms.ToTensor()
}),download=True)
mnist_test = DataLoader(mnist_test,batch_size=32, shuffle=True)
#测试:定义一个迭代器
#这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
x, _ = iter(mnist_train).next()
print('x:',x.shape)
print(1*28*28)
device = torch.device('cuda')
model = AE().to(device)
#这里使用mse作为损失函数
criteon = nn.MSELoss()
optimizer = optim.Adam(model.parameters(),lr=1e-3)
#这里查看一下网络结构:
print(model)
#生成一个visdom实例
viz = visdom.Visdom()
for epoch in range(1000):
for batchidx,(x,_) in enumerate(mnist_train):
# [b,1,28,28]
x = x.to(device)
#这里的x_hat表示重建过的结果
x_hat= model(x)
loss = criteon(x_hat,x)
#backpropagation
#先清零
optimizer.zero_grad()
#回退计算
loss.backward()
#跟新一次优化器
optimizer.step()
#loss是一个tensor类型,因此这里需要转为numpy
print('epoch:',epoch ,'; loss:', loss.item())
#测试数据
#这里是不需要梯度了
x,_ = iter(mnist_test).next()
x=x.to(device)
with torch.no_grad():
x_hat =model(x)
viz.images(x,nrow=8, win='x',opts=dict(title='x'))
viz.images(x_hat,nrow=8,win='x_hat',opts=dict(title='x_hat'))
if __name__ == '__main__':
main()
效果还是可以的,就是重建后有一些些模糊。
以上就完成了Auto-Encoders
2、Variational AutoEncoders
vae.py
在原来的ae.py基础上做一些调整:
import numpy as np
import torch
from torch import nn
class VAE(nn.Module):
def __init__(self):
super(VAE, self).__init__()
# 这里假设已经经过reshape了
# x: torch.Size([32, 1, 28, 28])
# =>[b,784]
# u:[b,10]
# Sigma:[b,10]
self.encoder = nn.Sequential(
nn.Linear(784, 256), # 降维操作
nn.ReLU(), # 激活函数
nn.Linear(256, 64), # 降维操作
nn.ReLU(),
nn.Linear(64, 20), # 再降维操作
nn.ReLU()
) # =>[b,20]
# 将hidden layer decode出来
# =>[b,20]
self.decoder = nn.Sequential(
nn.Linear(10, 64),
nn.ReLU(),
nn.Linear(64, 256),
nn.ReLU(),
nn.Linear(256, 784),
nn.Sigmoid() # 这里是用来将像素压缩到0-1
) # =>[b,784]
def forward(self, x):
"""
:param x: [b,1,28,28]
:return:
"""
batchsz = x.size(0)
# 进行flatten打平操作
x = x.view(batchsz, 784)
# 进行encoder
x = self.encoder(x) # =>[b,20],including mean and sigma
h_ = x
# [b,20] =>拆分为[b,10] and [b,10]
mu, sigma = h_.chunk(2, dim=1) # 这里chunk函数就是拆分
# Variational AutoEncoders是在encoder与decoder中间有所变化
# reparametrize trick这里是用来解决不能导sample的问题:
# epison~N(0,1)
# 形成了一个可导的sample的操作
h = mu + sigma * torch.randn_like(sigma)
# decoder
x_hat = self.decoder(h)
# reshpae
x_hat = x_hat.view(batchsz, 1, 28, 28)
# KL-divergence
kld = 0.5 * torch.sum(
torch.pow(mu, 2) +
torch.pow(sigma, 2) -
torch.log(1e-8 + torch.pow(sigma, 2)) - 1
) / (batchsz * 28 * 28)
# #进行decoder
# x = self.decoder(x) #=>[b,784]
# #因为是打平过的,所有我们需要reshape一下,变成图片
# x = x.view(batchsz,1,28,28)
return x_hat, kld
main.py
在原来的main.py基础上做一些调整:
import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
from torch import nn, optim
from ae import AE
from vae import VAE
# 利用visdom可视化生成图片
import visdom
def main():
# 加载mnist数据集
mnist_train = datasets.MNIST('mnist', True, transform=transforms.Compose({
transforms.ToTensor()
}), download=True)
mnist_train = DataLoader(mnist_train, batch_size=32, shuffle=True)
mnist_test = datasets.MNIST('mnist', False, transform=transforms.Compose({
transforms.ToTensor()
}), download=True)
mnist_test = DataLoader(mnist_test, batch_size=32, shuffle=True)
# 测试:定义一个迭代器
# 这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
x, _ = iter(mnist_train).next()
print('x:', x.shape)
print(1 * 28 * 28)
# 将mnist数据集输入到AE中进行重建
device = torch.device('cuda')
# model = AE().to(device)
model = VAE().to(device)
# 这里使用mse作为损失函数
criteon = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 这里查看一下网络结构:
print(model)
# 生成一个visdom实例
viz = visdom.Visdom()
for epoch in range(1000):
for batchidx, (x, _) in enumerate(mnist_train):
# [b,1,28,28]
x = x.to(device)
# 这里的x_hat表示重建过的结果
x_hat, kld = model(x)
loss = criteon(x_hat, x)
if kld is not None:
elbo = -loss - 1.0 * kld
loss = -elbo
# backpropagation
# 先清零
optimizer.zero_grad()
# 回退计算
loss.backward()
# 跟新一次优化器
optimizer.step()
# loss是一个tensor类型,因此这里需要转为numpy
print('epoch:', epoch, '; loss:', loss.item(), '; kld:', kld.item())
# 测试数据
# 这里是不需要梯度了
x, _ = iter(mnist_test).next()
x = x.to(device)
with torch.no_grad():
x_hat, kld = model(x)
viz.images(x, nrow=8, win='x', opts=dict(title='x'))
viz.images(x_hat, nrow=8, win='x_hat', opts=dict(title='x_hat'))
if __name__ == '__main__':
main()