我们如何使用自动编码器函数实现聚类?
无监督学习是机器学习的一个分支,它没有标签或输出值。我们只需要理解数据中存在的独特模式。让我们看看图3-1中的自动编码器架构。输入特征空间通过隐藏层转换为低维张量表示,并映射回相同的输入空间。正好在中间的那一层保存着自动编码器的值。
AutoEncoder
让我们看看下面的例子。torchvision库包含流行的数据集、模型架构和框架。自动编码器是从数据集中识别潜在特征的过程;它用于分类、预测和聚类。如果我们将输入数据放在输入层,将相同的数据集放在输出层,然后我们添加多层隐藏层,包含许多神经元,然后我们经过多次迭代。 我们在最内层的隐层中得到一组潜在特征。 中间隐藏层中的权值或参数称为自动编码器层。
import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import cm
torch.manual_seed(1)
# 超参数
EPOCH=10
BATCH_SIZE=64
LR=0.005
DOWNLOAD_MNIST=False
N_TEST_IMG=5
我们再次使用MNIST数据集来试验自动编码器的功能。这次我们取10个epoch,一个批大小64,学习率0.005,5张图片进行测试。
train_data = torchvision.datasets.MNIST(root="./mnist",
train=True,
transform=torchvision.transforms.ToTensor(),
# PIL.Image 或者numpy.ndarray 转为
# torch.FloatTensor形状CxHxW并归一化到[0.,1.]
download=DOWNLOAD_MNIST
)
从torchvision库加载并显示图像。
print(train_data.data.size())
print(train_data.targets.size())
plt.imshow(train_data.data[6].numpy(), cmap="gray")
plt.title("%i" % train_data.targets[6])
plt.show()
# torch.Size([60000, 28, 28])
# torch.Size([60000])
# 获得mini-batch数据,形状为(50, 1, 28, 28)
train_loader=Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
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个特征以便可视化
)
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
让我们讨论一下自编码器的架构。输入有784个特征。它的高是28,宽是28。我们将784个神经元从输入层传递到第一隐藏层,其中有128个神经元。
然后我们应用双曲正切函数将信息传递到下一个隐藏层。第二层隐含层包含128个输入神经元,输出为64个神经元。在第三个隐藏层中,我们应用双曲正切函数将信息传递给下一个隐藏层。最内层包含三个神经元,作为三个特征,是编码器层的末端。然后解码器函数将该层扩展回输出层中的784个特征。
autoencoder = AutoEncoder()
print(autoencoder)
optimizer=torch.optim.Adam(autoencoder.parameters(),lr=LR)
loss_func=nn.MSELoss()
# 可视化
view_data=train_data.data[:N_TEST_IMG].view(-1, 28*28).type(torch.FloatTensor)
# AutoEncoder(
# (encoder): Sequential(
# (0): Linear(in_features=784, out_features=128, bias=True)
# (1): Tanh()
# (2): Linear(in_features=128, out_features=64, bias=True)
# (3): Tanh()
# (4): Linear(in_features=64, out_features=12, bias=True)
# (5): Tanh()
# (6): Linear(in_features=12, out_features=3, bias=True)
# )
# (decoder): Sequential(
# (0): Linear(in_features=3, out_features=12, bias=True)
# (1): Tanh()
# (2): Linear(in_features=12, out_features=64, bias=True)
# (3): Tanh()
# (4): Linear(in_features=64, out_features=128, bias=True)
# (5): Tanh()
# (6): Linear(in_features=128, out_features=784, bias=True)
# (7): Sigmoid()
# )
# )
一旦我们设置了结构,接下来就是正常的过程,设置学习率,优化器以及损失函数。为了效果更好,需要训练多个epochs。
我们如何设置迭代次数来微调结果?
从概念上讲,自动编码器的工作原理与聚类模型相同。在无监督学习中,机器从数据中学习模式,并将其推广到新的数据集。学习是通过一组输入特征来进行的。自动编码器功能也用于特征工程。
让我们看看下面的例子。以相同的MNIST数据集为例,目的是了解迭代次数在实现更好的自动编码器层中的作用。我们增加epoch的大小以将错误减少到最小;然而,在实践中,增加epoch有许多挑战,包括内存限制。
for epoch in range(EPOCH):
for step, (x,y) in enumerate(train_loader):
b_x = x.view(-1, 28*28)
b_y = x.view(-1, 28*28)
b_label = y
encoded, decoded=autoencoder(b_x)
loss=loss_func(decoded,b_y) # MSE损失函数
optimizer.zero_grad() # 清除梯度
loss.backward() # 计算梯度
optimizer.step() # 更新梯度
if step % 500 == 0 and epoch in [0,5,EPOCH-1]:
print("Epoch: ", epoch, "| train loss: %.4f" % loss.item())
# 绘制解码器图像
_,decoded_data=autoencoder(view_data)
# 初始化画布
f,a=plt.subplots(2, N_TEST_IMG, figsize=(5,2))
for i in range(N_TEST_IMG):
a[0][i].imshow(np.reshape(view_data.data.numpy()[i], (28,28)),cmap="gray")
a[0][i].set_xticks(())
a[0][i].set_yticks(())
for i in range(N_TEST_IMG):
a[1][i].clear()
a[1][i].imshow(np.reshape(decoded_data.data.numpy()[i],(28,28)),cmap="gray")
a[1][i].set_xticks(())
a[1][i].set_yticks(())
plt.show()
利用编码器函数,我们可以将输入特征表示为一组潜在特征。然而,通过使用解码器函数,我们可以重建图像。从前面的一组图可以清楚地看出,随着epoch的增加,图像识别变得简单。
可视化
我们如何在3D图中可视化MNIST数据?
我们使用自动编码器函数来获得编码的特征,然后在3D平面上表示它。
让我们看看下面的例子。这个教程是关于如何在三维空间中表示由上一个教程中的自动编码器函数生成的最内层隐藏层,因为我们在最内层隐藏层有三个神经元。下面展示的是一个三维神经元。
# 可视化
view_data = train_data.data[:200].view(-1, 28*28).type(torch.FloatTensor)/255.
encoded_data,_=autoencoder(view_data)
fig=plt.figure(2)
ax=Axes3D(fig)
X,Y,Z=encoded_data.data[:,0].numpy(),encoded_data.data[:,1].numpy(),encoded_data.data[:,2].numpy()
values=train_data.targets[: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_xlim(Y.min(),Y.max())
ax.set_xlim(Z.min(),Z.max())
plt.show()