Aquí está la cifra clave del artículo de Science de 2006 de Hinton y Salakhutdinov:
![]()
Muestra la reducción de la dimensionalidad del conjunto de datos MNIST ( 28×28 imágenes en blanco y negro de un solo dígito) de las 784 dimensiones originales a dos.
Intentemos reproducirlo. No usaré Tensorflow directamente, porque es mucho más fácil usar Keras (una librería de alto nivel que se ejecuta sobre Tensorflow) para tareas sencillas de aprendizaje profundo como esta. H&S utilizado 784→1000→500→250→2→250→500→1000→784 arquitectura con unidades logísticas, preentrenadas con la pila de Máquinas de Boltzmann Restringidas. Diez años después, esto suena muy de la vieja escuela. Utilizaré un método más sencillo 784→512→128→2→128→512→784 arquitectura con unidades lineales exponenciales sin ningún tipo de entrenamiento previo. Utilizaré el optimizador Adam (una implementación particular del descenso de gradiente estocástico adaptativo con impulso).
El código está copiado de un cuaderno Jupyter. En Python 3.6 necesitas instalar matplotlib (para pylab), NumPy, seaborn, TensorFlow y Keras. Cuando se ejecuta en el shell de Python, es posible que tenga que añadir plt.show()
para mostrar los gráficos.
Inicialización
%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)));
Estas salidas:
PCA reconstruction error with 2 PCs: 0.056
Entrenamiento del 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
Esto toma ~35 segundos en mi escritorio de trabajo y salidas:
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
por lo que ya se puede ver que superamos la pérdida de PCA después de sólo dos épocas de entrenamiento.
(Por cierto, es instructivo cambiar todas las funciones de activación a activation='linear'
y observar cómo la pérdida converge precisamente a la pérdida PCA. Esto se debe a que el autoencoder lineal es equivalente al PCA).
Trazado de la proyección PCA junto a la representación del cuello de botella
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()
![enter image description here]()
Reconstrucciones
Y ahora veamos las reconstrucciones (primera fila - imágenes originales, segunda fila - PCA, tercera fila - 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()
![enter image description here]()
Se pueden obtener resultados mucho mejores con una red más profunda, cierta regularización y un entrenamiento más largo. Experimento. El aprendizaje profundo es fácil.