TensorflowでPCAを超えるオートエンコーダーを構築する


31

ニューラルネットワークでデータの次元数を削減するヒントンとサラクーディノフサイエンス 2006は、ディープオートエンコーダーの使用による非線形PCAを提案しました。Tensorflowを使用してPCAオートエンコーダーを何度も構築およびトレーニングしようとしましたが、線形PCAよりも良い結果を得ることができませんでした。

オートエンコーダを効率的にトレーニングするにはどうすればよいですか?

(@amoebaによる後の編集:この質問の元のバージョンには、正しく動作しなかったPython Tensorflowコードが含まれていました。編集履歴で見つけることができます。)


Layerクラスのアクティベーション関数にエラーが見つかりました。現在動作しているかどうかをテストしています
-Donbeo

エラーを修正しましたか?
ピノキオ

こんにちは、ドンベオ。私はあなたの質問からコードを削除するために自由を取りました(コードはまだ編集履歴で簡単に見つけることができます)。コードでは、あなたの質問は「バグを見つけるのを手伝ってください」タイプの質問のように見えました。同時に、このスレッドには4kのビューがあります。これは、おそらく多くの人がGoogle検索でここにアクセスすることを意味するため、質問を閉じたくありませんでした。オートエンコーダーウォークスルーを使用して回答を投稿することにしましたが、簡単にするために、生のTensorflowではなくKeras(Tensorflowの上で実行)を使用しました。これはあなたのQに答えると思いますか?
アメーバは、モニカを復活させる

回答:


42

ヒントンとサラクーディノフによる2006年のサイエンスペーパーの重要な図を次に示します。

元の784次元から2次元へのMNISTデータセット(1桁の白黒画像)の次元削減を示しています。28×28

再現してみましょう。このような単純な深層学習タスクにはKeras(Tensorflowの上で実行される高レベルのライブラリ)を使用する方がはるかに簡単なので、Tensorflowを直接使用しません。H&S は、制限付きボルツマンマシンのスタックで事前トレーニングされた、ロジスティックユニットを備えたアーキテクチャを使用しました。10年後、これは非常に古く聞こえます。事前トレーニングなしで指数線形ユニットを備えた、より単純なアーキテクチャを使用します。Adamオプティマイザー(運動量を伴う適応確率的勾配降下の特定の実装)を使用します。784 512 128 2 128 512 784

784100050025022505001000784
7845121282128512784

コードはJupyterノートブックからコピーアンドペーストされます。Python 3.6では、matplotlib(pylab用)、NumPy、seaborn、TensorFlow、およびKerasをインストールする必要があります。Pythonシェルで実行する場合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

トレーニングエポックを2回行っただけで、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()

ここに画像の説明を入力してください

再構成

次に、再構築を見てみましょう(1行目-元の画像、2行目-PCA、3行目-オートエンコーダ):

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
より多くのコンポーネントを使用すると、パフォーマンスの差はさらに大きくなります。2つではなく10を試しましたが、オートエンコーダの方がはるかに優れていました。欠点は、速度とメモリ消費量です
-Aksakal

1
Python 2の場合、次のインポートを追加する必要がありますfrom __future__ import absolute_import from __future__ import division from __future__ import print_function
user2589273

7

この素晴らしい例を作るための@amoebaへの巨大な小道具。その投稿で説明されている自動エンコーダのトレーニングと再構築の手順は、Rでも同様に簡単に実行できることを示したいと思います。以下の自動エンコーダは、アメーバの例を可能な限りエミュレートするようにセットアップされています-最適化とアーキテクチャ全体が同じです。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')
}

ここに画像の説明を入力してください

前述のように、より多くのエポックと、より深くおよび/またはよりスマートに訓練されたネットワークは、はるかに良い結果をもたらします。たとえば、 = 9 のPCA再構成エラーは約。トレーニングエポックを5から25に増やすだけで、上記のからほぼ同じエラー()を取得できます。このユースケースでは、自動エンコーダー派生コンポーネントは、9つの主要コンポーネントと同様の再構成エラーを提供します。クール!0.0356 0.0359k0.03560.0359


2
+1。いいね PythonのようにRでKerasを使用するのが簡単であることを見るのは良いことです。私の知る限り、ディープラーニングコミュニティでは誰もが最近Pythonを使用しているので、他の場所ではもっと難しくなるべきだと感じていました。
アメーバは、モニカを復活させる

2

以下は、結果を複製しようとするjupyterノートブックです。次の違いがあります。

  • テンソルフローを直接使用する代わりに、私はそれを使用して表示ケラ
  • 飽和を回避するためのreluではなく、leaky relu(つまり、エンコードされた出力が0)
    • これがAEのパフォーマンス低下の原因である可能性があります
  • 自動エンコーダ入力は[0,1]にスケーリングされたデータです
    • reluのオートエンコーダは[0-1]データで最適に動作することをどこかで読んだと思います
    • オートエンコーダーの入力がmean = 0、std = 1でノートブックを実行すると、AEのMSEがすべての次元数削減で0.7を超えたため、これが問題の1つである可能性があります
  • PCA入力は、mean = 0およびstd = 1のデータのままです
    • これは、PCAのMSE結果がPCAのMSE結果と比較できないことも意味する場合があります
    • たぶん、PCAとAEの両方の[0-1]データでこれを再実行するだけでしょう
  • PCA入力も[0-1]にスケーリングされます。PCAは(mean = 0、std = 1)データでも動作しますが、MSEはAEと比較できません

私のMSEは、1から6(入力に6列がある)の次元削減からのPCAと、dimからの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.buildNetworkとutils.ae_fit_encode_plot_mseなどの非標準関数をたくさん持っているように見えるutilsパッケージをインポートします...
Berowne Hlavaty

これは、ノートブックと同じレベルにある同じリポジトリ内の単なるファイルです。
-shadi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.