Ejercicio: Creación y entrenamiento de una red neuronal
En esta unidad, usará Keras para compilar y entrenar una red neuronal que realiza un análisis de sentimiento. Para entrenar una red neuronal, necesita datos con los que entrenarla. En lugar de descargar un conjunto de datos externo, usará el conjunto de datos Clasificación de sentimientos de críticas de películas de IMDB que se incluye con Keras. El conjunto de datos de IMDB contiene 50 000 críticas de películas que se han puntuado individualmente como positiva (1) o negativa (0). El conjunto de datos se divide en 25 000 reseñas para entrenamiento y 25 000 reseñas para pruebas. El sentimiento que se expresa en estas reseñas es la base para la que la red neuronal analizará el texto que se le muestre y otorgará un puntuación del sentimiento.
El conjunto de datos de IMDB es uno de los conjuntos de datos útiles incluidos con Keras. Para obtener una lista completa de los conjuntos de datos integrados, vea https://keras.io/datasets/.
Escriba o pegue el código siguiente en la primera celda del cuaderno y haga clic en el botón Ejecutar (o presione Mayús + Intro) para ejecutarlo y agregar una nueva celda debajo:
from keras.datasets import imdb top_words = 10000 (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=top_words)
Este código carga el conjunto de datos de IMDB que se incluye con Keras y crea un diccionario que asigna las palabras de todas las 50 000 reseñas a enteros que indican la frecuencia de repetición relativa de las palabras. Cada palabra se asigna a un entero único. A la palabra más común se le asigna el número 1, a la segunda palabra más común se le asigna el número 2, y así sucesivamente.
load_data
también devuelve un par de tuplas que contienen las reseñas de películas (en este ejemplo,x_train
yx_test
) y los números 1 y 0 que clasifican dichas reseñas como positivas y negativas (y_train
yy_test
).Confirme que ve el mensaje "Using TensorFlow backend" (Usar el back-end de TensorFlow), que indica que Keras está usando TensorFlow como back-end.
Carga del conjunto de datos de IMDB
Si quiere que Keras use Microsoft Cognitive Toolkit, también conocido como CNTK, como su back-end, puede hacerlo agregando unas líneas de código al principio del cuaderno. Para obtener un ejemplo, vea CNTK and Keras in Azure Notebooks (CNTK y Keras en Azure Notebooks).
¿Qué ha hecho exactamente la carga de la función
load_data
? La variable denominadax_train
es una lista de 25 000 registros, cada uno de los cuales representa una reseña de una película. (x_test
también es una lista de 25 000 listas que representan 25 000 revisiones.x_train
se usará para el entrenamiento, mientras quex_test
se usará para realizar pruebas). Pero las listas internas, las que representan las reseñas de películas, no contienen palabras; contienen enteros. A continuación se muestra cómo se describe en la documentación de Keras:La razón de que las listas internas contengan números en lugar de texto es que no se entrena una red neuronal con texto, sino con números. En concreto, se entrena con tensores. En este caso, cada revisión es un tensor unidimensional (piense en una matriz unidimensional) que contiene enteros que identifican las palabras que contiene la reseña. Para mostrarlo, escriba la siguiente instrucción de Python en una celda vacía y ejecútela para ver los enteros que representan la primera reseña del conjunto de entrenamiento:
x_train[0]
Enteros que componen la primera reseña del conjunto de entrenamiento de IMDB
El primer número de la lista, 1, no representa ninguna palabra. Marca el inicio de la reseña y es el mismo para cada reseña del conjunto de datos. También se reservan los números 0 y 2, y se resta 3 de los otros números para asignar un entero de una reseña al entero correspondiente del diccionario. El segundo número, 14, hace referencia a la palabra que se corresponde con el número 11 en el diccionario, el tercer número representa la palabra que tiene asignado el número 19 en el diccionario y así sucesivamente.
¿Tiene curiosidad por ver el aspecto que tiene el diccionario? Ejecute la instrucción siguiente en una nueva celda del cuaderno:
imdb.get_word_index()
Solo se muestra un subconjunto de las entradas del diccionario, pero en total, el diccionario contiene más de 88 000 palabras y sus correspondientes enteros. La salida que verá probablemente no coincidirá con la salida de la captura de pantalla porque el diccionario se genera de nuevo cada vez que se llama a
load_data
.Diccionario que asigna palabras a enteros
Como ha visto, cada reseña del conjunto de datos se codifica como una colección de enteros en lugar de palabras. ¿Es posible revertir la codificación de una reseña para poder ver el texto original que la compone? Escriba las siguientes instrucciones en una celda nueva y ejecútelas para mostrar la primera reseña de
x_train
en formato de texto:word_dict = imdb.get_word_index() word_dict = { key:(value + 3) for key, value in word_dict.items() } word_dict[''] = 0 # Padding word_dict['>'] = 1 # Start word_dict['?'] = 2 # Unknown word reverse_word_dict = { value:key for key, value in word_dict.items() } print(' '.join(reverse_word_dict[id] for id in x_train[0]))
En la salida, ">" marca el principio de la reseña, mientras que "?" marca palabras que no están entre las 10 000 palabras más comunes del conjunto de datos. Estas palabras "desconocidas" se representan mediante el número 2 en la lista de enteros que representan una reseña. ¿Recuerda el parámetro
num_words
que pasó aload_data
? Aquí es donde entra en juego. No reduce el tamaño del diccionario, pero restringe el intervalo de enteros que se usa para codificar las reseñas.La primera reseña en formato de texto
Las reseñas son "limpias" en el sentido de que las letras se han convertido en minúsculas y se han quitado los caracteres de puntuación. Pero no están preparadas para entrenar una red neuronal para analizar el sentimiento del texto. Cuando entrena una red neuronal con la colección de tensores, cada tensor debe ser de la misma longitud. Actualmente, las listas que representan las reseñas en
x_train
yx_test
tienen longitudes diferentes.Afortunadamente, Keras incluye una función que toma una lista de registros como entrada y convierte las listas internas a una longitud especificada truncándolas, si es necesario, o rellenándolas con ceros. Escriba el código siguiente en el cuaderno y ejecútelo para forzar que todas las listas que representan reseñas de películas en
x_train
yx_test
tengan una longitud de 500 enteros:from keras.preprocessing import sequence max_review_length = 500 x_train = sequence.pad_sequences(x_train, maxlen=max_review_length) x_test = sequence.pad_sequences(x_test, maxlen=max_review_length)
Ahora que los datos de entrenamiento y pruebas están preparados, es el momento de generar el modelo. Ejecute el siguiente código en el cuaderno para crear una red neuronal que realice análisis de sentimiento:
from keras.models import Sequential from keras.layers import Dense from keras.layers.embeddings import Embedding from keras.layers import Flatten embedding_vector_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vector_length, input_length=max_review_length)) model.add(Flatten()) model.add(Dense(16, activation='relu')) model.add(Dense(16, activation='relu')) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy',optimizer='adam', metrics=['accuracy']) print(model.summary())
Confirme que el resultado es similar a este:
Creación de una red neuronal con Keras
Este código es la esencia de cómo se debe crear una red neuronal con Keras. Primero crea una instancia de un objeto
Sequential
que representa un modelo "secuencial", que se compone de una pila de capas de un extremo a otro en el que la salida de una capa proporciona la entrada de la siguiente.Las siguientes instrucciones agregan capas al modelo. En primer lugar hay una capa de incrustación, que es fundamental para las redes neuronales que procesan las palabras. La capa de incrustación básicamente asigna matrices multidimensionales que contienen los índices de palabras de enteros en matrices de punto flotante que contienen menos dimensiones. También permite que las palabras con significados parecidos se traten de la misma forma. Un tratamiento completo de incrustaciones de palabras está fuera del ámbito de este laboratorio, pero puede obtener más información en Why You Need to Start Using Embedding Layers (Por qué necesita empezar a usar las capas de incrustación). Si prefiere obtener una explicación más académica, consulte Efficient Estimation of Word Representations in Vector Space (Estimación eficaz de representaciones de palabras en el espacio vectorial). La llamada a Flatten tras agregar la capa de incrustación cambia la forma de la salida para que sea la entrada de la siguiente capa.
Las tres siguientes capas agregadas al modelo son capas densas, también conocidas como capas completamente conectadas. Estas son las capas tradicionales que son comunes en las redes neuronales. Cada capa contiene n nodos o neuronas, y cada neurona recibe la entrada de cada neurona de la capa anterior, de ahí el término "totalmente conectado". Son estas capas las que permiten a una red neuronal "aprender" de los datos de entrada adivinando iterativamente la salida, comprobando los resultados y ajustando las conexiones para producir mejores resultados. Las dos primeras capas densas de esta red contienen 16 neuronas cada una. Este número se ha elegido arbitrariamente. Podría mejorar la precisión del modelo si experimenta con diferentes tamaños. La última capa densa contiene simplemente una neurona porque el objetivo final de la red consiste en predecir una salida, es decir, una puntuación de sentimiento de 0,0 a 1,0.
El resultado es la red neuronal que se muestra a continuación. La red contiene una capa de entrada, una capa de salida y dos capas ocultas (las capas densas que contiene 16 neuronas cada una). Como referencia, algunas de las redes neuronales más sofisticadas de hoy en día tienen más de 100 capas. Un ejemplo es ResNet-152 de Microsoft Research, cuya precisión en la identificación de objetos en fotografías a veces es superior a la de una persona. Podría crear ResNet-152 con Keras, pero primero necesitaría un clúster de equipos equipados con GPU para entrenarlo desde el principio.
Visualización de la red neuronal
La llamada a la función compile "compila" el modelo especificando parámetros importantes, como qué optimizador y qué métricas usar para evaluar la precisión del modelo de cada paso de entrenamiento. El entrenamiento no comienza hasta que se llama a la función
fit
del modelo, por lo que la llamada acompile
normalmente se ejecuta rápidamente.Ahora llame a la función fit para entrenar la red neuronal:
hist = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=5, batch_size=128)
El entrenamiento tardará aproximadamente 6 minutos, o poco más de 1 minuto por época.
epochs=5
indica a Keras que puede hacer cinco pasadas hacia delante y hacia atrás en el modelo. Con cada paso, el modelo aprende de los datos de entrenamiento y mide ("valida") la calidad de lo aprendido con los datos de prueba. Después, realiza ajustes y vuelve atrás para el siguiente paso o época. Esto se refleja en la salida de la funciónfit
, que muestra la precisión del entrenamiento (acc
) y la precisión de validación (val_acc
) de cada época.batch_size=128
indica a Keras que use 128 ejemplos de entrenamiento a la vez para entrenar la red. Un tamaño de lote más grande acelera el tiempo de entrenamiento (se necesitan menos pasos en cada época para consumir todos los datos de entrenamiento), pero los lotes más pequeños a veces aumentan la precisión. Una vez haya completado este laboratorio, es posible que quiera volver atrás y volver a entrenar el modelo con un tamaño de lote de 32 para ver qué efecto tiene en la precisión del modelo, si hay alguno. Casi se duplica el tiempo de entrenamiento.Entrenamiento del modelo
Este modelo es inusual, ya que aprende bien con tan solo unas épocas. La precisión de entrenamiento se amplía rápidamente casi al 100 %, mientras que la precisión de validación sube para una época o dos y, después, se mantiene. Por lo general, no quiere entrenar un modelo durante más tiempo del necesario para que se estabilicen estas precisiones. El riesgo es el sobreajuste, lo que provoca que el modelo se comporte bien con datos de prueba, pero no tan bien con datos reales. Una indicación de que un modelo tiene sobreajuste es una discrepancia creciente entre la precisión del entrenamiento y la precisión de validación. Para obtener una introducción excelente sobre el sobreajuste, consulte Overfitting in Machine Learning: What It Is and How to Prevent It (Sobreajuste en Machine Learning: qué es y cómo evitarlo).
Para visualizar los cambios en la precisión de entrenamiento y de validación a medida que avanza el entrenamiento, ejecute las siguientes instrucciones en una nueva celda del cuaderno:
import seaborn as sns import matplotlib.pyplot as plt %matplotlib inline sns.set() acc = hist.history['acc'] val = hist.history['val_acc'] epochs = range(1, len(acc) + 1) plt.plot(epochs, acc, '-', label='Training accuracy') plt.plot(epochs, val, ':', label='Validation accuracy') plt.title('Training and Validation Accuracy') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend(loc='upper left') plt.plot()
La precisión de datos procede del objeto
history
devuelto por la funciónfit
del modelo. En función del gráfico que ve, ¿recomendaría aumentar el número de épocas de entrenamiento, reducirlo o dejarlo igual?Otra manera de comprobar si existe sobreajuste es comparar la pérdida de entrenamiento con la pérdida de validación a medida que avanza el entrenamiento. Los problemas de optimización como este tratan de minimizar una función de pérdida. Puede leer más aquí. Para una época determinada, una pérdida de entrenamiento mucho mayor que una pérdida de validación puede ser un indicio de sobreajuste. En el paso anterior, ha usado las propiedades
acc
yval_acc
de la propiedadhistory
del objetohistory
para trazar la precisión de entrenamiento y de validación. La misma propiedad también contiene valores denominadosloss
yval_loss
que representan una pérdida de entrenamiento y de validación, respectivamente. Si quisiera trazar estos valores para generar un gráfico como el siguiente, ¿cómo modificaría el código anterior para hacerlo?Pérdida de entrenamiento y de validación
Dado que la brecha entre la pérdida de entrenamiento y de validación comienza a aumentar en la tercer época, ¿qué diría si alguien sugiere aumentar el número de épocas a 10 o 20?
Por último, llame al método
evaluate
del modelo para determinar la precisión con la que el modelo es capaz de cuantificar el sentimiento expresado en texto en función de los datos de prueba enx_test
(reseñas) yy_test
(ceros y unos, o "etiquetas", que indican qué reseñas son positivas y cuáles negativas):scores = model.evaluate(x_test, y_test, verbose=0) print("Accuracy: %.2f%%" % (scores[1] * 100))
¿Cuál es la precisión calculada del modelo?
Probablemente ha logrado una precisión de entre un 85 % y un 90 %. Esto es aceptable, considerando que ha compilado el modelo desde el principio (en lugar de usar una red neuronal previamente entrenada) y el tiempo de entrenamiento fue corto, incluso sin una GPU. Es posible conseguir precisiones de un 95 % o superiores con arquitecturas de redes neuronales alternativas, especialmente redes neuronales recurrentes (RNN) que usan capas de larga memoria a corto plazo (LSTM). Keras facilita la compilación de dichas redes, pero puede aumentar exponencialmente el tiempo de entrenamiento. El modelo que ha compilado consigue un equilibrio razonable entre la precisión y el tiempo de entrenamiento. Pero si quiere obtener más información sobre la creación de RNN con Keras, vea Understanding LSTM and its Quick Implementation in Keras for Sentiment Analysis (Descripción de LSTM y su implementación rápida en Keras para el análisis de sentimiento).