Blog
Proyecto: Secuencia de Clasificación de Críticas de Películas
- Publicado por: Rafael Fernandez
- Categoría: Deep Learning
-En este proyecto descubrirás cómo puedes desarrollar Modelos de red neural recurrente LSTM para problemas de clasificación de secuencias en Python usando la biblioteca de aprendizaje profundo de Keras.
-Cómo desarrollar un modelo LSTM para un problema de clasificación de secuencias.
-Cómo reducir la sobreadaptación en sus modelos LSTM mediante el uso de la salida.
-Cómo combinar los modelos LSTM con redes neuronales convolucionales que destacan en el aprendizaje de las relaciones espaciales.
La clasificación de secuencias es un problema de modelado predictivo en el que se tiene algunas secuencias de variables de entrada sobre el espacio o el tiempo y la tarea es predecir una categoría para la secuencia. Lo que hace que esta tarea sea un problema dificil es que las secuencias pueden variar en duración, estar formadas por un vocabulario muy amplio de símbolos de entrada y puede requerir que el modelo aprenda el contexto o las dependencias a largo plazo entre símbolos en la secuencia de entrada.
LSTM Simple para la Clasificación de Secuencias
El problema que usaremos para demostrar el aprendizaje secuencial en este tutorial es el IMDB problema de clasificación del sentimiento de la crítica o review cinematográfico, introducido en este post del curso de Deep Learning. Podemos rápidamente desarrollar un pequeño LSTM para el problema de la IMDB y lograr una buena precisión. Empecemos por importar las clases y funciones necesarias para este modelo e inicializar el número aleatorio a un valor constante para asegurarnos de que podemos reproducir fácilmente los resultados.
import numpy from keras.datasets import imdb from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers.embeddings import Embedding from keras.preprocessing import sequence # semilla aleatoria de reproducibilidad numpy.random.seed(7)
Necesitamos cargar el conjunto de datos IMDB. Estamos limitando el conjunto de datos a las 5.000 palabras principales. También dividimos el conjunto de datos en series de entranmientos (50%) y de pruebas (50%).
# cargar el conjunto de datos pero sólo mantener las n palabras principales, cero el resto top_words = 5000 (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words)
A continuación, debemos truncar y rellenar las secuencias de entrada para que tengan la misma longitud para el modelado. El modelo aprenderá que los valores cero no llevan información, de hecho las secuencias no tienen la misma longitud en términos de contenido, pero se requieren los mismos vectores de longitud para realizar el cálculo en Keras.
# truncar y rellenar secuencias de entrada 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 podemos definir, compilar y ajustar nuestro modelo LSTM. La primera capa es la capa incrustada que usa 32 vectores de longitud para representar cada palabra. La siguiente capa es la capa LSTM con 100 unidades de memoria (neuronas inteligentes). Finalmente, debido a que este es un problema de clasificación, usamos un Dense una capa de salida con una sola neurona y una función de activación sigmoide para hacer 0 o 1 predicciones para las dos clases (buena y mala) del problema. Porque es un problema de clasificación binaria, log loss se utiliza como función de pérdida (cruzamiento binario o binary_crossentropy en Keras). El antiguo ADAM utiliza un algoritmo de optimización. El modelo es de sólo 3 épocas porque se sobrepone rápidamente al problema. Un gran tamaño de lote de 64 revisiones se utiliza para espaciar las actualizaciones de peso.
# creacion del modelo embedding_vecor_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(LSTM(100)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary()) model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=3, batch_size=64)
Una vez ajustado, estimamos el rendimiento del modelo en revisiones no vistas.
# Final evaluacion del modelo scores = model.evaluate(X_test, y_test, verbose=0) print("Precision: %.2f%%" % (scores[1]*100))
Para mayor información, aquí está la lista completa de códigos para esta red LSTM con el conjunto de datos IMDB.
# LSTM Secuencia de Clasificación de Críticas de Películas con IMDB dataset import numpy from keras.datasets import imdb from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers.embeddings import Embedding from keras.preprocessing import sequence # semilla aleatoria de reproducibilidad numpy.random.seed(7) # cargar el conjunto de datos pero sólo mantener las n palabras principales, cero el resto top_words = 5000 (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words) # truncar y rellenar secuencias de entrada 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) # creacion del modelo embedding_vecor_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(LSTM(100)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary()) model.fit(X_train, y_train, epochs=3, batch_size=64) # Final evaluacion del modelo scores = model.evaluate(X_test, y_test, verbose=0) print("Precision: %.2f%%" % (scores[1]*100))
Al ejecutar este ejemplo se obtiene la siguiente salida.
Epoch 1/3 25000/25000 [==============================] - 162s 6ms/step - loss: 0.4615 - acc: 0.7824 Epoch 2/3 25000/25000 [==============================] - 155s 6ms/step - loss: 0.2989 - acc: 0.8808 Epoch 3/3 25000/25000 [==============================] - 155s 6ms/step - loss: 0.2997 - acc: 0.8828 Precision: 85.82%
Tenga en cuenta que si está utilizando TensorFlow como backend, es posible que vea algunos mensajes de advertencia relacionados con PoolAllocator, que puede ignorar por ahora.
Usted puede ver que este simple LSTM con poca afinación logra resultados casi de última generación en el problema de la IMDB. Es importante destacar que esta es una plantilla que puede utilizar para aplicar redes LSTM a sus propios problemas de clasificación de secuencias. Ahora, veamos algunas extensiones de este sencillo modelo que usted también puede aportar a sus propios problemas.
LSTM para la Clasificación de Secuencias con Abandono
Las redes neuronales recurrentes como LSTM generalmente tienen el problema de la sobreadaptación. El Dropout o abandono puede ser aplicado entre capas usando la capa de Dropout Keras. Podemos hacer esto fácilmente añadiendo nuevas capas de droppout entre las capas de incrustación o embedding y LSTM y las capas de salida de LSTM y Dense. Por ejemplo:
model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(Dropout(0.2)) model.add(LSTM(100)) model.add(Dropout(0.2)) model.add(Dense(1, activation='sigmoid'))
El código completo lo pueden encontrar aquí:
# LSTM para la Clasificación de Secuencias con Abandono import numpy from keras.datasets import imdb from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers import Dropout from keras.layers.embeddings import Embedding from keras.preprocessing import sequence # semilla aleatoria de reproducibilidad numpy.random.seed(7) # cargar el conjunto de datos pero sólo mantener las n palabras principales, cero el resto top_words = 5000 (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words) # truncar y rellenar secuencias de entrada 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) # creacion del modelo embedding_vecor_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(Dropout(0.2)) model.add(LSTM(100)) model.add(Dropout(0.2)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary()) model.fit(X_train, y_train, epochs=3, batch_size=64) # Final evaluacion del modelo scores = model.evaluate(X_test, y_test, verbose=0) print("Precision: %.2f%%" % (scores[1]*100))
Ejecutando obtenemos:
Epoch 1/3 25000/25000 [==============================] - 187s 7ms/step - loss: 0.4850 - acc: 0.7553 Epoch 2/3 25000/25000 [==============================] - 188s 8ms/step - loss: 0.3004 - acc: 0.8805 Epoch 3/3 25000/25000 [==============================] - 188s 8ms/step - loss: 0.2937 - acc: 0.8775 Precision: 87.76%
Podemos ver que el abandono tiene el impacto deseado en el entrenamiento con una tendencia ligeramente más lenta en la convergencia y, en este caso, una menor precisión. El modelo probablemente podría usar unas cuantas épocas más de entrenamiento y podría lograr una mayor habilidad (inténtelo y vea). Alternativamente, se puede aplicar una interrupción a las conexiones de entrada y a las conexiones recurrentes de las unidades de memoria con el LSTM de forma precisa y por separado. Keras proporciona esta capacidad con parámetros en la capa LSTM, la interrupción para congestionar la interrupción de entrada y la interrupción recurrente para congestionar la interrupción recurrente. Por ejemplo, podemos modificar el primer ejemplo para añadir la salida a la entrada y conexiones recurrentes de la siguiente manera:
model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length, dropout=0.2)) model.add(LSTM(100, dropout_W=0.2, dropout_U=0.2)) model.add(Dense(1, activation='sigmoid'))
El código completo es el siguiente:
# LSTM para la Clasificación de Secuencias con Abandono import numpy from keras.datasets import imdb from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers.embeddings import Embedding from keras.preprocessing import sequence # semilla aleatoria de reproducibilidad numpy.random.seed(7) # cargar el conjunto de datos pero sólo mantener las n palabras principales, cero el resto top_words = 5000 (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words) # truncar y rellenar secuencias de entrada 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) # creacion del modelo embedding_vecor_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(LSTM(100, dropout=0.2, recurrent_dropout=0.2)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary()) model.fit(X_train, y_train, epochs=3, batch_size=64) # Final evaluacion del modelo scores = model.evaluate(X_test, y_test, verbose=0) print("Precision: %.2f%%" % (scores[1]*100))
Ejecutando el código obtenemos:
Epoch 1/3 25000/25000 [==============================] - 207s 8ms/step - loss: 0.4916 - acc: 0.7642 Epoch 2/3 25000/25000 [==============================] - 229s 9ms/step - loss: 0.3927 - acc: 0.8346 Epoch 3/3 25000/25000 [==============================] - 202s 8ms/step - loss: 0.3539 - acc: 0.8539 Precision: 85.45%
Podemos ver que el abandono específico de LSTM tiene un efecto más pronunciado sobre la convergencia de la red que el abandono por capas. Como en el caso anterior, el número de épocas se mantuvo constante y se pudo aumentar para ver si la habilidad del modelo se puede elevar aún más. El abandono es una técnica poderosa para combatir la sobreadaptación en sus modelos LSTM y es una buena idea probar ambos métodos, pero puede apostar mejores resultados con el abandono por puerta-específica proporcionada en Keras.
LSTM y CNN para la Clasificación de Secuencias
Las redes neuronales convolucionales sobresalen en el aprendizaje de la estructura espacial en los datos de entrada. Los datos de IMDB de revisión tienen una estructura espacial unidimensional en la secuencia de palabras de las revisiones y la CNN puede elegir características invariables para el buen y el mal sentimiento. Este características espaciales aprendidas pueden entonces ser aprendidas como secuencias por una capa de LSTM. Podemos fácilmente añadir una capa unidimensional de CNN y capas de agrupación máximas después de la capa de embedding que luego alimentan las características consolidadas de la LSTM. Podemos usar un pequeño conjunto de 32 características con una pequeña longitud de 3. La capa de pooling puede utilizar la longitud estándar de 2 para reducir a la mitad el tamaño del mapa de características. Por ejemplo, crearíamos el modelo de la siguiente manera:
model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(Conv1D(filters=32, kernel_size=3, padding='same', activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100)) model.add(Dense(1, activation='sigmoid'))
El código completo es el siguiente:
# LSTM y CNN para la clasificación de secuencias en el conjunto de datos IMDB import numpy from keras.datasets import imdb from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers.convolutional import Conv1D from keras.layers.convolutional import MaxPooling1D from keras.layers.embeddings import Embedding from keras.preprocessing import sequence # semilla aleatoria de reproducibilidad numpy.random.seed(7) # cargar el conjunto de datos pero sólo mantener las n palabras principales, cero el resto top_words = 5000 (X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words) # truncar y rellenar secuencias de entrada 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) # creacion del modelo embedding_vecor_length = 32 model = Sequential() model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length)) model.add(Conv1D(filters=32, kernel_size=3, padding='same', activation='relu')) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print(model.summary()) model.fit(X_train, y_train, epochs=3, batch_size=64) # Final evaluacion del modelo scores = model.evaluate(X_test, y_test, verbose=0) print("Precision: %.2f%%" % (scores[1]*100))
Ejecutando el código obtenemos:
Podemos ver que conseguimos resultados similares a los del primer ejemplo, aunque con menos pesos y un tiempo de entrenamiento más rápido. Espero que se puedan lograr resultados aún mejores si este ejemplo se amplía usando el abandono.
➡ Sigue aprendiendo de Deep Learning en nuestro curso: