En 02-05 construimos un detector de fraude con el teorema de Bayes: partíamos de un prior (qué fracción de pedidos son fraudulentos), observábamos una evidencia y actualizábamos hacia el posterior. Aquella máquina de actualizar creencias era, sin saberlo, un clasificador a medio construir: le faltaba poder combinar varias evidencias a la vez sin que las cuentas explotaran. Naive Bayes completa la construcción con una suposición descaradamente falsa — que todas las features son independientes entre sí — que, contra todo pronóstico, produce uno de los clasificadores más rápidos, frugales y útiles de la práctica. En esta lección harás el viaje completo: del teorema al clasificador, un ejemplo numérico a mano con pedidos de MercaFresh, las tres variantes y cuándo usar cada una, y la implementación con scikit-learn.
Contenido
- Del teorema de Bayes al clasificador
- El supuesto "naive" y por qué funciona pese a ser falso
- Ejemplo numérico a mano: ¿es fraudulento este pedido?
- Suavizado de Laplace
- Las tres variantes: Gaussian, Multinomial, Bernoulli
- Implementación con scikit-learn
- Fortalezas y debilidades
Del teorema de Bayes al clasificador
Recordatorio exprés de 02-05, en vocabulario de clasificación:
$$P(\text{clase} \mid \text{datos}) = \frac{P(\text{datos} \mid \text{clase}) \cdot P(\text{clase})}{P(\text{datos})}$$
| Pieza | Nombre | En el detector de fraude |
|---|---|---|
| $P(\text{clase})$ | Prior | Fracción histórica de pedidos fraudulentos (p. ej. 1%) |
| $P(\text{datos} \mid \text{clase})$ | Verosimilitud | Con qué frecuencia los fraudes presentan esta evidencia |
| $P(\text{clase} \mid \text{datos})$ | Posterior | Lo que queremos: probabilidad de fraude dado lo observado |
| $P(\text{datos})$ | Evidencia | Normalizador: igual para todas las clases |
El clasificador bayesiano es directo: calcula el posterior de cada clase y elige la de posterior máximo. Como $P(\text{datos})$ es idéntico para todas las clases, ni hace falta calcularlo — basta comparar $P(\text{datos} \mid \text{clase}) \cdot P(\text{clase})$.
Nota la diferencia de filosofía con la regresión logística (04-02): aquella aprendía $P(\text{clase} \mid \mathbf{x})$ directamente, ajustando pesos; Naive Bayes la construye por piezas — aprende cómo se distribuyen los datos dentro de cada clase y deja que el teorema haga la síntesis. Por eso se dice que la logística es un modelo discriminativo y Naive Bayes uno generativo.
El supuesto "naive" y por qué funciona pese a ser falso
El obstáculo: con varias features, la verosimilitud es una probabilidad conjunta — $P(x_1, x_2, \dots, x_n \mid \text{clase})$. Estimarla honestamente exige haber visto muchas veces cada combinación de valores, y las combinaciones crecen exponencialmente: con 15 features es imposible (es otra cara de la maldición de la dimensionalidad de 04-05).
La salida "ingenua": suponer que, dentro de cada clase, las features son independientes entre sí. Entonces la conjunta se factoriza en un producto de términos individuales, cada uno trivial de estimar:
$$P(x_1, \dots, x_n \mid c) \approx P(x_1 \mid c) \cdot P(x_2 \mid c) \cdots P(x_n \mid c)$$
El supuesto es falso casi siempre — en MercaFresh, el importe de un pedido y su número de artículos están claramente correlacionados (02-03). ¿Por qué funciona entonces? Dos razones:
- Clasificar solo exige ordenar bien. No necesitamos que el posterior sea exacto, solo que la clase correcta quede por encima de las demás. Las violaciones de independencia distorsionan las probabilidades (suelen volverlas demasiado extremas: la evidencia correlacionada se "cuenta dos veces"), pero a menudo distorsionan a todas las clases en direcciones parecidas, y el ranking sobrevive.
- Menos parámetros, menos varianza. Estimar n distribuciones univariantes por clase requiere pocos datos y produce estimaciones estables; un modelo que respetara todas las dependencias necesitaría muchísimos más datos para no alucinar. Con datos escasos, el modelo sesgado-pero-estable gana al fiel-pero-tembloroso — un anticipo del compromiso sesgo-varianza de 06-05.
Consecuencia práctica: usa las etiquetas de Naive Bayes con confianza, y sus probabilidades con pinzas (tienden al 0.99 o al 0.01 injustificados).
Ejemplo numérico a mano: ¿es fraudulento este pedido?
Histórico de 1 000 pedidos de MercaFresh: 20 fraudulentos (prior del 2%) y 980 legítimos. Tres features binarias por pedido, con sus frecuencias en cada clase:
| Feature | P(sí | fraude) | P(sí | legítimo) |
|---|---|---|
importe_alto (> percentil 95) |
0.70 | 0.10 |
direccion_nueva |
0.80 | 0.15 |
madrugada (2:00–6:00) |
0.50 | 0.05 |
Llega un pedido con importe_alto = sí, direccion_nueva = sí, madrugada = no. Puntuación de cada clase (prior × producto de verosimilitudes, usando $P(\text{no}) = 1 - P(\text{sí})$):
- Fraude: $0.02 \times 0.70 \times 0.80 \times (1 - 0.50) = 0.02 \times 0.28 = 0.0056$
- Legítimo: $0.98 \times 0.10 \times 0.15 \times (1 - 0.05) = 0.98 \times 0.01425 = 0.01397$
Gana legítimo (0.01397 > 0.0056). Normalizando para obtener el posterior: $P(\text{fraude}) = \frac{0.0056}{0.0056 + 0.01397} \approx 0.29$.
Dos lecturas valiosas:
- El prior pesa muchísimo: pese a dos señales de alarma de tres, el 2% de partida mantiene el fraude por debajo del 50%. Es la misma lección de los falsos positivos de 02-05: con eventos raros, la evidencia tiene que ser abrumadora. Aun así, un posterior del 29% frente al 2% basal — el pedido es 14 veces más sospechoso que la media — justifica de sobra una revisión manual.
- Cada feature multiplica: la ausencia de
madrugadafavoreció a "legítimo" (0.95 contra 0.50). En Naive Bayes ninguna evidencia es neutral: todas empujan.
Esto es todo el algoritmo. Entrenar = contar frecuencias (o medias y varianzas). Predecir = multiplicar y comparar. De ahí su velocidad imbatible.
Suavizado de Laplace
Un fallo mortal del conteo puro: si en el histórico ningún fraude ocurrió en madrugada, entonces $P(\text{madrugada} \mid \text{fraude}) = 0$, y ese cero aniquila el producto entero — ningún pedido de madrugada podría ser clasificado jamás como fraude, por escandalosas que fueran sus demás señales. Un solo hueco en los datos vetaría una clase completa.
El suavizado de Laplace lo evita sumando un pequeño conteo ficticio $\alpha$ (típicamente 1) a cada casilla: en lugar de $\frac{\text{casos}}{\text{total}}$, se estima $\frac{\text{casos} + \alpha}{\text{total} + \alpha \cdot k}$ (con k valores posibles). Así ninguna probabilidad estimada es exactamente 0 ni 1 — "no haberlo visto aún" se trata como "muy raro", no como "imposible". En sklearn es el parámetro alpha de las variantes de conteo.
Las tres variantes: Gaussian, Multinomial, Bernoulli
Lo único que cambia entre variantes es cómo se modela $P(x_i \mid c)$ según el tipo de feature:
| Variante | Features que espera | Cómo modela cada feature | Caso de uso típico |
|---|---|---|---|
GaussianNB |
Continuas | Una normal por clase: media y varianza (02-02) | Datos tabulares como el churn de MercaFresh |
MultinomialNB |
Conteos (enteros ≥ 0) | Frecuencias relativas suavizadas | Texto: veces que aparece cada palabra |
BernoulliNB |
Binarias (0/1) | Probabilidad de "sí" por clase | Presencia/ausencia: flags como las del ejemplo a mano |
GaussianNB estima, para cada feature y cada clase, la media y la desviación de una campana de Gauss — la distribución normal de 02-02 trabajando de verosimilitud — y evalúa la densidad del valor observado en cada campana. MultinomialNB es el rey histórico de la clasificación de texto (spam, sentimientos): cada documento se representa como conteo de palabras, y la verosimilitud de cada palabra en cada clase se estima con Laplace; lo reencontrarás en el proyecto de análisis de sentimientos (09-03). Si tus features son mixtas, la vía pragmática es discretizar/binarizar hacia una variante, o dividir y combinar — aunque en tabular mixto suelen rendir mejor los árboles (04-03).
Implementación con scikit-learn
GaussianNB sobre el churn de MercaFresh, con el patrón de Pipeline habitual. Matiz de preprocesamiento: Naive Bayes no usa distancias, así que el escalado le es indiferente (como al árbol, 03-05); en cambio la transformación Yeo-Johnson de 03-03 le viene especialmente bien, porque acerca cada feature a la normalidad que GaussianNB presupone:
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import GaussianNB
# 'preprocesador': el ColumnTransformer de 03-06 (la rama Yeo-Johnson
# ayuda aqui de verdad; el RobustScaler ni ayuda ni estorba)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
nb = Pipeline([
("prep", preprocesador),
("modelo", GaussianNB()),
])
nb.fit(X_train, y_train) # entrenar = estimar medias/varianzas por clase
print(f"Accuracy en test: {nb.score(X_test, y_test):.2%}")
# Lo que ha aprendido: una campana por feature y clase
modelo = nb.named_steps["modelo"]
print("Priors aprendidos:", modelo.class_prior_.round(3)) # [P(fiel), P(churn)]
print("Media de cada feature en la clase churn:", modelo.theta_[1].round(2))
# Posterior para priorizar revisiones, como en 02-05
p_churn = nb.predict_proba(X_test)[:, 1]Puntos de lectura:
class_prior_son los priors estimados del train — la proporción de cada clase, el "2%" de nuestro ejemplo a mano.theta_yvar_guardan la media y varianza de cada feature en cada clase: todo el modelo cabe en dos tablas pequeñas. Compara con K-NN (04-05), que carga el dataset entero: son los dos extremos del espectro memoria/síntesis.- Velocidad: entrenar es una pasada de conteos y momentos — Naive Bayes entrena en milisegundos donde la SVM (04-04) tarda minutos. Por eso es el clasificador de referencia para líneas base instantáneas y para sistemas con reentrenamiento continuo.
- Para texto, el mismo esqueleto con
MultinomialNB(alpha=1.0)tras una vectorización de conteos — la receta completa, en 09-03.
Fortalezas y debilidades
| Fortalezas | Debilidades |
|---|---|
| Entrenamiento y predicción rapidísimos, escalan linealmente | Probabilidades mal calibradas (demasiado extremas) |
| Rinde decentemente con muy pocos datos (pocos parámetros) | El supuesto de independencia penaliza cuando las dependencias son la señal |
| Maneja miles de features con naturalidad (texto) | GaussianNB sufre con features muy alejadas de la normal |
Sin hiperparámetros críticos (solo alpha en las variantes de conteo) |
Rara vez es el modelo más preciso en tabular; es la línea base honesta |
Incremental: puede aprender por lotes (partial_fit) |
Features irrelevantes ruidosas empujan igual que las buenas (selección en 03-06 ayuda) |
Errores Comunes y Consejos
- Tomar
predict_probaal pie de la letra. Un 0.998 de Naive Bayes no significa 99.8% real: la evidencia correlacionada se contó varias veces. Para decidir etiquetas y ordenar riesgos vale; para comunicar probabilidades al negocio, no sin calibrar. - Usar
MultinomialNBcon features continuas o negativas. Espera conteos; con la salida delRobustScaler(valores negativos) directamente falla. Cada variante con su tipo de feature — la tabla del apartado 5 es el mapa. - Olvidar el suavizado con categorías raras. Con
alpha=0, una categoría nunca vista en una clase veta esa clase para siempre. Deja elalpha=1por defecto salvo motivo fundado. - Descartarlo por "demasiado simple". En texto y en líneas base sigue siendo competitivo, y como primer modelo de un proyecto da en segundos la referencia que los demás deben batir.
- Consejo: cuando
GaussianNBrinda mal, dibuja el histograma por clase de tus features (02-01). Si ves bimodalidades o asimetrías brutales, la campana que el modelo asume no se parece a la realidad — y la transformación de 03-03 o el binning pueden recuperar varios puntos de accuracy.
Ejercicios
Ejercicio 1. Con las tablas del ejemplo a mano, clasifica el pedido (importe_alto = no, direccion_nueva = sí, madrugada = sí) y calcula su posterior de fraude normalizado.
Ejercicio 2. En el histórico, ningún pedido fraudulento usó pago_contrareembolso (0 de 20). Sin suavizado, ¿qué posterior de fraude tendría un pedido con contrareembolso, importe alto, dirección nueva y madrugada? Recalcula $P(\text{contrareembolso} \mid \text{fraude})$ con Laplace ($\alpha = 1$, feature binaria) y explica el cambio cualitativo.
Ejercicio 3. Entrena sobre el churn GaussianNB, la regresión logística (04-02) y el árbol de 04-03, cronometrando el fit de cada uno (time.perf_counter). Compara accuracy y tiempo, y razona en qué escenario de negocio elegirías Naive Bayes aun no siendo el más preciso.
Soluciones
Ejercicio 1
- Fraude: $0.02 \times (1-0.70) \times 0.80 \times 0.50 = 0.02 \times 0.12 = 0.0024$
- Legítimo: $0.98 \times (1-0.10) \times 0.15 \times 0.05 = 0.98 \times 0.00675 = 0.006615$
Gana legítimo; posterior de fraude $= \frac{0.0024}{0.0024 + 0.006615} \approx 0.27$. Curioso: dos señales de alarma (dirección nueva y madrugada) contra una tranquilizadora (importe normal) dejan al pedido en zona de sospecha moderada — de nuevo el prior del 2% haciendo de ancla, como en 02-05.
Ejercicio 2
Sin suavizado, $P(\text{contrareembolso} \mid \text{fraude}) = 0/20 = 0$, y la puntuación de fraude es $0.02 \times 0 \times \dots = 0$: posterior de fraude exactamente 0, por alarmantes que sean las otras tres features. El cero actúa de veto absoluto. Con Laplace: $\frac{0 + 1}{20 + 1 \cdot 2} = \frac{1}{22} \approx 0.045$. La puntuación de fraude renace: $0.02 \times 0.045 \times 0.70 \times 0.80 \times 0.50 \approx 2.5 \times 10^{-4}$, comparable ya con la de legítimo (que también incorpora su término de contrareembolso). El cambio cualitativo: "nunca visto" pasa de imposible a muy improbable, y la decisión vuelve a depender del conjunto de la evidencia en vez de un solo veto.
Ejercicio 3
import time
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
candidatos = {
"GaussianNB": GaussianNB(),
"LogisticRegression": LogisticRegression(max_iter=1000),
"DecisionTree(d=4)": DecisionTreeClassifier(max_depth=4, random_state=42),
}
for nombre, modelo in candidatos.items():
pipe = Pipeline([("prep", preprocesador), ("modelo", modelo)])
t0 = time.perf_counter()
pipe.fit(X_train, y_train)
t = time.perf_counter() - t0
print(f"{nombre:20s} | fit: {t*1000:6.1f} ms | "
f"test: {pipe.score(X_test, y_test):.2%}")Resultado típico: Naive Bayes es el más rápido con diferencia y queda cerca (algo por debajo) de los otros en accuracy. Escenarios donde ganaría el puesto pese a ello: (1) reentrenamiento muy frecuente o incremental — p. ej. un filtro que se actualiza con cada tanda de pedidos usando partial_fit; (2) muy pocos datos etiquetados — el fraude confirmado de MercaFresh puede ser un puñado de casos, y NB es de lo poco que no sobreajusta ahí; (3) como línea base instantánea que fija el listón antes de invertir en modelos caros. La precisión no es el único eje: coste, latencia y frescura del modelo también deciden (lo retomaremos en 08-02).
Conclusión
Has cerrado el círculo abierto en 02-05: el teorema de Bayes, con el supuesto ingenuo de independencia como pegamento, se convierte en un clasificador completo — priors por conteo, verosimilitudes por feature (campanas gaussianas, frecuencias suavizadas con Laplace o Bernoullis según la variante), y el posterior máximo como decisión. Sabes hacer las cuentas a mano, sabes por qué el modelo funciona aunque su supuesto sea mentira, y sabes situarlo: el clasificador más rápido y frugal del módulo, ideal como línea base y para texto, con probabilidades que hay que mirar con escepticismo.
Con esto tienes seis algoritmos supervisados, cada uno con una idea-fuerza distinta: ajustar rectas, curvar probabilidades, encadenar preguntas, maximizar márgenes, votar entre vecinos, multiplicar evidencias. La última lección del módulo presenta a la familia que puede aprender la idea que haga falta: pequeñas unidades de cálculo — cada una sospechosamente parecida a la regresión logística — conectadas en capas que componen funciones arbitrariamente complejas. Llegan las redes neuronales, y con ellas el cierre y la comparativa final del módulo.
Curso de Machine Learning
Módulo 1: Introducción al Machine Learning
- ¿Qué es el Machine Learning?
- Historia y evolución del Machine Learning
- Tipos de Machine Learning
- Aplicaciones del Machine Learning
- El flujo de trabajo de un proyecto de Machine Learning
Módulo 2: Fundamentos de Estadística y Probabilidad
- Conceptos básicos de estadística
- Distribuciones de probabilidad
- Correlación y covarianza
- Inferencia estadística
- Teorema de Bayes
Módulo 3: Preprocesamiento de Datos
- Limpieza de datos
- Manejo de datos faltantes
- Transformación de datos
- Codificación de variables categóricas
- Normalización y estandarización
- Ingeniería de características
Módulo 4: Algoritmos de Machine Learning Supervisado
- Regresión lineal
- Regresión logística
- Árboles de decisión
- Máquinas de soporte vectorial (SVM)
- K-Vecinos más cercanos (K-NN)
- Naive Bayes
- Redes neuronales
Módulo 5: Algoritmos de Machine Learning No Supervisado
- Clustering: K-means
- Clustering jerárquico
- Análisis de componentes principales (PCA)
- Análisis de agrupamiento DBSCAN
- Visualización de datos con t-SNE y UMAP
Módulo 6: Evaluación y Validación de Modelos
- División de datos: entrenamiento, validación y prueba
- Métricas de evaluación
- Validación cruzada
- Curva ROC y AUC
- Overfitting y underfitting
Módulo 7: Técnicas Avanzadas y Optimización
- Regularización: Ridge, Lasso y Elastic Net
- Ensemble Learning
- Gradient Boosting
- Redes neuronales profundas (Deep Learning)
- Optimización de hiperparámetros
Módulo 8: Implementación y Despliegue de Modelos
- Frameworks y bibliotecas populares
- Implementación de modelos en producción
- Mantenimiento y monitoreo de modelos
- Consideraciones éticas y de privacidad
Módulo 9: Proyectos Prácticos
- Proyecto 1: Predicción de precios de viviendas
- Proyecto 2: Clasificación de imágenes
- Proyecto 3: Análisis de sentimientos en redes sociales
- Proyecto 4: Detección de fraudes
- Proyecto 5: Segmentación de clientes
