自編碼器,做主成分提取,了解一下

  • 2019 年 10 月 7 日
  • 筆記

今天我們一起學一下深度網絡中的自編碼器.

自編碼器是在無監督(訓練集未標註)的情況下,能夠學習有效表示數據(稱為編碼)的一種深度人工網絡。這些編碼一般跟輸入數據比起來有更低的維度,這使得自編碼器在數據降維方面比較有用。更重要的是,自編碼器可以作為強大的特徵檢測器,它可以在深度網絡中用於無監督的預訓練。最後,它可以隨機產生和訓練數據相似的新數據,這叫做生成模型。例如,我們可以訓練一個人臉圖像上的自編碼器,那麼它能夠產生新的人臉圖像。

咋一看,自編碼器的工作就是學會簡單的把輸入值copy到輸出,然而,訓練網絡的時候,可以從各個角度進行約束網絡,使它不能夠做簡單的複製,進而能夠訓練出有效的網絡。比方說,我們可以限制內部呈現的大小,或者在輸入圖像上加些噪聲來訓練網絡恢復原始輸入。這些約束可以防止網絡玩障眼法,或者說防止網絡去簡單的複製輸入到輸出。這就強制它去學習表示數據的有效方法。簡而言之,編碼是自編碼器在一些約束下嘗試學習恆等方程而得到的副產品。 接下來,我們將更進一步的學習自編碼器的工作原理,不管是在降維,特徵提取,無監督預訓練,還是生成模型,以及可以實施哪些約束,以及如何在tensorflow中實現。

有效的數據表示

我們來看兩組數字,下面哪一組數更好記呢?

• 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  

咋一看,感覺應該是第一組數據更好記一些,因為第一組短一點嘛。然而仔細看第二組數據就會發現,這條數據是有規律的,即:如果是偶數的話,那麼它後面跟的是該偶數的一半,如果是奇數的話,後面跟的是該數的3倍加1(其實該序列為希爾頓序列)。一旦我們發現了這個規律,那麼第二組數據就更為好記了。

關於記憶,感知和模式匹配的關係在 20 世紀 70 年代早期由 William Chase 和 Herbert Simon 研究。他們注意到,專業的棋手能夠通過觀察棋盤5秒鐘就記住所有棋子的位置,這個任務,大多數人都會認為不可思議。然而也只是在正常比賽中才會是這種情況,而不是隨機放置位置。這也就說明了,專業棋手並不是記憶力比我們好很多,而是由於豐富的經驗,他們更看到棋子背後的模式,也就是說模式能夠幫他們更為高效的存儲信息。

就像上面說的專業棋手一樣,自編碼器將輸入轉換成一種高效的內部模式呈現,並且輸出一些跟輸入很似接近的東西。一個自編碼器通常由兩部分組成:編碼(識別網絡)用來將輸入轉換成一個內部表示,解碼(生成網絡)用來將內部表示轉換為輸出,如下圖:

正如我們上圖看到的一樣,除了自編碼器的輸入輸出必須一致以外,一個自編碼器基本上跟多層感知機(MLP)有着相同的架構。在上面的例子中,僅僅有一個包含兩個神經元的隱藏層(編碼),和一個由三個神經元組成的輸出層(解碼)。由於自編碼器在儘力的恢復輸入數據,所以輸出通常也被稱為重構。損失函數為重構損失,往往會在跟輸入不一致的時候懲罰模型。

由於這個內部呈現跟輸入數據比起來維度更低,所以自編碼器被認為是不完整的,一個不完整的自編碼器不能簡單的複製輸入到編碼,也就是說,它必須找到一個模式來輸出輸入的近似。這就強制它去學習輸入數據的一些重要的特徵,而拋棄不重要的特徵。

現在,我們一起看一下,如何用不完整的自編碼器來實現降維的。

用不完整的線性編碼器實現PCA

如果自編碼器僅僅使用線性激活函數,並且用MSE作為損失函數的話,則它將最終收斂為我們之前學過的PCA(主成分分析)模型(見下鏈接)。 機器學習三人行(系列十)—-機器學習降壓神器(附代碼)

我們一起看下如何創建一個簡單的線性自編碼器來實現PCA,將一個三維數據映射到兩維:

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()  

這段代碼跟我們之前學的MLP基本一致,需要注意的是:

  1. 神經元的輸入數量和輸出數量一致。
  2. 為了實現PCA,這裡設置activation_fn=None,並且損失函數為MSE。

現在我們將加載數據,在訓練集上訓練模型,並且用模型去編碼測試數據(投影到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})  

下圖顯示了原始數據(左側),和自編碼器的隱藏層的輸出(編碼層,右圖),正如我們看到的,自編碼器找到了將數據投影到數據上的最佳二維平面,保留了數據的儘可能多的差異(就像 PCA 一樣)。