诗蕊 2019-03-12
在本教程中,我将回答一些关于自编码器的常见问题,我们将介绍以下模型的Python代码示例:
注意: - 我将使用keras版本2.0完成代码示例。
先决条件:
简单理解:为网络模型提供一些输入,并且输入通过一些无监督的方法来改变其维向量,最终的输出是维数变化的东西。
示例: - 通过网络传递MNIST数字数据,并获得数字图像的输出,像素稍微模糊。(即缩小图像的维向量)
以上是自编码器的直观表示,显示图像首先在编码器网络的帮助下进行编码,然后在解码器网络和最终重建输出的帮助下进行解码。
正式理解:
“自编码”是一种数据压缩算法,其中的压缩和解压缩函数是1)特定于数据的,2)有损的,3)从实例中自动学习而不是人工设计的。此外,几乎在所有使用“自编码器”的上下文中,压缩和解压缩函数都是用神经网络实现的。
它们很少在实际应用中使用。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)
创建一个单独的编码器模型:
# this model maps an input to its encoded representation encoder = Model(input_img, encoded)
解码器模型:
# 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))
现在让我们训练我们的自编码器重建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()
我们将对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
现在让我们训练我们的自编码器50个epochs:
autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, shuffle=True, validation_data=(x_test, x_test))
在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()
这就是我们得到的。上部是原始数字,底下是重建数字。
在前面的示例中,表示只受隐藏层(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)
让我们对这个模型进行100个epochs的训练(加上正则化,该模型不太可能过度拟合,并且可以训练更长时间)。模型以0.11的训练损失和0.10的测试损失结束。两者之间的差异主要是训练过程中损失中增加了正则化项(worth about 0.01)。
这是我们新结果的可视化:
它们看起来与之前的模型非常相似,唯一显著的区别是编码表示的稀疏性。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))
经过100个epochs,训练和测试损失达到了~0.097,比我们前面的机器学习模型稍好一些。我们重建的数字看起来也更好: