Voici le chiffre clé de l'article scientifique de 2006 de Hinton et Salakhutdinov:
Il montre la réduction de la dimensionnalité de l'ensemble de données MNIST ( images en noir et blanc de chiffres uniques) des 784 dimensions d'origine à deux.28×28
Essayons de le reproduire. Je n'utiliserai pas Tensorflow directement, car il est beaucoup plus facile d'utiliser Keras (une bibliothèque de niveau supérieur fonctionnant au-dessus de Tensorflow) pour de simples tâches d'apprentissage en profondeur comme celle-ci. H&S a utilisé une architecture avec des unités logistiques, pré-entraînée avec la pile de machines Boltzmann restreintes. Dix ans plus tard, cela semble très old-school. J'utiliserai une architecture plus simple avec des unités linéaires exponentielles sans aucune pré-formation. J'utiliserai l'optimiseur Adam (une implémentation particulière de la descente de gradient stochastique adaptatif avec momentum).784 → 512 → 128 → 2 → 128 → 512 → 784
784→1000→500→250→2→250→500→1000→784
784→512→128→2→128→512→784
Le code est copié-collé à partir d'un ordinateur portable Jupyter. Dans Python 3.6, vous devez installer matplotlib (pour pylab), NumPy, seaborn, TensorFlow et Keras. Lors de l'exécution dans le shell Python, vous devrez peut-être ajouter plt.show()
pour afficher les tracés.
Initialisation
%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)));
Cela produit:
PCA reconstruction error with 2 PCs: 0.056
Formation de l'autoencodeur
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
Cela prend environ 35 secondes sur mon bureau de travail et génère:
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
vous pouvez donc déjà voir que nous avons dépassé la perte de PCA après seulement deux périodes d'entraînement.
(Soit dit en passant, il est instructif de modifier toutes les fonctions d'activation activation='linear'
et d'observer comment la perte converge précisément vers la perte PCA. En effet, l'autoencodeur linéaire est équivalent à PCA.)
Tracer la projection PCA côte à côte avec la représentation du goulot d'étranglement
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()
Reconstructions
Et maintenant, regardons les reconstructions (première rangée - images originales, deuxième rangée - PCA, troisième rangée - encodeur automatique):
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()
On peut obtenir de bien meilleurs résultats avec un réseau plus profond, une certaine régularisation et une formation plus longue. Expérience. L'apprentissage en profondeur est facile!