Introducción
En este laboratorio, exploraremos la vectorización de texto, que es el proceso de representar datos de entrada no numéricos (como diccionarios o documentos de texto) como vectores de números reales. Compararemos dos métodos, FeatureHasher y DictVectorizer, mediante el uso de ambos métodos para vectorizar documentos de texto que se han preprocesado (tokenizados) con la ayuda de una función personalizada de Python.
Consejos sobre la VM
Una vez finalizada la inicialización de la VM, haga clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.
A veces, es posible que tenga que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no se puede automatizar debido a las limitaciones de Jupyter Notebook.
Si tiene problemas durante el aprendizaje, no dude en preguntar a Labby. Deje su retroalimentación después de la sesión y le resolveremos el problema inmediatamente.
Cargar datos
Cargaremos datos del 20newsgroups_dataset, que comprende alrededor de 18000 publicaciones de grupos de noticias en 20 temas divididos en dos subconjuntos: uno para el entrenamiento y otro para la prueba. Por simplicidad y para reducir el costo computacional, seleccionamos un subconjunto de 7 temas y usamos solo el conjunto de entrenamiento.
from sklearn.datasets import fetch_20newsgroups
categories = [
"alt.atheism",
"comp.graphics",
"comp.sys.ibm.pc.hardware",
"misc.forsale",
"rec.autos",
"sci.space",
"talk.religion.misc",
]
print("Loading 20 newsgroups training data")
raw_data, _ = fetch_20newsgroups(subset="train", categories=categories, return_X_y=True)
data_size_mb = sum(len(s.encode("utf-8")) for s in raw_data) / 1e6
print(f"{len(raw_data)} documents - {data_size_mb:.3f}MB")
Definir funciones de preprocesamiento
Un token puede ser una palabra, una parte de una palabra o cualquier cosa comprendida entre espacios o símbolos en una cadena. Aquí definimos una función que extrae los tokens utilizando una expresión regular simple (regex) que coincide con los caracteres de palabras Unicode. Esto incluye la mayoría de los caracteres que pueden ser parte de una palabra en cualquier idioma, así como números y el subrayado:
import re
def tokenize(doc):
"""Extraer tokens de doc.
Esto utiliza una regex simple que coincide con los caracteres de palabras para dividir las cadenas
en tokens. Para un enfoque más sistemático, consulte CountVectorizer o
TfidfVectorizer.
"""
return (tok.lower() for tok in re.findall(r"\w+", doc))
Definimos una función adicional que cuenta la (frecuencia de) ocurrencia de cada token en un documento dado. Devuelve un diccionario de frecuencias para ser utilizado por los vectorizadores.
from collections import defaultdict
def token_freqs(doc):
"""Extraer un diccionario que mapea tokens de doc a sus ocurrencias."""
freq = defaultdict(int)
for tok in tokenize(doc):
freq[tok] += 1
return freq
DictVectorizer
Evaluaremos el DictVectorizer, que es un método que recibe diccionarios como entrada.
from sklearn.feature_extraction import DictVectorizer
from time import time
t0 = time()
vectorizer = DictVectorizer()
vectorizer.fit_transform(token_freqs(d) for d in raw_data)
duration = time() - t0
print(f"hecho en {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} términos únicos")
FeatureHasher
Evaluaremos el FeatureHasher, que es un método que construye un vector de longitud predefinida aplicando una función de hash a las características (por ejemplo, tokens), luego utilizando los valores de hash directamente como índices de características y actualizando el vector resultante en esos índices.
from sklearn.feature_extraction import FeatureHasher
import numpy as np
t0 = time()
hasher = FeatureHasher(n_features=2**18)
X = hasher.transform(token_freqs(d) for d in raw_data)
duration = time() - t0
print(f"hecho en {duration:.3f} s")
print(f"Encontrados {len(np.unique(X.nonzero()[1]))} tokens únicos")
Comparación con vectorizadores de texto con fines especiales
Compararemos los métodos anteriores con el CountVectorizer y el HashingVectorizer.
from sklearn.feature_extraction.text import CountVectorizer, HashingVectorizer, TfidfVectorizer
t0 = time()
vectorizer = CountVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"hecho en {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} términos únicos")
t0 = time()
vectorizer = HashingVectorizer(n_features=2**18)
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"hecho en {duration:.3f} s")
t0 = time()
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"hecho en {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} términos únicos")
Graficar los resultados
Graficaremos la velocidad de los métodos anteriores para vectorizar.
import matplotlib.pyplot as plt
dict_count_vectorizers = {
"vectorizer": [
"DictVectorizer\nen diccionarios no de frecuencia",
"FeatureHasher\nen diccionarios no de frecuencia",
"FeatureHasher\nen tokens sin procesar",
"CountVectorizer",
"HashingVectorizer",
"TfidfVectorizer"
],
"speed": [
2.4, 4.4, 7.2, 5.1, 11.7, 2.9
]
}
fig, ax = plt.subplots(figsize=(12, 6))
y_pos = np.arange(len(dict_count_vectorizers["vectorizer"]))
ax.barh(y_pos, dict_count_vectorizers["speed"], align="center")
ax.set_yticks(y_pos)
ax.set_yticklabels(dict_count_vectorizers["vectorizer"])
ax.invert_yaxis()
_ = ax.set_xlabel("velocidad (MB/s)")
Resumen
En este laboratorio, exploramos la vectorización de texto al comparar dos métodos, FeatureHasher y DictVectorizer, y cuatro vectorizadores de texto con fines especiales, CountVectorizer, HashingVectorizer y TfidfVectorizer. Evaluamos los métodos de vectorización y graficamos los resultados. Concluimos que HashingVectorizer tiene un mejor rendimiento que CountVectorizer a expensas de la inversibilidad de la transformación debido a las colisiones de hash. Además, DictVectorizer y FeatureHasher tienen un mejor rendimiento que sus equivalentes de vectorización de texto en documentos con tokenización manual, ya que el paso de tokenización interno de los primeros vectorizadores compila una expresión regular una vez y luego la reutiliza para todos los documentos.