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

Este es el artículo número 14 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...

Este es el artículo número 14 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 la [bolsa de palabras] (https://en.wikipedia.org/wiki/Bag-of-words_model) enfoque. 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 esquema TF-FDI 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"
  • "Juan y yo jugamos al tenis"

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


           Play   Tennis   To   I   Football   Did   You   go

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


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 clasificador 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 tener un peso alto.

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 como nuestro ejemplo que usamos en el modelo de la bolsa de palabras.

  • "Me gusta jugar al fútbol"
  • "Saliste a jugar al tenis"
  • "Juan 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 yo hice juan como tu y ir yo jugar afuera jugar fútbol a tenis play
tennis


Paso 2: Buscar valores TF-IDF

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

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

1
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" ocurre 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 del documento y se puede calcular de la siguiente manera:

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

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

Encontremos la frecuencia IDF de la palabra "jugar". Dado que tenemos tres documentos y la palabra "jugar" aparece en los tres, el valor IDF de la palabra "jugar" 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 debemos crear un diccionario de frecuencias de palabras como se muestra a continuación:


Frecuencia de palabra yo 2 como 1 a 2 jugar 3 fútbol 1 Hizo 1 tu 1 ir 1 afuera 1 tenis 2 Juan 1 y 1


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


Frecuencia de palabra jugar 3 tenis 2 a 2 yo 2 fútbol 1 Hizo 1 tu 1 ir 1 afuera 1 como 1 Juan 1 y 1


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\   1\   3/1 = 3
        

\ \
Hizo\   1\   3/1 = 3
        

\ \
tú\   1\   3/1 = 3
        

\ \
ir\   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
           

\ \ \
I\   0,20 x 1,5 = 0,30\   0 x 1,5 = 0\   0,20 x 1,5 = 0,30
           

\ \ \
fútbol\   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
           

\ \ \
tú\   0 x3 = 0\   0,14 x 3 = 0,42\   0 x 3 = 0
           

\ \ \
ir\   0x 3 = 0\   0,14 x 3 = 0,42\   0 x 3 = 0
           


Los valores en las columnas para la oración 1, 2 y 3 son vectores TF-IDF correspondientes para cada palabra en las oraciones respectivas.

Tenga en cuenta 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:

1
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, en aras de la 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# -*- 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 raspamos el artículo de Wikipedia sobre Procesamiento natural del lenguaje. Luego lo procesamos previamente para eliminar todos los caracteres especiales y múltiples espacios vacíos. Finalmente, 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:

1
2
3
4
5
6
7
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, iteramos a través de la lista de palabras más frecuentes. 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 en 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 la variable doc_containing_word. 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 más frecuentes, 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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 script de arriba word_tf_values está nuestro diccionario. Para cada palabra, creamos una lista sent_tf_vector.

Luego iteramos a través de cada oración en el corpus y tokenizamos la oración. La palabra del bucle exterior se empareja con cada palabra de la oración. Si se encuentra una coincidencia, la variable doc_freq 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 más frecuentes. El diccionario final word_tf_values contendrá 200 palabras como claves. Para cada palabra, habrá una lista de 49 elementos como valor.

Si observa el diccionario word_tf_values, se ve así:

{.img-responsive}

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

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

1
2
3
4
5
6
7
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 diccionario word_tf_values. Estas teclas son básicamente las palabras que aparecen con más frecuencia. Usando estas palabras, recuperamos la lista de 49 dimensiones que contiene los valores de 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 la variable tf_idf_score. Luego, la variable se agrega a la lista tf_idf_sentences. Finalmente, la lista tf_idf_sentences se agrega a la lista tfidf_values.

Ahora, en este momento, 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:

1
tf_idf_model = np.asarray(tfidf_values)

Ahora, nuestra matriz numpy se ve así:

{.img-responsive}

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

1
tf_idf_model = np.transpose(tf_idf_model)

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

{.img-responsive}

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Subtítulos de imágenes con CNN y Transformers con Keras"](https://wikihtp.com/courses/image-captioning-with-cnns-and -transformadores-con-keras/#cta){target="_blank"}.

En este proyecto guiado, aprenderá a crear un modelo de subtítulos de imágenes, que acepta una imagen como entrada y produce un subtítulo de texto como salida.

Aprenderás a:

  • Preprocesar texto
  • Vectorizar la entrada de texto fácilmente
  • Trabaje con la API tf.data y cree conjuntos de datos de alto rendimiento
  • Cree Transformers desde cero con TensorFlow/Keras y KerasNLP: la adición horizontal oficial a Keras para crear modelos NLP de última generación
  • Cree arquitecturas híbridas donde la salida de una red se codifica para otra

¿Cómo enmarcamos los subtítulos de las imágenes? La mayoría lo considera un ejemplo de aprendizaje profundo generativo, porque estamos enseñando a una red a generar descripciones. Sin embargo, me gusta verlo como una instancia de traducción automática neuronal: estamos traduciendo las características visuales de una imagen en palabras. A través de la traducción, estamos generando una nueva representación de esa imagen, en lugar de simplemente generar un nuevo significado. Verlo como traducción, y solo por generación de extensión, enfoca la tarea bajo una luz diferente y la hace un poco más intuitiva.

Enmarcar el problema como uno de traducción hace que sea más fácil averiguar qué arquitectura querremos usar. Los transformadores solo de codificador son excelentes para comprender el texto (análisis de opinión, clasificación, etc.) porque los codificadores codifican representaciones significativas. Los modelos de solo decodificador son excelentes para la generación (como GPT-3), ya que los decodificadores pueden inferir representaciones significativas en otra secuencia con el mismo significado. La traducción generalmente se realiza mediante una arquitectura de codificador-decodificador, donde los codificadores codifican una representación significativa de una oración (o imagen, en nuestro caso) y los decodificadores aprenden a convertir esta secuencia en otra representación significativa que es más interpretable para nosotros (como una oració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.