Hier ist die Schlüsselfigur aus dem Science Paper von 2006 von Hinton und Salakhutdinov:
Es zeigt die Dimensionsreduktion des MNIST-Datensatzes ( Schwarzweißbilder mit einzelnen Ziffern) von den ursprünglichen 784-Dimensionen auf zwei.28 ×28
Versuchen wir es zu reproduzieren. Ich werde Tensorflow nicht direkt verwenden, da es viel einfacher ist, Keras (eine übergeordnete Bibliothek, die auf Tensorflow läuft) für einfache vertiefende Lernaufgaben wie diese zu verwenden. H & S verwendete eine Architektur von mit logistischen Einheiten, die mit dem Stapel der eingeschränkten Boltzmann-Maschinen vorab trainiert wurden. Zehn Jahre später klingt das sehr altmodisch. Ich werde eine einfachere Architektur mit exponentiellen Lineareinheiten ohne Vorkenntnisse verwenden. Ich werde den Adam-Optimierer verwenden (eine spezielle Implementierung des adaptiven stochastischen Gradientenabfalls mit Impuls).
784 → 1000 → 500 → 250 → 2 → 250 → 500 → 1000 → 784
784 → 512 → 128 → 2 → 128 → 512 → 784
Der Code wird aus einem Jupyter-Notizbuch kopiert. In Python 3.6 müssen Sie matplotlib (für pylab), NumPy, seaborn, TensorFlow und Keras installieren. Wenn Sie in der Python-Shell arbeiten, müssen Sie möglicherweise hinzufügen plt.show()
, um die Diagramme anzuzeigen.
Initialisierung
%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)));
Dies gibt aus:
PCA reconstruction error with 2 PCs: 0.056
Training des Autoencoders
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
Dies dauert ca. 35 Sekunden auf meinem Arbeits-Desktop und gibt Folgendes aus:
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
Sie sehen also bereits, dass wir den PCA-Verlust nach nur zwei Trainingsepochen überschritten haben.
(Übrigens ist es activation='linear'
aufschlussreich , alle Aktivierungsfunktionen zu ändern und zu beobachten, wie der Verlust genau zum PCA-Verlust konvergiert. Dies liegt daran, dass der lineare Autoencoder dem PCA äquivalent ist.)
Nebeneinanderzeichnen der PCA-Projektion mit der Flaschenhalsdarstellung
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()
Rekonstruktionen
Und jetzt schauen wir uns die Rekonstruktionen an (erste Reihe - Originalbilder, zweite Reihe - PCA, dritte Reihe - 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()
Mit einem tieferen Netzwerk, etwas Regularisierung und längerem Training kann man viel bessere Ergebnisse erzielen. Experiment. Tiefes Lernen ist einfach!