Python para PNL: Creación del modelo TF-IDF desde cero

    Este es el decimocuarto artículo de mi serie de artículos sobre Python para PNL. En mi artículo anterior, expliqué cómo convertir oraciones en vectores numéricos usando el bolsa de palabras Acercarse. Para comprender mejor el enfoque de la bolsa de palabras, implementamos la técnica en Python.

    En este artículo, nos basaremos en el concepto que aprendimos en el artículo anterior e implementaremos el TF-IDF esquema desde cero en Python. El término TF significa «frecuencia de término», mientras que el término IDF significa «frecuencia de documento inversa».

    Problema con el modelo de bolsa de palabras

    Antes de ver realmente el modelo TF-IDF, analicemos primero algunos problemas asociados con el modelo de la bolsa de palabras.

    En el último artículo, teníamos las siguientes tres oraciones de ejemplo:

    • «Me gusta jugar al fútbol»
    • «¿Saliste a jugar al tenis?»
    • «John y yo jugamos al tenis»

    El modelo de bolsa de palabras resultante se veía así:

    Jugar al tenis al fútbol ¿Fuiste?

    Oración 110111000
    Oración 211100111
    Oración 311010000

    Uno de los principales problemas asociados con el modelo de la bolsa de palabras es que asigna el mismo valor a las palabras, independientemente de su importancia. Por ejemplo, la palabra «jugar» aparece en las tres oraciones, por lo tanto esta palabra es muy común, por otro lado, la palabra «fútbol» solo aparece en una oración. Las palabras que son raras tienen más poder de clasificación en comparación con las palabras que son comunes.

    La idea detrás del enfoque TF-IDF es que las palabras que son más comunes en una oración y menos comunes en otras oraciones deben recibir un mayor peso.

    Teoría detrás de TF-IDF

    Antes de implementar el esquema TF-IDF en Python, primero estudiemos la teoría. Usaremos las mismas tres oraciones de nuestro ejemplo que usamos en el modelo de la bolsa de palabras.

    • «Me gusta jugar al fútbol»
    • «¿Saliste a jugar tenis?»
    • «John y yo jugamos al tenis»

    Paso 1: Tokenización

    Al igual que la bolsa de palabras, el primer paso para implementar el modelo TF-IDF es la tokenización.

    Oración 1 Oración 2 Oración 3

    yoHizoJohn
    me gustaUdsy
    aVamosyo
    jugarfuera dejugar
    fútbol americanoatenis
    jugar
    tenis

    Paso 2: Encuentre valores TF-IDF

    Una vez que haya tokenizado las oraciones, el siguiente paso es encontrar el valor TF-IDF para cada palabra de la oración.

    Como se discutió anteriormente, el valor de TF se refiere a la frecuencia del término y se puede calcular de la siguiente manera:

    TF = (Frequency of the word in the sentence) / (Total number of words in the sentence)
    

    Por ejemplo, observe la palabra «jugar» en la primera oración. Su frecuencia de término será 0.20 ya que la palabra «jugar» aparece solo una vez en la oración y el número total de palabras en la oración es 5, por lo tanto, 1/5 = 0.20.

    IDF se refiere a la frecuencia inversa de los documentos y se puede calcular de la siguiente manera:

    IDF: (Total number of sentences (documents))/(Number of sentences (documents) containing the word)
    

    Es importante mencionar que el valor IDF de una palabra permanece igual en todos los documentos, ya que depende del número total de documentos. Por otro lado, los valores de TF de una palabra difieren de un documento a otro.

    Encontremos la frecuencia IDF de la palabra «jugar». Como tenemos tres documentos y la palabra «reproducir» aparece en los tres, el valor IDF de la palabra «reproducir» es 3/3 = 1.

    Finalmente, los valores de TF-IDF se calculan multiplicando los valores de TF con sus correspondientes valores de IDF.

    Para encontrar el valor TF-IDF, primero necesitamos crear un diccionario de frecuencias de palabras como se muestra a continuación:

    Frecuencia de palabras

    yo2
    me gusta1
    a2
    jugar3
    fútbol americano1
    Hizo1
    Uds1
    Vamos1
    fuera de1
    tenis2
    John1
    y1

    A continuación, clasifiquemos el diccionario en orden descendente de frecuencia como se muestra en la siguiente tabla.

    Frecuencia de palabras

    jugar3
    tenis2
    a2
    yo2
    fútbol americano1
    Hizo1
    Uds1
    Vamos1
    fuera de1
    me gusta1
    John1
    y1

    Finalmente, filtraremos las 8 palabras más frecuentes.

    Como dije anteriormente, dado que los valores de IDF se calculan utilizando todo el corpus. Ahora podemos calcular el valor IDF para cada palabra. La siguiente tabla contiene valores IDF para cada tabla.

    Palabra

    Frecuencia

    IDF

    jugar 3 3/3 = 1
    tenis 2 3/2 = 1,5
    a 2 3/2 = 1,5
    yo 2 3/2 = 1,5
    fútbol americano 1 3/1 = 3
    Hizo 1 3/1 = 3
    Uds 1 3/1 = 3
    Vamos 1 3/1 = 3

    Puede ver claramente que las palabras que son raras tienen valores IDF más altos en comparación con las palabras que son más comunes.

    Busquemos ahora los valores TF-IDF para todas las palabras en cada oración.

    Palabra

    Oración 1

    Oración 2

    Oración 3

    jugar 0,20 x 1 = 0,20 0,14 x 1 = 0,14 0,20 x 1 = 0,20
    tenis 0 x 1,5 = 0 0,14 x 1,5 = 0,21 0,20 x 1,5 = 0,30
    a 0,20 x 1,5 = 0,30 0,14 x 1,5 = 0,21 0 x 1,5 = 0
    yo 0,20 x 1,5 = 0,30 0 x 1,5 = 0 0,20 x 1,5 = 0,30
    fútbol americano 0,20 x 3 = 0,6 0 x 3 = 0 0 x 3 = 0
    hizo 0 x 3 = 0 0,14 x 3 = 0,42 0 x 3 = 0
    Uds 0 x3 = 0 0,14 x 3 = 0,42 0 x 3 = 0
    Vamos 0x 3 = 0 0,14 x 3 = 0,42 0 x 3 = 0

    Los valores de las columnas para las frases 1, 2 y 3 son los vectores TF-IDF correspondientes para cada palabra en las frases respectivas.

    Nota el uso de la función de registro con TF-IDF.

    Es importante mencionar que para mitigar el efecto de palabras muy raras y muy comunes en el corpus, se puede calcular el logaritmo del valor IDF antes de multiplicarlo por el valor TF-IDF. En tal caso, la fórmula de IDF se convierte en:

    IDF: log((Total number of sentences (documents))/(Number of sentences (documents) containing the word))
    

    Sin embargo, dado que solo teníamos tres oraciones en nuestro corpus, por simplicidad no usamos log. En la sección de implementación, usaremos la función de registro para calcular el valor final de TF-IDF.

    Modelo TF-IDF desde cero en Python

    Como se explicó en la sección de teoría, los pasos para crear un diccionario ordenado de frecuencia de palabras son similares entre la bolsa de palabras y el modelo TF-IDF. Para comprender cómo creamos un diccionario ordenado de frecuencias de palabras, consulte mi último artículo. Aquí, solo escribiré el código. El modelo TF-IDF se basará en este código.

    # -*- coding: utf-8 -*-
    """
    Created on Sat Jul 6 14:21:00 2019
    
    @author: usman
    """
    
    import nltk
    import numpy as np
    import random
    import string
    
    import bs4 as bs
    import urllib.request
    import re
    
    raw_html = urllib.request.urlopen('https://en.wikipedia.org/wiki/Natural_language_processing')
    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
    
    corpus = nltk.sent_tokenize(article_text)
    
    for i in range(len(corpus )):
        corpus [i] = corpus [i].lower()
        corpus [i] = re.sub(r'W',' ',corpus [i])
        corpus [i] = re.sub(r's+',' ',corpus [i])
    
    wordfreq = {}
    for sentence in corpus:
        tokens = nltk.word_tokenize(sentence)
        for token in tokens:
            if token not in wordfreq.keys():
                wordfreq[token] = 1
            else:
                wordfreq[token] += 1
    
    import heapq
    most_freq = heapq.nlargest(200, wordfreq, key=wordfreq.get)
    

    En el script anterior, primero extraemos el artículo de Wikipedia sobre Procesamiento natural del lenguaje. Luego lo preprocesamos para eliminar todos los caracteres especiales y múltiples espacios vacíos. Por último, creamos un diccionario de frecuencias de palabras y luego filtramos las 200 palabras más frecuentes.

    El siguiente paso es encontrar los valores IDF para las palabras que aparecen con más frecuencia en el corpus. El siguiente script hace eso:

    word_idf_values = {}
    for token in most_freq:
        doc_containing_word = 0
        for document in corpus:
            if token in nltk.word_tokenize(document):
                doc_containing_word += 1
        word_idf_values[token] = np.log(len(corpus)/(1 + doc_containing_word))
    

    En el script anterior, creamos un diccionario vacío. word_idf_values. Este diccionario almacenará las palabras más frecuentes como claves y sus valores IDF correspondientes como valores de diccionario. A continuación, recorremos la lista de palabras que aparecen con mayor frecuencia. Durante cada iteración, creamos una variable doc_containing_word. Esta variable almacenará el número de documentos en los que aparece la palabra. A continuación, iteramos a través de todas las oraciones de nuestro corpus. La oración se tokeniza y luego verificamos si la palabra existe en la oración o no, si la palabra existe, incrementamos el doc_containing_word variable. Finalmente, para calcular el valor IDF dividimos el número total de oraciones por el número total de documentos que contienen la palabra.

    El siguiente paso es crear el diccionario TF para cada palabra. En el diccionario TF, la clave serán las palabras que aparecen con mayor frecuencia, mientras que los valores serán 49 vectores dimensionales ya que nuestro documento tiene 49 oraciones. Cada valor en el vector pertenecerá al valor TF de la palabra para la oración correspondiente. Mira el siguiente guión:

    word_tf_values = {}
    for token in most_freq:
        sent_tf_vector = []
        for document in corpus:
            doc_freq = 0
            for word in nltk.word_tokenize(document):
                if token == word:
                      doc_freq += 1
            word_tf = doc_freq/len(nltk.word_tokenize(document))
            sent_tf_vector.append(word_tf)
        word_tf_values[token] = sent_tf_vector
    

    En el script anterior, creamos un diccionario que contiene la palabra como clave y una lista de 49 elementos como valor ya que tenemos 49 oraciones en nuestro corpus. Cada elemento de la lista almacena el valor TF de la palabra para la oración correspondiente. En el guión de arriba word_tf_values es nuestro diccionario. Para cada palabra, creamos una lista. sent_tf_vector.

    Luego iteramos a través de cada oración en el corpus y la convertimos en token. La palabra del bucle exterior se corresponde con cada palabra de la oración. Si se encuentra una coincidencia, doc_freq La variable se incrementa en 1. Una vez que se repiten todas las palabras de la oración, doc_freq se divide por la longitud total de la oración para encontrar el valor TF de la palabra para esa oración. Este proceso se repite para todas las palabras de la lista de palabras que aparecen con más frecuencia. El final word_tf_values El diccionario contendrá 200 palabras como claves. Para cada palabra, habrá una lista de 49 elementos como valor.

    Si miras el word_tf_values diccionario, se ve así:

    Puedes ver que el word es la clave, mientras que una lista de 49 elementos es el valor de cada clave.

    Ahora tenemos los valores IDF de todas las palabras, junto con los valores TF de cada palabra en las oraciones. El siguiente paso es simplemente multiplicar los valores IDF por los valores TF.

    tfidf_values = []
    for token in word_tf_values.keys():
        tfidf_sentences = []
        for tf_sentence in word_tf_values[token]:
            tf_idf_score = tf_sentence * word_idf_values[token]
            tfidf_sentences.append(tf_idf_score)
        tfidf_values.append(tfidf_sentences)
    

    En el script anterior, creamos una lista llamada tfidf_values. Luego iteramos a través de todas las claves en el word_tf_values diccionario. Estas claves son básicamente las palabras que aparecen con más frecuencia. Usando estas palabras, recuperamos la lista de 49 dimensiones que contiene los valores TF para la palabra correspondiente a cada oración. A continuación, el valor TF se multiplica por el valor IDF de la palabra y se almacena en el tf_idf_score variable. Luego, la variable se agrega al tf_idf_sentences lista. Finalmente, el tf_idf_sentences La lista se adjunta al tfidf_values lista.

    Ahora, en este momento, el tfidf_values es una lista de listas. Donde cada elemento es una lista de 49 dimensiones que contiene valores TFIDF de una palabra en particular para todas las oraciones. Necesitamos convertir la lista bidimensional en una matriz numpy. Mira el siguiente guión:

    tf_idf_model = np.asarray(tfidf_values)
    

    Ahora, nuestra matriz numpy se ve así:

    Sin embargo, todavía hay un problema con este modelo TF-IDF. La dimensión de la matriz es 200 x 49, lo que significa que cada columna representa el vector TF-IDF para la oración correspondiente. Queremos que las filas representen los vectores TF-IDF. Podemos hacerlo simplemente transponiendo nuestra matriz numpy de la siguiente manera:

    tf_idf_model = np.transpose(tf_idf_model)
    

    Ahora tenemos una matriz numérica de 49 x 200 dimensiones donde las filas corresponden a los vectores TF-IDF, como se muestra a continuación:

    Conclusión

    El modelo TF-IDF es uno de los modelos más utilizados para la conversión de texto a numérico. En este artículo, revisamos brevemente la teoría detrás del modelo TF-IDF. Finalmente, implementamos un modelo TF-IDF desde cero en Python. En el próximo artículo veremos cómo implementar el modelo N-Gram desde cero en Python.

     

    Etiquetas:

    Deja una respuesta

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