- 什么是编码器
有一中数据压缩的、降维的意思
举个例子来说明,同一张图片,高清的和标清的我们都能识别出图片中的内容(这里就考虑识别这一个需求,其他需求暂不考虑),这是因为即使是标清的图片,也保留了进行识别的关键特征
。但是高清的在无论是在保存,还是在提取上都会更费工夫。深度学习处理起来亦是如此,深度学习会包含很多层,每层节点也很多,这种情况下,如果输入数据的规模太大,神经网络也很难训练出结果。那么,能在保留关键特征的基础上对数据尽心降维,就是一项一劳永逸的活动。
这个编码器要怎么用呢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1nZbFk6U-1582679206189)(https://morvanzhou.github.io/static/results/ML-intro/auto3.png)]
图片引自 什么是自编码 (Autoencoder)
编码器的构造跟自己的应用(或者分类,或者回归)上两套体系。编码器也是一个完整的训练流程,虽说叫编码器,其实其内部包括编码(上图中的压缩)和解码(上图中的解压)两部分,编码用来降维,解码用来将维度回复,通过维度恢复的数据(上图中的黑色X)与原始数据(上图的白色X)的误差来训练编码器参数,训练完成后编码部分将能压缩到原始数据的关键特征,极大地加速训练过程。
另外我觉得,翻译是一个很好的例子,自己有中思路可不可以做一种压缩(编码)一种万国语,存放在计算机中,计算机能识别的;甚至可以跨越表达方式,比如‘狗’、‘dog’ 另外还有一张狗的图片。他们在计算机中的表现形式是一样的,通过不同的模型可以翻译成‘狗’、‘dog’ 和狗的图片。
下面用一个例子来说明
这个程序的数据是手写数字识别的图片,分辨率为28*28,通过编码器将28*28维度的像素维度降维到3维;然后用3维数据在三维坐标平面内进行了可视化;最后用svm就编码之后的3维数据进行分类,因为压缩之后只有3个维度,为了节约时间只用了1000个训练数据,所以最终的准确率并没有很高。
-
导入支持包与设置超参数
import torch import torch.nn as nn import torch.utils.data as Data import torchvision import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D import os import numpy as np from sklearn import svm from sklearn.model_selection import GridSearchCV # 超参数 EPOCH = 10 BATCH_SIZE = 64 LR = 0.005 if os.path.exists('mnist/'): # 如果已经存在(下载)了就不用下载了 DOWNLOAD_MNIST = False else: DOWNLOAD_MNIST = True # 下过数据的话, 就可以设置成 False N_TEST_IMG = 5 # 到时候显示 5张图片看效果, 如上图一
-
获得手写数字图片数据
####################################### 获取手写数字图片数据 train_data = torchvision.datasets.MNIST( root='./mnist/', train=True, # this is training data transform=torchvision.transforms.ToTensor(), # Converts a PIL.Image or numpy.ndarray to # torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0] download=DOWNLOAD_MNIST, # download it if you don't have it ) test_data = torchvision.datasets.MNIST(root='./mnist/', train=False) # 批训练 50samples, 1 channel, 28x28 (50, 1, 28, 28) train_loader = Data.DataLoader( dataset=train_data, batch_size=BATCH_SIZE, shuffle=True, num_workers=0 )
-
构造编码器
################################# 构造编码器 class AutoEncoder(nn.Module): def __init__(self): super(AutoEncoder, self).__init__() # 编码网络 self.encoder = nn.Sequential( nn.Linear(28*28, 128), nn.Tanh(), nn.Linear(128, 64), nn.Tanh(), nn.Linear(64, 12), nn.Tanh(), nn.Linear(12, 3), # 压缩成3个特征, 是为了寿面好进行 3D 图像可视化 # 当然也可以压缩到5个特征,选其中的三个来作图 ) # 解码网络 self.decoder = nn.Sequential( nn.Linear(3, 12), nn.Tanh(), nn.Linear(12, 64), nn.Tanh(), nn.Linear(64, 128), nn.Tanh(), nn.Linear(128, 28*28), nn.Sigmoid(), # 激励函数让输出值在 (0, 1) ) def forward(self, x): encoded = self.encoder(x) decoded = self.decoder(encoded) return encoded, decoded # 定义一个编码器对象 autoencoder = AutoEncoder()
-
训练编码器
############################## 训练编码器 optimizer = torch.optim.Adam(autoencoder.parameters(), lr=LR) loss_func = nn.MSELoss() for epoch in range(EPOCH): for step, (x, b_label) in enumerate(train_loader): b_x = x.view(-1, 28*28) # batch x, shape (batch, 28*28) # b_y跟b_x是一样 encoded_x, decoded_x = autoencoder(b_x) loss = loss_func(decoded_x, b_x) # 这里如果写成b_x会更容易裂解 optimizer.zero_grad() # clear gradients for this training step loss.backward() # backpropagation, compute gradients optimizer.step() # apply gradients
-
可视化
########################### 画图的部分 # 取200个数据来作图 view_data = train_data.train_data[:200].view(-1, 28 * 28).type(torch.FloatTensor) / 255. encoded_data, _ = autoencoder(view_data) # 提取压缩的特征值 fig = plt.figure(2) ax = Axes3D(fig) # 3D 图 # x, y, z 的数据值 X = encoded_data.data[:, 0].numpy() Y = encoded_data.data[:, 1].numpy() Z = encoded_data.data[:, 2].numpy() values = train_data.train_labels[:200].numpy() # 标签值 for x, y, z, s in zip(X, Y, Z, values): c = cm.rainbow(int(255 * s / 9)) # 上色 ax.text(x, y, z, s, backgroundcolor=c) # 标位子 ax.set_xlim(X.min(), X.max()) ax.set_ylim(Y.min(), Y.max()) ax.set_zlim(Z.min(), Z.max()) plt.show() # 注意这里进行了plt.show(),程序会停在这里,需要把图片关闭之后下面的程序才能进行,也可以调换一下跟下面svm分类部分替换位置
注意这里进行了plt.show(),程序会停在这里,需要把图片关闭之后下面的程序才能进行,也可以调换一下跟下面svm分类部分替换位置
-
用SVM对编码(压缩)后的数据进行数字识别
################################### 用SVM分类 # 取1000个训练数据来训练svm svm_train = train_data.train_data[:1000].view(-1, 28 * 28).type(torch.FloatTensor) / 255. s_t_x_afterencoder = autoencoder(svm_train)[0].data.numpy() print(s_t_x_afterencoder.shape()) s_t_y = train_data.train_labels[:1000].numpy() # 标签值 print(s_t_y.shape()) # 取1000个训练数据来测试 svm_test = test_data.test_data[:1000].view(-1, 28 * 28).type(torch.FloatTensor) / 255. s_te_x_afterencoder = autoencoder(svm_test)[0].data.numpy() s_te_y = test_data.test_labels[:1000].numpy() # 标签值 c_can = np.logspace(-3, 2, 10) gamma_can = np.logspace(-3, 2, 10) model = svm.SVC(kernel='rbf', decision_function_shape='ovr', random_state=1) clf = GridSearchCV(model, param_grid={'C': c_can, 'gamma': gamma_can}, cv=5, n_jobs=5) clf.fit(s_t_x_afterencoder, s_t_y) print('测试集准确率:\t', clf.score(s_te_x_afterencoder, s_te_y)) # 因为压缩到了三个特征,准确率并不是很高 # 测试集准确率: 0.764
-
参考文献