SAE 入门(一)

SAE 入门(一)

Autoencoder 简介

自编码器(Autoencoder,AE),是一种利用反向传播(backpropagation,BP)算法使得输出值等于输入值的神经网络,它先将输入压缩成潜在空间表征,然后通过这种表征来重构输出。其中,空间表征可以看作是输入数据的高级抽象,通常是将高维度的数据抽象为低维度的数据。

自编码器由两部分组成:
编码器:这部分能将输入压缩成潜在空间表征,可以用编码函数 $h=f(x)$ 表示;
解码器:这部分能重构来自潜在空间表征的输入,可以用解码函数 $r=g(h)$ 表示。

因此,整个自编码器可以用函数 $g(f(x)) = r$ 来描述,其中输出 $r$ 与原始输入 $x$ 相近。

自动编码器的目标是最大程度地减少输入和输出之间的重构误差。这有助于自动编码器学习数据中存在的重要功能。当表征很好地重建其输入时,则表示这个表征很好地保留了输入中存在的许多信息。整个过程如下图。

自编码器网络结构示意图

Stacked Autoencoder 简介

Stacked Autoencoder 简写作 SAE。SAE 与 AE 的主要区别在于编码器与解码器的层数,栈式自编码器包含多层隐藏层。具体网络结构如下图所示,图中有两层编码层,两层解码层。

栈式自编码器网络结构示意图

代码实现

代码环境配置,请参考 GAN 网络之手写数字生成 第一小节——环境搭建。

自编码器只是一种思想,在具体实现中,编码器和解码器可以由多种深度学习模型构成,例如全连接层、卷积层和 LSTM 等,以下使用 Keras 来实现栈式自编码器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from keras.datasets import mnist
from keras.layers import Input, Dense
from keras.models import Model
import numpy as np
import matplotlib.pyplot as plt

EPOCHS = 50
BATCH_SIZE = 256


def train(x_train, x_test):
    input_img = Input(shape=(784,))

    # 三个编码层,将数据从 784 维向量编码为 128、64、32 维向量
    encoded = Dense(units=128, activation='relu')(input_img)
    encoded = Dense(units=64, activation='relu')(encoded)
    encoded = Dense(units=32, activation='relu')(encoded)

    # 三个解码层,将数据从 32 维向量解码成 64、128、784 维向量
    decoded = Dense(units=64, activation='relu')(encoded)
    decoded = Dense(units=128, activation='relu')(decoded)
    decoded = Dense(units=784, activation='sigmoid')(decoded)
    autoencoder = Model(input_img, decoded)
    encoder = Model(input_img, encoded)

    autoencoder.summary()
    encoder.summary()

    autoencoder.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    autoencoder.fit(x_train, x_train,
                    epochs=EPOCHS,
                    batch_size=BATCH_SIZE,
                    shuffle=True,
                    validation_data=(x_test, x_test))
    return encoder, autoencoder


def plot(encoded_imgs, decoded_imgs):
    plt.figure(figsize=(40, 4))
    for i in range(10):
        # 展示原始输入图像
        ax = plt.subplot(3, 20, i + 1)
        plt.imshow(x_test[i].reshape(28, 28))
        plt.gray()
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

        # 展示编码后的图像
        ax = plt.subplot(3, 20, i + 1 + 20)
        plt.imshow(encoded_imgs[i].reshape(8, 4))
        plt.gray()
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

        # 展示解码后的输入图像
        ax = plt.subplot(3, 20, 2 * 20 + i + 1)
        plt.imshow(decoded_imgs[i].reshape(28, 28))
        plt.gray()
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
    plt.show()


if __name__ == '__main__':
    # 加载数据,训练数据 60000 条,测试数据 10000 条,数据灰度值 [0, 255]
    (x_train, _), (x_test, _) = mnist.load_data()

    # 正则化数据,将灰度值区间转换为 [0, 1]
    x_train = x_train.astype('float32') / 255
    x_test = x_test.astype('float32') / 255

    # 将数据集从二维 (28, 28) 矩阵转换为长度为维度是 784 的向量
    x_train = x_train.reshape(len(x_train), np.prod(x_train.shape[1:]))
    x_test = x_test.reshape(len(x_test), np.prod(x_test.shape[1:]))
    print(x_train.shape)
    print(x_test.shape)

    # 训练数据
    encoder, autoencoder = train(x_train, x_test)

    # 获取编码后和解码后的图像
    encoded_imgs = encoder.predict(x_test)
    decoded_imgs = autoencoder.predict(x_test)

    # 绘制图像
    plot(encoded_imgs, decoded_imgs)

运行上述代码,可以从输出内容中得到以下信息:

  1. 输入数据是 60000 张手写数字的灰度图像,灰度取值范围是 [0, 255],我们将其灰度值按行依次存储到一个 1 * 784 的数组中;
  2. 输入数据形如 (0, 0, 0,…, 84, 185, 159,…, 170, 52,…, 0, 0),我们可以将每张图片(每个向量)理解为一个 784 维空间的中向量;
  3. 通过正则化后,输入数据每个维度区间变为 [0, 1];
  4. 编码层将输入的 784 维向量抽象为 128、64、32 维向量(dense,dense_1,dense_2);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Model: "functional_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         [(None, 784)]             0
_________________________________________________________________
dense (Dense)                (None, 128)               100480
_________________________________________________________________
dense_1 (Dense)              (None, 64)                8256
_________________________________________________________________
dense_2 (Dense)              (None, 32)                2080
=================================================================
Total params: 110,816
Trainable params: 110,816
Non-trainable params: 0
_________________________________________________________________
  1. 解码层将抽象后的 32 维向量还原维 64、128、784 维向量(dense_3,dense_4,dense_5);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Model: "functional_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         [(None, 784)]             0
_________________________________________________________________
dense (Dense)                (None, 128)               100480
_________________________________________________________________
dense_1 (Dense)              (None, 64)                8256
_________________________________________________________________
dense_2 (Dense)              (None, 32)                2080
_________________________________________________________________
dense_3 (Dense)              (None, 64)                2112
_________________________________________________________________
dense_4 (Dense)              (None, 128)               8320
_________________________________________________________________
dense_5 (Dense)              (None, 784)               101136
=================================================================
Total params: 222,384
Trainable params: 222,384
Non-trainable params: 0
_________________________________________________________________

训练完成之后,可以在输出内容中看到详细的训练数据,在 50 次训练之后,loss 已经降低到了 0.08。得到的输出图像如下图所示。

1
2
3
4
5
6
7
8
......

Epoch 48/50
235/235 [==============================] - 3s 12ms/step - loss: 0.0848 - accuracy: 0.0130 - val_loss: 0.0844 - val_accuracy: 0.0147
Epoch 49/50
235/235 [==============================] - 3s 12ms/step - loss: 0.0846 - accuracy: 0.0130 - val_loss: 0.0845 - val_accuracy: 0.0115
Epoch 50/50
235/235 [==============================] - 3s 12ms/step - loss: 0.0845 - accuracy: 0.0139 - val_loss: 0.0840 - val_accuracy: 0.0165

MNIST 手写数字自编码结果对比图

参考文献

  1. Sparse, Stacked and Variational Autoencoder
  2. Deep Learning Autoencoders
  3. Deep Autoencoder using Keras
  4. 自编码器是什么?有什么用?这里有一份入门指南(附代码)
  5. 反向传播算法 - 维基百科
  6. Autoencoder - Github
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy