深度学习系列
第一篇 局部最优点+鞍点+学习率的调节
第二篇 并行计算 深度学习 机器学习
第三篇 长短期记忆网络(LSTM)是什么怎么工作的呢?
第四篇 Dropout解析 代码实现
第五篇 Auto-encoder 自编码器
文章目录
前言
Auto-encoder是一个自监督学习的方法,它可以把高维度的数据转为低维度的数据(dimension reduction)。
一、基础问题
1.为什么要用Auto-Encoder自编码器
- 降低输入数据的维度,且优于PCA等传统降维方法.
2. Auto-Encoder的过程
自动编码器可以看作一个三层的神经网络结构:输入层、隐层、输出层。
它的另一个一般的说法:由一个编码器和一个解码器组合而成的。其实就是神经网络。
编码器部分:输入变量集合X通过一个神经网络(编码器)降维压缩得到隐变量集合H。
解码器部分:隐变量集合H通过另一个神经网络(解码器)将降维后的特征进行重构,得到输出变量集合Y。
下图的自编码器的图示
3. Auto-Encoder自编码器和整个模型的关系
一般是在一开始处理数据特征的,但是随着发展,也有其他使用方法。例如论文[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可视化
可以看到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的理解,后续有其他的见解会继续补充,希望配合上其他文章,能让初学者更容易理解。如果觉得有用,请大家点赞支持!!!!。