You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

autoencoder.py 11 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. # -*- coding: utf-8 -*-
  2. # ---
  3. # jupyter:
  4. # jupytext_format_version: '1.2'
  5. # kernelspec:
  6. # display_name: Python 3
  7. # language: python
  8. # name: python3
  9. # language_info:
  10. # codemirror_mode:
  11. # name: ipython
  12. # version: 3
  13. # file_extension: .py
  14. # mimetype: text/x-python
  15. # name: python
  16. # nbconvert_exporter: python
  17. # pygments_lexer: ipython3
  18. # version: 3.5.2
  19. # ---
  20. # # 自动编码器
  21. # 自动编码器最开始是作为一种数据压缩方法,同时还可以在卷积网络中进行逐层预训练,但是随后更多结构复杂的网络,比如 resnet 的出现使得我们能够训练任意深度的网络,自动编码器就不再使用在这个方面,下面我们讲一讲自动编码器的一个新的应用,这是随着生成对抗模型而出现的,就是使用自动编码器生成数据。
  22. #
  23. # 自动编码器的一般结构如下
  24. #
  25. # ![](https://ws1.sinaimg.cn/large/006tNc79ly1fmzr05igw3j30ni06j3z4.jpg)
  26. #
  27. # 由上面的图片,我们能够看到,第一部分是编码器(encoder),第二部分是解码器(decoder),编码器和解码器都可以是任意的模型,通常我们可以使用神经网络作为我们的编码器和解码器,输入的数据经过神经网络降维到一个编码,然后又通过另外一个神经网络解码得到一个与原始数据一模一样的生成数据,通过比较原始数据和生成数据,希望他们尽可能接近,所以最小化他们之间的差异来训练网络中编码器和解码器的参数。
  28. #
  29. # 当训练完成之后,我们如何生成数据呢?非常简单,我们只需要拿出解码器的部分,然后随机传入 code,就可以通过解码器生成各种各样的数据
  30. #
  31. # ![](https://ws3.sinaimg.cn/large/006tNc79ly1fmzrx3d3ygj30nu06ijs2.jpg)
  32. #
  33. # 下面我们使用 mnist 数据集来说明一个如何构建一个简单的自动编码器
  34. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:20.758909Z", "end_time": "2018-01-01T10:09:21.223959Z"}}
  35. import os
  36. import torch
  37. from torch.autograd import Variable
  38. from torch import nn
  39. from torch.utils.data import DataLoader
  40. from torchvision.datasets import MNIST
  41. from torchvision import transforms as tfs
  42. from torchvision.utils import save_image
  43. # -
  44. # 进行数据预处理和迭代器的构建
  45. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:21.341312Z", "end_time": "2018-01-01T10:09:21.368959Z"}}
  46. im_tfs = tfs.Compose([
  47. tfs.ToTensor(),
  48. tfs.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 标准化
  49. ])
  50. train_set = MNIST('./mnist', transform=im_tfs)
  51. train_data = DataLoader(train_set, batch_size=128, shuffle=True)
  52. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:23.489417Z", "end_time": "2018-01-01T10:09:23.526707Z"}}
  53. # 定义网络
  54. class autoencoder(nn.Module):
  55. def __init__(self):
  56. super(autoencoder, self).__init__()
  57. self.encoder = nn.Sequential(
  58. nn.Linear(28*28, 128),
  59. nn.ReLU(True),
  60. nn.Linear(128, 64),
  61. nn.ReLU(True),
  62. nn.Linear(64, 12),
  63. nn.ReLU(True),
  64. nn.Linear(12, 3) # 输出的 code 是 3 维,便于可视化
  65. )
  66. self.decoder = nn.Sequential(
  67. nn.Linear(3, 12),
  68. nn.ReLU(True),
  69. nn.Linear(12, 64),
  70. nn.ReLU(True),
  71. nn.Linear(64, 128),
  72. nn.ReLU(True),
  73. nn.Linear(128, 28*28),
  74. nn.Tanh()
  75. )
  76. def forward(self, x):
  77. encode = self.encoder(x)
  78. decode = self.decoder(encode)
  79. return encode, decode
  80. # -
  81. # 这里定义的编码器和解码器都是 4 层神经网络作为模型,中间使用 relu 激活函数,最后输出的 code 是三维,注意解码器最后我们使用 tanh 作为激活函数,因为输入图片标准化在 -1 ~ 1 之间,所以输出也要在 -1 ~ 1 这个范围内,最后我们可以验证一下
  82. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:26.657447Z", "end_time": "2018-01-01T10:09:26.677033Z"}}
  83. net = autoencoder()
  84. x = Variable(torch.randn(1, 28*28)) # batch size 是 1
  85. code, _ = net(x)
  86. print(code.shape)
  87. # -
  88. # 可以看到最后得到的 code 就是三维的
  89. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:27.726089Z", "end_time": "2018-01-01T10:09:27.739067Z"}}
  90. criterion = nn.MSELoss(size_average=False)
  91. optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
  92. def to_img(x):
  93. '''
  94. 定义一个函数将最后的结果转换回图片
  95. '''
  96. x = 0.5 * (x + 1.)
  97. x = x.clamp(0, 1)
  98. x = x.view(x.shape[0], 1, 28, 28)
  99. return x
  100. # + {"ExecuteTime": {"start_time": "2018-01-01T10:09:28.323220Z", "end_time": "2018-01-01T11:03:15.048160Z"}}
  101. # 开始训练自动编码器
  102. for e in range(100):
  103. for im, _ in train_data:
  104. im = im.view(im.shape[0], -1)
  105. im = Variable(im)
  106. # 前向传播
  107. _, output = net(im)
  108. loss = criterion(output, im) / im.shape[0] # 平均
  109. # 反向传播
  110. optimizer.zero_grad()
  111. loss.backward()
  112. optimizer.step()
  113. if (e+1) % 20 == 0: # 每 20 次,将生成的图片保存一下
  114. print('epoch: {}, Loss: {:.4f}'.format(e + 1, loss.data[0]))
  115. pic = to_img(output.cpu().data)
  116. if not os.path.exists('./simple_autoencoder'):
  117. os.mkdir('./simple_autoencoder')
  118. save_image(pic, './simple_autoencoder/image_{}.png'.format(e + 1))
  119. # -
  120. # 训练完成之后我们可以看看生成的图片效果
  121. #
  122. # ![](https://ws2.sinaimg.cn/large/006tNc79ly1fmzw2c26qtj306q0a2abh.jpg)
  123. #
  124. # 可以看出,图片还是具有较好的清晰度
  125. # + {"ExecuteTime": {"start_time": "2018-01-01T11:03:19.489154Z", "end_time": "2018-01-01T11:03:21.396147Z"}}
  126. import matplotlib.pyplot as plt
  127. from matplotlib import cm
  128. from mpl_toolkits.mplot3d import Axes3D
  129. # %matplotlib inline
  130. # 可视化结果
  131. view_data = Variable((train_set.train_data[:200].type(torch.FloatTensor).view(-1, 28*28) / 255. - 0.5) / 0.5)
  132. encode, _ = net(view_data) # 提取压缩的特征值
  133. fig = plt.figure(2)
  134. ax = Axes3D(fig) # 3D 图
  135. # x, y, z 的数据值
  136. X = encode.data[:, 0].numpy()
  137. Y = encode.data[:, 1].numpy()
  138. Z = encode.data[:, 2].numpy()
  139. values = train_set.train_labels[:200].numpy() # 标签值
  140. for x, y, z, s in zip(X, Y, Z, values):
  141. c = cm.rainbow(int(255*s/9)) # 上色
  142. ax.text(x, y, z, s, backgroundcolor=c) # 标位子
  143. ax.set_xlim(X.min(), X.max())
  144. ax.set_ylim(Y.min(), Y.max())
  145. ax.set_zlim(Z.min(), Z.max())
  146. plt.show()
  147. # -
  148. # 可以看到,不同种类的图片进入自动编码器之后会被编码得不同,而相同类型的图片经过自动编码之后的编码在几何示意图上距离较近,在训练好自动编码器之后,我们可以给一个随机的 code,通过 decoder 生成图片
  149. # + {"ExecuteTime": {"start_time": "2018-01-01T11:06:01.958234Z", "end_time": "2018-01-01T11:06:02.107432Z"}}
  150. code = Variable(torch.FloatTensor([[1.19, -3.36, 2.06]])) # 给一个 code 是 (1.19, -3.36, 2.06)
  151. decode = net.decoder(code)
  152. decode_img = to_img(decode).squeeze()
  153. decode_img = decode_img.data.numpy() * 255
  154. plt.imshow(decode_img.astype('uint8'), cmap='gray') # 生成图片 3
  155. # -
  156. # 这里我们仅仅使用多层神经网络定义了一个自动编码器,当然你会想到,为什么不使用效果更好的卷积神经网络呢?我们当然可以使用卷积神经网络来定义,下面我们就重新定义一个卷积神经网络来进行 autoencoder
  157. # + {"ExecuteTime": {"start_time": "2018-01-01T11:06:06.284342Z", "end_time": "2018-01-01T11:06:06.346907Z"}}
  158. class conv_autoencoder(nn.Module):
  159. def __init__(self):
  160. super(conv_autoencoder, self).__init__()
  161. self.encoder = nn.Sequential(
  162. nn.Conv2d(1, 16, 3, stride=3, padding=1), # (b, 16, 10, 10)
  163. nn.ReLU(True),
  164. nn.MaxPool2d(2, stride=2), # (b, 16, 5, 5)
  165. nn.Conv2d(16, 8, 3, stride=2, padding=1), # (b, 8, 3, 3)
  166. nn.ReLU(True),
  167. nn.MaxPool2d(2, stride=1) # (b, 8, 2, 2)
  168. )
  169. self.decoder = nn.Sequential(
  170. nn.ConvTranspose2d(8, 16, 3, stride=2), # (b, 16, 5, 5)
  171. nn.ReLU(True),
  172. nn.ConvTranspose2d(16, 8, 5, stride=3, padding=1), # (b, 8, 15, 15)
  173. nn.ReLU(True),
  174. nn.ConvTranspose2d(8, 1, 2, stride=2, padding=1), # (b, 1, 28, 28)
  175. nn.Tanh()
  176. )
  177. def forward(self, x):
  178. encode = self.encoder(x)
  179. decode = self.decoder(encode)
  180. return encode, decode
  181. # + {"ExecuteTime": {"start_time": "2018-01-01T11:06:06.944171Z", "end_time": "2018-01-01T11:06:10.043014Z"}}
  182. conv_net = conv_autoencoder()
  183. if torch.cuda.is_available():
  184. conv_net = conv_net.cuda()
  185. optimizer = torch.optim.Adam(conv_net.parameters(), lr=1e-3, weight_decay=1e-5)
  186. # -
  187. # 对于卷积网络中,我们可以对输入进行上采样,那么对于卷积神经网络,我们可以使用转置卷积进行这个操作,这里我们先不展开讨论转置卷积,如果想先了解转置卷积,可以看看[语义分割](https://github.com/SherlockLiao/code-of-learn-deep-learning-with-pytorch/blob/master/chapter9_Computer-Vision/segmentation/fcn.ipynb)的部分,里面有转置卷积的介绍
  188. #
  189. # 在 pytorch 中使用转置卷积就是上面的操作,`torch.nn.ConvTranspose2d()` 就可以了
  190. # + {"ExecuteTime": {"start_time": "2018-01-01T11:06:24.760698Z", "end_time": "2018-01-01T11:15:44.595927Z"}}
  191. # 开始训练自动编码器
  192. for e in range(40):
  193. for im, _ in train_data:
  194. if torch.cuda.is_available():
  195. im = im.cuda()
  196. im = Variable(im)
  197. # 前向传播
  198. _, output = conv_net(im)
  199. loss = criterion(output, im) / im.shape[0] # 平均
  200. # 反向传播
  201. optimizer.zero_grad()
  202. loss.backward()
  203. optimizer.step()
  204. if (e+1) % 20 == 0: # 每 20 次,将生成的图片保存一下
  205. print('epoch: {}, Loss: {:.4f}'.format(e+1, loss.data[0]))
  206. pic = to_img(output.cpu().data)
  207. if not os.path.exists('./conv_autoencoder'):
  208. os.mkdir('./conv_autoencoder')
  209. save_image(pic, './conv_autoencoder/image_{}.png'.format(e+1))
  210. # -
  211. # 为了时间更短,只跑 40 次,如果有条件可以再 gpu 上跑跑
  212. #
  213. # 最后我们看看结果
  214. #
  215. # ![](https://ws1.sinaimg.cn/large/006tNc79ly1fmzww48to3j306q0a20ud.jpg)
  216. # 这里我们展示了简单的自动编码器,也用了多层神经网络和卷积神经网络作为例子,但是自动编码器存在一个问题,我们并不能任意生成我们想要的数据,因为我们并不知道 encode 之后的编码到底是什么样的概率分布,所以有一个改进的版本变分自动编码器,其能够解决这个问题

机器学习越来越多应用到飞行器、机器人等领域,其目的是利用计算机实现类似人类的智能,从而实现装备的智能化与无人化。本课程旨在引导学生掌握机器学习的基本知识、典型方法与技术,通过具体的应用案例激发学生对该学科的兴趣,鼓励学生能够从人工智能的角度来分析、解决飞行器、机器人所面临的问题和挑战。本课程主要内容包括Python编程基础,机器学习模型,无监督学习、监督学习、深度学习基础知识与实现,并学习如何利用机器学习解决实际问题,从而全面提升自我的《综合能力》。