簡單易懂的自動編碼器

歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)

引言

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

本文將會講述自動編碼器的基本原理以及常用的自動編碼器模型:堆棧自動編碼器(Stacked Autoencoder)。後序的文章會講解自動編碼器其他模型:去噪自動編碼器(Denoising Autoencoder),稀疏自動編碼器(Sparse Autoencoder)以及變分自動編碼器。所有的模型都會使用Tensorflow進行編程實現。

自動編碼器原理

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

圖1 自動編碼器的編碼與解碼

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

y=f(Wx+b)

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

x=f(Wx+b)

我們希望重構出的 x 和儘可能一致,可以採用最小化負對數似然的損失函數來訓練這個模型:

L=-logP(x|x)

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

堆棧自動編碼器

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

圖2 堆棧自動編碼器架構

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

n_inputs = 28*28nn_hidden1 = 300nn_hidden2 = 150nn# 定義輸入佔位符:不需要ynX = tf.placeholder(tf.float32, [None, n_inputs])nn# 定義訓練參數ninitializer = tf.contrib.layers.variance_scaling_initializer()nW1 = tf.Variable(initializer([n_inputs, n_hidden1]), name="W1")nb1 = tf.Variable(tf.zeros([n_hidden1,]), name="b1")nW2 = tf.Variable(initializer([n_hidden1, n_hidden2]), name="W2")nb2 = tf.Variable(tf.zeros([n_hidden2,]), name="b2")nW3 = tf.transpose(W2, name="W3")nb3 = tf.Variable(tf.zeros([n_hidden1,]), name="b3")nW4 = tf.transpose(W1, name="W4")nb4 = tf.Variable(tf.zeros([n_inputs,]), name="b4")nn# 構建模型nh1 = tf.nn.sigmoid(tf.nn.xw_plus_b(X, W1, b1))nh2 = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W2, b2))nh3 = tf.nn.sigmoid(tf.nn.xw_plus_b(h2, W3, b3))noutputs = tf.nn.sigmoid(tf.nn.xw_plus_b(h3, W4, b4))nn# 定義lossnloss = -tf.reduce_mean(tf.reduce_sum(X * tf.log(outputs) +n (1 - X) * tf.log(1 - outputs), axis=1))ntrain_op = tf.train.AdamOptimizer(1e-02).minimize(loss)n

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

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

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

# 構建模型nh1 = tf.nn.sigmoid(tf.nn.xw_plus_b(X, W1, b1))nh1_recon = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W4, b4))nh2 = tf.nn.sigmoid(tf.nn.xw_plus_b(h1, W2, b2))nh2_recon = tf.nn.sigmoid(tf.nn.xw_plus_b(h2, W3, b3))noutputs = tf.nn.sigmoid(tf.nn.xw_plus_b(h2_recon, W4, b4))nnlearning_rate = 1e-02n# X->h1nwith tf.name_scope("layer1"):n layer1_loss = -tf.reduce_mean(tf.reduce_sum(X * tf.log(h1_recon) +n (1-X) * tf.log(1-h1_recon), axis=1))n layer1_train_op = tf.train.AdamOptimizer(learning_rate).minimize(layer1_loss,n var_list=[W1, b1, b4])nn# h1->h2nwith tf.name_scope("layer2"):n layer2_loss = -tf.reduce_mean(tf.reduce_sum(h1 * tf.log(h2_recon) +n (1 - h1) * tf.log(1 - h2_recon), axis=1))n layer2_train_op = tf.train.AdamOptimizer(learning_rate).minimize(layer2_loss,n var_list=[W2, b2, b3])n

分層訓練之後,最終得到如圖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.

歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)


推薦閱讀:

人工智慧vs人類智能小傳——再議阿爾法狗
TensorFlow實現神經網路入門篇
Deep Residual Network 深度殘差網路
深度學習(Deep Learning)基礎概念1:神經網路基礎介紹及一層神經網路的python實現
SPPNet-引入空間金字塔池化改進RCNN

TAG:神经网络 | 深度学习DeepLearning | 计算机视觉 |