Python para PNL: desarrollo de un relleno de texto automático con N-Grams

    Este es el decimoquinto artículo de mi serie de artículos sobre Python para PNL. En mi artículo anterior, expliqué cómo implementar TF-IDF enfoque desde cero en Python. Antes de eso, estudiamos cómo implementar el enfoque de bolsa de palabras desde cero en Python.

    Hoy, estudiaremos el enfoque de N-Grams y veremos cómo se puede utilizar el enfoque de N-Grams para crear un relleno de texto automático simple o un motor de sugerencias. El relleno automático de texto es una aplicación muy útil y es ampliamente utilizada por Google y diferentes teléfonos inteligentes donde un usuario ingresa un texto y la aplicación completa o sugiere automáticamente el texto restante.

    Problemas con TF-IDF y el enfoque de bolsa de palabras

    Antes de implementar el modelo de N-Grams, primero analicemos el inconveniente de la bolsa de palabras y los enfoques TF-IDF.

    En la bolsa de palabras y el enfoque TF-IDF, las palabras se tratan individualmente y cada palabra se convierte en su contraparte numérica. La información de contexto de la palabra no se conserva. Considere dos frases “gran máquina roja y alfombra” y “gran alfombra roja y máquina”. Si usa un enfoque de bolsa de palabras, obtendrá los mismos vectores para estas dos oraciones. Sin embargo, podemos ver claramente que en la primera frase estamos hablando de una “gran máquina roja”, mientras que la segunda frase contiene información sobre la “gran alfombra roja”. Por tanto, la información de contexto es muy importante. El modelo de N-Grams básicamente nos ayuda a capturar la información del contexto.

    Teoría del modelo de N-gramos

    Wikipedia define un N-Gram como “una secuencia contigua de N elementos de una muestra determinada de texto o voz”. Aquí, un elemento puede ser un carácter, una palabra o una oración y N puede ser cualquier número entero. Cuando N es 2, llamamos a la secuencia un bigrama. De manera similar, una secuencia de 3 elementos se denomina triagrama, y ​​así sucesivamente.

    Para entender el modelo de N-Gramos, primero tenemos que entender cómo funcionan las cadenas de Markov.

    Conexión de N-Grams con cadenas de Markov

    UN Cadena de Markov es una secuencia de estados. Considere un sistema de Markov con 2 estados, X e Y. En una cadena de Markov, puede permanecer en un estado o pasar al otro. En nuestro ejemplo, nuestros estados tienen el siguiente comportamiento:

    • La probabilidad de pasar de X a Y es del 50% y, de manera similar, la probabilidad de permanecer en X es del 50%.
    • Asimismo, la probabilidad de permanecer en Y es del 50%, mientras que la posibilidad de volver a X también es del 50%.

    De esta forma se puede generar una secuencia de Markov, como XXYX, etc.

    En un modelo de N-gramos, un elemento de una secuencia puede tratarse como un estado de Markov. Veamos un ejemplo simple de bigramas de caracteres donde cada personaje es un estado de Markov.

    Football is a very famous game
    

    Los bigramas de caracteres para la oración anterior serán: fo, oo, ot, tb, ba, al, ll, l, i, is y así. Puede ver que los bigramas son básicamente una secuencia de dos caracteres que ocurren consecutivamente.

    De manera similar, los trigramas son una secuencia de tres caracteres contiguos, como se muestra a continuación:

    foo, oot, otb, tba y así.

    En los dos ejemplos anteriores, vimos bigramas y trigramas de caracteres. También podemos tener bigramas y trigramas de palabras.

    Volvamos a nuestro ejemplo anterior, “gran máquina roja y alfombra”. El bigrama de esta oración será “gran rojo”, “máquina roja”, “máquina y”, “y alfombra”. Del mismo modo, los bigramas para la oración “gran alfombra roja y máquina” serán “gran rojo”, “alfombra roja”, “alfombra y”, “y máquina”.

    Aquí, en este caso con bigramas, obtenemos una representación vectorial diferente para ambas oraciones.

    En la siguiente sección, implementaremos el modelo de N-Grams desde cero en Python y veremos cómo podemos crear un relleno de texto automático usando N-Grams como estos.

    N-Grams desde cero en Python

    Crearemos dos tipos de modelos N-Grams en esta sección: un modelo de caracteres N-Grams y un modelo de palabras N-Gram.

    Personajes Modelo N-Gramos

    En esta sección, explicaré cómo crear un modelo N-Gram de caracteres simples. En la siguiente sección, veremos cómo implementar el modelo de palabra N-Gram.

    Para crear nuestro corpus, rasparemos el artículo de Wikipedia sobre tenis. Primero importemos las bibliotecas que necesitamos para descargar y analizar el artículo de Wikipedia.

    import nltk
    import numpy as np
    import random
    import string
    
    import bs4 as bs
    import urllib.request
    import re
    

    Estaremos usando el Beautifulsoup4 biblioteca para analizar los datos de Wikipedia. Además, la biblioteca de expresiones regulares de Python, re, se utilizará para algunas tareas de preprocesamiento en el texto.

    Como dijimos anteriormente, usaremos el artículo de Wikipedia sobre Tenis para crear nuestro corpus. El siguiente script recupera el artículo de Wikipedia y extrae todos los párrafos del texto del artículo. Por último, el texto se convierte a minúsculas para facilitar su procesamiento.

    raw_html = urllib.request.urlopen('https://en.wikipedia.org/wiki/Tennis')
    raw_html = raw_html.read()
    
    article_html = bs.BeautifulSoup(raw_html, 'lxml')
    article_paragraphs = article_html.find_all('p')
    article_text=""
    
    for para in article_paragraphs:
        article_text += para.text
    
    article_text = article_text.lower()
    

    A continuación, eliminamos todo de nuestro conjunto de datos, excepto letras, puntos y espacios:

    article_text = re.sub(r'[^A-Za-z. ]', '', article_text)
    

    Hemos preprocesado nuestro conjunto de datos y ahora es el momento de crear un modelo de N-Grams. Crearemos un modelo de trigrama de personajes. Ejecute el siguiente script:

    ngrams = {}
    chars = 3
    
    for i in range(len(article_text)-chars):
        seq = article_text[i:i+chars]
        print(seq)
        if seq not in ngrams.keys():
            ngrams[seq] = []
        ngrams[seq].append(article_text[i+chars])
    

    En el script de arriba, creamos un diccionario. ngrams. Las claves de este diccionario serán los trigramas de caracteres de nuestro corpus y los valores serán los caracteres que aparecen junto a los trigramas. A continuación, dado que estamos creando N-Gram de tres caracteres, declaramos una variable chars. Después de eso, iteramos a través de todos los caracteres de nuestro corpus, comenzando por el cuarto carácter.

    A continuación, dentro del ciclo, extraemos el trigrama filtrando los siguientes tres caracteres. El trigrama se almacena en el seq variable. Luego verificamos si el trigrama existe en el diccionario. Si no existe en el ngrams diccionario agregamos el trigrama al diccionario. Después de eso, asignamos una lista vacía como valor al trigrama. Finalmente, el carácter que existe después del trigrama se agrega como valor a la lista.

    Si abre el diccionario ngrams en el explorador de variables de Spyder. Debería ver algo como esto:

    Puede ver los trigramas como claves y los caracteres correspondientes, que aparecen después de los trigramas en el texto, como valores. Es posible que vea claves con dos caracteres en el diccionario, pero en realidad no son dos caracteres. El tercer carácter es en realidad un espacio.

    Intentemos ahora generar texto usando los primeros tres caracteres de nuestro corpus como entrada. Los primeros tres caracteres de nuestro corpus son “diez”. Mira el siguiente guión:

    curr_sequence = article_text[0:chars]
    output = curr_sequence
    for i in range(200):
        if curr_sequence not in ngrams.keys():
            break
        possible_chars = ngrams[curr_sequence]
        next_char = possible_chars[random.randrange(len(possible_chars))]
        output += next_char
        curr_sequence = output[len(output)-chars:len(output)]
    
    print(output)
    

    En el script anterior, primero almacenamos el primer trigrama, es decir ten en el curr_sequence variable. Generaremos un texto de doscientos caracteres, por lo que inicializamos un ciclo que se repite 200 veces. Durante cada iteración, comprobamos si el curr_sequence o el trigrama está en el ngrams diccionario. Si el trigrama no se encuentra en el ngrams diccionario, simplemente salimos del ciclo.

    A continuación, el curr_sequence trigrama se pasa como clave para el ngrams diccionario, que devuelve la lista de posibles caracteres siguientes. De la lista de posibles caracteres siguientes, se elige aleatoriamente un índice, que se pasa al possible_chars list para obtener el siguiente carácter del trigrama actual. A continuación, se añade el siguiente carácter al output variable que contiene la salida final.

    Finalmente, el curr_sequence se actualiza con el siguiente trigrama del corpus de texto. Si imprime el output variable que contiene doscientos caracteres generados automáticamente, debería ver algo como esto (es importante mencionar que dado que el siguiente carácter se elige al azar, su salida puede ser diferente):

    Salida:

    tent pointo somensiver tournamedal pare the greak in the next peak sweder most begal tennis sport. the be has siders with sidernaments as was that adming up is coach rackhanced ball of ment. a game and
    

    La salida no tiene mucho sentido aquí en este caso. Si aumenta el valor de la chars variable a 4. Debería ver resultados similares a los siguientes resultados:

    tennis ahead with the club players under.most coaching motion us . the especific at the hit and events first predomination but of ends on the u.s. cyclops have achieved the end or net inches call over age
    

    Puede ver que los resultados son un poco mejores que los que obtuvimos con 3 gramos. Nuestra sugerencia / llenado de texto continuará mejorando a medida que aumentemos el número N-Gram.

    En la siguiente sección, implementaremos el modelo Words N-Grams. Verás que el texto generado tendrá mucho más sentido en el caso del modelo Words N-Grams.

    Palabras Modelo N-Gramos

    En el modelo de Words N-Grams, cada palabra del texto se trata como un elemento individual. En esta sección, implementaremos el modelo Words N-Grams y lo usaremos para crear un relleno de texto automático.

    El conjunto de datos que vamos a usar es el mismo que usamos en la última sección.

    Primero creemos un diccionario que contenga trigramas de palabras como claves y la lista de palabras que aparecen después de los trigramas como valores.

    ngrams = {}
    words = 3
    
    words_tokens = nltk.word_tokenize(article_text)
    for i in range(len(words_tokens)-words):
        seq = ' '.join(words_tokens[i:i+words])
        print(seq)
        if  seq not in ngrams.keys():
            ngrams[seq] = []
        ngrams[seq].append(words_tokens[i+words])
    

    En el script anterior, creamos un modelo de trigrama de palabras. El proceso es similar al que se sigue para utilizar trigramas de caracteres. Sin embargo, en el script anterior, primero convertimos nuestro corpus en palabras.

    A continuación, iteramos a través de todas las palabras y luego unimos las tres palabras actuales para formar un trigrama. Después de eso, verificamos si la palabra trigrama existe en el ngrams diccionario. Si el trigrama aún no existe, simplemente lo insertamos en el ngrams diccionario como clave.

    Finalmente, adjuntamos la lista de palabras que siguen al trigrama en todo el corpus, como valor en el diccionario.

    Ahora si miras el ngrams diccionario, en el explorador de variables, se verá así:

    Puede ver los trigramas como claves de diccionario y las palabras correspondientes como valores de diccionario.

    Creemos ahora un relleno de texto automático, usando los trigramas de palabras que acabamos de crear.

    curr_sequence=" ".join(words_tokens[0:words])
    output = curr_sequence
    for i in range(50):
        if curr_sequence not in ngrams.keys():
            break
        possible_words = ngrams[curr_sequence]
        next_word = possible_words[random.randrange(len(possible_words))]
        output += ' ' + next_word
        seq_words = nltk.word_tokenize(output)
        curr_sequence=" ".join(seq_words[len(seq_words)-words:len(seq_words)])
    
    print(output)
    

    En el script anterior, inicializamos el curr_sequence variable con el primer trigrama del corpus. El primer trigrama es “el tenis es un”. Generaremos 50 palabras usando el primer trigrama como entrada. Para ello, ejecutamos un ciclo for que se ejecuta 50 veces. Durante cada iteración, primero se comprueba si la palabra trigrama existe en el ngrams diccionario. Si no, el bucle se rompe. De lo contrario, la lista de palabras que probablemente seguirán el trigrama se recuperará del ngrams diccionario pasando trigrama como valor. De la lista de palabras posibles, se elige una palabra al azar y se agrega al final de la salida. Finalmente, el curr_sequence La variable se actualiza con el valor del siguiente trigrama del diccionario.

    El texto generado se ve así. Puede ver que en el caso de los trigramas de palabras, el texto generado automáticamente tiene mucho más sentido.

    Salida:

    tennis is a racket sport that can be played individually against a single opponent singles or between two teams of two players each doubles. each player uses a tennis racket include a handle known as the grip connected to a neck which joins a roughly elliptical frame that holds a matrix of
    

    Si establece el valor de la variable de palabras en 4 (use 4 gramos) para generar texto, su salida se verá aún más sólida como se muestra a continuación:

    tennis is a racket sport that can be played individually against a single opponent singles or between two teams of two players each doubles . each player uses a tennis racket that is strung with cord to strike a hollow rubber ball covered with felt over or around a net and into the opponents
    

    Puede ver que la salida tiene aún más sentido con 4 gramos. Esto se debe en gran parte a que nuestro generador está regenerando principalmente el mismo texto del artículo de Wikipedia, pero con algunas mejoras leves en el generador y un corpus más grande, nuestro generador también podría generar fácilmente oraciones nuevas y únicas.

    Conclusión

    El modelo N-Grams es uno de los modelos de oración a vector más utilizados, ya que captura el contexto entre N palabras en una oración. En este artículo, vio la teoría detrás del modelo de N-Grams. También viste cómo implementar los modelos N-Grams de caracteres y N-Grams de palabras. Finalmente, estudió cómo crear un relleno de texto automático utilizando ambos enfoques.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *