用pytorch实现Autoencoder
原文连接: https://debuggercafe.com/implementing-deep-autoencoder-in-pytorch/
本文将简述pytorch环境下的线性自编码器的实现:
本文内容:
- autoencoder简介;
- 方法;
- Pytorch实现(线性层)
- 图片重构
一、autoencoder简介
深度学习自编码器是一种神经网络类型,可以从潜在code空间中重构图片;
这里涉及到三个概念:
1)encoder 2)decoder 3) code
下面以图像为例进行说明:
encoder:是个网络结构;输入图像,输出code;
decoder:也是个网络结构;输入code,输出图像;
code:可以理解为图像潜在特征表示
下面用一张图来对其进行表示:
二、方法
Deep autoencoder
三、Pytorch实现
数据集: Fashion MNIST
有70000张灰度图,其中60000作为训练,10000作为测试集;
包含有10个类别;
代码实现:
1)导入需要的包
# import packages import os import torch import torchvision import torch.nn as nn import torchvision.transforms as transforms import torch.optim as optim import matplotlib.pyplot as plt import torch.nn.functional as F from torchvision import datasets from torch.utils.data import DataLoader from torchvision.utils import save_image
重要包说明:
- torchvision: 包含许多流行的计算机视觉数据集,深度神经网络架构,以及图像处理模块;我们将使用这个包来下载Fashion MNIST和CIFAR10数据集;
- torch.nn:包含了许多深度学习网络层,比如Linear()、Conv2d()等;
- transforms:帮助定义图像变换和正则化;
- optim:包含了深度学习的优化器;
- functional:在本文中的激活函数将用到它;
- Dataloader:使数据成为可迭代的训练和测试形式;
2)数据准备以及超参数定义
超参数是指,人工预先设定的,不通过网络学习得到的参数;
主要有, 迭代的次数NUM_EPOCHS,学习率LEARNING_RATE,batch的大小BATCH_SIZE
代码如下:
# constants NUM_EPOCHS = 50 LEARNING_RATE = 1e-3 BATCH_SIZE = 128 # image transformations transform = transforms.Compose([ transforms.ToTensor(), ])
transforms.Compose([transforms.ToTensor(),])是将pixel的值转换为tensor;并且将其缩放至[-1,1]范围;
训练集和测试的准备:
trainset = datasets.FashionMNIST( root='./data', train=True, download=True, transform=transform ) testset = datasets.FashionMNIST( root='./data', train=False, download=True, transform=transform ) trainloader = DataLoader( trainset, batch_size=BATCH_SIZE, shuffle=True ) testloader = DataLoader( testset, batch_size=BATCH_SIZE, shuffle=True )
这里的trainloader以及testloader中的batchsize大小均为128; data loaders是可迭代的;
trainloader中含有60000/128个batches,testloader中含有10000/128个batches。
3) 实用函数 utility functions
写这些函数的目的在于:节省时间,避免代码重复;
本文包含三个实用函数:
a. get_device() 设备判断函数——返回GPU或者是CPU
b.make_dir()创建一个目录来存储训练中重构的图片;
c. save_decode_image() 用于存储自编码器重构的图片。
# utility functions def get_device(): if torch.cuda.is_available(): device = 'cuda:0' else: device = 'cpu' return device def make_dir(): image_dir = 'FashionMNIST_Images' if not os.path.exists(image_dir): os.makedirs(image_dir) def save_decoded_image(img, epoch): img = img.view(img.size(0), 1, 28, 28) save_image(img, './FashionMNIST_Images/linear_ae_image{}.png'.format(epoch))
4)定义自编码-解码网络
Autoencoder()具有两个部分:编码encoder部分以及解码decoder部分;
encoder部分:
a. encoder将28*28维的图像进行展平处理,成为28*28=784维的向量;
b. 定义了5个Linear()层,直到最后一个输出特征是16;
encoder部分生成了潜在code表示;之后进行decoder用于重构;
decoder:
a. 将code中的16个维度通过线性层逐层递增;
b. 最后得到输出的784维的特征;
在forward()函数中,对每层的后面都接了ReLU激活函数,最后将网络结构进行返回;
net = Autoencoder() 创建了一个Autoencoder()实例,当我们需要使用神经网络的时候就可以调用它;
实际上,对于Fashion MNIST来说,我们甚至不需要这么大的网络;即使是两层网络也可以很好的捕捉到图像的重要特征;
class Autoencoder(nn.Module): def __init__(self): super(Autoencoder, self).__init__() # encoder self.enc1 = nn.Linear(in_features=784, out_features=256) self.enc2 = nn.Linear(in_features=256, out_features=128) self.enc3 = nn.Linear(in_features=128, out_features=64) self.enc4 = nn.Linear(in_features=64, out_features=32) self.enc5 = nn.Linear(in_features=32, out_features=16) # decoder self.dec1 = nn.Linear(in_features=16, out_features=32) self.dec2 = nn.Linear(in_features=32, out_features=64) self.dec3 = nn.Linear(in_features=64, out_features=128) self.dec4 = nn.Linear(in_features=128, out_features=256) self.dec5 = nn.Linear(in_features=256, out_features=784) def forward(self, x): x = F.relu(self.enc1(x)) x = F.relu(self.enc2(x)) x = F.relu(self.enc3(x)) x = F.relu(self.enc4(x)) x = F.relu(self.enc5(x)) x = F.relu(self.dec1(x)) x = F.relu(self.dec2(x)) x = F.relu(self.dec3(x)) x = F.relu(self.dec4(x)) x = F.relu(self.dec5(x)) return x net = Autoencoder() print(net)
输出:
Autoencoder( (enc1): Linear(in_features=784, out_features=256, bias=True) (enc2): Linear(in_features=256, out_features=128, bias=True) (enc3): Linear(in_features=128, out_features=64, bias=True) (enc4): Linear(in_features=64, out_features=32, bias=True) (enc5): Linear(in_features=32, out_features=16, bias=True) (dec1): Linear(in_features=16, out_features=32, bias=True) (dec2): Linear(in_features=32, out_features=64, bias=True) (dec3): Linear(in_features=64, out_features=128, bias=True) (dec4): Linear(in_features=128, out_features=256, bias=True) (dec5): Linear(in_features=256, out_features=784, bias=True) )
5)定义网络的损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=LEARNING_RATE)
6) 定义使用的train和test函数
train()函数
有一些需要注意的地方:
第6行,只提取了数据,而没有提取Label;
第8行,将28*28的图像展成784维的形式;
在每次迭代的时候,我们都将loss值存储到train_loss中,在函数最后进行返回;
每5次迭代后,我们将存储重构的图像;可以直观可视化神经网络的变现性能;
test()函数:test_image_reconstruction()
将重构一个batch的图像;
1 def train(net, trainloader, NUM_EPOCHS): 2 train_loss = [] 3 for epoch in range(NUM_EPOCHS): 4 running_loss = 0.0 5 for data in trainloader: 6 img, _ = data 7 img = img.to(device) 8 img = img.view(img.size(0), -1) 9 optimizer.zero_grad() 10 outputs = net(img) 11 loss = criterion(outputs, img) 12 loss.backward() 13 optimizer.step() 14 running_loss += loss.item() 15 16 loss = running_loss / len(trainloader) 17 train_loss.append(loss) 18 print('Epoch {} of {}, Train Loss: {:.3f}'.format( 19 epoch+1, NUM_EPOCHS, loss)) 20 if epoch % 5 == 0: 21 save_decoded_image(outputs.cpu().data, epoch) 22 return train_loss 23 def test_image_reconstruction(net, testloader): 24 for batch in testloader: 25 img, _ = batch 26 img = img.to(device) 27 img = img.view(img.size(0), -1) 28 outputs = net(img) 29 outputs = outputs.view(outputs.size(0), 1, 28, 28).cpu().data 30 save_image(outputs, 'fashionmnist_reconstruction.png') 31 break
7)训练自编码网络
接下来,接可以调用之前定义的utility函数,训练和检验我们的网络;
代码说明:
line 2 得到计算设备 cuda: cpu/gpu
line 5 加载网络到设备上;
line 6 为重构的图像建立目录文件;
line 10 绘制训练损失;
line 19 对图像进行重构来检验单个batch中的影像;
1 # get the computation device 2 device = get_device() 3 print(device) 4 # load the neural network onto the device 5 net.to(device) 6 make_dir() 7 # train the network 8 train_loss = train(net, trainloader, NUM_EPOCHS) 9 plt.figure() 10 plt.plot(train_loss) 11 plt.title('Train Loss') 12 plt.xlabel('Epochs') 13 plt.ylabel('Loss') 14 plt.savefig('deep_ae_fashionmnist_loss.png') 15 # test the network 16 test_image_reconstruction(net, testloader)
8) 分析损失图和图像重构结果
四、重构图像:
可以看出随着迭代次数的增加,图像的质量得到了显著提升;
总结与结论:
本文是关于Pytorch实现一个简单的深度自编码器的研究。
在深度学习中,如果你能够很清晰的理解一个简单的概念,那么相关的概念也会较为容易理解的。
我在此希望你已经学到了如何使用Pytorch实现深度自编码器。
接下来,我们将实现卷积自编码器。