堆疊式自動編碼器


堆疊式自動編碼器

自動編碼器可以具有多個隱藏層。在這種情況下,它們被稱為堆疊式自動編碼器(或深度自動編碼器)。添加更多的層有助於自動編碼器學習更多的復雜的編碼。就是說,要注意不要使自動編碼器過於強大。想象一個強大的編碼器,它只是學會了把每個輸入映射到單個任意數字(而解碼器則學習反向映射)。顯然這樣的自動編碼器可以完美地重建訓練數據,但是它不會學到任何有用的數據表征(並且不太可能將其很好地泛化到新實例中)

堆疊式自動編碼器的架構典型地相對於中間隱藏層(編碼層)堆成。

使用Keras實現堆疊式自動編碼器

可以實現常規的深度MLP那樣來實現堆疊式編碼器。特別地,可以使用用於訓練深度網絡相同的技術。

# 使用SELU激活函數為Fashion MNIST構建了一個堆疊式自動編碼器
from tensorflow import keras

fashion_mnist = keras.datasets.fashion_mnist
(X_train_all, y_train_all), (X_test, y_test) = fashion_mnist.load_data()
X_valid, X_train = X_train_all[:5000] / 255., X_train_all[5000:] / 255.
y_valid, y_train = y_train_all[:5000], y_train_all[5000:]
print(X_train_all.shape)
stacked_encoder = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(100, activation='gelu'),
    keras.layers.Dense(30, activation='gelu')
])
stacked_decoder = keras.models.Sequential([
    keras.layers.Dense(100, activation='gelu', input_shape=[30]),
    keras.layers.Dense(28 * 28, activation='sigmoid'),
    keras.layers.Reshape([28, 28])
])
stacked_ae = keras.models.Sequential([stacked_encoder, stacked_decoder])
stacked_ae.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam())
history = stacked_ae.fit(X_train, X_train, epochs=10, validation_data=(X_valid, X_valid))
(60000, 28, 28)
Epoch 1/10
1719/1719 [==============================] - 7s 3ms/step - loss: 0.3156 - val_loss: 0.2933
Epoch 2/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2916 - val_loss: 0.2852
Epoch 3/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2861 - val_loss: 0.2817
Epoch 4/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2833 - val_loss: 0.2796
Epoch 5/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2817 - val_loss: 0.2783
Epoch 6/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2806 - val_loss: 0.2774
Epoch 7/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2797 - val_loss: 0.2766
Epoch 8/10
1719/1719 [==============================] - 4s 3ms/step - loss: 0.2789 - val_loss: 0.2758
Epoch 9/10
1719/1719 [==============================] - 4s 3ms/step - loss: 0.2783 - val_loss: 0.2753
Epoch 10/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.2777 - val_loss: 0.2746
  • 將自動編碼器模型分為兩個字模型:編碼器和解碼器
  • 編碼器使用\(28\times28\)像素的灰度圖像,先將它們展平,以便每個圖像表示為大小784的向量,然后通過兩個尺寸遞減的Dense層(100個神經元然后是30個)來處理這些向量,兩個都使用GELU激活函數。對於每個輸入圖像,編碼器輸出大小為30的向量
  • 解碼器使用大小為30的編碼(由編碼器輸出),並通過兩個大小遞增的Dense層(100個神經元然后784個)來處理它們,並將最終向量重構為\(28\times28\)的數組,因此解碼器的輸出具有與編碼器輸入相同的形狀
  • 在編譯堆疊式自動編碼器時,使用二元交叉熵損失代替均方誤差。將重建任務視為多標簽二元分類問題:每個像素強度代表像素應為黑色的概率,以這種方法(而不是回歸問題)往往會使得模型收斂得更快
  • 最后,使用X_train作為輸入和目標來訓練函數(類似地,使用X_valid作為驗證輸入和輸出目標)

可視化重構

確保對自動編碼器進行了恰當訓練的一種方法是比較輸入和輸出:差異不應該很明顯。

import matplotlib.pyplot as plt


def plot_image(image):
    plt.imshow(image, cmap='binary')
    plt.axis('off')


def show_reconstructions(model, n_images=5):
    reconstructions = model.predict(X_valid[:n_images])
    fig = plt.figure(figsize=(n_images * 1.5, 3))
    for image_index in range(n_images):
        plt.subplot(2, n_images, 1 + image_index)
        plot_image(X_valid[image_index])
        plt.subplot(2, n_images, 1 + n_images + image_index)
        plot_image(reconstructions[image_index])


show_reconstructions(stacked_ae)


重構是可識別的,但損失有點大,可能需要訓練模型更持久一點,或者使編碼器和解碼器更深,或者使編碼更大。但是,如果使網絡過於強大,它就能夠在沒有學習到數據中任何有用模式的情況下,進行完美的重構

可視化Fashion MNIST數據集

現在已經訓練了一個堆疊式自動編碼器,可以使用它來減少數據集的維度。對於可視化而言,與其他降維算法相比,它並沒有給出很好的結果,但是自動編碼器的一大優勢是它們可以處理具有許多實例和許多特征的大型數據集。因此,一種策略是使用自動編碼器將維度降低到合理水平,然后使用另一維降維算法進行可視化。以下使用這種策略來可視化Fashion MNIST

首先,使用堆疊式自動編碼器中的編碼器將維度縮小到30,然后使用Scikit-Learn中的t-SNE算法的實現將維度減小到2來進行可視化:

from sklearn.manifold import TSNE

X_valid_compressed = stacked_encoder.predict(X_valid)
tsne = TSNE()
X_valid_2D = tsne.fit_transform(X_valid_compressed)
plt.scatter(X_valid_2D[:, 0], X_valid_2D[:, 1], c=y_valid, s=10, cmap='tab10')
C:\ProgramData\Miniconda3\envs\tf2\lib\site-packages\sklearn\manifold\_t_sne.py:780: FutureWarning: The default initialization in TSNE will change from 'random' to 'pca' in 1.2.
  warnings.warn(
C:\ProgramData\Miniconda3\envs\tf2\lib\site-packages\sklearn\manifold\_t_sne.py:790: FutureWarning: The default learning rate in TSNE will change from 200.0 to 'auto' in 1.2.
  warnings.warn(





<matplotlib.collections.PathCollection at 0x2324edf9820>


t-SNE算法識別出幾個與類合理匹配的集群(每個類用不同顏色表示)

自動編碼器可用於降維。另一個應用是無監督學習

使用堆疊式自動編碼器的無監督預訓練

如果要處理一個復雜的有監督任務,但是沒有很多標記好的數據,一種解決方案是找到執行類似任務的神經網絡並重用其較低層網絡。這樣就可以使用很少的訓練數據來訓練一個高性能的模型,因此神經網絡不必學習所有的底層特征,它只會重用現有網絡學到的特征檢測器

同樣,如果有一個大型數據集,但大多數未被標記,可以先使用所有數據訓練一個堆疊的自動編碼器,然后重用較低層為實際任務創建神經網絡,並使用標記的數據對其進行訓練。

實現:適用所有訓練數據(標記的和未標記的)來訓練自動編碼器,然后重用其編碼器層來創建一個新的神經網絡

綁定權重

當一個自動編碼器整齊對稱時,一種常見的技術是將解碼器層的權重與編碼器層的權重綁定起來。這樣可以將模型中權重的數量減少一半,從而加快訓練速度並降低過擬合的風險。具體來說,如果自動編碼器有N層(不算輸入層),並且\(W_L\)表示第\(L\)層的連接權重,解碼器層可以簡單定義為\(W_{N-L+1}=W_L^T\)(其中,\(L=1,2,\dots,N/2)\)

# 為了使用Keras綁定各層之間的權重,需要定義一個自定義層
import tensorflow as tf


class DenseTranspose(keras.layers.Layer):
    def __init__(self, dense, activation=None, **kwargs):
        self.dense = dense
        self.activation = keras.activations.get(activation)
        super().__init__(**kwargs)

    def build(self, batch_input_shape):
        self.biases = self.add_weight(name='bias', initializer='zeros', shape=[self.dense.input_shape[-1]])
        super().build(batch_input_shape)

    def call(self, inputs):
        z = tf.matmul(inputs, self.dense.weights[0], transpose_b=True)
        return self.activation(z + self.biases)


這個自定義層的作用類似於常規的Dense層,但是它使用了另一個Dense層的權重,並對它進行了轉置(設置transpose_b=True等效於轉置第二個參數,但是它的效率更高,因為它可以在matmul()操作中即時執行轉置)。但是,它使用自己的偏置向量,接下來,可以創建一個新的堆疊式自動編碼器,和前一個非常相似,但是將解碼器的Dense綁定到了編碼器的密集層

dense_1 = keras.layers.Dense(100, activation='gelu')
dense_2 = keras.layers.Dense(30, activation='gelu')
tied_encoder = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    dense_1,
    dense_2
])
tied_decoder = keras.models.Sequential([
    DenseTranspose(dense_2, activation='gelu'),
    DenseTranspose(dense_1, activation='gelu'),
    keras.layers.Reshape([28, 28])
])
tied_ae = keras.models.Sequential([tied_encoder, tied_decoder])
tied_ae.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam())
history = tied_ae.fit(X_train, X_train, epochs=10, validation_data=(X_valid, X_valid))
Epoch 1/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.6947 - val_loss: 0.4871
Epoch 2/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.4224 - val_loss: 0.3859
Epoch 3/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3779 - val_loss: 0.3609
Epoch 4/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3565 - val_loss: 0.3362
Epoch 5/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3400 - val_loss: 0.3309
Epoch 6/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3319 - val_loss: 0.3201
Epoch 7/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3360 - val_loss: 0.3591
Epoch 8/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3279 - val_loss: 0.3173
Epoch 9/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3215 - val_loss: 0.3300
Epoch 10/10
1719/1719 [==============================] - 5s 3ms/step - loss: 0.3327 - val_loss: 0.3436

與前一個模型相比,該模型的參數數量減少了幾乎一半

一次訓練一個自動編碼器

與其像一次訓練整個堆疊式自動編碼器那樣,不如一次訓練一個淺層自動編碼器,然后將它們全部堆疊成一個堆疊式自動編碼器

在訓練的第一階段,第一個自動編碼器學習重建輸入。然后,使用第一個自動編碼器對整個訓練集進行編碼,這提供了一個新的(壓縮)訓練集。然后,在此新的數據集上訓練第二個自動編碼器,這是訓練的第二階段。最后,使用所有這些自動編碼器來構建堆疊式自動編碼器


免責聲明!

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



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