El balance de clases es un problema silencioso pero frecuente: cuando una categoría es mucho más común que la otra, el modelo aprende a ignorar la rara... que suele ser justo la que nos importa (el fraude, la enfermedad, la falla). Reconocerlo y corregirlo es clave para que un clasificador sirva de algo.
El problema: cuando una clase manda
Imagina detección de fraude donde el 99 % de las transacciones son legítimas y solo el 1 % es fraude. Un modelo perezoso que diga "nunca es fraude" acierta el 99 %... y es completamente inútil: no detecta ni un solo caso.
Como vimos en la matriz de confusión, la exactitud engaña con clases desbalanceadas. Lo primero, entonces, no es cambiar los datos, sino mirar las métricas correctas.
Primer paso: usar las métricas adecuadas
Con datos desbalanceados, olvida la exactitud y mira:
- Recall de la clase rara: ¿cuántos fraudes reales detecté?
- Precisión: de los que marqué como fraude, ¿cuántos lo eran?
- F1 o el área bajo la curva PR (precisión-recall): resúmenes honestos cuando hay desbalance.
A menudo, con solo optimizar la métrica correcta el problema mejora mucho.
Segundo paso: reequilibrar
Si hace falta más, hay tres familias de técnicas:
- Pesos por clase (class weights): decirle al modelo que equivocarse en la clase rara cuesta más. Es la opción más simple y limpia: no toca los datos.
- Sobremuestreo (oversampling): replicar o generar ejemplos de la clase minoritaria. El método clásico es SMOTE, que crea ejemplos sintéticos interpolando entre vecinos reales.
- Submuestreo (undersampling): descartar ejemplos de la clase mayoritaria. Útil cuando sobran datos, con el riesgo de tirar información.
En código
Lo más directo es el peso por clase, que casi todos los modelos de scikit-learn soportan:
from sklearn.linear_model import LogisticRegression
# "balanced" pesa cada clase de forma inversa a su frecuencia.
modelo = LogisticRegression(class_weight="balanced").fit(X_train, y_train)
Cuando eso no basta, imbalanced-learn aplica SMOTE solo sobre el entrenamiento:
from imblearn.over_sampling import SMOTE
X_bal, y_bal = SMOTE(random_state=0).fit_resample(X_train, y_train)
# ahora las clases están equilibradas; entrena tu modelo con X_bal, y_bal
Regla de oro: reequilibra solo el conjunto de entrenamiento, nunca el de prueba. El de prueba debe conservar la proporción real del mundo, o las métricas mentirán (fuga de datos).
Para llevar
Con clases desbalanceadas, la exactitud miente y el modelo tiende a ignorar la clase rara. La defensa tiene dos frentes: medir bien (recall, F1, PR-AUC en vez de exactitud) y, si hace falta, reequilibrar con pesos por clase, sobremuestreo (SMOTE) o submuestreo, siempre solo en el entrenamiento. Detectar la clase rara suele ser el verdadero objetivo del problema.
