El aprendizaje automático se usa en última instancia para predecir los resultados dado un conjunto de características. Por lo tanto, cualquier cosa que podamos hacer para generalizar el rendimiento de nuestro modelo se considera una ganancia neta.
La deserción es una técnica utilizada para evitar que un modelo se adapte excesivamente. El abandono funciona estableciendo aleatoriamente los bordes salientes de las unidades ocultas (neuronas que forman capas ocultas) en 0 en cada actualización de la fase de entrenamiento.
Si echa un vistazo a la documentación de Keras para la capa de deserción, verá un enlace a un documento escrito por Geoffrey Hinton y sus amigos, que contiene la teoría detrás de la deserción.
https://medium.com/ media / 6c95e40358f629f4f9a1833e094530a1 / href
Código
En el ejemplo a continuación, usaremos Keras para construir una red neuronal con el objetivo de reconocer dígitos escritos a mano.
from keras.datasets import mnist
from matplotlib import pyplot as plt
plt.style.use(‘dark_background’)
from keras.models import Sequential
from keras.layers import Dense, Flatten, Activation, Dropout
from keras.utils import normalize, to_categorical
Usamos Keras para importar los datos a nuestro programa. Los datos ya están divididos en los conjuntos de entrenamiento y pruebas.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
Echemos un vistazo para ver con qué estamos trabajando.
plt.imshow(x_train[0], cmap = plt.cm.binary)
plt.show()
Después de que hayamos entrenado nuestro modelo, debería poder reconocer la imagen anterior como un cinco .
Hay un poco de preprocesamiento que debemos realizar de antemano. Normalizamos los píxeles (características) de modo que van de 0 a 1. Esto permitirá que el modelo converja hacia una solución mucho más rápida.
A continuación, transformamos cada una de las etiquetas de destino para una muestra dada en una matriz de 1s y 0s donde el índice del número 1 indica el dígito que representa la imagen. Hacemos esto porque de lo contrario nuestro modelo interpretaría que el dígito 9 tiene una prioridad más alta que el número 3.
X_train = normalize(X_train, axis=1) X_test = normalize(X_test, axis=1) y_train = to_categorical(y_train) y_test = to_categorical(y_test)
Sin Dropout
Antes de introducir una matriz bidimensional en una red neuronal, utilizamos una capa plana que la transforma en una matriz unidimensional mediante la adición.
Cada fila subsiguiente a la que la precedió. Vamos a utilizar dos capas ocultas que consisten en 128 neuronas cada una y una capa de salida que consta de 10 neuronas, cada una para uno de los 10 dígitos posibles.
La función de activación de softmax devolverá la probabilidad de que una muestra represente un dígito dado.
model = Sequential() model.add(Flatten(input_shape=(28, 28))) model.add(Dense(128)) model.add(Activation('relu')) model.add(Dense(128)) model.add(Activation('relu')) model.add(Dense(10)) model.add(Activation('softmax')) model.summary()
Desde que estamos al intentar predecir las clases, utilizamos la crossentropy categórica como nuestra función de pérdida.
Mediremos el rendimiento del modelo con precisión.
model.compile( loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] )
Apartamos el 10% de los datos para la validación. Usaremos esto para comparar la tendencia de un modelo a adaptarse con y sin deserción.
Un tamaño de lote de 32 implica que calcularemos el gradiente y daremos un paso en la dirección del gradiente con una magnitud igual a la velocidad de aprendizaje.
Después de haber pasado 32 muestras a través de la red neuronal. Hacemos esto un total de 10 veces según lo especificado por el número de épocas.
history = model.fit( X_train, y_train, epochs=10, batch_size=32, validation_split=0.1, verbose = 1, shuffle=True )
Podemos trazar las precisiones de entrenamiento y validación en cada época usando la variable de historial devuelta por ajustada función.
loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(loss) + 1) plt.plot(epochs, loss, 'y', label='Training loss') plt.plot(epochs, val_loss, 'r', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
Como puede ver, sin abandono, la pérdida de validación deja de disminuir después del tercero época
acc = history.history['acc'] val_acc = history.history['val_acc'] plt.plot(epochs, acc, 'y', label='Training acc') plt.plot(epochs, val_acc, 'r', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
Como puede ver, sin deserción, la precisión de la validación tiende a estabilizarse en la tercera época.
Al usar este modelo simple, pudimos obtener una precisión de más del 97%.
test_loss, test_acc = model.evaluate(X_test, y_test) test_acc
Abandono
Existe cierto debate sobre si el abandono se debe colocar antes o después de la función de activación. Como regla general, coloque el abandono después de la función de activación para todas las funciones de activación que no sean relu .
Al pasar 0.5, cada unidad oculta (neurona) se establece en 0 con una probabilidad de 0.5. En otras palabras, hay un cambio del 50% de que la salida de una neurona dada será forzada a 0.
model_dropout = Sequential() model_dropout.add(Flatten(input_shape=(28, 28))) model_dropout.add(Dense(128)) model_dropout.add(Dropout(0.5)) model_dropout.add(Activation('relu')) model_dropout.add(Dense(128)) model_dropout.add(Dropout(0.5)) model_dropout.add(Activation('relu')) model_dropout.add(Dense(10)) model_dropout.add(Activation('softmax')) model_dropout.summary()
Nuevamente, ya que estamos tratando de predecir las clases, utilizamos la función de pérdida cruzada categórica.
model_dropout.compile( loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] )
Al proporcionar el parámetro de división de validaciones, el modelo separará una fracción de los datos de entrenamiento y evaluará t.
La pérdida y cualquier métrica del modelo sobre estos datos al final de cada época. Si la premisa detrás de la deserción se mantiene, entonces deberíamos ver una diferencia notable en la precisión de validación en comparación con el modelo anterior.
El parámetro de orden aleatorio mezclará los datos de entrenamiento antes de cada época.
history_dropout = model_dropout.fit( X_train, y_train, epochs=10, batch_size=32, validation_split=0.1, verbose = 1, shuffle=True )
Como puede ver, la pérdida de validación es significativamente menor que la obtenida con el modelo regular.
loss = history_dropout.history['loss'] val_loss = history_dropout.history['val_loss'] epochs = range(1, len(loss) + 1) plt.plot(epochs, loss, 'y', label='Training loss') plt.plot(epochs, val_loss, 'r', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
Como puede ver, el modelo convergió mucho más rápido y obtuvo una precisión de casi el 98% en el conjunto de validación, mientras que el modelo anterior se estabilizó alrededor de la tercera época.
acc = history_dropout.history['acc'] val_acc = history_dropout.history['val_acc'] plt.plot(epochs, acc, 'y', label='Training acc') plt.plot(epochs, val_acc, 'r', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
La precisión obtenida en el conjunto de prueba no es muy diferente de la obtenida del modelo sin abandono. Esto se debe con toda probabilidad al número limitado de muestras.
test_loss, test_acc = model_dropout.evaluate(X_test, y_test) test_acc
Consideraciones finales
La deserción puede ayudar a un modelo a generalizar el ajuste aleatorio salida de una neurona dada a 0.
Al configurar la salida a 0, la función de costo se vuelve más sensible a las neuronas vecinas, cambiando la forma en que se actualizarán los pesos durante el proceso de propagación hacia atrás.