Побудова автоінкодера в Тенсдорфлоу, щоб перевершити PCA


31

Гінтон і Салаххутдінов у зменшенні розмірності даних за допомогою нейронних мереж Science 2006 запропонували нелінійну PCA шляхом використання глибокого автокодера. Я кілька разів намагався створити і навчити автокодер PCA з Tensorflow, але мені ніколи не вдалося отримати кращий результат, ніж лінійний PCA.

Як я можу ефективно тренувати автокодер?

(Пізніше редагуйте @amoeba: оригінальна версія цього питання містила код Python Tensorflow, який працював неправильно. Його можна знайти в історії редагування.)


Я виявив помилку в функції активації класу Шару. Я тестую, чи зараз це працює
Донбео

ви виправили свою помилку?
Буратіно

Привіт Донбео. Я зважився на видалення коду з вашого запитання (код все ще можна легко знайти в історії редагування). Що стосується коду, ваше запитання було схоже на тип "Допоможіть мені знайти помилку", яке тут не тематично. У той же час ця тема має 4-х перегляди, ймовірно, це означає, що багато людей приходять сюди за допомогою пошукових запитів Google, тому я не хотів закривати ваше запитання. Я вирішив опублікувати відповідь за допомогою проходження автокодера, але для причин простоти я використав Keras (працює на вершині Tensorflow) замість сирого Tensorflow. Як ви думаєте, це відповідає на ваш Q?
амеба каже, що повернеться до Моніки

Відповіді:


42

Ось ключова фігура з наукової праці 2006 року Гінтона та Салахутдінова:

Він показує зменшення розмірності набору даних MNIST ( чорно-білих зображень одноцифрових) від початкових 784 розмірів до двох.28×28

Спробуємо її відтворити. Я не буду використовувати Tensorflow безпосередньо, бо набагато простіше використовувати Keras (бібліотеку вищого рівня, що працює над Tensorflow) для простих завдань глибокого навчання, як це. H&S використовувала архітектуру з логістичними одиницями, попередньо підготовленими з набором обмежених машин Больцмана. Через десять років це звучить дуже старо-шкільно. Я буду використовувати простіший 784 512 128 2 128 512

784100050025022505001000784
архітектура з експоненціальними лінійними одиницями без будь-якої попередньої підготовки. Я буду використовувати оптимізатор Адама (конкретна реалізація адаптивного стохастичного градієнтного спуску з імпульсом).
7845121282128512784

Код копіюється із зошита Юпітера. У 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

тож ви вже бачите, що ми перевершили втрати 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, третій ряд - 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()

введіть тут опис зображення

З глибшою мережею, деякою регуляризацією та тривалістю навчання можна отримати набагато кращі результати. Експеримент. Глибоке навчання легко!


2
Я здивований, як добре PCA працював із лише 2 компонентами! дякую за розміщення коду
Aksakal

2
Фантастичний! Дурність!
Метью Друрі

2
@shadi Я фактично знаходжу прямий дзвінок на svd () простіше :)
амеба каже Відновити Моніку

1
Діапазон продуктивності ще більший при використанні більшої кількості компонентів. Я спробував 10 замість двох і автоенкодер був набагато краще. Недоліком є ​​швидкість і споживання пам’яті
Аксакал

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

введіть тут опис зображення

к0,03560,0359


2
+1. Приємно. Добре бачити, що керасом в R так само просто, як і в Python. Наскільки я бачу, в громаді глибокого навчання сьогодні всі користуються Python, тому я мав враження, що в інших місцях має бути складніше.
Амеба каже: Відновіть Моніку

2

Ось мій зошит з юпітера, де я намагаюся повторити ваш результат із такими відмінностями:

  • замість того, щоб безпосередньо використовувати tensorflow, я використовую його для перегляду керас
  • leaky relu замість relu, щоб уникнути насичення (тобто закодований вихід 0)
    • це може бути причиною поганої роботи АЕ
  • вхід автокодера - дані масштабуються до [0,1]
    • Я думаю, я десь прочитав, що автокодери з relu найкраще працюють з даними [0-1]
    • запуск мого ноутбука з вводом автокодерів середнім = 0, std = 1 дав MSE для AE> 0,7 для всіх зменшень розмірності, тому, можливо, це одна з ваших проблем
  • Вхід PCA зберігається як дані з середнім = 0 і std = 1
    • Це також може означати, що результат MSE PCA не порівнянний з результатом MSE PCA
    • Можливо, я просто повторю це пізніше з даними [0-1] для PCA та AE
  • Вхід PCA також масштабується до [0-1]. PCA також працює з (середнім = 0, std = 1) даними, але MSE буде непорівнянним з AE

Мої MSE результати для PCA від зменшення розмірності від 1 до 6 (де на вході є 6 стовпців) і для AE з dim. червоний. від 1 до 6 нижче:

Якщо вхід PCA (середнє = 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, оскільки він може просто натиснути все, що не вдалося вписати в останній компонент.


Шаді, ваш ноутбук імпортує пакет утиліт, який, здається, має багато нестандартних функцій utils.buildNetwork та utils.ae_fit_encode_plot_mse, наприклад ...
Berowne Hlavaty

Це просто файл у тому ж сховищі на тому ж рівні, що і ноутбук.
шаді
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.