使用Keras构建自编码器

诗蕊 2019-03-12

在本教程中,我将回答一些关于自编码器的常见问题,我们将介绍以下模型的Python代码示例:

  1. 连接层网络上的简单(vanilla)自编码器
  2. 稀疏自编码器

注意: - 我将使用keras版本2.0完成代码示例。

先决条件:

  1. 了解python和keras API。
  2. 熟悉深度学习和PCA等一些数据压缩技术!

什么是自编码器?

简单理解:为网络模型提供一些输入,并且输入通过一些无监督的方法来改变其维向量,最终的输出是维数变化的东西。

示例: - 通过网络传递MNIST数字数据,并获得数字图像的输出,像素稍微模糊。(即缩小图像的维向量)

使用Keras构建自编码器

以上是自编码器的直观表示,显示图像首先在编码器网络的帮助下进行编码,然后在解码器网络和最终重建输出的帮助下进行解码。

正式理解:

“自编码”是一种数据压缩算法,其中的压缩和解压缩函数是1)特定于数据的,2)有损的,3)从实例中自动学习而不是人工设计的。此外,几乎在所有使用“自编码器”的上下文中,压缩和解压缩函数都是用神经网络实现的。

  1. 自编码器是特定于数据的,这意味着它们只能压缩类似于他们所训练的数据。这与MPEG-2 Audio Layer III (MP3)压缩算法不同,后者只对一般的“声音”进行假设,而不包含特定类型的声音。对人脸图片进行训练的自编码器在压缩树木图片方面做得相当差,因为它将学到的特征是特定于人脸的。
  2. 自编码器是有损的,这意味着与原始输入相比,解压缩的输出会降级(类似于MP3或JPEG压缩)。这与无损算术压缩不同。
  3. 自编码器是从数据实例中自动学习的,这是一个有用的属性:它意味着很容易训练算法的特定实例,这些实例将在特定类型的输入上表现良好。它不需要任何新的工程,只需要适当的训练数据。
  4. 要构建自编码器,您需要三件事:编码函数,解码函数,以及数据压缩表示与解压缩表示之间信息损失量之间的距离函数(即“loss”函数)。编码器和解码器将被选择为参数函数(通常是神经网络),并且相对于距离函数是可微分的,因此可以使用随机梯度下降来优化编码/解码函数的参数以最小化重建损失。

自编码器有什么用?

它们很少在实际应用中使用。2012年,他们在深度卷积神经网络的贪婪分层预训练中发现了一个应用,但随着我们开始意识到更好的随机权重初始化方案足以从头开始训练深度神经网络,这种方法很快就过时了。2014年,批归一化开始允许更深层次的网络,从2015年末开始,我们可以使用残差学习从头开始训练任意深度的网络。

今天,自编码器的两个有趣的实际应用是数据去噪,以及数据可视化的降维。通过适当的维度和稀疏性约束,自编码器可以学习比PCA或其他基本技术更有趣的数据投影。

特别是对于2D可视化,t-SNE可能是最好的算法,但它通常需要相对低维的数据。因此,在高维数据中可视化相似关系的良好策略是,首先使用自编码器将数据压缩到低维空间(例如32维),然后使用t-SNE将压缩数据映射到2D平面。请注意,Keras中t-SNE的一个很好的参数化实现是由Kyle McDonald开发的,可以在Github上获得。scikit-learn也有一个简单实用的实现。

他们擅长数据压缩吗?

通常不是。例如,在图像压缩中,训练一个比JPEG等基本算法更好的自编码器是非常困难的,通常,实现此功能的唯一方法是将自己限制在非常特定的图片类型上(例如,JPEG不能很好地处理这种类型的图片)。事实上,自编码器是特定于数据的,这使得它们通常不适用于实际的数据压缩问题:您只能在与它们所训练的内容类似的数据上使用它们,因此,为使它们更通用需要大量的训练数据。

让我们构建最简单的自编码器

我们将从简单开始,使用单个完全连接的神经层作为编码器和解码器,Python代码如下:

from keras.layers import Input, Dense
from keras.models import Model
# this is the size of our encoded representations
encoding_dim = 32 # 32 floats -> compression of factor 24.5, assuming the input is 784 floats
# this is our input placeholder
input_img = Input(shape=(784,))
# "encoded" is the encoded representation of the input
encoded = Dense(encoding_dim, activation='relu')(input_img)
# "decoded" is the lossy reconstruction of the input
decoded = Dense(784, activation='sigmoid')(encoded)
# this model maps an input to its reconstruction
autoencoder = Model(input_img, decoded)

使用Keras构建自编码器

创建一个单独的编码器模型:

# this model maps an input to its encoded representation
encoder = Model(input_img, encoded)

使用Keras构建自编码器

解码器模型:

# create a placeholder for an encoded (32-dimensional) input
encoded_input = Input(shape=(encoding_dim,))
# retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]
# create the decoder model
decoder = Model(encoded_input, decoder_layer(encoded_input))

使用Keras构建自编码器

现在让我们训练我们的自编码器重建MNIST数字。

首先,我们将配置我们的模型以使用每个像素的二元交叉熵损失和Adadelta优化器:

autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')

让我们准备输入数据。我们正在使用MNIST数字,丢弃标签(因为我们只对编码/解码输入图像感兴趣)。

from keras.datasets import mnist
import numpy as np
(x_train, _), (x_test, _) = mnist.load_data()

使用Keras构建自编码器

我们将对0到1之间的所有值进行归一化,并将28x28图像flatten为大小为784的向量。

x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
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

使用Keras构建自编码器

现在让我们训练我们的自编码器50个epochs:

autoencoder.fit(x_train, x_train,
 epochs=50,
 batch_size=256,
 shuffle=True,
 validation_data=(x_test, x_test))

使用Keras构建自编码器

在50个epochs之后,自编码器似乎达到稳定的训练/测试损失值约为0.11。我们可以尝试可视化重建的输入和编码的表示。使用Matplotlib,Python实现如下。

# encode and decode some digits
# note that we take them from the *test* set
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
# use Matplotlib (don't ask)
import matplotlib.pyplot as plt
n = 10 # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
 # display original
 ax = plt.subplot(2, n, i + 1)
 plt.imshow(x_test[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
 # display reconstruction
 ax = plt.subplot(2, n, i + 1 + n)
 plt.imshow(decoded_imgs[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
plt.show()

使用Keras构建自编码器

这就是我们得到的。上部是原始数字,底下是重建数字。

使用Keras构建自编码器

在编码表示上添加稀疏性约束

在前面的示例中,表示只受隐藏层(32)大小的限制。在这种情况下,通常发生的是隐藏层正在学习PCA(主成分分析)的近似。但是约束表示为紧凑的另一种方法是对隐藏表示的活动添加稀疏约束,这样在给定的时间内会“触发”更少的单元。在Keras中,这可以通过添加activity_regularizer到我们的Dense层来实现:

from keras import regularizers
encoding_dim = 32
input_img = Input(shape=(784,))
# add a Dense layer with a L1 activity regularizer
encoded = Dense(encoding_dim, activation='relu',
 activity_regularizer=regularizers.l1(10e-5))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
autoencoder = Model(input_img, decoded)

使用Keras构建自编码器

让我们对这个模型进行100个epochs的训练(加上正则化,该模型不太可能过度拟合,并且可以训练更长时间)。模型以0.11的训练损失和0.10的测试损失结束。两者之间的差异主要是训练过程中损失中增加了正则化项(worth about 0.01)。

这是我们新结果的可视化:

使用Keras构建自编码器

它们看起来与之前的模型非常相似,唯一显著的区别是编码表示的稀疏性。encoded_imgs.mean()生成的值为3.33(超过10,000个测试图像),而前一个模型的值为7.30。因此,我们的新模型产生了两倍稀疏的编码表示。

深度自编码器

我们不必将自己局限于单层作为编码器或解码器,我们可以改为使用一堆层,例如:

input_img = Input(shape=(784,))
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(32, activation='relu')(encoded)
decoded = Dense(64, activation='relu')(encoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train,
 epochs=100,
 batch_size=256,
 shuffle=True,
 validation_data=(x_test, x_test))

使用Keras构建自编码器

经过100个epochs,训练和测试损失达到了~0.097,比我们前面的机器学习模型稍好一些。我们重建的数字看起来也更好:

使用Keras构建自编码器

相关推荐