【機器學習】自動編碼器

自動編碼器

簡介:

自動編碼器是一種無監督的神經網絡模型,它可以學習到輸入數據的隱含特徵,這稱爲編碼(coding),同時用學習到的新特徵可以重構出原始輸入數據,稱之爲解碼(decoding)。從直觀上來看,自動編碼器可以用於特徵降維,類似主成分分析PCA,但是其相比PCA其性能更強,這是由於神經網絡模型可以提取更有效的新特徵。除了進行特徵降維,自動編碼器學習到的新特徵可以送入有監督學習模型中,所以自動編碼器可以起到特徵提取器的作用。作爲無監督學習模型,自動編碼器還可以用於生成與訓練樣本不同的新數據,這樣自動編碼器(變分自動編碼器,Variational Autoencoders)就是生成式模型。

自動編碼器原理

自動編碼器的基本結構如圖1所示,包括編碼和解碼兩個過程:

圖1 自動編碼器的編碼後和解碼

自動編碼器是將輸入x進行編碼得到新的特徵Y,並且希望原始的輸入X能夠從新特徵Y重構出來。編碼過程如下:
y=f(Wx+b) y=f(Wx+b)

可以看到,和神經網絡的結構一樣,其編碼就是線性組合之後加上非線性的激活函數。如果沒有非線性的包裝,那麼自動編碼器就和普通的PCA沒有本質區別了。利用新的特徵 Y,可以對輸入X重構,即解碼過程:
x=f(Wx+b) x' = f(W'x+b')

我們希望重構出的x’和x儘可能一致,可以採用最小化負對數似然的損失函數來訓練這個模型:
L=LogP(xx) L=-LogP(x|x')

對於高斯分佈的數據,採用均方誤差就好,而對於伯努利分佈可以採用交叉熵,這個是可以根據似然函數推導出來的。一般情況下,我們會對自動編碼器加上一些限制,常用的是使W=WTW'=W^T,這稱爲綁定權重(tied weights),本文所有的自動編碼器都加上這個限制。有時候,我們還會給自動編碼器加上更多的約束條件,去噪自動編碼器以及稀疏自動編碼器就屬於這種情況,因爲大部分時候單純地重構原始輸入並沒有什麼意義,我們希望自動編碼器在近似重構原始輸入的情況下能夠捕捉到原始輸入更有價值的信息。

堆棧自動編碼器

前面我們講了自動編碼器的原理,不過所展示的自動編碼器只是簡答的含有一層,其實可以採用更深層的架構,這就是堆棧自動編碼器或者深度自動編碼器,本質上就是增加中間特徵層數。這裏我們以MNIST數據爲例來說明自動編碼器,建立兩個隱含層的自動編碼器,如圖2所示:

圖2 堆棧自動編碼器架構對於MNIST來說,其輸入是28*28=784維度的特徵,這裏使用了兩個隱含層其維度分別爲300和150,可以看到是不斷降低特徵的維度了。得到的最終編碼爲150維度的特徵,使用這個特徵進行反向重構得到重建的特徵,我們希望重建特徵和原始特徵儘量相同。由於MNIST是0,1量,可以採用交叉熵作爲損失函數,TF的代碼核心代碼如下:

n_inputs = 28*28
n_hidden1 = 300
n_hidden2 = 150

# 定義輸入佔位符:不需要y
X = tf.placeholder(tf.float32, [None, n_inputs])

# 定義訓練參數
initializer = tf.contrib.layers.variance_scaling_initializer()
W1 = tf.Variable(initializer([n_inputs, n_hidden1]), name="W1")
b1 = tf.Variable(tf.zeros([n_hidden1,]), name="b1")
W2 = tf.Variable(initializer([n_hidden1, n_hidden2]), name="W2")
b2 = tf.Variable(tf.zeros([n_hidden2,]), name="b2")
W3 = tf.transpose(W2, name="W3")
b3 = tf.Variable(tf.zeros([n_hidden1,]), name="b3")
W4 = tf.transpose(W1, name="W4")
b4 = tf.Variable(tf.zeros([n_inputs,]), name="b4")

# 構建模型
h1 = tf.nn.sigmoid(tf.nn.xw_plus_b(X, W1, b1))
h2 = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W2, b2))
h3 = tf.nn.sigmoid(tf.nn.xw_plus_b(h2, W3, b3))
outputs = tf.nn.sigmoid(tf.nn.xw_plus_b(h3, W4, b4))

# 定義loss
loss = -tf.reduce_mean(tf.reduce_sum(X * tf.log(outputs) +
                    (1 - X) * tf.log(1 - outputs), axis=1))
train_op = tf.train.AdamOptimizer(1e-02).minimize(loss)

當訓練這個模型後,你可以將原始MNIST的數字手寫體與重構出的手寫體做個比較,如圖3所示,上面是原始圖片,而下面是重構圖片,基本上沒有差別了。儘管我們將維度從784降低到了150,得到的新特徵還是抓取了原始特徵的核心信息。

圖3 原始圖片(上)與重構圖片對比(下)
有一點,上面的訓練過程是一下子訓練完成的,其實對於堆棧編碼器來說,有時候會採用逐層訓練方式。直白點就是一層一層地訓練:先訓練X->h1的編碼,使h1->X的重構誤差最小化;之後再訓練h1->h2的編碼,使h2->h1的重構誤差最小化。其實現代碼如下:

# 構建模型
h1 = tf.nn.sigmoid(tf.nn.xw_plus_b(X, W1, b1))
h1_recon = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W4, b4))
h2 = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W2, b2))
h2_recon = tf.nn.sigmoid(tf.nn.xw_plus_b(h2, W3, b3))
outputs = tf.nn.sigmoid(tf.nn.xw_plus_b(h2_recon, W4, b4))

learning_rate = 1e-02
# X->h1
with tf.name_scope("layer1"):
    layer1_loss = -tf.reduce_mean(tf.reduce_sum(X * tf.log(h1_recon) +
                            (1-X) * tf.log(1-h1_recon), axis=1))
    layer1_train_op = tf.train.AdamOptimizer(learning_rate).minimize(layer1_loss,
                                            var_list=[W1, b1, b4])

# h1->h2
with tf.name_scope("layer2"):
    layer2_loss = -tf.reduce_mean(tf.reduce_sum(h1 * tf.log(h2_recon) +
                                    (1 - h1) * tf.log(1 - h2_recon), axis=1))
    layer2_train_op = tf.train.AdamOptimizer(learning_rate).minimize(layer2_loss,
                                                                 var_list=[W2, b2, b3])

分層訓練之後,最終得到如圖4所示的對比結果,結果還是不錯的。

圖4 原始圖片(上)與重構圖片對比(下)

小結:

自動編碼器應該是最通俗易懂的無監督神經網絡模型,這裏我們介紹了其基本原理及堆棧自動編碼器。

參考文獻:

  1. Hands-On Machine Learning with Scikit-Learn and TensorFlow, Aurélien Géron, 2017.
  2. Deep Learning Tutorials:Auto Encoders, Denoising Autoencoders.
  3. Learning deep architectures for AI, Foundations and Trends in Machine Learning, Y. Bengio, 2009.
發佈了44 篇原創文章 · 獲贊 98 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章