Blog
Proyecto: Desarrollar un Modelo de Lenguaje Neural para la Generación de Textos
- Publicado por: Rafael Fernandez
- Categoría: Uncategorized
Un modelo de lenguaje puede predecir la probabilidad de la siguiente palabra en la secuencia, basándose en las palabras ya observadas en la secuencia. Los modelos de redes neuronales son un método preferido para desarrollar modelos de lenguaje estadístico porque pueden usar una representación distribuida donde diferentes palabras con significados similares tienen una representación similar y porque pueden usar un gran contexto de palabras recientemente observadas al hacer predicciones. En este tutorial, descubrirás cómo desarrollar un modelo de lenguaje estadístico utilizando el Deep Learning en Python. Después de completar este tutorial, tú sabrás:
- Cómo preparar el texto para desarrollar un modelo de lenguaje basado en palabras.
- Cómo diseñar y adaptar un modelo de lenguaje neural con una incrustación aprendida y una capa oculta de LSTM.
- Cómo utilizar el modelo del idioma aprendido para generar un nuevo texto con propiedades estadísticas similares a las del texto fuente.
Vamos a empezar.
Descripción general del tutorial
Este tutorial está dividido en las siguientes partes:
- La República de Platón
- Preparación de datos
- Modelo de entrenamiento de idiomas
- Modelo de uso del lenguaje
La República de Platón
La República de Platón es la obra más famosa del filósofo griego clásico Platón. Está estructurado como un diálogo (por ejemplo, una conversación) sobre el tema del orden y la justicia dentro de una ciudad o estado. El texto completo está disponible gratuitamente en el dominio público. Está disponible en el sitio web del Proyecto Gutenberg en varios formatos. Puedes descargar la versión en texto ASCII de todo el libro (o libros) aquí (puede que necesites abrir la URL dos veces):
- Descargar The Republic by Plato.
http://www.gutenberg.org/cache/epub/1497/pg1497.txt
Descarga el texto del libro y colócalo en tu trabajo actual directamente con el nombre de archivo republic.txt. Abre el archivo en un editor de texto y elimina el anverso y reverso. Esto incluye detalles sobre el libro al principio, un largo análisis e información sobre la licencia al final. El texto debe empezar por:
BOOK I.
I went down yesterday to the Piraeus with Glaucon the son of Ariston, …
Y terminar con:
… And it shall be well with us both in this life and in the pilgrimage of a thousand years which we have been describing.
Guarda la versión limpia como republic_clean.txt en tu directorio de trabajo actual. El archivo debe tener unas 15.802 líneas de texto. Ahora podemos desarrollar un modelo de lenguaje a partir de este texto.
Preparación de datos
Comenzaremos por preparar los datos para el modelado. El primer paso es mirar los datos.
Revisión del texto
Abra el texto en un editor y simplemente mire los datos del texto. Por ejemplo, aquí está el primer pedazo de diálogo:
BOOK I.
I went down yesterday to the Piraeus with Glaucon the son of Ariston, that I might offer up my prayers to the goddess (Bendis, the Thracian Artemis.); and also because I wanted to see in what manner they would celebrate the festival, which was a new thing. I was delighted with the procession of the inhabitants; but that of the
Thracians was equally, if not more, beautiful. When we had finished our prayers and viewed the spectacle, we turned in the direction of the city; and at that instant Polemarchus the son of Cephalus chanced to catch sight of us from a distance as we were starting on our way home, and told his servant to run and bid us wait for him.
The servant took hold of me by the cloak behind, and said: Polemarchus desires you to wait.I turned round, and asked him where his master was.
There he is, said the youth, coming after you, if you will only wait.
Certainly we will, said Glaucon; and in a few minutes Polemarchus appeared, and with him Adeimantus, Glaucon’s brother, Niceratus the son of Nicias, and several others who had been at the procession.
Polemarchus said to me: I perceive, Socrates, that you and your companion are already on your way to the city.
You are not far wrong, I said.…
¿Qué crees que tendremos que manejar en la preparación de los datos? Esto es lo que veo de un vistazo rápido:
- Encabezamientos de libros/capítulos (por ejemplo, BOOK I.).
- Mucha puntuación (por ejemplo -, ;-, ?-, y más).
- Nombres extraños (por ejemplo, Polemarchus).
- Algunos monólogos largos que se extienden por cientos de líneas.
- Algunos diálogos citados (e.g. ‘…’).
- Estas observaciones, y más, sugieren formas en que podemos desear preparar los datos del texto.
- La forma específica en que preparamos los datos realmente depende de cómo pretendemos modelarlos, lo que a su vez depende de cómo pretendemos utilizarlos.
Diseño de Modelos de Lenguaje
En este tutorial, desarrollaremos un modelo del texto que podremos utilizar para generar nuevas secuencias de texto. El modelo de lenguaje será estadístico y predecirá la probabilidad de cada palabra dada una secuencia de entrada de texto. La palabra predicha será introducida como entrada para generar a su vez la siguiente palabra. Una decisión clave de diseño es la duración de las secuencias de entrada. Necesitan ser lo suficientemente largos para permitir que el modelo aprenda el contexto de las palabras a predecir. Esta longitud de entrada también definirá la longitud del texto de la semilla usada para generar nuevas secuencias cuando usamos el modelo.
No hay una respuesta correcta. Con tiempo y recursos suficientes, podríamos explorar la capacidad del modelo para aprender con secuencias de entrada de diferentes tamaños. En su lugar, escogeremos una longitud de 50 palabras para la longitud de las secuencias de entrada, de forma algo arbitraria. Podríamos procesar los datos para que el modelo sólo se ocupe de las frases autónomas y rellenar o truncar el texto para cumplir este requisito para cada secuencia de entrada. Tú podrías explorar esto como una extensión de este tutorial.
En cambio, para que el ejemplo sea breve, dejaremos que todo el texto fluya junto y capacitaremos al modelo para predecir la siguiente palabra a través de oraciones, párrafos e incluso libros o capítulos en el texto. Ahora que tenemos un diseño de modelo, podemos ver la transformación del texto en bruto en secuencias de 100 palabras de entrada a 1 palabra de salida, listo para adaptarse a un modelo.
Cargar texto
El primer paso es cargar el texto en la memoria. Podemos desarrollar una pequeña función para cargar todo el archivo de texto en la memoria y devolverlo. La función se llama load_doc() y se lista a continuación. Si se le da un nombre de archivo, devuelve una secuencia de texto cargado.
# cargar doc en la memoria def load_doc(filename): # abrir archivo en modo solo lectura file = open(filename, 'r') # leer todo el texto text = file.read() # cerrar el archivo file.close() return text
Usando esta función, podemos cargar la versión más limpia del documento en el archivo republic_clean.txt de la siguiente manera:
# cargar documento in_filename = 'republic_clean.txt' doc = load_doc(in_filename) print(doc[:200])
Al ejecutar este fragmento se carga el documento y se imprimen los primeros 200 caracteres como una comprobación de cordura.
BOOK I. I went down yesterday to the Piraeus with Glaucon the son of Ariston, that I might offer up my prayers to the goddess (Bendis, the Thracian Artemis.); and also because I wanted to see in what
Hasta ahora, todo bien. A continuación, limpiemos el texto.
Limpiando el texto
Necesitamos transformar el texto en bruto en una secuencia de tokens o palabras que podamos usar como fuente para entrenar al modelo. Basado en la revisión del texto en bruto (arriba), a continuación se presentan algunas operaciones específicas que realizaremos para limpiar el texto. Es posible que desee explorar más operaciones de limpieza usted mismo como una extensión.
- Sustituir ‘-‘ por un espacio en blanco para que podamos dividir mejor las palabras.
- Dividir las palabras según el espacio en blanco.
- Eliminar todos los signos de puntuación de las palabras para reducir el tamaño del vocabulario (por ejemplo,’¿Qué? Qué’).
- Eliminar todas las palabras que no estén en orden alfabético para eliminar los signos de puntuación independientes.
- Normalizar todas las palabras en minúsculas para reducir el tamaño del vocabulario.
- El tamaño del vocabulario es un gran problema con el modelado del lenguaje.
Un vocabulario más pequeño resulta en un modelo más pequeño que entrena más rápido. Podemos implementar cada una de estas operaciones de limpieza en este orden en una función. Abajo está la función clean_doc() que toma un documento cargado como argumento y devuelve una matriz de tokens limpios.
# Convierte a doc en tokens limpios def clean_doc(doc): # Reemplace '--' con un espacio doc = doc.replace('--', ' ') # dividido en tokens por el espacio en blanco tokens = doc.split() # prepara a regex para el filtrado de caracteres re_punc = re.compile('[%s]' % re.escape(string.punctuation)) # Elimina la puntuación de cada palabra tokens = [re_punc.sub('', w) for w in tokens] # eliminar los tokens restantes que no estén en orden alfabético tokens = [word for word in tokens if word.isalpha()] # en minúsculas tokens = [word.lower() for word in tokens] return tokens
Podemos ejecutar esta operación de limpieza en nuestro documento cargado e imprimir algunos de los tokens y estadísticas como un control de sanidad.
# documento limpio tokens = clean_doc(doc) print(tokens[:200]) print('Total Tokens: %d' % len(tokens)) print('Unique Tokens: %d' % len(set(tokens)))
Primero, podemos ver una bonita lista de tokens que parecen más limpios que el texto crudo. Podríamos quitar los marcadores de capítulos del BOOK I y más, pero este es un buen comienzo.
['book', 'i', 'i', 'went', 'down', 'yesterday', 'to', 'the', 'piraeus', 'with', 'glaucon', 'the', 'son', 'of', 'ariston', 'that', 'i', 'might', 'offer', 'up', 'my', 'prayers', 'to', 'the', 'goddess', 'bendis', 'the', 'thracian', 'artemis', 'and', 'also', 'because', 'i', 'wanted', 'to', 'see', 'in', 'what', 'manner', 'they', 'would', 'celebrate', 'the', 'festival', 'which', 'was', 'a', 'new', 'thing', 'i', 'was', 'delighted', 'with', 'the', 'procession', 'of', 'the', 'inhabitants', 'but', 'that', 'of', 'the', 'thracians', 'was', 'equally', 'if', 'not', 'more', 'beautiful', 'when', 'we', 'had', 'finished', 'our', 'prayers', 'and', 'viewed', 'the', 'spectacle', 'we', 'turned', 'in', 'the', 'direction', 'of', 'the', 'city', 'and', 'at', 'that', 'instant', 'polemarchus', 'the', 'son', 'of', 'cephalus', 'chanced', 'to', 'catch', 'sight', 'of', 'us', 'from', 'a', 'distance', 'as', 'we', 'were', 'starting', 'on', 'our', 'way', 'home', 'and', 'told', 'his', 'servant', 'to', 'run', 'and', 'bid', 'us', 'wait', 'for', 'him', 'the', 'servant', 'took', 'hold', 'of', 'me', 'by', 'the', 'cloak', 'behind', 'and', 'said', 'polemarchus', 'desires', 'you', 'to', 'wait', 'i', 'turned', 'round', 'and', 'asked', 'him', 'where', 'his', 'master', 'was', 'there', 'he', 'is', 'said', 'the', 'youth', 'coming', 'after', 'you', 'if', 'you', 'will', 'only', 'wait', 'certainly', 'we', 'will', 'said', 'glaucon', 'and', 'in', 'a', 'few', 'minutes', 'polemarchus', 'appeared', 'and', 'with', 'him', 'adeimantus', 'glaucons', 'brother', 'niceratus', 'the', 'son', 'of', 'nicias', 'and', 'several', 'others', 'who', 'had', 'been', 'at', 'the', 'procession', 'polemarchus', 'said']
También tenemos algunas estadísticas sobre el documento limpio. Podemos ver que hay poco menos de 120.000 palabras en el texto limpio y un vocabulario de poco menos de 7.500 palabras. Esto es más bien pequeño y los modelos que encajan en estos datos deberían ser manejables en hardware modesto.
Total Tokens: 118684 Unique Tokens: 7409
Guardar texto limpio
Podemos organizar la larga lista de tokens en secuencias de 50 palabras de entrada y 1 palabra de salida. Es decir, secuencias de 51 palabras. Podemos hacer esto iterando sobre la lista de tokens desde el token 51 en adelante y tomando los 50 tokens anteriores como una secuencia, luego repitiendo este proceso hasta el final de la lista de tokens. Transformaremos los tokens en cadenas separadas por espacios para su posterior almacenamiento en un archivo. El código para dividir la lista de tokens limpios en secuencias con una longitud de 51 tokens se muestra a continuación.
# organizar en secuencias de tokens length = 50 + 1 sequences = list() for i in range(length, len(tokens)): # seleccionar secuencia de tokens seq = tokens[i-length:i] # convertir en una línea line =''.join(seq) # acumular sequences.append(line) print('Total Sequences: %d'% len(sequences))
La ejecución de esta pieza crea una larga lista de líneas. Imprimiendo las estadísticas en la lista, podemos ver que tendremos exactamente 118.633 patrones de entrenamiento que se ajustan a nuestro modelo.
Total Sequences: 118633preparation for dialectic should be presented to the name of idle spendthrifts of whom the other is the manifold and the unjust and is the best and the other which delighted to be the opening of the soul of the soul and the embroiderer will have to be said atPuedes ver que el texto parece razonable. De hecho, la adición de la concatenación ayudaría a interpretar la semilla y el texto generado. Sin embargo, el texto generado obtiene el tipo correcto de palabras en el orden correcto. Intente ejecutar el ejemplo unas cuantas veces para ver otros ejemplos de texto generado.
Extensiones
En esta sección se enumeran algunas ideas para ampliar el tutorial que tal vez desee explorar.
- Texto de semillas artificiales: Haz a mano o selecciona el texto de la semilla y evalúa cómo el texto de la semilla impacta el texto generado, específicamente las palabras o frases iniciales generadas.
- Simplifiqua el vocabulario: Explora un vocabulario más simple, tal vez con palabras de tallo o palabras de parada eliminadas.
- Limpieza de datos: Considera la posibilidad de usar más o menos limpieza del texto, tal vez dejar en alguna puntuación o tal vez reemplazar todos los nombres de fantasía con uno o un puñado. Evaluar cómo estos cambios en el tamaño del vocabulario afectan al texto generado.
- Modelo Tune: Ajusta el modelo, como el tamaño de la incrustación o el número de celdas de memoria en la capa oculta, para ver si puede desarrollar un modelo mejor.
- Modelo más profundo: Extiende el modelo para que tenga múltiples capas ocultas de LSTM, tal vez con la función "droppout" para ver si puede desarrollar un modelo mejor.
- Desarrollo de Embedded Pre-Formado: Extiende el modelo para usar vectores de Word2Vec ya entrenados para ver si resulta en un mejor modelo.
- Utiliza GloVe Embedding: Utiliza los vectores de inserción de palabras de GloVe con y sin ajuste fino por parte de la red y evalúe cómo afecta al entrenamiento y a las palabras generadas.
- Longitud de secuencia: Explora el entrenamiento del modelo con diferentes secuencias de entrada de longitud, tanto más cortas como más largas, y evalúa cómo afecta a la calidad del texto generado.
- Reduce el alcance: Considera entrenar el modelo en un libro (capítulo) o en un subconjunto del texto original y evalúa el impacto sobre la formación, la velocidad de la formación y el texto resultante generado.
- Modelo de Frases Sabias: Divide los datos en bruto según las frases y coloca cada frase en una longitud fija (por ejemplo, la longitud de frase más larga).
Si exploras alguna de estas extensiones, me encantaría saberlo.
➡ Continúa aprendiendo en nuestro curso de Procesamiento de Lenguaje Natural: