收縮自編碼器(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,其中還詳細推導了收縮正則項的計算形式,有興趣的可以看一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM