16. Procesamiento Lenguaje Natural (NLP) III#
16.1. Representación o Vectorización del Texto#
¿Qué es la Representación del texto?
- Para cuantificar la semántica de las palabras.
- Para capturar su significado contextualizado. Hay palabras que, fuera de su contexto, pueden tener significados completamente diferentes.
- Porque los modelos no pueden procesar texto directamente.
Para Profundizar
16.1.1. Término de Frecuencia (Term Frequency – Inverse Document Frequency)#
¿Qué significa el Término de Frecuencia(TF-IDF)?
16.1.1.1. Ejemplo para explicar TF-IDF#
Supongamos que tenemos los siguientes tres documentos:
Documento 1: “Me gusta caminar por el bosque”
Documento 2: “Caminar por el bosque es sanador”
Documento 3: “Me gusta el bosque”
Término |
Documento 1 |
Documento 2 |
Documento 3 |
|---|---|---|---|
“Me” |
1/5 = 0.2 |
0 |
1/4 = 0.25 |
“gusta” |
1/5 = 0.2 |
0 |
0 |
“caminar” |
1/5 = 0.2 |
1/5 = 0.2 |
0 |
“por” |
1/5 = 0.2 |
1/5 = 0.2 |
0 |
“el” |
0 |
0 |
1/4 = 0.25 |
“bosque” |
1/5 = 0.2 |
1/5 = 0.2 |
1/4 = 0.25 |
“es” |
0 |
1/5 = 0.2 |
0 |
“sanador” |
0 |
1/5 = 0.2 |
0 |
N = 3 (número total de documentos)
Término |
IDF |
|---|---|
“Me” |
(\log(3/2) \approx 0.176) |
“gusta” |
(\log(3/2) \approx 0.176) |
“caminar” |
(\log(3/2) \approx 0.176) |
“por” |
(\log(3/2) \approx 0.176) |
“el” |
(\log(3/1) = \log(3) \approx 0.477) |
“bosque” |
(\log(3/2) \approx 0.176) |
“es” |
(\log(3/1) = \log(3) \approx 0.477) |
“sanador” |
(\log(3/1) = \log(3) \approx 0.477) |
Término |
Documento 1 |
Documento 2 |
Documento 3 |
|---|---|---|---|
“Me” |
(0.2 \cdot 0.176 \approx 0.035) |
(0 \cdot 0.176 = 0) |
(0.25 \cdot 0.176 \approx 0.044) |
“gusta” |
(0.2 \cdot 0.176 \approx 0.035) |
(0 \cdot 0.176 = 0) |
(0 \cdot 0 = 0) |
“caminar” |
(0.2 \cdot 0.176 \approx 0.035) |
(0.2 \cdot 0.176 \approx 0.035) |
(0 \cdot 0 = 0) |
“por” |
(0.2 \cdot 0.176 \approx 0.035) |
(0.2 \cdot 0.176 \approx 0.035) |
(0 \cdot 0 = 0) |
“el” |
(0 \cdot 0.477 = 0) |
(0 \cdot 0.477 = 0) |
(0.25 \cdot 0.477 \approx 0.119) |
“bosque” |
(0.2 \cdot 0.176 \approx 0.035) |
(0.2 \cdot 0.176 \approx 0.035) |
(0.25 \cdot 0.176 \approx 0.044) |
“es” |
(0 \cdot 0.477 = 0) |
(0.2 \cdot 0.477 \approx 0.095) |
(0 \cdot 0 = 0) |
“sanador” |
(0 \cdot 0.477 = 0) |
(0.2 \cdot 0.477 \approx 0.095) |
(0 \cdot 0 = 0) |
16.1.1.2. Ejemplo de código para explicar TF-IDF#
- Tokenización y conteo de palabras: Divide cada documento en palabras y cuenta la frecuencia de cada término en cada documento.
- Calcular TF (Frecuencia de Término): Calcula la frecuencia de cada término en cada documento.
- Calcular IDF (Frecuencia de Documento Inversa): Calcula la frecuencia inversa de los documentos para cada término.
- Calcular TF-IDF: Multiplica los valores de TF por los valores de IDF para obtener los valores de TF-IDF.
import math
from collections import Counter
# Documentos de ejemplo
doc1 = "Me gusta caminar por el bosque"
doc2 = "Caminar por el bosque es sanador"
doc3 = "Me gusta el bosque"
# Lista de documentos
docs = [doc1, doc2, doc3]
# Tokenización y conteo de palabras
tokenized_docs = [doc.lower().split() for doc in docs]
term_counts = [Counter(doc) for doc in tokenized_docs]
# Calcular TF (Frecuencia de Término)
def compute_tf(term_counts):
tfs = []
for doc_counts in term_counts:
doc_len = sum(doc_counts.values())
tf = {term: count / doc_len for term, count in doc_counts.items()}
tfs.append(tf)
return tfs
# Calcular IDF (Frecuencia de Documento Inversa)
def compute_idf(term_counts):
N = len(term_counts)
all_terms = set(term for doc_counts in term_counts for term in doc_counts)
idf = {}
for term in all_terms:
doc_freq = sum(1 for doc_counts in term_counts if term in doc_counts)
idf[term] = math.log(N / doc_freq)
return idf
# Calcular TF-IDF
def compute_tfidf(tfs, idf):
tfidf = []
for tf in tfs:
tfidf_doc = {term: tf_val * idf[term] for term, tf_val in tf.items()}
tfidf.append(tfidf_doc)
return tfidf
# Ejecutar las funciones
tfs = compute_tf(term_counts)
idf = compute_idf(term_counts)
tfidf = compute_tfidf(tfs, idf)
# Imprimir resultados
print("TF:")
for i, tf in enumerate(tfs):
print(f"Documento {i+1}: {tf}")
print("\nIDF:")
print(idf)
print("\nTF-IDF:")
for i, tfidf_doc in enumerate(tfidf):
print(f"Documento {i+1}: {tfidf_doc}")
TF:
Documento 1: {'me': 0.16666666666666666, 'gusta': 0.16666666666666666, 'caminar': 0.16666666666666666, 'por': 0.16666666666666666, 'el': 0.16666666666666666, 'bosque': 0.16666666666666666}
Documento 2: {'caminar': 0.16666666666666666, 'por': 0.16666666666666666, 'el': 0.16666666666666666, 'bosque': 0.16666666666666666, 'es': 0.16666666666666666, 'sanador': 0.16666666666666666}
Documento 3: {'me': 0.25, 'gusta': 0.25, 'el': 0.25, 'bosque': 0.25}
IDF:
{'por': 0.4054651081081644, 'gusta': 0.4054651081081644, 'caminar': 0.4054651081081644, 'el': 0.0, 'es': 1.0986122886681098, 'sanador': 1.0986122886681098, 'me': 0.4054651081081644, 'bosque': 0.0}
TF-IDF:
Documento 1: {'me': 0.06757751801802739, 'gusta': 0.06757751801802739, 'caminar': 0.06757751801802739, 'por': 0.06757751801802739, 'el': 0.0, 'bosque': 0.0}
Documento 2: {'caminar': 0.06757751801802739, 'por': 0.06757751801802739, 'el': 0.0, 'bosque': 0.0, 'es': 0.1831020481113516, 'sanador': 0.1831020481113516}
Documento 3: {'me': 0.1013662770270411, 'gusta': 0.1013662770270411, 'el': 0.0, 'bosque': 0.0}
16.1.2. One-hot-encoding#
¿Qué significa One-hot-encoding?
16.1.2.1. Ejemplo para explicar One-hot-encoding#
- Documento 1: "Me gusta caminar por el bosque"
- Documento 2: "Caminar por el bosque es sanador"
- Vocabulario: ["Me", "gusta", "caminar", "por", "el", "bosque", "es", "sanador"]
Vocabulario y sus vectores one-hot:
“Me”: [1, 0, 0, 0, 0, 0, 0, 0]
“gusta”: [0, 1, 0, 0, 0, 0, 0, 0]
“caminar”: [0, 0, 1, 0, 0, 0, 0, 0]
“por”: [0, 0, 0, 1, 0, 0, 0, 0]
“el”: [0, 0, 0, 0, 1, 0, 0, 0]
“bosque”: [0, 0, 0, 0, 0, 1, 0, 0]
“es”: [0, 0, 0, 0, 0, 0, 1, 0]
“sanador”: [0, 0, 0, 0, 0, 0, 0, 1]
“Me”: [1, 0, 0, 0, 0, 0, 0, 0]
“gusta”: [0, 1, 0, 0, 0, 0, 0, 0]
“caminar”: [0, 0, 1, 0, 0, 0, 0, 0]
“por”: [0, 0, 0, 1, 0, 0, 0, 0]
“el”: [0, 0, 0, 0, 1, 0, 0, 0]
“bosque”: [0, 0, 0, 0, 0, 1, 0, 0]
“caminar”: [0, 0, 1, 0, 0, 0, 0, 0]
“por”: [0, 0, 0, 1, 0, 0, 0, 0]
“el”: [0, 0, 0, 0, 1, 0, 0, 0]
“bosque”: [0, 0, 0, 0, 0, 1, 0, 0]
“es”: [0, 0, 0, 0, 0, 0, 1, 0]
“sanador”: [0, 0, 0, 0, 0, 0, 0, 1]
16.1.2.2. Ejemplo de código para explicar One-hot-encoding#
- Documentos de ejemplo: Se definen dos documentos como cadenas de texto.
- Crear el vocabulario:
- Se crea una lista de todas las palabras únicas en los documentos, convertidas a minúsculas y ordenadas alfabéticamente.
- Se crea un diccionario que asigna un índice a cada palabra del vocabulario.
- Crear la representación one-hot para cada palabra: La función one_hot_vector toma una palabra, el tamaño del vocabulario y el diccionario word_to_index como entrada y devuelve un vector one-hot para esa palabra.
- Representar los documentos usando la codificación one-hot: La función document_to_one_hot toma un documento, el tamaño del vocabulario y el diccionario word_to_index como entrada y devuelve una lista de vectores one-hot para cada palabra en el documento.
- Imprimir los resultados: Se imprimen el vocabulario, los índices de las palabras y la representación one-hot para cada documento.
¿Qué es defaultdict?
from collections import defaultdict
# Documentos de ejemplo
doc1 = "Me gusta caminar por el bosque"
doc2 = "Caminar por el bosque es sanador"
# Crear una lista de documentos
docs = [doc1, doc2]
# Paso 1: Crear el vocabulario
vocabulario = sorted(set(word.lower() for doc in docs for word in doc.split()))
# Crear un diccionario que asigne un índice a cada palabra del vocabulario
word_to_index = {word: idx for idx, word in enumerate(vocabulario)}
# Paso 2: Crear la representación one-hot para cada palabra
def one_hot_vector(word, vocab_size, word_to_index):
vector = [0] * vocab_size
index = word_to_index[word]
vector[index] = 1
return vector
# Representar los documentos usando la codificación one-hot
def document_to_one_hot(doc, vocab_size, word_to_index):
words = doc.lower().split()
one_hot_vectors = [one_hot_vector(word, vocab_size, word_to_index) for word in words]
return one_hot_vectors
# Tamaño del vocabulario
vocab_size = len(vocabulario)
# Representación one-hot para cada documento
one_hot_doc1 = document_to_one_hot(doc1, vocab_size, word_to_index)
one_hot_doc2 = document_to_one_hot(doc2, vocab_size, word_to_index)
# Imprimir los resultados
print("Vocabulario:", vocabulario)
print("Índices de las palabras:", word_to_index)
print("\nRepresentación one-hot Documento 1:")
for word, vector in zip(doc1.split(), one_hot_doc1):
print(f"{word}: {vector}")
print("\nRepresentación one-hot Documento 2:")
for word, vector in zip(doc2.split(), one_hot_doc2):
print(f"{word}: {vector}")
Vocabulario: ['bosque', 'caminar', 'el', 'es', 'gusta', 'me', 'por', 'sanador']
Índices de las palabras: {'bosque': 0, 'caminar': 1, 'el': 2, 'es': 3, 'gusta': 4, 'me': 5, 'por': 6, 'sanador': 7}
Representación one-hot Documento 1:
Me: [0, 0, 0, 0, 0, 1, 0, 0]
gusta: [0, 0, 0, 0, 1, 0, 0, 0]
caminar: [0, 1, 0, 0, 0, 0, 0, 0]
por: [0, 0, 0, 0, 0, 0, 1, 0]
el: [0, 0, 1, 0, 0, 0, 0, 0]
bosque: [1, 0, 0, 0, 0, 0, 0, 0]
Representación one-hot Documento 2:
Caminar: [0, 1, 0, 0, 0, 0, 0, 0]
por: [0, 0, 0, 0, 0, 0, 1, 0]
el: [0, 0, 1, 0, 0, 0, 0, 0]
bosque: [1, 0, 0, 0, 0, 0, 0, 0]
es: [0, 0, 0, 1, 0, 0, 0, 0]
sanador: [0, 0, 0, 0, 0, 0, 0, 1]
16.1.3. Bag of Words#
¿Qué significa Bag of words (BoW)?
16.1.3.1. Ejemplo para explicar Bag of Words#
- Documento 1: "Me gusta caminar por el bosque"
- Documento 2: "Caminar por el bosque es sanador"
- Vocabulario: ["Me", "gusta", "caminar", "por", "el", "bosque", "es", "sanador"]
“Me” aparece 1 vez
“gusta” aparece 1 vez
“caminar” aparece 1 vez
“por” aparece 1 vez
“el” aparece 1 vez
“bosque” aparece 1 vez
“es” aparece 0 veces
“sanador” aparece 0 veces
“Me” aparece 0 vez
“gusta” aparece 0 vez
“caminar” aparece 1 vez
“por” aparece 1 vez
“el” aparece 1 vez
“bosque” aparece 1 vez
“es” aparece 1 vez
“sanador” aparece 1 vez
Documento 1: [1, 1, 1, 1, 1, 1, 0, 0]
Documento 2: [0, 0, 1, 1, 1, 1, 1, 1]
16.1.3.2. Ejemplo de código para explicar Bag of Words#
- Documentos de ejemplo: Se definen dos documentos como cadenas de texto.
- Crear el vocabulario: Se crea una lista de todas las palabras únicas en los documentos, convertidas a minúsculas y ordenadas alfabéticamente.
- Vectorizar cada documento:
- La función `vectorize` toma un documento y el vocabulario como entrada y devuelve un vector de conteos para ese documento.
- Para cada palabra en el vocabulario, cuenta cuántas veces aparece en el documento y construye un vector de estos conteos.
- Imprimir la representación BoW:
- Se imprimen el vocabulario y los vectores de conteos para ambos documentos.
- Se construye y se imprime la representación BoW para los documentos.
# Documentos de ejemplo
doc1 = "Me gusta caminar por el bosque"
doc2 = "Caminar por el bosque es sanador"
# Crear una lista de documentos
docs = [doc1, doc2]
# Paso 1: Crear el vocabulario
vocabulario = sorted(set(word.lower() for doc in docs for word in doc.split()))
# Paso 2: Representar cada documento como un vector de conteos
def vectorize(doc, vocabulario):
vector = [0] * len(vocabulario)
word_count = Counter(doc.lower().split())
for i, word in enumerate(vocabulario):
vector[i] = word_count[word]
return vector
# Vectorizar documentos
vector_doc1 = vectorize(doc1, vocabulario)
vector_doc2 = vectorize(doc2, vocabulario)
# Paso 3: Imprimir la representación BoW para los documentos
print("Vocabulario:", vocabulario)
print("Vector para Documento 1:", vector_doc1)
print("Vector para Documento 2:", vector_doc2)
# Representación BoW
bow_representation = [vector_doc1, vector_doc2]
print("\nRepresentación BoW:")
for i, vector in enumerate(bow_representation):
print(f"Documento {i+1}: {vector}")
Vocabulario: ['bosque', 'caminar', 'el', 'es', 'gusta', 'me', 'por', 'sanador']
Vector para Documento 1: [1, 1, 1, 0, 1, 1, 1, 0]
Vector para Documento 2: [1, 1, 1, 1, 0, 0, 1, 1]
Representación BoW:
Documento 1: [1, 1, 1, 0, 1, 1, 1, 0]
Documento 2: [1, 1, 1, 1, 0, 0, 1, 1]
Para tener en cuenta