Post


Generación de textos con cadenas de Markov: Introducción al uso de Markovify

Esta es una introducción simple a las cadenas de Markov para la generación de texto. Vamos a entrenar un modelo utilizando la biblioteca Markovify en la novel del Don Quijote de la Mancha y ver si lo que generamos es legible y coherente. Soy consciente de que estos dos términos son un tanto nebulosos, pero creo que la mayoría de la gente entenderá mi intención al utilizarlos, sobre todo cuando vean la variación en el texto que se genera.

Markovify es una librería python que se autodenomina "Un generador de cadenas de Markov simple y extensible". Sus usos incluyen la generación de frases aleatorias semi-plausibles basadas en un texto existente". Y debo admitir que es increíblemente fácil y rápido de usar. Las propias cadenas de Markov son creaciones ingeniosas que dan probabilidades de "permanencia" y "cambio" para procesos multiestado. No voy a profundizar aquí en las matemáticas de las cadenas de Markov, pero siéntete libre de consultar esto y esto para una visión global y visualizaciones, respectivamente. Para nuestro propósito, explicaré las cadenas de Markov visualmente.

 

Si observamos la imagen anterior, podemos ver que tenemos tres estados posibles: Nublado, Lluvioso y Soleado. Las cadenas de Markov se basan en el estado actual para predecir un resultado futuro. Si observamos que hoy está Lloviendo, nuestras probabilidades son las siguientes: La probabilidad de que mañana siga lloviendo es del 60%, la probabilidad de que esté nublado es del 30% y la probabilidad de que haga sol es del 10%. La misma lógica se puede aplicar cuando empezamos en los estados Nublado y Soleado.

¿Y cómo demonios funciona esto con el texto? Básicamente, cada palabra de nuestro corpus está "conectada" a todas las demás con probabilidades variables mediante cadenas de Markov. Así, si nuestra palabra (estado) inicial es "Tú", Markovify asigna una probabilidad a todas las demás palabras de nuestro corpus en función de la probabilidad de que sigan a nuestra palabra inicial. Por ejemplo, "debería" tiene un 65% de probabilidades de seguir a "tú", "es" un 20%, "puede" un 10%, y así sucesivamente para todo nuestro corpus, que constituirá el último 5%. Tenga en cuenta que la probabilidad de que "Tú" se repita a sí misma debería ser cercana al 0%, ya que una palabra que se repitiera a sí misma no tendría mucho sentido y es así para casi todas las palabras. Si desea profundizar en el tema, consulte el desglose de Películas, métricas y reflexiones aquí.

Así que finalmente estamos listos para implementar Markovify para generar texto. Puedes encontrar mi cuaderno Colab aquí en Github. Primero necesitamos instalar nuestras librerías y paquetes.

!pip install nltk !pip install spacy !pip install markovify !pip install -m spacy download en

Vamos a utilizar NLTK y spaCy para el preprocesamiento de texto, ya que son los más comunes y nuestro modelo generará mejor el texto si lo analizamos primero. Ahora podemos importar nuestras librerías.

import spacy import re import markovify import nltk from nltk.corpus import gutenberg import warnings warnings.filterwarnings('ignore') nltk.download('gutenberg') !python -m spacy download en

Para esta demostración, vamos a utilizar tres de la novela de Cervantes del corpus NLTK del Proyecto Gutenberg. Primero imprimiremos todos los documentos del corpus Gutenberg para que pueda mezclarlos y combinarlos a su gusto.

#inspect Gutenberg corpus
print(gutenberg.fileids())

Para esta demostración utilizaremos la novela de Cervantes: Don Quijote. A continuación las importaremos e inspeccionaremos el texto.
 

#import novels as text objects quijote = gutenberg.raw('donquijote.txt') miocid = gutenberg.raw('cantodelmiosit.txt') julio = gutenberg.raw('caballerocarmelo.txt') #print first 100 characters of each print(' Raw: ', quijote[:100]) print(' Raw: ', miocid[:100]) print(' Raw: ', julio[:100])

A continuación construiremos una función de utilidad para limpiar nuestro texto utilizando la biblioteca re. Esta función eliminará los espacios innecesarios y sangrías, puntuaciones, y tal.

#utility function for text cleaning def text_cleaner(text): text = re.sub(r'--', ' ', text) text = re.sub('[[].*?[]]', '', text) text = re.sub(r'(|s+-?|^-?)(d+|d*.d+)','', text) text = ' '.join(text.split()) return text

A continuación, seguiremos limpiando nuestros textos eliminando los títulos de los capítulos y los indicadores, y aplicaremos nuestra función de limpieza de textos.

#remove chapter indicator quijote = re.sub(r'Chapter d+', '', quijote) miocid = re.sub(r'Chapter d+', '', miocid) julio = re.sub(r'Chapter d+', '', julio) #apply cleaning function to corpus quijote = text_cleaner(quijote) miocid = text_cleaner(miocid) julio = text_cleaner(julio)

Ahora queremos utilizar spaCy para analizar nuestros documentos. Aquí encontrarás más información sobre el procesamiento de textos.

#parse cleaned novels nlp = spacy.load('es') quijote_doc = nlp(quijote) miocid_doc = nlp(miocid) julio_doc = nlp(julio)

Ahora que nuestros textos están limpios y procesados, podemos crear frases y combinar nuestros documentos.

quijote_sents = ' '.join([sent.text for sent in quijote_doc.sents if len(sent.text) > 1]) miocid_sents = ' '.join([sent.text for sent in miocid_doc.sents if len(sent.text) > 1]) julio_sents = ' '.join([sent.text for sent in julio_doc.sents if len(sent.text) > 1]) cervantes_sents = quijote_sents + miocid_sents + julio_sents #inspect our text print(cervantes_sents)

Nuestro preprocesamiento de texto está hecho y podemos empezar a usar Markovify para generar frases.

#create text generator using markovify
generator_1 = markovify.Text(cervantes_sents, state_size=3)

Y ahora viene la parte divertida. Sólo tenemos que escribir un bucle para generar tantas sentencias como queramos. A continuación, crearemos 3 sentencias de longitud indefinida y 3 más con una longitud inferior a 100 caracteres.

#We will randomly generate three sentences for i in range(3): print(generator_1.make_sentence()) #We will randomly generate three more sentences of no more than 100 characters for i in range(3): print(generator_1.make_short_sentence(max_chars=100))

Texto de ejemplo:

 

No está mal para el español de Cercantes. Pero creo que podemos hacerlo mejor. Implementaremos POSifiedText utilizando SpaCy para intentar mejorar nuestra predicción de texto.

#next we will use spacy's part of speech to generate more legible text class POSifiedText(markovify.Text): def word_split(self, sentence): return ['::'.join((word.orth_, word.pos_)) for word in nlp(sentence)] def word_join(self, words): sentence = ' '.join(word.split('::')[0] for word in words) return sentence #Call the class on our text generator_2 = POSifiedText(cervantes_sents, state_size=3)

Y por último, imprime más frases utilizando nuestro nuevo generador.

#now we will use the above generator to generate sentences for i in range(5): print(generator_2.make_sentence()) #print 100 characters or less sentences for i in range(5): print(generator_2.make_short_sentence(max_chars=100))

Aqui el texto:
 

Para terminar
En este artículo hemos visto cómo implementar rápida y fácilmente Markovify para la generación de texto utilizando cadenas de Markov. Puedes ver lo fácil que es implementarlo y ponerlo en marcha una vez que tienes un texto limpio. Tengo previsto publicar más modelos de PNL/generación de texto utilizando redes neuronales, transformadores y otros con este mismo corpus con el objetivo de comparar la complejidad y el rendimiento entre ellos.



INICIO ---------------------------------------------------------------------------------------------------------------------------