Redes Bayesianas
De los fundamentos de probabilidad al aprendizaje profundo bayesiano: teorema de Bayes, grafos dirigidos acíclicos, tablas de probabilidad condicional, inferencia exacta y aproximada, aprendizaje de estructura, BNNs, MC Dropout, Bayes by Backprop, y aplicaciones reales con código en Python (pgmpy, PyMC, PyTorch).
¿Qué es la probabilidad?
Antes de hablar de redes bayesianas necesitamos hablar de probabilidad. La probabilidad es el lenguaje que usamos para cuantificar la incertidumbre. No todo en la vida es blanco o negro: a veces llovemos, a veces no; a veces el email es spam, a veces no. La probabilidad nos da un número entre 0 y 1 para expresar cuán seguros estamos de algo.
La interpretación bayesiana fue propuesta por Thomas Bayes en 1763 (publicada póstumamente) y desarrollada extensamente por Pierre-Simon Laplace. Fue minoritaria durante gran parte del siglo XX frente al enfoque frecuentista, pero resurgió con fuerza en la segunda mitad del siglo gracias a avances computacionales (MCMC, Gibbs Sampling) que permitieron aplicar el método a problemas complejos. Hoy, el enfoque bayesiano es dominante en áreas como el modelado probabilístico, la robótica, la medicina y el deep learning con incertidumbre.
McGrayne, S.B. (2011). The Theory That Would Not Die. Yale University Press. — Historia fascinante de cómo el teorema de Bayes fue rechazado y reivindicado a lo largo de 250 años.Probabilidad conjunta y marginal
Cuando tenemos varias variables aleatorias, necesitamos describir cómo se relacionan entre sí. La probabilidad conjunta \( P(X, Y) \) nos dice la probabilidad de que ocurran simultáneamente X = x e Y = y.
A partir de la conjunta, podemos obtener la probabilidad marginal sumando (o integrando) sobre las variables que no nos interesan:
🚗 Ejemplo: Lluvia y Tráfico
Imaginemos dos variables: Lluvia (sí/no) y Tráfico (alto/bajo).
| Tráfico alto | Tráfico bajo | Marginal Lluvia | |
|---|---|---|---|
| Lluvia = sí | 0.25 | 0.05 | 0.30 |
| Lluvia = no | 0.20 | 0.50 | 0.70 |
| Marginal Tráfico | 0.45 | 0.55 | 1.00 |
Las marginales (en morado) se obtienen sumando filas o columnas. P(Lluvia=sí) = 0.25 + 0.05 = 0.30.
Probabilidad condicional
La probabilidad condicional responde a la pregunta: «dado que sé Y, ¿cómo cambia mi creencia sobre X?».
Del ejemplo anterior: ¿cuál es la probabilidad de tráfico alto dado que llueve?
La probabilidad condicional es el mecanismo fundamental de las redes bayesianas. Cada nodo en una red almacena exactamente esto: la distribución condicional de esa variable dados sus padres en el grafo. Cuando observamos nueva evidencia, las probabilidades condicionales se propagan por toda la red, actualizando nuestras creencias de forma coherente. Este flujo de información es lo que hace a las redes bayesianas tan poderosas para el razonamiento bajo incertidumbre: cada pieza de evidencia puede cambiar nuestras creencias sobre variables aparentemente lejanas en el grafo.
Independencia y dependencia condicional
Dos variables son independientes si conocer una no aporta información sobre la otra:
La independencia condicional es aún más potente: X e Y son condicionalmente independientes dado Z si:
🏠 Ejemplo cotidiano
Alarma de incendios (A), Incendio (I), Cocinar (C). La alarma puede sonar por un incendio o por cocinar. Si no sabes nada, A e I están correlacionadas. Pero si ya sabes que estás cocinando (C = sí), saber que la alarma sonó no te da más información sobre un incendio real: \( I \perp A \mid C \).
Este concepto es la base de las redes bayesianas: modelar qué variables son condicionalmente independientes permite factorizar distribuciones complejas.
En una red bayesiana con n variables binarias, la distribución conjunta completa requeriría \( 2^n - 1 \) parámetros. Pero si cada variable solo depende de unos pocos padres (digamos \( k \ll n \)), la factorización en condicionales locales reduce esto a \( O(n \cdot 2^k) \), que es exponencialmente más pequeño. La independencia condicional no es solo una propiedad estadística: refleja la estructura causal del mundo. Un médico sabe que los síntomas de un paciente dependen de su enfermedad, no directamente de los síntomas de otro paciente. Esta modularidad permite construir modelos enormes pieza a pieza.
El Teorema de Bayes
El teorema de Bayes es la pieza central de todo el enfoque bayesiano. Nos dice cómo actualizar nuestras creencias cuando recibimos nueva evidencia:
| Término | Nombre | Significado |
|---|---|---|
| P(θ) | Prior (a priori) | Lo que creemos sobre θ antes de ver datos |
| P(D | θ) | Verosimilitud (likelihood) | Cuán probable es observar D si θ fuera cierto |
| P(D) | Evidencia (marginal likelihood) | Constante de normalización: P(D) = Σ P(D|θ)P(θ) |
| P(θ | D) | Posterior (a posteriori) | Lo que creemos sobre θ después de ver datos |
🧮 Calculadora interactiva del Teorema de Bayes
Ejemplo clásico: test médico. Ajusta la prevalencia de la enfermedad, la sensibilidad del test y la tasa de falsos positivos.
Distribuciones de probabilidad
Las redes bayesianas usan distribuciones de probabilidad para describir cada variable. Aquí las más importantes:
| Distribución | Tipo | Fórmula | Uso en BNs |
|---|---|---|---|
| Bernoulli | Discreta | \( P(X=1) = p \) | Variables binarias (sí/no, spam/no-spam) |
| Categórica | Discreta | \( P(X=k) = p_k \) | Variables con múltiples categorías |
| Gaussiana | Continua | \( \mathcal{N}(\mu, \sigma^2) \) | Variables continuas, redes gaussianas |
| Beta | Continua | \( \text{Beta}(\alpha, \beta) \) | Prior conjugado para parámetros de Bernoulli |
| Dirichlet | Continua | \( \text{Dir}(\alpha_1, \ldots, \alpha_k) \) | Prior conjugado para parámetros categóricos |
Un prior conjugado es una distribución previa que, al combinarse con una verosimilitud particular, produce un posterior de la misma familia. Esto hace los cálculos tratables analíticamente.
Si observamos k éxitos en n ensayos, simplemente sumamos a los parámetros del prior. Elegante y computacionalmente trivial.
Regla de la cadena y factorización
La regla de la cadena nos permite descomponer cualquier distribución conjunta en un producto de condicionales:
¿El problema? Con n variables binarias, la tabla conjunta tiene \( 2^n \) entradas. Para n = 30 eso son más de mil millones de valores. Inviable.
Donde \( \text{Pa}(X_i) \) son los padres de \( X_i \) en el DAG. Cada nodo solo depende de sus padres directos, no de todo el historial. Pasamos de exponencial a un producto de tablas locales. ¡Esta es la magia!
📉 Reducción de complejidad
| Variables | Tabla completa | Red Bayesiana (≤3 padres) | Reducción |
|---|---|---|---|
| 10 | 1,024 | ~80 | ~13× |
| 20 | 1,048,576 | ~160 | ~6,500× |
| 50 | ~1015 | ~400 | ~1012× |
| 100 | ~1030 | ~800 | ~1027× |
¿Qué es una red bayesiana?
Una red bayesiana (Bayesian Network, BN) es un modelo gráfico probabilístico que representa un conjunto de variables y sus dependencias condicionales mediante un grafo dirigido acíclico (DAG).
Donde \( G = (V, E) \) es el DAG (vértices y aristas) y \( \Theta \) es el conjunto de parámetros (tablas de probabilidad condicional).
Esta factorización es posible gracias a las independencias condicionales codificadas en el grafo. Cada nodo solo necesita conocer el estado de sus padres directos, no de toda la red. Pearl (1988) demostró que esta propiedad, conocida como la condición de Markov local, es suficiente para definir una distribución conjunta única y consistente. En la práctica, esto significa que un experto del dominio puede construir una red bayesiana especificando relaciones locales ("la lluvia depende del estado de nubosidad") sin tener que pensar en la distribución conjunta global.
Tablas de probabilidad condicional (CPTs)
Cada nodo almacena una CPT (Conditional Probability Table) que define cómo se distribuye esa variable dado el estado de sus padres. Los nodos sin padres (raíces) tienen simplemente una distribución marginal.
☁️ CPT de Nublado (nodo raíz)
| C | P(C) |
|---|---|
| Sí | 0.50 |
| No | 0.50 |
💧 CPT de Riego | Nublado
| Nublado | P(Riego=sí) | P(Riego=no) |
|---|---|---|
| Sí | 0.10 | 0.90 |
| No | 0.50 | 0.50 |
Intuición: si está nublado, es menos probable que riegues el jardín.
🌧️ CPT de Lluvia | Nublado
| Nublado | P(Lluvia=sí) | P(Lluvia=no) |
|---|---|---|
| Sí | 0.80 | 0.20 |
| No | 0.20 | 0.80 |
🌿 CPT de Césped mojado | Riego, Lluvia
| Riego | Lluvia | P(Mojado=sí) | P(Mojado=no) |
|---|---|---|---|
| No | No | 0.00 | 1.00 |
| Sí | No | 0.90 | 0.10 |
| No | Sí | 0.90 | 0.10 |
| Sí | Sí | 0.99 | 0.01 |
En la práctica, las CPTs pueden especificarse de tres formas: (1) conocimiento experto: un médico estima probabilidades basadas en experiencia; (2) datos: se estiman por frecuencias relativas (MLE) o con priors bayesianos; (3) híbrido: el experto proporciona la estructura y un prior, y los datos ajustan. Para variables continuas, las CPTs se reemplazan por distribuciones condicionales paramétricas, típicamente relaciones lineales gaussianas en las redes gaussianas (GBN).
🔮 Propagación en la red del césped
Selecciona la evidencia observada y calcula las probabilidades posteriores de cada variable.
d-Separación: leyendo independencias del grafo
La d-separación (separación direccional) es la regla gráfica que nos permite leer las independencias condicionales directamente del DAG, sin calcular ninguna probabilidad.
Dados tres conjuntos de nodos X, Y, Z, decimos que X está d-separado de Y dado Z si todos los caminos entre X e Y están «bloqueados» por Z.
La d-separación es una herramienta fundamental porque permite verificar de forma puramente gráfica —sin ningún cálculo numérico— si dos variables son condicionalmente independientes. Esto tiene aplicaciones directas: en el algoritmo PC para aprendizaje de estructura, en la simplificación de consultas de inferencia, y en la validación de modelos causales. Verlet y Bareinboim (2020) extendieron estos criterios al ámbito de la inferencia causal transportable entre dominios.
Geiger, D., Verma, T. & Pearl, J. (1990). d-Separation: From Theorems to Algorithms. Machine Intelligence and Pattern Recognition, 10, 139–148.Manta de Markov
La manta de Markov de un nodo X es el conjunto mínimo de nodos que hace a X condicionalmente independiente del resto de la red:
🛡️ ¿Por qué importa?
- Para hacer inferencia local: solo necesitas la manta de Markov, no toda la red.
- En Gibbs sampling: muestrear X solo depende de su manta de Markov.
- Para selección de features: la manta de Markov de la variable objetivo es el conjunto óptimo de predictores.
Tipos de redes bayesianas
| Tipo | Variables | CPTs | Uso típico |
|---|---|---|---|
| Discreta | Categóricas | Tablas finitas | Diagnóstico médico, spam, averías |
| Gaussiana (GBN) | Continuas | Relaciones lineales + ruido gaussiano | Modelos financieros, sensores |
| Híbrida | Mixtas | CLG (Conditional Linear Gaussian) | Sistemas reales complejos |
| Dinámica (DBN) | Con tiempo | CPTs entre time-slices | Series temporales, tracking |
| Naive Bayes | Categóricas | Clase → features (indep. entre features) | Clasificación simple, filtro de spam |
| TAN | Categóricas | Naive Bayes + 1 dependencia entre features | Clasificación con dependencias |
Una DBN es como una red bayesiana «desenrollada» en el tiempo. Cada time-slice contiene las variables del sistema y las aristas inter-slice modelan las dependencias temporales.
Casos especiales famosos:
- Hidden Markov Model (HMM): un nodo oculto + un nodo observado por slice
- Kalman Filter: DBN gaussiana lineal
Las DBN generalizan a HMMs y Kalman filters: son el marco unificador para modelos probabilísticos temporales.
Redes bayesianas vs otros modelos gráficos
| Propiedad | Red Bayesiana (BN) | Markov Random Field (MRF) | Factor Graph |
|---|---|---|---|
| Grafo | Dirigido acíclico (DAG) | No dirigido | Bipartito (variables + factores) |
| Semántica | Causal / generativa | Correlaciones | General |
| Parámetros | CPTs (probabilidades) | Potenciales (no normalizados) | Factores arbitrarios |
| Independencias | d-separación | Separación por nodos | Según estructura |
| Ejemplo famoso | Diagnóstico médico | Segmentación de imágenes (CRF) | Decodificación LDPC |
¿Qué es la inferencia en una red bayesiana?
Inferir es la tarea central de una red bayesiana: dada evidencia (variables observadas), calcular la distribución posterior de las variables de interés (query).
Eliminación de variables
La eliminación de variables (Variable Elimination, VE) es el algoritmo exacto más directo. La idea: en vez de computar la distribución conjunta completa y luego marginalizar, vamos eliminando variables una a una, sumándolas «localmente».
Queremos calcular P(L | M = sí) en la red del césped.
Factores iniciales: P(C), P(R|C), P(L|C), P(M|R,L)
Fijar evidencia: M = sí → P(M=sí | R, L)
- Eliminar R: τ₁(C, L) = Σᵣ P(R|C) · P(M=sí|R,L)
- Eliminar C: τ₂(L) = Σ_c P(C) · P(L|C) · τ₁(C, L)
- Normalizar: P(L|M=sí) = τ₂(L) / Σₗ τ₂(L)
Resultado: P(L=sí | M=sí) ≈ 0.708. Si el césped está mojado, hay un 71% de probabilidad de que haya llovido.
La eficiencia de la eliminación de variables depende críticamente del orden de eliminación. Encontrar el orden óptimo es NP-hard (equivalente al problema del treewidth mínimo), pero existen heurísticas efectivas como min-degree y min-fill que funcionan bien en la práctica. La conexión entre eliminación de variables y las tensor networks de la física cuántica es un área activa de investigación: ambas se reducen a operaciones de contracción de tensores.
Koller, D. & Friedman, N. (2009). Probabilistic Graphical Models: Principles and Techniques. MIT Press. Cap. 9: Variable Elimination.Propagación de creencias (Belief Propagation)
La propagación de creencias (BP), también llamada message passing o algoritmo de Pearl (1988), es una forma elegante de hacer inferencia exacta en árboles (poly-trees) y aproximada en grafos generales (Loopy BP).
📨 Mensajes en el grafo
Cada nodo envía «mensajes» a sus vecinos con dos tipos:
- λ-mensaje (de hijo a padre): «aquí abajo, la evidencia sugiere que…»
- π-mensaje (de padre a hijo): «arriba, la evidencia sugiere que…»
La creencia de un nodo X se calcula combinando todos los mensajes que recibe:
| Propiedad | Variable Elimination | Belief Propagation |
|---|---|---|
| Tipo | Exacto (siempre) | Exacto en árboles, aproximado en loops |
| Eficiencia para múltiples queries | Hay que re-ejecutar por cada query | Una sola pasada calcula todas las marginales |
| Complejidad | Depende del orden de eliminación | O(n · k²) para árboles con k estados |
| Implementación | Más simple | Más eficiente en práctica |
Algoritmo de Junction Tree
El Junction Tree (JT) es el algoritmo exacto más general y eficiente. Transforma cualquier BN en un árbol de cliques donde se puede aplicar BP exacto.
Inferencia aproximada: MCMC
Cuando la inferencia exacta es intratable (redes grandes, variables continuas), recurrimos a métodos de Monte Carlo por Cadenas de Markov (MCMC). La idea: generar muestras de la distribución posterior y estimar probabilidades contando.
🎲 Gibbs Sampling interactivo
Ejecuta Gibbs Sampling en la red del césped para estimar P(Lluvia | Césped mojado = sí). Observa cómo converge con más muestras.
En la práctica, un aspecto crítico de MCMC es el diagnóstico de convergencia: ¿cómo sabemos que las muestras representan realmente la distribución posterior y no son solo ruido? Las herramientas más usadas incluyen el estadístico \( \hat{R} \) de Gelman-Rubin (compara varianza intra- e inter-cadena), el effective sample size (ESS), y la inspección visual de las trace plots. El sampler NUTS (No U-Turn Sampler), implementado en PyMC y Stan, ajusta automáticamente los parámetros de HMC y es hoy el estándar de facto para inferencia bayesiana en modelos continuos.
Hoffman, M.D. & Gelman, A. (2014). The No-U-Turn Sampler. JMLR, 15, 1593–1623. [arXiv]Inferencia variacional
La inferencia variacional (VI) reformula la inferencia como un problema de optimización: encontrar la distribución \( q(\theta) \) más cercana al posterior real \( p(\theta \mid D) \).
Como la KL no se puede computar directamente (necesitaríamos p(D)), maximizamos el ELBO (Evidence Lower Bound):
| Propiedad | MCMC | Inferencia Variacional |
|---|---|---|
| Naturaleza | Muestreo estocástico | Optimización determinista |
| Convergencia | Garantizada (teórica) al verdadero posterior | A una aproximación (sesgo posible) |
| Velocidad | Lenta (especialmente en alta dimensión) | Rápida (aprovecha SGD, GPUs) |
| Escalabilidad | Difícil en big data | Escala bien (SVI, amortized VI) |
| Uso en DL | MC Dropout | VAE, Bayes by Backprop |
La inferencia variacional ha experimentado una revolución con la llegada de las técnicas de amortized inference: en lugar de optimizar \( q \) para cada dato, entrenamos una red neuronal (el encoder) que directamente mapea datos a parámetros variacionales. Esto es la base del VAE y ha abierto la puerta a modelos generativos a gran escala. Variantes modernas como Normalizing Flows (Rezende & Mohamed, 2015) permiten familias variacionales mucho más expresivas que la asunción gaussiana clásica de mean-field.
Blei, D.M., Kucukelbir, A. & McAuliffe, J.D. (2017). Variational Inference: A Review for Statisticians. JASA, 112(518), 859–877. [arXiv]Aprendizaje de parámetros
Una vez fijada la estructura (el DAG), necesitamos estimar los valores de las CPTs a partir de datos. Hay dos enfoques principales:
MLE con datos completos
Si todos los datos son observados, MLE es trivial: contar frecuencias. Para un nodo \( X_i \) con padres \( \text{Pa}_i \):
🧮 Ejemplo numérico
Si tenemos 100 observaciones donde Nublado = sí, y en 82 de ellas Lluvia = sí:
Simple, rápido, pero puede sobreajustar con pocos datos (¿qué pasa si N = 5?).
Estimación bayesiana con prior de Dirichlet
Para evitar estimaciones de 0 o 1 con pocos datos, añadimos un prior de Dirichlet (el conjugado de la distribución categórica):
Aprendizaje de estructura
El problema más desafiante: ¿cómo descubrir el DAG a partir de datos? El número de DAGs posibles crece superexponencialmente con el número de variables.
| Nodos | DAGs posibles |
|---|---|
| 3 | 25 |
| 5 | 29,281 |
| 8 | ~7.8 × 10¹¹ |
| 10 | ~4.2 × 10¹⁸ |
| 20 | ~2.3 × 10⁷² |
Hay tres familias principales de algoritmos:
Funciones de puntuación (scores)
Las funciones de puntuación evalúan cuán bien un DAG explica los datos, penalizando la complejidad para evitar sobreajuste.
| Score | Fórmula (idea) | Propiedades |
|---|---|---|
| BIC / MDL | \( \text{BIC} = \log P(D \mid \hat{\theta}, G) - \frac{d}{2} \log N \) | Consistente, descomponible, penaliza complejidad |
| BDeu | \( \text{BDeu} = \log \int P(D \mid \theta, G) P(\theta \mid G) d\theta \) | Bayesiano (marginal likelihood), score equivalence |
| BGe | Versión gaussiana de BD para datos continuos | Para redes gaussianas, normal-Wishart prior |
| K2 | BDeu con prior uniforme (α=1) | Históricamente popular, simple |
| AIC | \( \text{AIC} = \log P(D \mid \hat{\theta}, G) - d \) | Menos conservador que BIC, tiende a sobreajustar más |
Un score es descomponible si se puede escribir como suma de scores locales, uno por nodo:
Esto es crucial para la eficiencia: al evaluar un cambio local en el grafo (añadir/eliminar/invertir una arista), solo recalculamos el score del nodo afectado, no de toda la red.
Algoritmo PC (constraint-based)
El algoritmo PC (Peter & Clark, Spirtes et al., 1993) descubre la estructura mediante tests de independencia condicional.
Un avance reciente es NOTEARS (Zheng et al., 2018), que reformula el aprendizaje de estructura como un problema de optimización continua, eliminando la restricción combinatoria de aciclicidad mediante una penalización diferenciable: \( h(W) = \text{tr}(e^{W \circ W}) - d = 0 \). Esto permite usar gradientes y escalar a redes con cientos de variables, algo impensable con métodos clásicos.
Zheng, X. et al. (2018). DAGs with NO TEARS: Continuous Optimization for Structure Learning. NeurIPS. [arXiv]Hill Climbing (score-based)
El Hill Climbing (HC) es el algoritmo de búsqueda más usado en la práctica. Es simple pero efectivo:
🏗️ Score de estructura
Compara el BIC score de diferentes estructuras para un dataset con 3 variables. Selecciona las aristas presentes y observa cómo cambia el score.
Clases de equivalencia de Markov
Varios DAGs diferentes pueden codificar exactamente las mismas independencias condicionales. Estos DAGs forman una clase de equivalencia de Markov, representada por un CPDAG (Completed Partially Directed Acyclic Graph).
🔄 Ejemplo de DAGs equivalentes
Estos tres DAGs son equivalentes de Markov (codifican las mismas independencias):
- A → B → C
- A ← B → C (fork)
- A ← B ← C
En todos los casos, A ⊥ C | B. Solo la V-structure (A → B ← C) pertenece a una clase diferente.
Judea Pearl formalizó la distinción entre «ver» (condicionamiento) e «intervenir» (do-operator):
P(Y | X=x): ¿qué observamos cuando X toma valor x? (correlación)
P(Y | do(X=x)): ¿qué pasa si forzamos X a valor x? (causalidad)
El criterio back-door y el front-door de Pearl nos dicen cuándo y cómo podemos estimar efectos causales a partir de datos observacionales usando la estructura de la BN.
Pearl, J. (2009). Causality: Models, Reasoning, and Inference. Cambridge University Press.La distinción entre correlación y causalidad es uno de los temas más importantes en ciencia de datos moderna. Las redes bayesianas, junto con los frameworks causales de Pearl y Rubin, proporcionan las herramientas matemáticas para abordar esta cuestión de forma rigurosa. En esta web, puedes profundizar en estos conceptos en el submódulo de Aprendizaje por Refuerzo, donde las decisiones causales son fundamentales para la política del agente.
La conexión entre redes bayesianas y deep learning
Las redes neuronales clásicas producen predicciones puntuales: «este email es spam con 92% de confianza». Pero, ¿cuán fiable es ese 92%? Las redes neuronales bayesianas (BNNs) resuelven esto tratando los pesos como variables aleatorias con distribuciones, no como valores fijos.
| Propiedad | NN clásica | BNN (Bayesian Neural Network) |
|---|---|---|
| Pesos | Valores fijos (punto) | Distribuciones P(w) |
| Predicción | f(x; w*) | ∫ f(x; w) P(w|D) dw |
| Incertidumbre | No cuantificada | Epistémica + aleatórica |
| Sobreajuste | Necesita regularización explícita | Regularización natural (prior) |
| Calibración | Generalmente pobre | Generalmente mejor |
| Coste computacional | Menor | Mayor (muestreo / VI) |
La conexión entre redes bayesianas y deep learning va más allá de una simple extensión técnica. MacKay (1992) ya propuso el tratamiento bayesiano de redes neuronales, mostrando que el weight decay (regularización L2) es equivalente a asumir un prior gaussiano sobre los pesos. Neal (1996) demostró que una BNN con una capa oculta infinitamente ancha converge a un Gaussian Process, conectando elegantemente dos mundos aparentemente distintos. Más recientemente, Wilson & Izmailov (2020) argumentaron que la perspectiva bayesiana explica por qué las redes neuronales generalizan tan bien.
Tipos de incertidumbre
Distinguir entre ambos tipos de incertidumbre es crucial para tomar decisiones informadas. Por ejemplo, en un sistema de diagnóstico médico por imágenes: si la incertidumbre epistémica es alta (el modelo «no ha visto» ese tipo de imagen), la decisión correcta es derivar a un especialista humano, no dar un diagnóstico posiblemente erróneo. Si la incertidumbre aleatoria es alta (la imagen es ruidosa por naturaleza), la decisión es pedir una nueva imagen. Kendall & Gal (2017) formalizaron esta descomposición para redes neuronales profundas, mostrando cómo capturar ambas simultáneamente.
Kendall, A. & Gal, Y. (2017). What Uncertainties Do We Need in Bayesian Deep Learning for Computer Vision? NeurIPS. [arXiv]MC Dropout: inferencia bayesiana «gratis»
MC Dropout (Gal & Ghahramani, 2016) es la forma más simple de obtener estimaciones bayesianas de una red neuronal: mantener dropout activado durante la inferencia y hacer múltiples pasadas forward.
import torch
import torch.nn as nn
class MCDropoutModel(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, drop_rate=0.2):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(drop_rate),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(drop_rate),
nn.Linear(hidden_dim, output_dim)
)
def forward(self, x):
return self.net(x)
def mc_predict(model, x, T=100):
"""Predicción bayesiana con MC Dropout."""
model.train() # ← Mantener dropout activo
preds = torch.stack([model(x) for _ in range(T)]) # (T, batch, out)
mean = preds.mean(dim=0) # Predicción promedio
std = preds.std(dim=0) # Incertidumbre
return mean, std
# Ejemplo de uso
model = MCDropoutModel(input_dim=10, hidden_dim=64, output_dim=1)
x_test = torch.randn(1, 10)
mean, uncertainty = mc_predict(model, x_test, T=200)
print(f"Predicción: {mean.item():.3f} ± {uncertainty.item():.3f}")
# Si uncertainty es alta → el modelo no está seguro → ¡pedir más datos!
Bayes by Backprop
Bayes by Backprop (Blundell et al., 2015) es un método de inferencia variacional para BNNs. En vez de aprender un valor por peso, aprendemos una distribución \( q(w) = \mathcal{N}(\mu, \sigma^2) \) por cada peso.
🔧 Truco de la reparametrización
Para poder hacer backpropagation a través de un muestreo, usamos el reparameterization trick:
Así, el muestreo es una operación determinista + ruido externo, y podemos calcular gradientes respecto a μ y σ. ¡El mismo truco que usa el VAE!
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class BayesianLinear(nn.Module):
"""Capa lineal con pesos distribuidos N(μ, σ²)."""
def __init__(self, in_features, out_features):
super().__init__()
# Parámetros variacionales: media y log-varianza
self.w_mu = nn.Parameter(torch.randn(out_features, in_features) * 0.1)
self.w_rho = nn.Parameter(torch.full((out_features, in_features), -3.0))
self.b_mu = nn.Parameter(torch.zeros(out_features))
self.b_rho = nn.Parameter(torch.full((out_features,), -3.0))
# Prior: N(0, 1)
self.prior_mu = 0.0
self.prior_sigma = 1.0
def forward(self, x):
# Reparameterization trick
w_sigma = torch.log1p(torch.exp(self.w_rho)) # softplus para σ > 0
b_sigma = torch.log1p(torch.exp(self.b_rho))
w = self.w_mu + w_sigma * torch.randn_like(w_sigma)
b = self.b_mu + b_sigma * torch.randn_like(b_sigma)
# KL divergence (vs prior gaussiano)
self.kl = self._kl_divergence(self.w_mu, w_sigma) + \
self._kl_divergence(self.b_mu, b_sigma)
return F.linear(x, w, b)
def _kl_divergence(self, mu, sigma):
"""KL(N(mu, sigma²) || N(0, 1))"""
return 0.5 * torch.sum(
sigma.pow(2) + mu.pow(2) - 1 - 2 * torch.log(sigma)
)
class BayesianNet(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.l1 = BayesianLinear(input_dim, hidden_dim)
self.l2 = BayesianLinear(hidden_dim, output_dim)
def forward(self, x):
x = F.relu(self.l1(x))
return self.l2(x)
def kl_loss(self):
return self.l1.kl + self.l2.kl
# Entrenamiento
model = BayesianNet(10, 64, 1)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(100):
pred = model(x_train)
nll = F.mse_loss(pred, y_train)
kl = model.kl_loss() / len(x_train) # Normalizar por N
loss = nll + kl # ELBO = NLL + KL
optimizer.zero_grad()
loss.backward()
optimizer.step()
Comparativa de métodos de Bayesian Deep Learning
| Método | Idea | Coste extra | Calidad | Facilidad |
|---|---|---|---|---|
| MC Dropout | Dropout en inferencia | ~T× forward passes | Aproximación razonable | ⭐⭐⭐⭐⭐ |
| Bayes by Backprop | VI con pesos gaussianos | 2× parámetros + sampling | Buena | ⭐⭐⭐ |
| Deep Ensembles | Entrenar M modelos | M× entrenamiento | Muy buena (SOTA práctico) | ⭐⭐⭐⭐ |
| SWAG | Gaussian sobre SGD trajectory | Bajo (post-hoc) | Buena | ⭐⭐⭐⭐ |
| Laplace Approx. | Gaussiana en MAP | Hessiana (aproximada) | Moderada | ⭐⭐⭐ |
| HMC / NUTS | MCMC con gradientes | Muy alto | Gold standard | ⭐⭐ |
Una tendencia reciente es combinar métodos: Multi-SWAG (Wilson & Izmailov, 2020) entrena un ensemble de modelos SWAG, consiguiendo lo mejor de ambos mundos. Laplace Redux (Daxberger et al., 2021) aplica una aproximación de Laplace post-hoc sobre una red ya entrenada, añadiendo incertidumbre sin reentrenar. Para aplicaciones de visión por computador con incertidumbre, puedes consultar el submódulo de Redes Convolucionales.
Daxberger, E. et al. (2021). Laplace Redux: Effortless Bayesian Deep Learning. NeurIPS. [arXiv]📊 Explorador de incertidumbre
Simula una BNN simple con MC Dropout. Observa cómo la incertidumbre crece cuando la entrada se aleja de los datos de entrenamiento.
Modelos generativos profundos bayesianos
Las ideas bayesianas están en el corazón de varios modelos generativos profundos modernos:
El VAE es, en esencia, una red bayesiana con dos nodos: la variable latente z y los datos x:
El encoder \( q_\phi(z \mid x) \) aproxima el posterior intractable \( p(z \mid x) \). El objetivo ELBO conecta directamente con la inferencia variacional que vimos en redes bayesianas clásicas.
Kingma, D.P. & Welling, M. (2014). Auto-Encoding Variational Bayes. ICLR.Código Python: redes bayesianas con pgmpy
pgmpy es la librería de referencia en Python para redes bayesianas clásicas. Permite definir estructuras, CPTs, inferencia exacta y aproximada, y aprendizaje de estructura.
from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
from pgmpy.inference import VariableElimination
# 1. Definir la estructura (DAG)
model = BayesianNetwork([
('Nublado', 'Riego'),
('Nublado', 'Lluvia'),
('Riego', 'CespedMojado'),
('Lluvia', 'CespedMojado')
])
# 2. Definir las CPTs
cpd_nublado = TabularCPD('Nublado', 2, [[0.5], [0.5]])
cpd_riego = TabularCPD('Riego', 2,
[[0.5, 0.9], # P(Riego=no | Nublado)
[0.5, 0.1]], # P(Riego=sí | Nublado)
evidence=['Nublado'], evidence_card=[2])
cpd_lluvia = TabularCPD('Lluvia', 2,
[[0.8, 0.2], # P(Lluvia=no | Nublado)
[0.2, 0.8]], # P(Lluvia=sí | Nublado)
evidence=['Nublado'], evidence_card=[2])
cpd_mojado = TabularCPD('CespedMojado', 2,
[[1.0, 0.1, 0.1, 0.01], # P(Mojado=no | Riego, Lluvia)
[0.0, 0.9, 0.9, 0.99]], # P(Mojado=sí | Riego, Lluvia)
evidence=['Riego', 'Lluvia'], evidence_card=[2, 2])
model.add_cpds(cpd_nublado, cpd_riego, cpd_lluvia, cpd_mojado)
assert model.check_model() # Verificar consistencia
# 3. Inferencia: P(Lluvia | CespedMojado = sí)
infer = VariableElimination(model)
result = infer.query(['Lluvia'], evidence={'CespedMojado': 1})
print(result)
# ╒══════════╤══════════════╕
# │ Lluvia │ phi(Lluvia)│
# ╞══════════╪══════════════╡
# │ Lluvia_0 │ 0.2921 │ ← No llueve
# │ Lluvia_1 │ 0.7079 │ ← Llueve (71%!)
# ╘══════════╧══════════════╛
import pandas as pd
from pgmpy.estimators import HillClimbSearch, BicScore, BayesianEstimator
# Generar datos de ejemplo
import numpy as np
np.random.seed(42)
N = 1000
nublado = np.random.binomial(1, 0.5, N)
riego = np.random.binomial(1, np.where(nublado, 0.1, 0.5))
lluvia = np.random.binomial(1, np.where(nublado, 0.8, 0.2))
mojado = np.random.binomial(1,
np.where(riego & lluvia, 0.99,
np.where(riego | lluvia, 0.9, 0.0)))
data = pd.DataFrame({
'Nublado': nublado, 'Riego': riego,
'Lluvia': lluvia, 'CespedMojado': mojado
})
# Aprender estructura con Hill Climbing + BIC
hc = HillClimbSearch(data)
best_model = hc.estimate(scoring_method=BicScore(data))
print("Aristas descubiertas:", best_model.edges())
# [('Nublado', 'Lluvia'), ('Nublado', 'Riego'),
# ('Riego', 'CespedMojado'), ('Lluvia', 'CespedMojado')]
# ¡Recuperó la estructura correcta!
# Aprender parámetros con estimación bayesiana
from pgmpy.models import BayesianNetwork
model = BayesianNetwork(best_model.edges())
model.fit(data, estimator=BayesianEstimator, prior_type='BDeu',
equivalent_sample_size=10)
Código Python: inferencia bayesiana con PyMC
PyMC es la librería estrella para modelado bayesiano en Python. Usa MCMC (NUTS) y VI, ideal para modelos bayesianos complejos y BNNs.
import pymc as pm
import numpy as np
import arviz as az
# Datos sintéticos
np.random.seed(42)
X = np.random.randn(100)
y = 2.5 * X + 1.0 + np.random.randn(100) * 0.5
# Modelo bayesiano
with pm.Model() as model:
# Priors
alpha = pm.Normal('alpha', mu=0, sigma=10) # intercepto
beta = pm.Normal('beta', mu=0, sigma=10) # pendiente
sigma = pm.HalfNormal('sigma', sigma=1) # ruido
# Verosimilitud
mu = alpha + beta * X
y_obs = pm.Normal('y_obs', mu=mu, sigma=sigma, observed=y)
# Inferencia MCMC (NUTS)
trace = pm.sample(2000, tune=1000, cores=2, random_seed=42)
# Resumen del posterior
print(az.summary(trace, var_names=['alpha', 'beta', 'sigma']))
# mean sd hdi_3% hdi_97%
# alpha 1.02 0.05 0.93 1.11 ← ¡Cerca de 1.0!
# beta 2.48 0.05 2.39 2.57 ← ¡Cerca de 2.5!
# sigma 0.50 0.04 0.44 0.57 ← ¡Cerca de 0.5!
# Visualizar posteriors
az.plot_posterior(trace, var_names=['alpha', 'beta'])
Código Python: BNN completa con PyTorch
Aquí tienes un ejemplo completo de una red neuronal bayesiana entrenada con Bayes by Backprop para clasificación en el dataset Iris.
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# ── Datos ──
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
scaler = StandardScaler().fit(X_train)
X_train = torch.tensor(scaler.transform(X_train), dtype=torch.float32)
X_test = torch.tensor(scaler.transform(X_test), dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.long)
y_test = torch.tensor(y_test, dtype=torch.long)
# ── Capa bayesiana ──
class BayesianLinear(nn.Module):
def __init__(self, in_f, out_f):
super().__init__()
self.w_mu = nn.Parameter(torch.randn(out_f, in_f) * 0.1)
self.w_rho = nn.Parameter(torch.full((out_f, in_f), -3.0))
self.b_mu = nn.Parameter(torch.zeros(out_f))
self.b_rho = nn.Parameter(torch.full((out_f,), -3.0))
def forward(self, x):
w_sig = F.softplus(self.w_rho)
b_sig = F.softplus(self.b_rho)
w = self.w_mu + w_sig * torch.randn_like(w_sig)
b = self.b_mu + b_sig * torch.randn_like(b_sig)
self.kl = 0.5 * (w_sig.pow(2) + self.w_mu.pow(2) - 1 - 2*w_sig.log()).sum() + \
0.5 * (b_sig.pow(2) + self.b_mu.pow(2) - 1 - 2*b_sig.log()).sum()
return F.linear(x, w, b)
# ── Modelo ──
class BNN(nn.Module):
def __init__(self):
super().__init__()
self.l1 = BayesianLinear(4, 32)
self.l2 = BayesianLinear(32, 16)
self.l3 = BayesianLinear(16, 3)
def forward(self, x):
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
return self.l3(x)
def kl(self):
return self.l1.kl + self.l2.kl + self.l3.kl
# ── Entrenamiento ──
model = BNN()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(500):
logits = model(X_train)
nll = F.cross_entropy(logits, y_train)
kl = model.kl() / len(X_train)
loss = nll + kl
opt.zero_grad(); loss.backward(); opt.step()
# ── Predicción bayesiana (múltiples muestras) ──
T = 100
model.train()
preds = torch.stack([F.softmax(model(X_test), dim=-1) for _ in range(T)])
mean_probs = preds.mean(dim=0)
uncertainty = preds.std(dim=0).mean(dim=-1) # Incertidumbre por muestra
predicted = mean_probs.argmax(dim=-1)
accuracy = (predicted == y_test).float().mean()
print(f"Accuracy: {accuracy:.1%}")
print(f"Incertidumbre media: {uncertainty.mean():.4f}")
Aplicaciones reales de redes bayesianas
Las redes bayesianas se complementan con muchas otras técnicas de machine learning. Para aplicaciones de series temporales con componente probabilístico, las redes recurrentes (LSTM/GRU) pueden combinarse con priors bayesianos. En problemas de clasificación con datos tabulares, el Naive Bayes sigue siendo competitivo frente a métodos más complejos. Para optimización de hiperparámetros, la Optimización Bayesiana usa Gaussian Processes (una extensión continua de las BNs) para explorar el espacio de hiperparámetros de forma eficiente.
El sistema QMR-DT (Quick Medical Reference - Decision Theoretic) usaba una red bayesiana con ~600 enfermedades y ~4000 síntomas para asistir en diagnóstico médico.
¿Cómo funciona? El médico introduce los síntomas observados como evidencia. La BN calcula la probabilidad posterior de cada enfermedad. Se presenta un ranking de diagnósticos más probables.
Ventajas sobre otros enfoques:
- Transparencia: el médico puede inspeccionar las relaciones causales
- Incertidumbre: cuantifica la confianza en cada diagnóstico
- Datos parciales: funciona con síntomas incompletos
Las BNNs son ideales para active learning: seleccionar los datos más informativos para etiquetar. La estrategia BALD (Bayesian Active Learning by Disagreement) selecciona las muestras donde el modelo tiene mayor incertidumbre epistémica.
Puntos con alto BALD: el modelo es incierto Y los diferentes pesos predicen cosas muy diferentes → etiquetar este punto maximiza la información ganada.
Houlsby, N. et al. (2011). Bayesian Active Learning for Classification and Preference Learning. arXiv:1112.5745.Frameworks y herramientas
| Herramienta | Tipo | Lenguaje | Uso principal |
|---|---|---|---|
| pgmpy | BN clásica | Python | Definir, inferir, aprender estructura de BNs discretas |
| PyMC | Modelado bayesiano | Python | MCMC (NUTS), VI, modelos jerárquicos, BNNs |
| NumPyro | Modelado bayesiano | Python (JAX) | MCMC/VI ultrarrápido con aceleración JAX/GPU |
| Pyro | Probabilístico profundo | Python (PyTorch) | BNNs, VAEs, modelos generativos, SVI |
| TF Probability | Probabilístico profundo | Python (TF) | Capas bayesianas, VI, modelos probabilísticos en TF |
| bnlearn | BN clásica | R (y Python wrapper) | Aprendizaje de estructura, referencia en investigación |
Papers y recursos fundamentales
📚 Papers esenciales
- Pearl, J. (1988). Probabilistic Reasoning in Intelligent Systems. Morgan Kaufmann. El libro fundacional de las redes bayesianas.
- Pearl, J. (2009). Causality: Models, Reasoning, and Inference. Cambridge University Press. Fundamentos de causalidad y do-calculus.
- Koller, D. & Friedman, N. (2009). Probabilistic Graphical Models. MIT Press. El textbook definitivo de modelos gráficos (1200+ páginas).
- Gal, Y. & Ghahramani, Z. (2016). Dropout as a Bayesian Approximation. ICML. [arXiv] MC Dropout como inferencia bayesiana aproximada.
- Blundell, C. et al. (2015). Weight Uncertainty in Neural Networks. ICML. [arXiv] Bayes by Backprop: VI para BNNs.
- Lakshminarayanan, B. et al. (2017). Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles. NeurIPS. [arXiv] Deep Ensembles como baseline de incertidumbre.
- Kingma, D.P. & Welling, M. (2014). Auto-Encoding Variational Bayes. ICLR. [arXiv] El paper del VAE: inferencia variacional + deep learning.
- Spirtes, P. et al. (1993). Causation, Prediction, and Search. Springer. Algoritmo PC y descubrimiento causal.
- Heckerman, D. (1995). A Tutorial on Learning with Bayesian Networks. Microsoft Research. [arXiv] Tutorial clásico de aprendizaje en BNs.
- Murphy, K.P. (2012). Machine Learning: A Probabilistic Perspective. MIT Press. Textbook completo con amplia cobertura de métodos bayesianos.
- Wilson, A.G. & Izmailov, P. (2020). Bayesian Deep Learning and a Probabilistic Perspective of Generalization. NeurIPS. [arXiv] Perspectiva bayesiana de la generalización en deep learning.
🔗 Recursos de aprendizaje
- pgmpy — Getting Started: tutorial oficial de BNs clásicas
- PyMC Examples Gallery: decenas de ejemplos de modelado bayesiano
- Pyro Tutorials: BNNs, VAEs, SVI con PyTorch
- TF Probability Tutorials: capas bayesianas en TensorFlow
- Coursera — PGM (Daphne Koller): el curso de referencia en modelos gráficos
- GitHub — Bayesian Network repos: repositorios open-source sobre redes bayesianas
- Murphy, K.P. (2022). Probabilistic Machine Learning: libro online gratuito, versión actualizada del clásico de 2012
🧰 ¿Qué herramienta bayesiana necesito?
Responde estas preguntas y te recomendaremos la herramienta adecuada.