收縮自編碼器(CAE)
自編碼器是一種很好的降維技術,它可以學習到資料中非常有用的資訊。而收縮自編碼器作為正則自編碼器的一種,其非線性降維效果非常好,並且它的過程可以通過流形知識來解釋。
基礎知識
1、自編碼器
自編碼器是一種降維的技術,由編碼器和解碼器兩部分組成,架構圖如下。編碼器 \(f\) 用來輸出降維後的表示 \(h\),而解碼器 \(g\) 則通過最小化代價函式從編碼器的輸出 \(h\) 來重構原始的輸入 \(x\),輸出 \(r\)。
編碼器 \(f\) 和解碼器 \(g\) 的內部結構是一個仿射函式(線性組合)再加一個(線性或非線性)啟用函式,形式如下:
\[ h=f\left( x\right) =s _{f}\left( Wx+b _{h}\right),\]
\[ r=g\left( h\right) =s _{g}\left( W'h+b _{r}\right). \]
其中,\(s\) 是啟用函式,\(W\) 是權重矩陣,\(b\) 是偏置向量。為了防止自編碼器學習到整體收縮再放大的無用對映,一般 \(W'=W ^{T}\)。
詳細的自編碼器介紹可以參考 Introduction to autoencoders.
2、流形切平面
流形的一個重要特徵是切平面的集合。\(d\) 維流形上的一點 \(x\),切平面由能張成流形上允許變動的區域性方向的 \(d\) 維基向量給出。這是《Deep Learning》上的定義,其實切平面就是切線、切面拓展到高維的情況,類似於超平面的概念。
3、Keras
Keras 是一個用 Python 編寫的高階神經網路 API,它能夠以 TensorFlow、CNTK 或者 Theano 作為後端執行。Keras 的開發重點是支援快速的實驗。能夠以最小的時延把你的想法轉換為實驗結果,是做好研究的關鍵。
使用 Keras 前一定要安裝 TensorFlow、CNTK 和 Theano 三個框架中的任一一個,並且要注意每個框架適用的 Python 版本,提前配置好相應的環境。
收縮自編碼器(CAE)
1、定義
為了提高對訓練集資料點周圍小擾動的魯棒性,收縮自編碼器在基礎自編碼器上添加了正則項,其形式為編碼器的對映 \(f\) 關於輸入 \(x\) 的 \(Jacobian\) 矩陣的 \(Frobenius\) 範數(具體形式如下),目的是迫使其學習在訓練樣本上有更強收縮作用的對映。
\[ \left\| J _{f}\left( x\right) \right\| ^{2} _{F}=\sum _{ij}\left( \dfrac {\partial h _{j}\left( x\right) }{\partial x _{i}}\right) ^{2}. \]
假設訓練集為 \(D _{n}\),我們通過最小化重構誤差以及對梯度的懲罰來學習自編碼器的引數,完整的代價函式如下:
\[ J _{CAE}\left( \theta \right) =\sum _{x\in D _{n}}\left( L(x,g(f(x)))+\lambda \left\| J _{f}\left( x\right) \right\| ^{2} _{F}\right). \]
其中 \(L\) 是重構誤差,形式為平方誤差(線性自編碼器)或者交叉熵損失(非線性誤差),而\(\lambda \) 則是控制正則化強度的超引數。
2、解釋
從代價函式來看,收縮自編碼器通過兩種相反的推動力學習有用資訊--重構誤差和收縮懲罰(正則項)。收縮懲罰迫使自編碼器學習到的所有對映對於輸入的梯度都很小,即把輸入都降維到一個很小的區域(點附近),而重構誤差迫使自編碼器學習一個恆等對映,保留完整的資訊。兩種推動力衝擊下,使得大部分對映對於輸入的梯度都很小,而只有少部分的大梯度。這樣在輸入具有小擾動時,小梯度會減小這些擾動,達到增加自編碼器對輸入附近小擾動的魯棒性。
3、學習流形
學習流形的介紹可以看我以前的部落格 “學習流形”的初認識 。
從流行角度來進一步探索,訓練資料是位於一個低維流形上的。資料中的變化對應於流形上的區域性變化(沿著切平面的方向),而資料中的不變方向是對應於正交於流形的方向。只要我們學習到資料中的變化和不變方向,那麼流形的結構也就被捕捉到了。
回頭再看收縮自編碼器學習的兩種推動力,收縮懲罰想要使學習到的特徵在所有方向上不變(對所有方向都有收縮作用),而重構誤差則想要能將學習到的特徵重構回輸入。所以在學習的過程中,重構誤差的推動力使資料中的變化方向(即流形切平面的方向)能夠抵抗收縮作用,體現在其對應的 \(Jacobian\) 矩陣中的奇異值很大;而抵抗不了收縮作用的方向則對應於資料中不變的方向(正交於流形的方向),其在 \(Jacobian\) 矩陣中的梯度則會變得很小。
由此可以看出收縮自編碼器可以很好地捕捉流形結構。
程式碼實現
下面收縮自編碼器的實現是基於 Keras 框架的。
匯入資料:
from tensorflow.examples.tutorials.mnist import input_data from keras.layers import Input, Dense from keras.models import Model import numpy as np import matplotlib.pyplot as plt import keras.backend as K
匯入 MNISR 資料:
mnist = input_data.read_data_sets('../data/MNIST_data', one_hot=True) X_train, y_train = mnist.train.images, mnist.train.labels X_val, y_val = mnist.validation.images, mnist.validation.labels X_test, y_test = mnist.test.images, mnist.test.labels
定義收縮自編碼器:
def contractive_autoencoder(X, lam=1e-3): X = X.reshape(X.shape[0], -1) M, N = X.shape N_hidden = 64 N_batch = 100 inputs = Input(shape=(N,)) encoded = Dense(N_hidden, activation='sigmoid', name='encoded')(inputs) outputs = Dense(N, activation='linear')(encoded) model = Model(input=inputs, output=outputs) def contractive_loss(y_pred, y_true): mse = K.mean(K.square(y_true - y_pred), axis=1) W = K.variable(value=model.get_layer('encoded').get_weights()[0])# N x N_hidden W = K.transpose(W)# N_hidden x N h = model.get_layer('encoded').output dh = h * (1 - h)# N_batch x N_hidden # N_batch x N_hidden * N_hidden x 1 = N_batch x 1 contractive = lam * K.sum(dh**2 * K.sum(W**2, axis=1), axis=1) return mse + contractive model.compile(optimizer='adam', loss=contractive_loss) model.fit(X, X, batch_size=N_batch, nb_epoch=3) return model, Model(input=inputs, output=encoded)
訓練模型並測試,畫出重構後的圖形:
model, representation = contractive_autoencoder(X_train) idxs = np.random.randint(0, X_test.shape[0], size=5) X_recons = model.predict(X_test[idxs]) for X_recon in X_recons: plt.imshow(X_recon.reshape(28, 28), cmap='Greys_r') plt.show()
程式碼主要參考了 Deriving Contractive Autoencoder and Implementing it in Keras ,其中還詳細推導了收縮正則項的計算形式,有興趣的可以看一下。