t-SNE与AE对MNIST可视化

王 茂南 2020年7月2日07:02:43
评论
1 6445字阅读21分29秒
摘要本文介绍关于使用t-SNE进行可视化, 会使用MNIST作为测试数据集. 本文会主要分为两个部分, 首先使用AE对数据进行降维, 降到24维, 接着使用t-SNE对数据进一步降维, 降低到2维, 并进行可视化.

简介

本文介绍关于使用t-SNE进行可视化, 会使用MNIST作为测试数据集. 因为t-SNE的计算速度比较慢, 所以通常的做法是:

  • 首先使用AutoEncoder进行降维, 降低到较低的维度 (例如30维)
  • 接着对上面的数据使用t-SNE进行进一步的降维, 来进行可视化.

在这一部分, 我们会首先使用AutoEncoder将MNIST图片降到24维, 接着使用t-SNE进一步进行降维, 并进行可视化.

于是, 这里实验可以分为两个大部分, 分别是训练AE使用t-SNE进行降维可视化, 下面我们来分开进行介绍.

参考资料

 

训练AutoEncoder

首先我们将MNIST图片使用AE来进行降维, 关于完整的代码还是可以参考Github的链接.

加载数据集

  1. batch_size = 100
  2. # MNIST dataset
  3. train_dataset = torchvision.datasets.MNIST(root='./data',
  4.                                            train=True,
  5.                                            transform=transforms.ToTensor(),
  6.                                            download=True)
  7. test_dataset = torchvision.datasets.MNIST(root='./data',
  8.                                           train=False,
  9.                                           transform=transforms.ToTensor())
  10. # Data loader
  11. train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
  12.                                            batch_size=batch_size,
  13.                                            shuffle=True)
  14. test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
  15.                                           batch_size=batch_size,
  16.                                           shuffle=False)

 

定义网络, 定义AutoEncoder

这里我们使用卷积网络来组成encode, 使用逆卷积来组成decode.

  1. # 定义网络
  2. class DeepAutoEncoder(nn.Module):
  3.     def __init__(self):
  4.         super(DeepAutoEncoder, self).__init__()
  5.         self.encoder = nn.Sequential(
  6.             nn.Conv2d(in_channels=1, out_channels=3, kernel_size=2, stride=1, padding=0, bias=False),
  7.             nn.BatchNorm2d(3),
  8.             nn.LeakyReLU(0.2, inplace=True),
  9.             nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=2, padding=0, bias=False),
  10.             nn.BatchNorm2d(6),
  11.             nn.LeakyReLU(0.2, inplace=True),
  12.             nn.Conv2d(in_channels=6, out_channels=6, kernel_size=3, stride=2, padding=0, bias=False),
  13.             nn.BatchNorm2d(6),
  14.             nn.LeakyReLU(0.2, inplace=True),
  15.             nn.Conv2d(in_channels=6, out_channels=6, kernel_size=4, stride=2, padding=0, bias=False),
  16.             nn.BatchNorm2d(6),
  17.             nn.LeakyReLU(0.2, inplace=True),
  18.         ) # encoder可以将图片大小转换为 1*28*28 -> 6*2*2
  19.         self.decoder = nn.Sequential(
  20.             nn.ConvTranspose2d(in_channels=6, out_channels=6, kernel_size=4, stride=2, bias=False), # 1->4
  21.             nn.BatchNorm2d(6),
  22.             nn.ReLU(True),
  23.             nn.ConvTranspose2d(in_channels=6, out_channels=6, kernel_size=3, stride=2, bias=False), # 1->4
  24.             nn.BatchNorm2d(6),
  25.             nn.ReLU(True),
  26.             nn.ConvTranspose2d(in_channels=6, out_channels=3, kernel_size=3, stride=2, bias=False), # 4 -> 10
  27.             nn.BatchNorm2d(3),
  28.             nn.ReLU(True),
  29.             nn.ConvTranspose2d(in_channels=3, out_channels=1, kernel_size=2, stride=1, bias=False), # 10 -> 22
  30.             nn.BatchNorm2d(1),
  31.             # nn.Tanh()
  32.         ) # decoder可以将图片大小转换为 6*2*2 -> 1*28*28
  33.     def forward(self, x):
  34.         x = self.encoder(x)
  35.         # print(x.shape)
  36.         x = self.decoder(x)
  37.         return x

在上面设计网络的时候, 我们可以简单使用数据集进行测试. 我们可以查看输出的大小.

  1. AE = DeepAutoEncoder().to(device) # 定义分类器
  2. # 进行测试
  3. for i, (datas, labels) in enumerate(train_loader):
  4.     datas = datas.to(device)
  5.     x = AE(datas)
  6.     print(x.shape)
  7.     break
  8. """
  9. torch.Size([100, 1, 28, 28])
  10. """

 

模型的训练

在定义模型完毕之后, 就是模型的训练. 我们这里使用L1 Loss进行训练, 使用Adam优化器.

  1. # ----------
  2. # 初始化网络
  3. # ----------
  4. AE = DeepAutoEncoder().to(device) # 定义分类器
  5. # ------------
  6. # 定义损失函数
  7. # ------------
  8. criterion = nn.L1Loss()
  9. # -----------------------
  10. # 定义损失函数和优化器
  11. # -----------------------
  12. learning_rate = 0.0005
  13. optimizer = torch.optim.Adam(AE.parameters(), lr=learning_rate)
  14. lossList = []
  15. # ---------
  16. # 开始训练
  17. # ---------
  18. num_epochs = 20
  19. total_step = len(train_loader) # 依次epoch的步骤
  20. # 开始训练
  21. for epoch in range(num_epochs):
  22.     totalLoss = 0 # 总的误差
  23.     for i, (images, _) in enumerate(train_loader):
  24.         batch_size = images.size(0)
  25.         images = images.to(device)
  26.         # ---------------------
  27.         # 开始训练discriminator
  28.         # ---------------------
  29.         AE.train()
  30.         # 首先计算真实的图片
  31.         fake_image = AE(images) # 计算重构之后的内容
  32.         loss = criterion(images, fake_image) # 计算loss
  33.         optimizer.zero_grad() # 优化器梯度都要清0
  34.         loss.backward() # 反向传播
  35.         optimizer.step() # 进行优化
  36.         totalLoss = totalLoss + loss.item()
  37.         # ---------
  38.         # 打印结果
  39.         # ---------
  40.         if (i+2) % 280 == 0:
  41.             t = datetime.now() #获取现在的时间
  42.             print('Time {}, Epoch [{}/{}], Step [{}/{}], loss:{:.4f}'.format(t, epoch, num_epochs, i+1, total_step, totalLoss/(i+1)))
  43.     lossList.append(totalLoss/(i+1))

之后绘制loss的变化曲线.

  1. # 绘制loss的变化
  2. fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(13,7))
  3. axes.plot(lossList, 'k--')
t-SNE与AE对MNIST可视化

 

重构图像查看

接着我们使用上面训练的AE来进行图像的重构, 看一下最后output的结果. 首先我们选出16张测试的图片, 并查看原始图片.

  1. # 查看重构的图像
  2. T_images = torch.stack(([train_dataset[i][0] for i in range(16)]))
  3. show(make_grid(T_images, nrow=4, padding=0, normalize=Truerange=None, scale_each=False, pad_value=0))
t-SNE与AE对MNIST可视化

接着我们同样对上面的16张图片, 将他们作为input, 来查看output.

  1. # 查看重构的图片
  2. fake_image = AE(T_images.to(device))
  3. show(make_grid(fake_image.cpu().detach(), nrow=4, padding=0, normalize=Truerange=None, scale_each=False, pad_value=0))
t-SNE与AE对MNIST可视化

 

使用t-SNE降维可视化

在训练好上面的encode之后, 我们使用t-SNE对encode的输出进行可视化.

  1. from sklearn.manifold import TSNE

 

提取数据

因为这里需要接收Numpy的数据, 所以我们需要首先将数据从trainloader中提取出来.

  1. # 进行测试, 生成测试数据
  2. for i, (datas, labels) in enumerate(test_loader):
  3.     batch_size = images.size(0)
  4.     datas = datas.to(device)
  5.     x = AE.encoder(datas)
  6.     x = x.cpu().detach().numpy().reshape(batch_size, -1)
  7.     if i == 0:
  8.         featureList = x # 保存encode之后的特征
  9.         labelsList = labels # 保存对应的label
  10.     else:
  11.         featureList = np.append(featureList, x, axis=0)
  12.         labelsList = np.append(labelsList, labels, axis=0)
  13.     # if i == 3:
  14.     #     break
  15. print(featureList.shape, labelsList.shape)
  16. """
  17. ((10000, 24), (10000,))
  18. """

 

使用t-SNE降维可视化

接着我们对上面encode之后的结果使用t-SNE进行降维

  1. # 降到2维
  2. x_encode = TSNE(n_components=2).fit_transform(featureList) # 接着使用tSNE进行降维
  3. print(x_encode.shape)
  4. """
  5. (10000, 2)
  6. """

最后对上面得到的结果进行可视化即可:

  1. # 进行可视化
  2. cmap = plt.get_cmap('plasma',10) # 数字与颜色的转换
  3. # 获得可视化数据
  4. v_x = x_encode
  5. v_y = labelsList
  6. # 进行可视化
  7. fig = plt.figure(figsize=(14,8))
  8. ax = fig.add_subplot(1,1,1)
  9. classes = [0,1,2,3,4,5,6,7,8,9]
  10. for key in classes:
  11.     ix = np.where(v_y==key)
  12.     ax.scatter(v_x[ix][:,0], v_x[ix][:,1], color=cmap(key), label=key)
  13.     ax.text(np.mean(v_x[ix][:,0]), np.mean(v_x[ix][:,1]), key, fontsize=18, bbox=dict(facecolor='white', alpha=0.5))
  14. ax.legend()
  15. plt.show()

最终的结果如下所示:

t-SNE与AE对MNIST可视化

 

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南
  • 本文由 发表于 2020年7月2日07:02:43
  • 转载请务必保留本文链接:https://mathpretty.com/12451.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: