在Tensorflow中构建自动编码器以超越PCA


31

Hinton和Salakhutdinov在利用神经网络降低数据的维数方面,《科学》(Science) 2006年提出了通过使用深度自动编码器来实现非线性PCA的方法。我曾多次尝试使用Tensorflow构建和训练PCA自动编码器,但我从未获得过比线性PCA更好的结果。

如何有效地训练自动编码器?

(后来由@amoeba编辑:这个问题的原始版本包含Python Tensorflow代码,这些代码无法正常运行。您可以在编辑历史记录中找到它。)


我在Layer类的激活函数中发现一个错误。我正在测试现在是否可以正常工作
Donbeo

您是否解决了错误?
Pinocchio

嗨Donbeo。我冒昧地从您的问题中删除了代码(仍然可以在编辑历史记录中轻松找到该代码)。使用该代码,您的问题看起来有点像“帮助我发现错误”类型的问题,这里没有主题。同时,此线程有4k的视图,这可能意味着很多人通过Google搜索来到这里,所以我不想关闭您的问题。我决定发布一个自动编码器演练答案,但出于简单起见,我使用Keras(在Tensorflow之上运行)代替了原始的Tensorflow。您认为这可以回答您的问题吗?
变形虫说恢复莫妮卡

Answers:


42

这是Hinton和Salakhutdinov在2006年《科学》杂志上发表的论文中的关键人物:

它显示了MNIST数据集(单个数字的黑白图像)的维数从原始784维减少为二维。28×28

让我们尝试重现它。我不会直接使用Tensorflow,因为使用Keras(在Tensorflow之上运行的更高级别的库)来进行诸如此类的简单深度学习要容易得多。H&S使用了具有物流单元的体系结构,这些体系已通过限制性玻尔兹曼机器的堆栈进行了预训练。十几年过去了,这听起来老派。我将使用更简单的784 512 128 2 128 512

784100050025022505001000784
架构具有指数线性单位,无需任何预训练。我将使用Adam优化器(具有动量的自适应随机梯度下降的特定实现)。
7845121282128512784

该代码是从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()

在此处输入图片说明

通过更深入的网络,一定程度的规范化和更长的培训,可以获得更好的结果。实验。深度学习很容易!


2
我很惊讶PCA仅使用2个组件就表现出色!感谢您发布代码
Aksakal

2
太棒了!!
马修·德鲁里

2
@shadi我实际上找到了一个直接调用svd()的方法:)
阿米巴说恢复莫妮卡

1
当使用更多组件时,性能差异会更大。我尝试使用10而不是2,并且自动编码器要好得多。缺点是速度和内存消耗
Aksakal

1
对于python 2,您需要添加以下导入from __future__ import absolute_import from __future__ import division from __future__ import print_function
user2589273,

7

@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')
}

在此处输入图片说明

ķ0.03560.0359


2
+1。真好 很高兴看到在R中使用Keras就像在Python中一样简单。据我所知,如今在深度学习社区中,每个人都在使用Python,因此我感到在其他地方应该更困难。
变形虫说莫妮卡(Reonica Monica)

2

是我的Jupyter笔记本,我尝试在其中复制您的结果,但有以下差异:

  • 而不是直接使用tensorflow,我使用它查看keras
  • 泄漏的relu而不是relu以避免饱和(即编码输出为0)
    • 这可能是AE性能不佳的原因
  • 自动编码器输入的数据缩放为[0,1]
    • 我想我读过某个地方,带有relu的自动编码器最适合[0-1]数据
    • 在自动编码器的输入为mean = 0的情况下运行我的笔记本,std = 1使所有降维的AE> MSE> 0.7,所以这可能是您的问题之一
  • PCA输入保持为均值= 0和std = 1的数据
    • 这也可能意味着PCA的MSE结果与PCA的MSE结果不具有可比性
    • 也许稍后我会使用PCA和AE的[0-1]数据重新运行
  • PCA输入也被缩放为[0-1]。PCA也可以处理(mean = 0,std = 1)数据,但是MSE将与AE无可比拟

我的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

  • 9e-15:PCA6
  • .0094:PCA5
  • .0502:AE5
  • .0507:AE6
  • .0514:AE4
  • .0532:AE3
  • .0772:PCA4
  • .1231:PCA3
  • .2588:AE2
  • .2831:PCA2
  • .3773:AE1
  • .3885:PCA1

不降低尺寸的线性PCA可以达到9e-15,因为它可以将无法安装的零件推入最后一个零件。


shadi,您的笔记本电脑导入了一个utils软件包,该软件包似乎具有很多非标准功能utils.buildNetwork和utils.ae_fit_encode_plot_mse ...
Berowne Hlavaty,

那只是与笔记本相同级别的同一存储库中的文件。
shadi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.