深度学习之⾃编码器AutoEncoder
⼀、什么是⾃编码器(Autoencoder)
⾃动编码器是⼀种数据的压缩算法,其中数据的压缩和解压缩函数是数据相关的、有损的、从样本中⾃动学习的。在⼤部分提到⾃动编码器的场合,压缩和解压缩的函数是通过神经⽹络实现的。
1)⾃动编码器是数据相关的(data-specific 或 data-dependent),这意味着⾃动编码器只能压缩那些与训练数据类似的数据。⽐如,使⽤⼈脸训练出来的⾃动编码器在压缩别的图⽚,⽐如树⽊时性能很差,因为它学习到的特征是与⼈脸相关的。
2)⾃动编码器是有损的,意思是解压缩的输出与原来的输⼊相⽐是退化的,MP3,JPEG等压缩算法也是如此。这与⽆损压缩算法不同。3)⾃动编码器是从数据样本中⾃动学习的,这意味着很容易对指定类的输⼊训练出⼀种特定的编码器,⽽不需要完成任何新⼯作。
搭建⼀个⾃动编码器需要完成下⾯三样⼯作:搭建编码器,搭建解码器,设定⼀个损失函数,⽤以衡量由于压缩⽽损失掉的信息。编码器和解码器⼀般都是参数化的⽅程,并关于损失函数可导,典型情况是使⽤神经⽹络。编码器和解码器的参数可以通过最⼩化损失函数⽽优化,例如SGD。
⾃编码器是⼀个⾃监督的算法,并不是⼀个⽆监督算法。⾃监督学习是监督学习的⼀个实例,其标签产
⽣⾃输⼊数据。要获得⼀个⾃监督的模型,你需要⼀个靠谱的⽬标跟⼀个损失函数,仅仅把⽬标设定为重构输⼊可能不是正确的选项。基本上,要求模型在像素级上精确重构输⼊不是机器学习的兴趣所在,学习到⾼级的抽象特征才是。事实上,当主要任务是分类、定位之类的任务时,那些对这类任务⽽⾔的最好的特征基本上都是重构输⼊时的最差的那种特征。
⽬前⾃编码器的应⽤主要有两个⽅⾯,第⼀是数据去噪,第⼆是为进⾏可视化⽽降维。配合适当的维度和稀疏约束,⾃编码器可以学习到⽐PCA等技术更有意思的数据投影。
对于2D的数据可视化,t-SNE(读作tee-snee)或许是⽬前最好的算法,但通常还是需要原数据的维度相对低⼀些。所以,可视化⾼维数据的⼀个好办法是⾸先使⽤⾃编码器将维度降低到较低的⽔平(如32维),然后再使⽤t-SNE将其投影在2D平⾯上。
⼆、⼏种⾃编码器
⾃编码器(autoencoder)是神经⽹络的⼀种,经过训练后能尝试将输⼊复制到输出。⾃编码器()autoencoder)内部有⼀个隐藏层 h,可以产⽣编码(code)表⽰输⼊。该⽹络可以看作由两部分组成:⼀个由函数 h = f(x) 表⽰的编码器和⼀个⽣成重构的解码器 r = g(h)。如果⼀个⾃编码器只是简单地学会将处处设置为 g(f(x)) = x,那么这个⾃编码器就没什么特别的⽤处。相反,我们不应该将⾃编码器设计成输⼊到输出完全相等。这通常需要向⾃编码器强加⼀些约束,使它只能近似地复制,并只
能复制与训练数据相似的输⼊。这些约束强制模型考虑输⼊数据的哪些部分需要被优先复制,因此它往往能学习到数据的有⽤特性。
1. ⽋完备⾃编码器
从⾃编码器获得有⽤特征的⼀种⽅法是限制 h的维度⽐ x ⼩,这种编码维度⼩于输⼊维度的⾃编码器称为⽋完备(undercomplete)⾃编码器。学习⽋完备的表⽰将强制⾃编码器捕捉训练数据中最显著的特征。
学习过程可以简单地描述为最⼩化⼀个损失函数L(x,g(f(x))),其中 L 是⼀个损失函数,惩罚g(f(x)) 与 x 的差异,如均⽅误差。当解码器是线性的且 L 是均⽅误差,⽋完备的⾃编码器会学习出与 PCA 相同的⽣成⼦空间。这种情况下,⾃编码器在训练来执⾏复制任务的同时学到了训据的主元⼦空间。如果编码器和解码器被赋予过⼤的容量,⾃编码器会执⾏复制任务⽽捕捉不到任何有关数据分布的有⽤信息。
杯水车薪是什么意思2. 正则⾃编码器
正则⾃编码器使⽤的损失函数可以⿎励模型学习其他特性(除了将输⼊复制到输出),⽽不必限制使⽤浅层的编码器和解码器以及⼩的编码维数来限制模型的容量。这些特性包括稀疏表⽰、表⽰的⼩导
数、以及对噪声或输⼊缺失的鲁棒性。即使模型容量⼤到⾜以学习⼀个⽆意义的恒等函数,⾮线性且过完备的正则⾃编码器仍然能够从数据中学到⼀些关于数据分布的有⽤信息。
2.1 稀疏⾃编码器
稀疏⾃编码器简单地在训练时结合编码层的稀疏惩罚Ω(h) 和重构误差:L(x,g(f(x))) + Ω(h),其中 g(h) 是解码器的输出,通常 h 是编码器的输出,即 h = f(x)。稀疏⾃编码器⼀般⽤来学习特征,以便⽤于像分类这样的任务。稀疏正则化的⾃编码器必须反映训练数据集的独特统计特征,⽽不是简单地充当恒等函数。以这种⽅式训练,执⾏附带稀疏惩罚的复制任务可以得到能学习有⽤特征的模型。
2.2 去噪⾃编码器
去噪⾃编码器(denoisingautoencoder, DAE)最⼩化L(x,g(f(˜ x))),其中 ˜ x 是被某种噪声损坏的 x 的副本。因此去噪⾃编码器必须撤消这些损坏,⽽不是简单地复制输⼊。
2.3 收缩⾃编码器
另⼀正则化⾃编码器的策略是使⽤⼀个类似稀疏⾃编码器中的惩罚项Ω,
这迫使模型学习⼀个在 x 变化⼩时⽬标也没有太⼤变化的函数。因为这个惩罚只对训练数据适⽤,它
帕朱丸迫使⾃编码器学习可以反映训练数据分布信息的特征。这样正则化的⾃编码器被称为收缩⾃编码器(contractive autoencoder, CAE)。这种⽅法与去噪⾃编码器、流形学习和概率模型存在⼀定理论联系。落地价是什么意思
3. 表⽰能⼒、层的⼤⼩和深度
万能近似定理保证⾄少有⼀层隐藏层且隐藏单元⾜够多的前馈神经⽹络能以任意精度近似任意函数(在很⼤范围⾥),这是⾮平凡深度(⾄少有⼀层隐藏层)的⼀个主要优点。这意味着具有单隐藏层的⾃编码器在数据域内能表⽰任意近似数据的恒等函数。但是,从输⼊到编码的映射是浅层的。这意味这我们不能任意添加约束,⽐如约束编码稀疏。深度⾃编码器(编码器⾄少包含⼀层额外隐藏层)在给定⾜够多的隐藏单元的情况下,能以任意精度近似任何从输⼊到编码的映射。
深度可以指数地降低表⽰某些函数的计算成本。深度也能指数地减少学习⼀些函数所需的训练数据量。实验中,深度⾃编码器能⽐相应的浅层或线性⾃编码器产⽣更好的压缩效率。
训练深度⾃编码器的普遍策略是训练⼀堆浅层的⾃编码器来贪⼼地预训练相应的深度架构。所以即使最终⽬标是训练深度⾃编码器,我们也经常会遇到浅层⾃编码器。
4. 去噪⾃编码器
去噪⾃编码器(denoisingautoencoder, DAE)是⼀类接受损坏数据作为输⼊,并训练来预测原始未被损坏数据作为输出的⾃编码器。DAE 的训练准则(条件⾼斯p(x | h))能让⾃编码器学到能估计数据分布得分的向量场 (g(f(x)) − x) ,这是 DAE 的⼀个重要特性。
5. 收缩⾃编码器
收缩⾃编码器 (Rifai et al.,2011a,b) 在编码 h = f(x) 的基础上添加了显式的正则项,⿎励 f 的导数尽可能⼩:
惩罚项Ω(h) 为平⽅ Frobenius范数(元素平⽅之和),作⽤于与编码器的函数相关偏导数的 Jacobian 矩阵。
收缩(contractive)源于 CAE 弯曲空间的⽅式。具体来说,由于 CAE 训练为抵抗输⼊扰动,⿎励将输⼊点邻域映射到输出点处更⼩的邻域。我们能认为这是将输⼊的邻域收缩到更⼩的输出邻域。
三、使⽤Keras建⽴简单的⾃编码器
1. 单隐含层⾃编码器
建⽴⼀个全连接的编码器和解码器。也可以单独使⽤编码器和解码器,在此使⽤Keras的函数式模型A
PI即Model可以灵活地构建⾃编码器。50个epoch后,看起来我们的⾃编码器优化的不错了,损失val_loss: 0.1037。
2. 稀疏⾃编码器、深层⾃编码器
男羊配什么属相最好为码字加上稀疏性约束。如果我们对隐层单元施加稀疏性约束的话,会得到更为紧凑的表达,只有⼀⼩部分神经元会被激活。在Keras 中,我们可以通过添加⼀个activity_regularizer 达到对某层激活值进⾏约束的⽬的。
encoded = Den(encoding_dim, activation='relu',activity_regularizer=regularizers.activity_l1(10e-5))(input_img)
把多个⾃编码器叠起来即加深⾃编码器的深度,50个epoch 后,损失val_loss:0.0926,⽐1个隐含层的⾃编码器要好⼀些。01. from keras.layers import Input, Den 02. from dels import Model 03. from keras.datats import mnist 04. import numpy as np 05. import matplotlib.pyplot as plt 06. 07. (x_train, _), (x_test, _) = mnist.load_data() 08. 09. x_train = x_train.astype('float32') / 255. 10. x_test = x_test.astype('float32') / 255. 11. x_train = shape((len(x_train), np.prod(x_train.shape[1:]))) 12. x_test = shape((len(x_test), np.prod(x_test.shape[1:]))) 13. print (x_train.shape) 14. print (x_test.shape) 15. 16. encoding_dim = 32 17. input_img = Input(sha
pe=(784,)) 18. 19. encoded = Den(encoding_dim, activation='relu')(input_img) 20. decoded = Den(784, activation='sigmoid')(encoded) 21. 22. autoencoder = Model(inputs=input_img, outputs=decoded) 23. encoder = Model(inputs=input_img, outputs=encoded) 24. 25. encoded_input = Input(shape=(encoding_dim,)) 26. decoder_layer = autoencoder.layers[-1] 27. 28. decoder = Model(inputs=encoded_input, outputs=decoder_layer(encoded_input)) 29. 30. pile(optimizer='adadelta', loss='binary_crosntropy') 31. 32. autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, 33. shuffle=True, validation_data=(x_test, x_test)) 34. 35. encoded_imgs = encoder.predict(x_test) 36. decoded_imgs = decoder.predict(encoded_imgs) 37. 38. n = 10 # how many digits we will display 39. plt.figure(figsize=(20, 4)) 40. for i in range(n): 41. ax = plt.subplot(2, n, i + 1) 42. plt.imshow(x_test[i].reshape(28, 28)) 43. ay() 44. ax.get_xaxis().t_visible(Fal) 45. ax.get_yaxis().t_visible(Fal) 46. 47. ax = plt.subplot(2, n, i + 1 + n) 48. plt.imshow(decoded_imgs[i].reshape(28, 28)) 49. ay() 50. ax.get_xaxis().t_visible(Fal) 51. ax.get_yaxis().t_visible(Fal) 52.
plt.show()
慈悲串经3. 卷积⾃编码器:⽤卷积层构建⾃编码器
当输⼊是图像时,使⽤卷积神经⽹络是更好的。卷积⾃编码器的编码器部分由卷积层和MaxPooling 层构成,MaxPooling 负责空域下采样。⽽解码器由卷积层和上采样层构成。50个epoch 后,损失val_loss: 0.1018。
4. 使⽤⾃动编码器进⾏图像去噪
我们把训练样本⽤噪声污染,然后使解码器解码出⼲净的照⽚,以获得去噪⾃动编码器。⾸先我们把原图⽚加⼊⾼斯噪声,然后把像素值clip 到0~1。01. input_img = Input(shape=(784,)) 02. encoded = Den(128, activation='relu')(input_img) 03. encoded = Den(64, activation='relu')(encoded) 04. decoded_input = Den(32, activation='relu')(encoded) 05. 06. decoded = Den(64, activation='relu')(decoded_input) 07. decoded = Den(128, activation='relu')(decoded) 08. decoded = Den(784, activation='sigmoid')(encoded) 09. 10. autoencoder = Model(inputs=input_img, outputs=decoded) 11. encoder = Model(inputs=input_img, outputs=decoded_input) 12. 13. pile(optimizer='adadelta', loss='binary_crosntropy') 14. 15. autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, 16. shuffle=True, validation_data=(x_test, x_test)) 17. 18. encoded_imgs = encoder.predict(x_test) 19.
平淡如水decoded_imgs = autoencoder.predict(x_test) [python]
01. input_img = Input(shape=(28, 28, 1)) 02. 03. x = Convolution2D(16, (3, 3), activation='relu', padding='same')(input_img) 04. x = MaxPooling2D((2, 2), padding='same')(x) 05. x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) 06. x = MaxPooling2D((2, 2), padding='same')(x) 07. x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) 08. encoded = MaxPooling2D((2, 2), padding='same')(x) 09. 10. x = Convolution2D(8, (3, 3), activation='relu', padding='same')(encoded) 11. x = UpSampling2D((2, 2))(x) 12. x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) 13. x = UpSampling2D((2, 2))(x) 14. x = Convolution2D(16, (3, 3), activation='relu')(x) 15. x = UpSampling2D((2, 2))(x) 16. decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x) 17. 18. autoencoder = Model(inputs=input_img, outputs=decoded) 19. pile(optimizer='adadelta', loss='binary_crosntropy') 20. 21. # 打开⼀个终端并启动TensorBoard ,终端中输⼊ tensorboard --logdir=/autoencoder 22. autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, 23. shuffle=True, validation_data=(x_test, x_test), 24. callbacks=[TensorBoard(log_dir='autoencoder')]) 25. 26.
decoded_imgs = autoencoder.predict(x_test)
01. from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D
02. dels import Model
03. from keras.datats import mnist
04. import numpy as np
05. import matplotlib.pyplot as plt
06. from keras.callbacks import TensorBoard
07.
08. (x_train, _), (x_test, _) = mnist.load_data()
09. x_train = x_train.astype('float32') / 255.
10. x_test = x_test.astype('float32') / 255.
11. x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
12. x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))
13. noi_factor = 0.5
14. x_train_noisy = x_train + noi_factor * al(loc=0.0, scale=1.0, size=x_train.shape)
15. x_test_noisy = x_test + noi_factor * al(loc=0.0, scale=1.0, size=x_test.shape)
16. x_train_noisy = np.clip(x_train_noisy, 0., 1.)
17. x_test_noisy = np.clip(x_test_noisy, 0., 1.)
18. print(x_train.shape)大头鹦鹉
19. print(x_test.shape)百日宴邀请短信
20.
21. input_img = Input(shape=(28, 28, 1))
22.
23. x = Convolution2D(32, (3, 3), activation='relu', padding='same')(input_img)
24. x = MaxPooling2D((2, 2), padding='same')(x)
25. x = Convolution2D(32, (3, 3), activation='relu', padding='same')(x)
26. encoded = MaxPooling2D((2, 2), padding='same')(x)
27.
28. x = Convolution2D(32, (3, 3), activation='relu', padding='same')(encoded)
29. x = UpSampling2D((2, 2))(x)
30. x = Convolution2D(32, (3, 3), activation='relu', padding='same')(x)
31. x = UpSampling2D((2, 2))(x)
32. decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x)
33.
34. autoencoder = Model(inputs=input_img, outputs=decoded)
35. pile(optimizer='adadelta', loss='binary_crosntropy')
36.
37. # 打开⼀个终端并启动TensorBoard,终端中输⼊ tensorboard --logdir=/autoencoder
38. autoencoder.fit(x_train_noisy, x_train, epochs=10, batch_size=256,
39. shuffle=True, validation_data=(x_test_noisy, x_test),
40. callbacks=[TensorBoard(log_dir='autoencoder', write_graph=Fal)])
41.
42. decoded_imgs = autoencoder.predict(x_test_noisy)
43.
44. n = 10
45. plt.figure(figsize=(30, 6))
46. for i in range(n):
47. ax = plt.subplot(3, n, i + 1)
48. plt.imshow(x_test[i].reshape(28, 28))
49. ay()
50. ax.get_xaxis().t_visible(Fal)
51. ax.get_yaxis().t_visible(Fal)
52.
53. ax = plt.subplot(3, n, i + 1 + n)
54. plt.imshow(x_test_noisy[i].reshape(28, 28))
55. ay()
56. ax.get_xaxis().t_visible(Fal)
57. ax.get_yaxis().t_visible(Fal)
58.
59. ax = plt.subplot(3, n, i + 1 + 2*n)
60. plt.imshow(decoded_imgs[i].reshape(28, 28))
61. ay()
62. ax.get_xaxis().t_visible(Fal)
63. ax.get_yaxis().t_visible(Fal)
64. plt.show()