Hinton和Salakhutdinov在利用神经网络降低数据的维数方面,《科学》(Science) 2006年提出了通过使用深度自动编码器来实现非线性PCA的方法。我曾多次尝试使用Tensorflow构建和训练PCA自动编码器,但我从未获得过比线性PCA更好的结果。
如何有效地训练自动编码器?
(后来由@amoeba编辑:这个问题的原始版本包含Python Tensorflow代码,这些代码无法正常运行。您可以在编辑历史记录中找到它。)
Hinton和Salakhutdinov在利用神经网络降低数据的维数方面,《科学》(Science) 2006年提出了通过使用深度自动编码器来实现非线性PCA的方法。我曾多次尝试使用Tensorflow构建和训练PCA自动编码器,但我从未获得过比线性PCA更好的结果。
如何有效地训练自动编码器?
(后来由@amoeba编辑:这个问题的原始版本包含Python Tensorflow代码,这些代码无法正常运行。您可以在编辑历史记录中找到它。)
Answers:
这是Hinton和Salakhutdinov在2006年《科学》杂志上发表的论文中的关键人物:
它显示了MNIST数据集(单个数字的黑白图像)的维数从原始784维减少为二维。
让我们尝试重现它。我不会直接使用Tensorflow,因为使用Keras(在Tensorflow之上运行的更高级别的库)来进行诸如此类的简单深度学习要容易得多。H&S使用了具有物流单元的体系结构,这些体系已通过限制性玻尔兹曼机器的堆栈进行了预训练。十几年过去了,这听起来很老派。我将使用更简单的784 → 512 → 128 → 2 → 128 → 512 →
该代码是从Jupyter笔记本复制粘贴的。在Python 3.6中,您需要安装matplotlib(用于pylab),NumPy,seaborn,TensorFlow和Keras。在Python Shell中运行时,可能需要添加plt.show()
以显示图。
初始化
%matplotlib notebook
import pylab as plt
import numpy as np
import seaborn as sns; sns.set()
import keras
from keras.datasets import mnist
from keras.models import Sequential, Model
from keras.layers import Dense
from keras.optimizers import Adam
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784) / 255
x_test = x_test.reshape(10000, 784) / 255
PCA
mu = x_train.mean(axis=0)
U,s,V = np.linalg.svd(x_train - mu, full_matrices=False)
Zpca = np.dot(x_train - mu, V.transpose())
Rpca = np.dot(Zpca[:,:2], V[:2,:]) + mu # reconstruction
err = np.sum((x_train-Rpca)**2)/Rpca.shape[0]/Rpca.shape[1]
print('PCA reconstruction error with 2 PCs: ' + str(round(err,3)));
输出:
PCA reconstruction error with 2 PCs: 0.056
训练自动编码器
m = Sequential()
m.add(Dense(512, activation='elu', input_shape=(784,)))
m.add(Dense(128, activation='elu'))
m.add(Dense(2, activation='linear', name="bottleneck"))
m.add(Dense(128, activation='elu'))
m.add(Dense(512, activation='elu'))
m.add(Dense(784, activation='sigmoid'))
m.compile(loss='mean_squared_error', optimizer = Adam())
history = m.fit(x_train, x_train, batch_size=128, epochs=5, verbose=1,
validation_data=(x_test, x_test))
encoder = Model(m.input, m.get_layer('bottleneck').output)
Zenc = encoder.predict(x_train) # bottleneck representation
Renc = m.predict(x_train) # reconstruction
这在我的工作桌面上需要约35秒,并输出:
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 7s - loss: 0.0577 - val_loss: 0.0482
Epoch 2/5
60000/60000 [==============================] - 7s - loss: 0.0464 - val_loss: 0.0448
Epoch 3/5
60000/60000 [==============================] - 7s - loss: 0.0438 - val_loss: 0.0430
Epoch 4/5
60000/60000 [==============================] - 7s - loss: 0.0423 - val_loss: 0.0416
Epoch 5/5
60000/60000 [==============================] - 7s - loss: 0.0412 - val_loss: 0.0407
因此您已经看到,仅经过两个训练时期,我们就超过了PCA损失。
(顺便说一句,将所有激活功能更改为activation='linear'
并观察损耗如何精确地收敛到PCA损耗是有益的。这是因为线性自动编码器等效于PCA。)
与瓶颈表示并排绘制PCA投影
plt.figure(figsize=(8,4))
plt.subplot(121)
plt.title('PCA')
plt.scatter(Zpca[:5000,0], Zpca[:5000,1], c=y_train[:5000], s=8, cmap='tab10')
plt.gca().get_xaxis().set_ticklabels([])
plt.gca().get_yaxis().set_ticklabels([])
plt.subplot(122)
plt.title('Autoencoder')
plt.scatter(Zenc[:5000,0], Zenc[:5000,1], c=y_train[:5000], s=8, cmap='tab10')
plt.gca().get_xaxis().set_ticklabels([])
plt.gca().get_yaxis().set_ticklabels([])
plt.tight_layout()
改建
现在让我们看一下重构(第一行-原始图像,第二行-PCA,第三行-自动编码器):
plt.figure(figsize=(9,3))
toPlot = (x_train, Rpca, Renc)
for i in range(10):
for j in range(3):
ax = plt.subplot(3, 10, 10*j+i+1)
plt.imshow(toPlot[j][i,:].reshape(28,28), interpolation="nearest",
vmin=0, vmax=1)
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.tight_layout()
通过更深入的网络,一定程度的规范化和更长的培训,可以获得更好的结果。实验。深度学习很容易!
from __future__ import absolute_import from __future__ import division from __future__ import print_function
@amoeba的巨大道具就是一个很好的例子。我只想证明该文章中描述的自动编码器训练和重构过程也可以在R中轻松完成。设置了下面的自动编码器,以便它尽可能模拟amoeba的示例-相同的优化器和整体架构。由于TensorFlow后端未类似地植入,因此确切的成本无法重现。
初始化
library(keras)
library(rARPACK) # to use SVDS
rm(list=ls())
mnist = dataset_mnist()
x_train = mnist$train$x
y_train = mnist$train$y
x_test = mnist$test$x
y_test = mnist$test$y
# reshape & rescale
dim(x_train) = c(nrow(x_train), 784)
dim(x_test) = c(nrow(x_test), 784)
x_train = x_train / 255
x_test = x_test / 255
PCA
mus = colMeans(x_train)
x_train_c = sweep(x_train, 2, mus)
x_test_c = sweep(x_test, 2, mus)
digitSVDS = svds(x_train_c, k = 2)
ZpcaTEST = x_test_c %*% digitSVDS$v # PCA projection of test data
自动编码器
model = keras_model_sequential()
model %>%
layer_dense(units = 512, activation = 'elu', input_shape = c(784)) %>%
layer_dense(units = 128, activation = 'elu') %>%
layer_dense(units = 2, activation = 'linear', name = "bottleneck") %>%
layer_dense(units = 128, activation = 'elu') %>%
layer_dense(units = 512, activation = 'elu') %>%
layer_dense(units = 784, activation='sigmoid')
model %>% compile(
loss = loss_mean_squared_error, optimizer = optimizer_adam())
history = model %>% fit(verbose = 2, validation_data = list(x_test, x_test),
x_train, x_train, epochs = 5, batch_size = 128)
# Unsurprisingly a 3-year old laptop is slower than a desktop
# Train on 60000 samples, validate on 10000 samples
# Epoch 1/5
# - 14s - loss: 0.0570 - val_loss: 0.0488
# Epoch 2/5
# - 15s - loss: 0.0470 - val_loss: 0.0449
# Epoch 3/5
# - 15s - loss: 0.0439 - val_loss: 0.0426
# Epoch 4/5
# - 15s - loss: 0.0421 - val_loss: 0.0413
# Epoch 5/5
# - 14s - loss: 0.0408 - val_loss: 0.0403
# Set the auto-encoder
autoencoder = keras_model(model$input, model$get_layer('bottleneck')$output)
ZencTEST = autoencoder$predict(x_test) # bottleneck representation of test data
与瓶颈表示并排绘制PCA投影
par(mfrow=c(1,2))
myCols = colorRampPalette(c('green', 'red', 'blue', 'orange', 'steelblue2',
'darkgreen', 'cyan', 'black', 'grey', 'magenta') )
plot(ZpcaTEST[1:5000,], col= myCols(10)[(y_test+1)],
pch=16, xlab = 'Score 1', ylab = 'Score 2', main = 'PCA' )
legend( 'bottomright', col= myCols(10), legend = seq(0,9, by=1), pch = 16 )
plot(ZencTEST[1:5000,], col= myCols(10)[(y_test+1)],
pch=16, xlab = 'Score 1', ylab = 'Score 2', main = 'Autoencoder' )
legend( 'bottomleft', col= myCols(10), legend = seq(0,9, by=1), pch = 16 )
改建
我们可以用通常的方法来重建数字。(第一行是原始数字,中间一行是PCA重构,而下一行是自动编码器重构。)
Renc = predict(model, x_test) # autoencoder reconstruction
Rpca = sweep( ZpcaTEST %*% t(digitSVDS$v), 2, -mus) # PCA reconstruction
dev.off()
par(mfcol=c(3,9), mar = c(1, 1, 0, 0))
myGrays = gray(1:256 / 256)
for(u in seq_len(9) ){
image( matrix( x_test[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays,
xaxt='n', yaxt='n')
image( matrix( Rpca[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays ,
xaxt='n', yaxt='n')
image( matrix( Renc[u,], 28,28, byrow = TRUE)[,28:1], col = myGrays,
xaxt='n', yaxt='n')
}
这是我的Jupyter笔记本,我尝试在其中复制您的结果,但有以下差异:
我的MSE结果是将PCA降维1到6(其中输入有6列),将AE降为暗。红色。以下是1至6:
当PCA输入为(mean = 0,std = 1)且AE输入为[0-1]范围时-4e-15:PCA6-.015:PCA5-.0502:AE5-.0508:AE6-.051:AE4- .053:AE3-.157:PCA4-.258:AE2-.259:PCA3-.377:AE1-.483:PCA2-.682:PCA1
不降低尺寸的线性PCA可以达到9e-15,因为它可以将无法安装的零件推入最后一个零件。