天天看點

第十五章——自編碼器(Autoencoders)

自編碼器是一種能夠通過無監督學習,學到輸入資料高效表示的人工神經網絡。輸入資料的這一高效表示稱為編碼(codings),其次元一般遠小于輸入資料,使得自編碼器可用于降維(檢視第八章)。更重要的是,自編碼器可作為強大的特征檢測器(feature detectors),應用于深度神經網絡的預訓練(檢視第十一章)。此外,自編碼器還可以随機生成與訓練資料類似的資料,這被稱作生成模型(generative model)。比如,可以用人臉圖檔訓練一個自編碼器,它可以生成新的圖檔。

自編碼器通過簡單地學習将輸入複制到輸出來工作。這一任務(就是輸入訓練資料, 再輸出訓練資料的任務)聽起來似乎微不足道,但通過不同方式對神經網絡增加限制,可以使這一任務變得極其困難。比如,可以限制内部表示的尺寸(這就實作降維了),或者對訓練資料增加噪聲并訓練自編碼器使其能恢複原有。這些限制條件防止自編碼器機械地将輸入複制到輸出,并強制它學習資料的高效表示。簡而言之,編碼(就是輸入資料的高效表示)是自編碼器在一些限制條件下學習恒等函數(identity function)的副産品。(這句話有點抽象,不過看完15.1就明白了)

15.1 高效的資料表示

下面有兩組數字,哪組更容易記憶呢?

  • 40, 27, 25, 36, 81, 57, 10, 73, 19, 68
  • 50, 25, 76, 38, 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20

乍一看可能覺得第一行數字更容易記憶,畢竟更短。但仔細觀察就會發現,第二組數字是有規律的:偶數後面是其二分之一,奇數後面是其三倍加一(這就是著名的hailstone sequence)。如果識别出了這一模式,第二組資料隻需要記住這兩個規則、第一個數字、以及序列長度。如果你的記憶能力超強,可以記住很長的随機數字序列,那你可能就不會去關心一組數字是否存在規律了。是以我們要對自編碼器增加限制來強制它去探索資料中的模式。

記憶(memory)、感覺(perception)、和模式比對(pattern matching)的關系在1970s早期就被William Chase和Herbert Simon研究過。他們發現國際象棋大師觀察棋盤5秒,就能記住所有棋子的位置,而常人是無法辦到的。但棋子的擺放必須是實戰中的棋局(也就是棋子存在規則,就像第二組數字),棋子随機擺放可不行(就像第一組數字)。象棋大師并不是記憶力優于我們,而是經驗豐富,很擅于識别象棋模式,進而高效地記憶棋局。

和棋手的記憶模式類似,一個自編碼器接收輸入,将其轉換成高效的内部表示,然後再輸出輸入資料的類似物。自編碼器通常包括兩部分:encoder(也稱為識别網絡)将輸入轉換成内部表示,decoder(也稱為生成網絡)将内部表示轉換成輸出。(如圖15-1)

圖15-1 象棋大師的記憶模式(左)和一個簡單的自編碼器

正如上圖所示,自編碼器的結構和多層感覺機(檢視第十章)類似,除了輸入神經元和輸出神經元的個數相等。在上圖的例子中,自編碼器隻有一個包含兩個神經元的隐層(encoder),以及包含3個神經元的輸出層(decoder)。輸出是在設法重建輸入,損失函數是重建損失(reconstruction loss)。

由于内部表示(也就是隐層的輸出)的次元小于輸入資料(用2D取代了原來的3D), 這稱為不完備自編碼器(undercomplete autoencoder)。

undercomplete應該是個數學機率,不用深究了,畢竟在Wikipedia上面的解釋隻有一句話:Describing a frame (in linear algebra) having a set of functions less than a basis。

15.2 不完備線性自編碼器實作PCA(Performing PCA with an Undercomplete Linear Autoencoder)

如果自編碼器使用線性激活函數并且損失函數是均方差(Mean Squared Error,MSE),那它就可以用來實作主成分分析(檢視第八章)。

下面的代碼實作了一個簡單的線性自編碼器,将3D資料投影為2D:

import tensorflow as tf
from tensorflow.contrib.layers import fully_connected

n_inputs = 3 # 3D inputs
n_hidden = 2 # 2D codings
n_outputs = n_inputs

learning_rate = 0.01

X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden = fully_connected(X, n_hidden, activation_fn=None)
outputs = fully_connected(hidden, n_outputs, activation_fn=None)

reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE

optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(reconstruction_loss)

init = tf.global_variables_initializer()      

然後載入資料集,在訓練集上訓練模型,并對測試集進行編碼(也就是投影為2D):

X_train, X_test = [...] # load the dataset

n_iterations = 1000
codings = hidden # the output of the hidden layer provides the codings

with tf.Session() as sess:
    init.run()
    for iteration in range(n_iterations):
        training_op.run(feed_dict={X: X_train}) # no labels (unsupervised)
    codings_val = codings.eval(feed_dict={X: X_test})
      

15.3 棧式自編碼器(Stacked Autoencoders)

和其他的神經網絡一樣,自編碼器可以有多個隐層,這被稱作棧式自編碼器(或者深度自編碼器)。增加隐層可以學到更複雜的編碼,但千萬不能使自編碼器過于強大。想象一下,一個encoder過于強大,它僅僅是學習将輸入映射為任意數(然後decoder學習其逆映射)。很明顯這一自編碼器可以很好的重建資料,但它并沒有在這一過程中學到有用的資料表示。(而且也不能推廣到新的執行個體)

棧式自編碼器的架構一般是關于中間隐層對稱的,如圖15-3所示。

圖15-3 棧式自編碼器

15.3.1 TensorFlow 實作

參考:本書代碼

15.3.2 捆綁權重

如果一個自編碼器的層次是嚴格軸對稱的(如圖15-3),一個常用的技術是将decoder層的權重捆綁到encoder層。這使得模型參數減半,加快了訓練速度并降低了過拟合風險。具體的,假設自編碼器一共有$N$層(不算輸入層),$W_L$表示第$L$層的權重(例如,第一層是第一個隐層,第$frac{2}{N}$層是編碼層,第$N$層是輸出層),那麼decoder層的權重可以表示為$W_{N-L+1} = W_L^T , L = 1,2,cdots ,frac{N}{2}$。

不過偏置項不會捆綁。

15.3.3 一次訓練一個自編碼器

與之前訓練整個棧式自編碼器不同,可以訓練多個淺層的自編碼器,然後再将它們合并為一體,這樣要快得多。如圖15-4

圖15-4 一次訓練一個淺層自編碼器

首先,第一個自編碼器學習去重建輸入。然後,第二個自編碼器學習去重建第一個自編碼器隐層的輸出。最後,這兩個自編碼器被整合到一起,如圖15-4。可以使用這種方式,建立一個很深的棧式自編碼器。

另一個實作方法首先建立一個包含完整棧式編碼器的圖,然後再每一個訓練時期增加額外的操作,如圖15-5:

圖15-5 

其中,

  • 中間的一列是完整的棧式編碼器,這部分在訓練完成之後可以使用。
  • 左側一列是最先需要訓練的,它跳過第二和第三個隐層,直接建立一個輸出層。這個輸出層與棧式自編碼器的輸出層共享同樣的權重和偏置。
  • 随後是右側一列的訓練。它使得第三個隐層的輸出與第一個隐層的輸出盡可能的接近。

15.4 使用棧式自編碼器進行無監督預訓練

正如第十一章所讨論的,如果我們要處理一個複雜的有監督學習問題又沒有足夠的标注資料,一個解決方案是找到一個解決類似任務的訓練好的模型,複用低層。類似的,如果有一個很大的資料集但絕大部分是未标注資料,可以使用所有的資料先訓練一個棧式自編碼器,然後複用低層來完成真正的任務。如圖15-8所示:

圖15-8 使用自編碼器進行無監督預訓練

15.5 去噪自編碼器

另一種強制自編碼器學習有用特征的方式是最輸入增加噪聲,通過訓練之後得到無噪聲的輸出。這防止了自編碼器簡單的将輸入複制到輸出,進而提取出資料中有用的模式。如圖15-9左側所示。

使用自編碼器去燥的思想在1980s提出(比如,在1987年Yann LeCun的碩士論文中有所提及)。在一篇2008年的論文中,Pascal Vincent等人表明自編碼器可用于特征提取。在一篇2010年的論文中,Vincent等人提出棧式去燥自編碼器(stacked denoising autoencoders)。

噪聲可以是添加到輸入的純高斯噪聲,也可以是随機丢棄輸入層的某個特征,類似于dropout。如圖15-9右側所示。

圖15-9 圖中自編碼器,通過高斯噪聲(左)或者Dropout(右)