Cómo crear un modelo de clasificación de texto en Machine Learning
Este tutorial muestra cómo transformar texto en características y entrenar un modelo de clasificación en Machine Learning para categorizar mensajes o reseñas. Es útil para automatizar la triage de soporte, análisis de sentimiento o clasificación de contenidos con un ejemplo práctico en Python y scikit-learn. Explicaré el porqué de cada paso y daré consejos prácticos para mejorar resultados reales.
Requisitos previos
- Python 3.8+ instalado
- Paquetes: scikit-learn, pandas, numpy (pip install scikit-learn pandas numpy)
- Conocimientos básicos de Python y pandas
Recomiendo trabajar en un entorno virtual (venv) para evitar conflictos de versiones. Para conjuntos de datos reales, idealmente tienes algunas centenas a miles de ejemplos para obtener modelos robustos — con sólo 50-100 muestras los resultados son muy inestables.
Paso 1: Preparar un conjunto de datos sencillo
Comenzamos con un pequeño conjunto de ejemplos etiquetados (por ejemplo, mensajes de entrenamiento). Mantén texto y etiqueta en un pandas DataFrame. Estructurar bien los datos facilita la posterior expansión a CSV o bases de datos.
import pandas as pd
data = [
("Adorei o produto, chegou rápido e funciona bem", "positivo"),
("Produto avariado, quero reembolso", "negativo"),
("Entrega atrasada, má experiência", "negativo"),
("Muito satisfeito com a qualidade", "positivo"),
("Suporte lento e pouco útil", "negativo"),
("Excelente, recomendo", "positivo")
]
df = pd.DataFrame(data, columns=["text", "label"])
print(df.head())
Este dataframe es sólo un ejemplo didáctico. En producción, guarda también un id, fecha y metadatos (canal, producto) para análisis posteriores.
Paso 2: Limpieza básica del texto
Normalizamos mayúsculas/minúsculas y eliminamos espacios superfluos. Dependiendo del caso puedes eliminar puntuación, URLs o teléfonos; sin embargo, TF-IDF suele ignorar palabras muy frecuentes si se configura correctamente. Evita el over-preprocessing: a veces la eliminación excesiva pierde señales importantes (p. ej.: "não gosto"). Aquí hacemos un preprocesado simple y legible:
def clean_text(s):
return s.lower().strip()
df["text_clean"] = df["text"].apply(clean_text)
print(df[["text", "text_clean"]].head())
Si tienes clases desequilibradas (p. ej.: 90% positivo), considera técnicas como remuestreo o pesos de clase en el clasificador.
Paso 3: Dividir datos en entrenamiento y prueba
Usa train_test_split para evaluar el modelo en datos nuevos. La estratificación es importante cuando existen pocas muestras por clase. En conjuntos pequeños usa test_size=0.2-0.33; en conjuntos grandes puedes reservar sólo el 10%.
from sklearn.model_selection import train_test_split
X = df["text_clean"]
y = df["label"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.33, random_state=42, stratify=y
)
Con stratify=y garantizamos que la proporción de clases en entrenamiento/prueba se mantenga. Para validación más robusta, usa Cross-Validation en lugar de una única división.
Paso 4: Transformar texto en vectores con TF-IDF
TF-IDF convierte texto en vectores numéricos ponderando términos por su importancia relativa. Aquí elegimos ngram_range=(1,2) para captar uni- y bi-gramas (p. ej.: "muito bom", "não gostei") y usamos max_df/min_df para reducir ruido.
from sklearn.feature_extraction.text import TfidfVectorizer
vect = TfidfVectorizer(ngram_range=(1,2), max_df=0.9, min_df=1)
X_train_tfidf = vect.fit_transform(X_train)
X_test_tfidf = vect.transform(X_test)
print("Número de features:", X_train_tfidf.shape[1])
En una muestra pequeña tendrás pocas features (10-100). En conjuntos de datos medianos (1000+ textos) normalmente tienes miles de features; en ese caso evalúa reducción dimensional (TruncatedSVD) o límites mayores para min_df.
Paso 5: Entrenar un clasificador sencillo (Logistic Regression)
Logistic Regression funciona bien para texto y es rápido. El parámetro max_iter=1000 evita no convergencia en casos con muchas features.
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=1000)
model.fit(X_train_tfidf, y_train)
y_pred = model.predict(X_test_tfidf)
Alternativas para probar: SVM (bueno para datos dispersos y de alta dimensionalidad) o RandomForest (más lento, puede necesitar vectores densos).
Paso 6: Evaluar el modelo
Calcula métricas simples: precisión, recall y matriz de confusión para entender errores comunes (falsos positivos/negativos). En problemas desequilibrados, el f1-score es más informativo que la accuracy.
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
acc = accuracy_score(y_test, y_pred)
print("Acurácia:", acc)
print(classification_report(y_test, y_pred))
print("Matriz de confusión:\n", confusion_matrix(y_test, y_pred))
En un toy dataset la accuracy puede variar mucho — valores entre 50% y 90% son comunes dependiendo de la partición. En conjuntos de datos reales, se busca >70-80% para tareas sencillas de sentimiento; para tareas más complejas podrías necesitar modelos más avanzados o más datos.
Paso 7: Interpretar las features más importantes
Comprueba qué términos más contribuyen a cada clase usando los coeficientes del modelo. Esto ayuda a entender errores y a mejorar el preprocesamiento (por ejemplo, tratar negaciones).
import numpy as np
feature_names = vect.get_feature_names_out()
coefs = model.coef_[0]
top_pos = np.argsort(coefs)[-10:][[::-1]][0]
top_neg = np.argsort(coefs)[:10]
print("Top termos positivos:")
for i in top_pos:
print(feature_names[i], round(coefs[i], 3))
print("Top termos negativos:")
for i in top_neg:
print(feature_names[i], round(coefs[i], 3))
Si encuentras términos ambiguos, considera ajustar ngram_range o normalizar expresiones (p. ej.: "não gostei" manteniendo la negación).
Verificar el resultado
Confirma que tienes una accuracy coherente e inspecciona la matriz de confusión. Prueba el modelo con frases nuevas para ver si las predicciones tienen sentido:
samples = [
"Quero devolução, chegou danificado",
"Produto excelente, muito bom",
"A entrega demorou mas o produto é bom"
]
samples_tfidf = vect.transform([s.lower().strip() for s in samples])
print(model.predict(samples_tfidf))
Haz también pruebas de esfuerzo con 50-100 frases variadas para evaluar estabilidad. Si el modelo falla sistemáticamente en frases con negaciones, añade reglas de preprocesamiento o features específicas.
Conclusión
Ahora tienes un pipeline completo: limpieza básica, TF-IDF, entrenamiento y evaluación con Logistic Regression para clasificación de texto. Próximos pasos prácticos: aumentar el conjunto de datos a cientos de ejemplos, probar modelos como RandomForest o SVM, y añadir limpieza avanzada (eliminación de stopwords, stemming, lematización). Usa Cross-Validation y GridSearchCV para elegir hiperparámetros y reducir overfitting. Observa qué términos están causando más errores e itera en el preprocesamiento hasta obtener la robustez deseada.