- Caso práctico: Análisis de sentimiento con SVM y selección de kernel
Caso práctico: Análisis de sentimiento con SVM y selección de kernel
Taller práctico: En esta lección, nos sumergiremos en un caso de uso clásico del procesamiento de lenguaje natural: el análisis de sentimiento sobre reseñas de productos. Utilizaremos el dataset de IMDb (o uno similar) para entrenar un clasificador SVM que determine si una reseña es positiva o negativa. Exploraremos en detalle el preprocesamiento de texto, la elección del kernel, la optimización de hiperparámetros y la interpretación de los resultados.
Objetivos del taller:
- Comprender el flujo completo de un proyecto de análisis de sentimiento con SVM.
- Aplicar técnicas de preprocesamiento de texto: tokenización y vectorización TF-IDF.
- Entrenar SVM con kernels lineal y RBF, comparando su rendimiento.
- Evaluar el modelo mediante curvas ROC y métricas AUC.
- Interpretar los coeficientes del modelo lineal para identificar palabras clave.
- Analizar el efecto del parámetro C en el balance sesgo-varianza.
1. Preparación y carga del dataset
Comenzaremos cargando el dataset de IMDb. Trabajaremos con una muestra representativa para agilizar los cálculos, pero el principio es escalable a todo el dataset.
import pandas as pd
import numpy as np
from sklearn.datasets import load_files
from sklearn.model_selection import train_test_split
# Cargar el dataset (ajusta la ruta según tu entorno)
reviews = load_files('ruta/a/imdb_reviews', shuffle=True)
X, y = reviews.data, reviews.target
# Dividir en entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
print(f"Muestras de entrenamiento: {len(X_train)}")
print(f"Muestras de prueba: {len(X_test)}")
El dataset contiene reseñas etiquetadas como positivas (1) o negativas (0). Cada reseña es un texto crudo que debemos transformar en una representación numérica.
2. Preprocesamiento de texto: Tokenización y TF-IDF
El texto no puede ser alimentado directamente a un modelo SVM. Necesitamos convertirlo en vectores numéricos. Para ello, seguiremos estos pasos:
- Tokenización: Dividir el texto en palabras individuales (tokens).
- Limpieza: Eliminar palabras vacías (stop words), convertir a minúsculas y aplicar stemming o lematización (opcional).
- Vectorización TF-IDF: Asignar un peso a cada palabra basado en su frecuencia en el documento y su rareza en el corpus general.
from sklearn.feature_extraction.text import TfidfVectorizer
# Configurar el vectorizador TF-IDF con parámetros típicos
vectorizer = TfidfVectorizer(
max_features=5000, # Limitar el vocabulario a las 5000 palabras más importantes
stop_words='english', # Eliminar palabras vacías en inglés
lowercase=True, # Convertir todo a minúsculas
min_df=5, # Ignorar palabras que aparecen en menos de 5 documentos
max_df=0.7, # Ignorar palabras que aparecen en más del 70% de los documentos
ngram_range=(1,2) # Incluir unigramas y bigramas
)
# Transformar los textos en matrices TF-IDF
X_train_tfidf = vectorizer.fit_transform(X_train)
X_test_tfidf = vectorizer.transform(X_test)
print(f"Matriz TF-IDF de entrenamiento: {X_train_tfidf.shape}")
print(f"Matriz TF-IDF de prueba: {X_test_tfidf.shape}")
El resultado es una matriz dispersa donde cada fila es una reseña y cada columna representa un término (palabra o bigrama). Los valores TF-IDF capturan la relevancia de cada término en cada documento, penalizando términos muy comunes.
3. Entrenamiento de SVM con diferentes kernels
Probaremos dos kernels fundamentales: lineal y RBF (Radial Basis Function). El kernel lineal es rápido e interpretable, mientras que el RBF puede capturar relaciones no lineales en los datos.
3.1. SVM con kernel lineal
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report
# SVM lineal con parámetro C por defecto (C=1.0)
svm_lineal = SVC(kernel='linear', C=1.0, random_state=42, probability=True)
svm_lineal.fit(X_train_tfidf, y_train)
# Predicciones y evaluación
y_pred_lineal = svm_lineal.predict(X_test_tfidf)
accuracy_lineal = accuracy_score(y_test, y_pred_lineal)
print(f"Precisión (kernel lineal): {accuracy_lineal:.4f}")
print("\nReporte de clasificación (lineal):")
print(classification_report(y_test, y_pred_lineal))
3.2. SVM con kernel RBF
# SVM con kernel RBF (gamma='scale' es el valor por defecto recomendado)
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42, probability=True)
svm_rbf.fit(X_train_tfidf, y_train)
# Predicciones y evaluación
y_pred_rbf = svm_rbf.predict(X_test_tfidf)
accuracy_rbf = accuracy_score(y_test, y_pred_rbf)
print(f"Precisión (kernel RBF): {accuracy_rbf:.4f}")
print("\nReporte de clasificación (RBF):")
print(classification_report(y_test, y_pred_rbf))
Análisis comparativo inicial:
- El kernel lineal suele ser más rápido de entrenar y escalable a grandes conjuntos de datos.
- El kernel RBF puede lograr mayor precisión si la frontera de decisión es intrínsecamente no lineal, pero requiere ajuste de hiperparámetros (C y gamma).
- En datos de texto de alta dimensionalidad (pocos documentos, muchas características), el kernel lineal a menudo funciona sorprendentemente bien.
4. Evaluación mediante curvas ROC y AUC
La precisión por sí sola no siempre es suficiente. Las curvas ROC (Receiver Operating Characteristic) y el área bajo la curva (AUC) nos permiten evaluar el rendimiento del clasificador en todos los umbrales de decisión posibles.
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, roc_auc_score
# Obtener probabilidades para la clase positiva (1)
y_prob_lineal = svm_lineal.predict_proba(X_test_tfidf)[:, 1]
y_prob_rbf = svm_rbf.predict_proba(X_test_tfidf)[:, 1]
# Calcular curvas ROC
fpr_lineal, tpr_lineal, _ = roc_curve(y_test, y_prob_lineal)
fpr_rbf, tpr_rbf, _ = roc_curve(y_test, y_prob_rbf)
# Calcular AUC
auc_lineal = roc_auc_score(y_test, y_prob_lineal)
auc_rbf = roc_auc_score(y_test, y_prob_rbf)
print(f"AUC (kernel lineal): {auc_lineal:.4f}")
print(f"AUC (kernel RBF): {auc_rbf:.4f}")
# Graficar curvas ROC (opcional, solo para visualización conceptual)
# plt.figure(figsize=(8,6))
# plt.plot(fpr_lineal, tpr_lineal, label=f'SVM Lineal (AUC = {auc_lineal:.4f})')
# plt.plot(fpr_rbf, tpr_rbf, label=f'SVM RBF (AUC = {auc_rbf:.4f})')
# plt.xlabel('Tasa de Falsos Positivos (FPR)')
# plt.ylabel('Tasa de Verdaderos Positivos (TPR)')
# plt.title('Curvas ROC: Comparación de kernels SVM')
# plt.legend()
# plt.grid(True)
# plt.show()
Interpretación de las curvas ROC:
- Una curva ROC que se aproxima a la esquina superior izquierda (TPR alta, FPR baja) indica un clasificador excelente.
- Un AUC cercano a 1.0 sugiere un rendimiento casi perfecto; un AUC de 0.5 es equivalente a una clasificación aleatoria.
- Compara los valores de AUC entre los dos kernels para determinar cuál es más robusto en términos de separabilidad de clases.
5. Interpretabilidad: Coeficientes del SVM lineal
Una ventaja clave del SVM con kernel lineal es su interpretabilidad. Los coeficientes (pesos) asignados a cada característica nos indican qué palabras influyen más en la decisión de clasificación.
# Extraer los coeficientes del modelo lineal
coefficients = svm_lineal.coef_.toarray().flatten()
feature_names = vectorizer.get_feature_names_out()
# Crear un DataFrame con los pesos
import pandas as pd
coef_df = pd.DataFrame({
'palabra': feature_names,
'coeficiente': coefficients
}).sort_values('coeficiente', ascending=False)
# Mostrar las 10 palabras más positivas (reseña positiva) y más negativas
print("10 palabras con mayor peso positivo (asociadas a reseñas positivas):")
print(coef_df.head(10))
print("\n10 palabras con menor peso (asociadas a reseñas negativas):")
print(coef_df.tail(10))
Los coeficientes positivos altos indican términos que empujan la clasificación hacia la clase positiva (reseña favorable), mientras que los coeficientes negativos fuertes se asocian con reseñas negativas. Por ejemplo, palabras como "excellent", "amazing", "wonderful" suelen tener pesos positivos, mientras que "terrible", "boring", "awful" aparecen con pesos negativos.
6. Efecto del parámetro C en el balance sesgo-varianza
El parámetro C controla la penalización por errores de clasificación en el margen del SVM. Un valor pequeño de C permite un margen más amplio (mayor sesgo, menor varianza), mientras que un C grande minimiza los errores de entrenamiento (menor sesgo, mayor varianza, riesgo de sobreajuste).
# Evaluar diferentes valores de C para el kernel lineal
C_values = [0.01, 0.1, 1.0, 10, 100]
train_scores = []
test_scores = []
for C in C_values:
svm = SVC(kernel='linear', C=C, random_state=42)
svm.fit(X_train_tfidf, y_train)
train_pred = svm.predict(X_train_tfidf)
test_pred = svm.predict(X_test_tfidf)
train_scores.append(accuracy_score(y_train, train_pred))
test_scores.append(accuracy_score(y_test, test_pred))
# Mostrar resultados en una tabla
print("Efecto del parámetro C en la precisión:")
print(f"{'C':
Análisis del balance sesgo-varianza:
- C pequeña (ej. 0.01): Alto sesgo, modelo demasiado simple, bajo rendimiento tanto en entrenamiento como en prueba (subajuste).
- C intermedia (ej. 1.0): Buen balance, el modelo generaliza adecuadamente. Es el valor por defecto por una razón.
- C grande (ej. 100): Baja sesgo, alta varianza. El modelo se ajusta muy bien a los datos de entrenamiento, pero puede fallar en prueba (sobreajuste).
- La elección óptima de C depende del dataset y se determina típicamente mediante validación cruzada.
7. Conclusiones y mejores prácticas
Este caso práctico demuestra el poder de SVM para análisis de sentimiento en texto, siempre que se realice un preprocesamiento adecuado. Las principales lecciones aprendidas son:
- Preprocesamiento de texto: La vectorización TF-IDF es una técnica estándar y efectiva. El ajuste de parámetros como
max_features,ngram_rangey la eliminación de stop words es crucial. - Selección de kernel: Para datos de texto con alta dimensionalidad, el kernel lineal es a menudo suficiente y altamente interpretable. El kernel RBF puede mejorar el rendimiento si hay interacciones no lineales, pero requiere un ajuste cuidadoso de C y gamma.
- Evaluación robusta: Las curvas ROC y AUC proporcionan una visión más completa que la precisión simple, especialmente en datasets desbalanceados.
- Interpretabilidad: Los coeficientes del SVM lineal permiten extraer información valiosa sobre qué características (palabras) son más relevantes para la clasificación.
- Control del sobreajuste: El parámetro C es la herramienta principal para equilibrar sesgo y varianza. La validación cruzada es indispensable para encontrar el valor óptimo.
Próximos pasos recomendados:
- Realizar una búsqueda de hiperparámetros más exhaustiva usando
GridSearchCVoRandomizedSearchCV. - Explorar técnicas de preprocesamiento avanzadas como lematización (WordNetLemmatizer) o embeddings de palabras preentrenados (Word2Vec, GloVe).
- Aplicar el pipeline completo a datasets más grandes, evaluando también el tiempo de entrenamiento y la escalabilidad.
Este taller te ha proporcionado las herramientas para abordar problemas de análisis de sentimiento con SVM de manera profesional. La combinación de un preprocesamiento riguroso, una selección informada del kernel y un análisis cuidadoso de los parámetros es la clave para construir clasificadores robustos y explicables.
No hay comentarios por ahora.
Compartir este contenido
Compartir enlace
Compartir en redes sociales
Compartir por correo electrónico
Please iniciar sesión para compartir esto Artículo por correo electrónico.