Membangun autoencoder di Tensorflow untuk melampaui PCA

31

Hinton dan Salakhutdinov dalam Mengurangi Dimensi Data dengan Neural Networks, Science 2006 mengusulkan PCA non-linear melalui penggunaan autoencoder yang mendalam. Saya telah mencoba membangun dan melatih autoencoder PCA dengan Tensorflow beberapa kali tetapi saya tidak pernah dapat memperoleh hasil yang lebih baik daripada PCA linier.

Bagaimana saya bisa melatih autoencoder secara efisien?

(Kemudian diedit oleh @amoeba: versi asli dari pertanyaan ini berisi kode Python Tensorflow yang tidak berfungsi dengan benar. Orang dapat menemukannya di riwayat edit.)

Donbeo
sumber
Saya telah menemukan kesalahan dalam fungsi aktivasi kelas Layer. Saya menguji apakah sekarang ini berfungsi
Donbeo
apakah Anda memperbaiki kesalahan Anda?
Pinocchio
Hai Donbeo. Saya mengambil kebebasan untuk menghapus kode dari pertanyaan Anda (kode tersebut masih dapat dengan mudah ditemukan dalam riwayat edit). Dengan kodenya, pertanyaan Anda tampak seperti jenis "Bantu saya menemukan bug" yang bukan topik di sini. Pada saat yang sama, utas ini memiliki tampilan 4k, mungkin berarti banyak orang datang ke sini melalui pencarian google, jadi saya tidak ingin menutup pertanyaan Anda. Saya memutuskan untuk mengirim jawaban dengan autoencoder walk-through, tetapi untuk alasan kesederhanaan saya menggunakan Keras (berjalan di atas Tensorflow) alih-alih Tensorflow mentah. Apakah Anda pikir ini menjawab Q Anda?
Amoeba mengatakan Reinstate Monica

Jawaban:

42

Berikut adalah tokoh kunci dari makalah Science 2006 oleh Hinton dan Salakhutdinov:

Ini menunjukkan pengurangan dimensi dataset MNIST ( gambar hitam dan putih dari satu digit) dari 784 dimensi asli menjadi dua.28×28

784100050025022505001000784
7845121282128512784

Kode ini disalin dari notebook Jupyter. Dalam Python 3.6 Anda perlu menginstal matplotlib (untuk pylab), NumPy, seaborn, TensorFlow dan Keras. Saat menjalankan shell Python, Anda mungkin perlu menambahkan plt.show()untuk memperlihatkan plot.

Inisialisasi

%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)));

Output ini:

PCA reconstruction error with 2 PCs: 0.056

Pelatihan autoencoder

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

Ini membutuhkan ~ 35 detik pada desktop pekerjaan saya dan menghasilkan:

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

jadi Anda sudah bisa melihat bahwa kami melampaui kehilangan PCA setelah hanya dua zaman pelatihan.

(Omong-omong, itu adalah instruktif untuk mengubah semua fungsi aktivasi ke activation='linear'dan untuk mengamati bagaimana kerugian konvergen tepat ke hilangnya PCA. Itu karena autoencoder linier setara dengan PCA.)

Merencanakan proyeksi PCA berdampingan dengan representasi bottleneck

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()

masukkan deskripsi gambar di sini

Rekonstruksi

Dan sekarang mari kita lihat rekonstruksi (baris pertama - gambar asli, baris kedua - PCA, baris ketiga - autoencoder):

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()

masukkan deskripsi gambar di sini

Seseorang dapat memperoleh hasil yang lebih baik dengan jaringan yang lebih dalam, beberapa regularisasi, dan pelatihan yang lebih lama. Percobaan. Belajar mendalam itu mudah!

amuba kata Reinstate Monica
sumber
2
Saya terkejut seberapa baik PCA bekerja hanya dengan 2 komponen! terima kasih telah memposting kode
Aksakal
2
Fantastis! Kebodohan!
Matthew Drury
2
@ shadi Saya benar-benar menemukan panggilan langsung ke svd () lebih sederhana :)
amoeba mengatakan Reinstate Monica
1
Perbedaan kinerja bahkan lebih besar ketika menggunakan lebih banyak komponen. Saya mencoba 10 bukannya dua dan autoencoder jauh lebih baik. Kerugiannya adalah kecepatan dan konsumsi memori
Aksakal
1
untuk python 2 Anda perlu menambahkan impor berikutfrom __future__ import absolute_import from __future__ import division from __future__ import print_function
user2589273
7

Alat peraga besar untuk @amoeba untuk membuat contoh yang bagus. Saya hanya ingin menunjukkan bahwa pelatihan auto-encoder dan prosedur rekonstruksi yang dijelaskan dalam pos itu dapat dilakukan juga di R dengan kemudahan yang sama. Auto-encoder di bawah ini adalah setup sehingga mengemulasi contoh amuba sedekat mungkin - pengoptimal yang sama dan arsitektur keseluruhan. Biaya pastinya tidak dapat direproduksi karena back-end TensorFlow tidak diunggulkan dengan cara yang sama.

Inisialisasi

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

Autoencoder

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

Merencanakan proyeksi PCA berdampingan dengan representasi bottleneck

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 )

masukkan deskripsi gambar di sini

Rekonstruksi

Kita bisa melakukan rekonstruksi digit dengan cara biasa. (Baris atas adalah digit asli, baris tengah rekonstruksi PCA dan baris bawah rekonstruksi autoencoder.)

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

masukkan deskripsi gambar di sini

k0,03560,0359

usεr11852 kata Reinstate Monic
sumber
2
+1. Bagus. Sangat bagus untuk melihat bahwa menggunakan Keras dalam R sama mudahnya dengan Python. Sejauh yang saya bisa lihat, dalam komunitas pembelajaran yang mendalam setiap orang menggunakan Python hari ini, jadi saya mendapat kesan bahwa itu seharusnya lebih sulit di tempat lain.
Amuba kata Reinstate Monica
2

Ini adalah notebook jupyter saya tempat saya mencoba mereplikasi hasil Anda, dengan perbedaan-perbedaan berikut:

  • alih-alih menggunakan tensorflow secara langsung, saya menggunakannya dengan tampilan keras
  • leaky relu bukannya relu untuk menghindari saturasi (yaitu output yang disandikan menjadi 0)
    • ini mungkin menjadi alasan untuk kinerja AE yang buruk
  • input autoencoder adalah data yang diskalakan ke [0,1]
    • Saya rasa saya membaca di suatu tempat bahwa autoencoder dengan relu bekerja paling baik dengan data [0-1]
    • menjalankan notebook saya dengan input autoencoder menjadi mean = 0, std = 1 memberi MSE untuk AE> 0,7 untuk semua pengurangan dimensi, jadi mungkin ini adalah salah satu masalah Anda
  • Input PCA disimpan menjadi data dengan mean = 0 dan std = 1
    • Ini juga dapat berarti bahwa hasil MSE PCA tidak sebanding dengan hasil MSE PCA
    • Mungkin saya akan menjalankannya kembali nanti dengan data [0-1] untuk PCA dan AE
  • Input PCA juga diskalakan ke [0-1]. PCA bekerja dengan data (rata-rata = 0, std = 1) juga, tetapi MSE tidak dapat dibandingkan dengan AE

Hasil MSE saya untuk PCA dari pengurangan dimensi 1 hingga 6 (di mana input memiliki 6 kolom) dan untuk AE dari redup. merah. dari 1 hingga 6 di bawah ini:

Dengan input PCA sedang (berarti = 0, std = 1) sedangkan input AE menjadi [0-1] berkisar - 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
  • 0,0094: PCA5
  • .0502: AE5
  • .0507: AE6
  • .0514: AE4
  • .0532: AE3
  • .0772: PCA4
  • .1231: PCA3
  • .2588: AE2
  • .2831: PCA2
  • 0,3773: AE1
  • .3885: PCA1

PCA linier tanpa pengurangan dimensi dapat mencapai 9e-15 karena hanya dapat mendorong apa pun yang tidak dapat masuk ke komponen terakhir.

shadi
sumber
shadi, notebook Anda mengimpor paket utils yang tampaknya memiliki banyak fungsi non-standar utils.buildNetwork dan utils.ae_fit_encode_plot_mse misalnya ...
Berowne Hlavaty
Itu hanya file di repositori yang sama di tingkat yang sama dengan notebook.
shadi