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

P

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.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad