Auto-encoder 自编码器 附Pytorch代码 结果可视化

深度学习系列

第一篇 局部最优点+鞍点+学习率的调节
第二篇 并行计算 深度学习 机器学习
第三篇 长短期记忆网络(LSTM)是什么怎么工作的呢?
第四篇 Dropout解析 代码实现
第五篇 Auto-encoder 自编码器


前言

Auto-encoder是一个自监督学习的方法,它可以把高维度的数据转为低维度的数据(dimension reduction)。


一、基础问题

1.为什么要用Auto-Encoder自编码器

  • 降低输入数据的维度,且优于PCA等传统降维方法.

2. Auto-Encoder的过程

自动编码器可以看作一个三层的神经网络结构:输入层、隐层、输出层。

它的另一个一般的说法:由一个编码器和一个解码器组合而成的。其实就是神经网络。
编码器部分:输入变量集合X通过一个神经网络(编码器)降维压缩得到隐变量集合H。

解码器部分:隐变量集合H通过另一个神经网络(解码器)将降维后的特征进行重构,得到输出变量集合Y。

下图的自编码器的图示

自编码器图示

3. Auto-Encoder自编码器和整个模型的关系

一般是在一开始处理数据特征的,但是随着发展,也有其他使用方法。例如论文[3] 中提到了,作者使用两个自编码器,第一个是作为类似于预处理的作用,去提取特征,增强数据,另一个自编码器和神经网络并行运行,最后才合并进入分类器。如下图(图来源于论文[3])
论文3 的过程

4. 为什么Auto-Encoder的目标函数是自己?有什么用?

我们看他的作用是提取数据的特征,缩小维度。我们进行维度的缩小,但是缩小的同时不能让数据偏差太多,所以我们要提取出主要的特征,这就让神经网络去判断哪一些特征是缩小后距离模型不会偏差太远,这些就是主要特征。所以我们在编码解码的时候,要求目标函数L(X,Y)尽可能达到最小值。

二、Pytorch代码实现

代码说明

本代码采用Mnist数据集进行实验,将数据结果放到三维空间中进行展示。由于三维空间需要三个坐标,所以我们encoder的最后维度为3(这三个数据可能是没意义的,但是相似的样本,最后这三个维度是数据是差不多的,所以在三维空间上靠得比较近

import torch
from torchvision import datasets, transforms
import torch.utils.data as Data
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import numpy as np
import matplotlib.pyplot as plt
import time

# 设置变量
BATCH_SIZE = 8
EPOCH = 10

dataset = datasets.MNIST('../data/mnist', train=True, transform=transforms.Compose([transforms.ToTensor()]),
                         download=True)
data_loader = Data.DataLoader(dataset, shuffle=True, batch_size=BATCH_SIZE)

# 设置机器
is_gpu = torch.cuda.is_available()
if is_gpu:
    print("GPU is available,Training on GPU")
else:
    print("GPU is not available,Training on CPU")

device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")


class AutoEncoder(torch.nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        # 压缩
        self.encoder = torch.nn.Sequential(torch.nn.Linear(28 * 28, 128),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(128, 64),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(64, 12),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(12, 3))  # 因为最后要在三维图上展示,所以最后是三维,也可以是其他
        # 压缩
        self.decoder = torch.nn.Sequential(torch.nn.Linear(3, 12),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(12, 64),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(64, 128),
                                           torch.nn.Tanh(),
                                           torch.nn.Linear(128, 28 * 28),
                                           torch.nn.Sigmoid(),  # 让输出值在(0,1),因为minist原始数据集的数值就是在0-1,0白表示白,1表示黑,中间值表示灰度
                                           )

    def forward(self, x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return encoded, decoded


auto_encoder = AutoEncoder()
auto_encoder = auto_encoder.to(device)
optimizer = torch.optim.Adam(auto_encoder.parameters(), lr=0.005)
criterion = torch.nn.MSELoss()

since = time.time()

for epoch in range(EPOCH):
    epoch_loss = 0
    for step, (x, label) in enumerate(data_loader):
        x = x.view(-1, 28 * 28)  # batch x, shape (batch, 28*28),原本是torch.Size([1, 28, 28])
        y = x.view(-1, 28 * 28)  # 标签也是自己
        x = x.to(device)    # 放入机器中
        y = y.to(device)    # 放入机器中
        encoded, decoded = auto_encoder(x)
        optimizer.zero_grad()
        loss = criterion(decoded, y)  # 注意是decoded和y对比,不是encoded
        epoch_loss += loss.item()
        loss.backward()
        optimizer.step()
    epoch_time = time.time() - since
    print("epoch:{}, {:0f}m{:0f}s".format(epoch, epoch_time // 60, epoch % 60))
    print("epoch:{}, loss:{:.4f}".format(epoch, epoch_loss/len(data_loader.dataset)))

# 画3D图,看一下根据最后提取的3个维度特征,数据的分布构成
# 注意 同样的数字,最后的三维特征是相似的,所以会在3D图上差不多的地方

view_data = dataset.train_data[:200].view(-1, 28 * 28).type(torch.FloatTensor)      # 挑两百个数据展示
view_data=view_data.to(device)
encoded_data, _ = auto_encoder(view_data)   # 通过上面训练的模型转为3维数据

train_on_gpu = torch.cuda.is_available()
if train_on_gpu:
    X = encoded_data[:, 0].cpu().detach().numpy()  # 把torch转为numpy数据
    Y = encoded_data[:, 1].cpu().detach().numpy()
    Z = encoded_data[:, 2].cpu().detach().numpy()
else:
    X = encoded_data[:, 0].detach().numpy()      # 把torch转为numpy数据
    Y = encoded_data[:, 1].detach().numpy()
    Z = encoded_data[:, 2].detach().numpy()

value = dataset.train_labels[:200].numpy()      # 读取标签是什么

fig = plt.figure(2)     # 画布大小
ax = Axes3D(fig)

for x, y, z, s in zip(X, Y, Z, value):              #设置颜色和坐标
    color = cm.rainbow(int(255 * s / 9))
    ax.text(x, y, z, s, backgroundcolor=color)

ax.set_xlim(X.min(), X.max())       # 设置边界的最大最小值
ax.set_ylim(Y.min(), Y.max())       # 设置边界的最大最小值
ax.set_zlim(Z.min(), Z.max())       # 设置边界的最大最小值
plt.show()

控制台输出结果

GPU is available,Training on GPU
epoch:0, 0.000000m0.000000s
epoch:0, loss:0.0060
epoch:1, 1.000000m1.000000s
epoch:1, loss:0.0053
epoch:2, 2.000000m2.000000s
epoch:2, loss:0.0052
epoch:3, 2.000000m3.000000s
epoch:3, loss:0.0051
epoch:4, 3.000000m4.000000s
epoch:4, loss:0.0051
epoch:5, 4.000000m5.000000s
epoch:5, loss:0.0051
epoch:6, 4.000000m6.000000s
epoch:6, loss:0.0050
epoch:7, 5.000000m7.000000s
epoch:7, loss:0.0050
epoch:8, 5.000000m8.000000s
epoch:8, loss:0.0050
epoch:9, 6.000000m9.000000s
epoch:9, loss:0.0050

结果3D可视化

3D可视化
可以看到1和0这个数字是比较容易识别的,所以分在一起,但是像7和2相对像一点,所以比较难分别一些,在3维空间上也离得比较近。

三、Reference

李宏毅老师讲解的课程,更多是自编码器在实际的应用及变体。
[1]Deep Residual Learning for Image Recognition
[2]ABOLHASANZADEH B. Nonlinear dimensionality reduction for intrusion detection using auto-encoder bottleneck features[C].2015 7th Conference on Information and Knowledge Technology (IKT). IEEE, 2015: 1-5.
[3]基于自监督特征增强的CNN-LSTM网络入侵检测方法_梁欣怡


总结

以上就是我个人对Auto-encoder的理解,后续有其他的见解会继续补充,希望配合上其他文章,能让初学者更容易理解。如果觉得有用,请大家点赞支持!!!!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的PyTorch自动编码器代码: ``` python import torch import torch.nn as nn import torch.optim as optim # 定义自动编码器模型 class Autoencoder(nn.Module): def __init__(self): super(Autoencoder, self).__init__() self.encoder = nn.Sequential( nn.Linear(28*28, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 12), nn.ReLU(), nn.Linear(12, 3) ) self.decoder = nn.Sequential( nn.Linear(3, 12), nn.ReLU(), nn.Linear(12, 64), nn.ReLU(), nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 28*28), nn.Sigmoid() ) def forward(self, x): x = self.encoder(x) x = self.decoder(x) return x # 加载数据 train_loader = torch.utils.data.DataLoader( torchvision.datasets.MNIST('../data', train=True, download=True, transform=torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize( (0.1307,), (0.3081,)) ])), batch_size=32, shuffle=True) # 定义模型、优化器、损失函数 autoencoder = Autoencoder() optimizer = optim.Adam(autoencoder.parameters(), lr=0.001) criterion = nn.MSELoss() # 训练模型 for epoch in range(10): for data in train_loader: x, _ = data x = x.view(-1, 28*28) optimizer.zero_grad() output = autoencoder(x) loss = criterion(output, x) loss.backward() optimizer.step() print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, 10, loss.item())) # 测试模型 with torch.no_grad(): for data in train_loader: x, _ = data x = x.view(-1, 28*28) output = autoencoder(x) break # 可视化结果 import matplotlib.pyplot as plt fig, axes = plt.subplots(nrows=2, ncols=10, sharex=True, sharey=True, figsize=(25,4)) for i in range(10): axes[0,i].imshow(x[i].reshape(28,28), cmap='gray') axes[1,i].imshow(output[i].reshape(28,28), cmap='gray') plt.show() ``` 这个自动编码器模型使用MNIST数据集进行训练和测试,其中包含了一个三层的编码器和一个三层的解码器。模型的编码器将MNIST图像的28x28像素展平为784维的向量,然后通过128、64和12个隐藏单元的全连接层进行编码,最终输出3维的编码向量。模型的解码器将这个3维的编码向量通过12、64和128个隐藏单元的全连接层进行解码,最终输出一个与输入图像相同大小的图像。 训练过程中,模型的目标是最小化重构误差(即输入图像和输出图像之间的均方误差)。在每个训练周期结束时,模型会在MNIST训练集的一个小批量上计算重构误差,并输出训练周期的平均误差。在测试过程中,模型会将MNIST训练集的一个小批量输入自动编码器,并输出重构图像。这些图像可以用来可视化自动编码器的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值