Skip to content

LDS Bayesiano

Esta guía documenta en detalle el combinador LDS Bayesiano implementado en:

  • scripts/run_test_offline_pipeline.py
  • scripts/run_lds_bayesian_optimizacion.py
  • mlops/lds_bayesian.py

El objetivo de esta documentación es explicar:

  1. qué problema resuelve el LDS Bayesiano dentro del pipeline,
  2. cómo combina señales de múltiples motores de detección,
  3. qué significan sus parámetros probabilísticos,
  4. cómo se configura en test_offline_pipeline,
  5. cómo se optimiza offline con Optuna,
  6. y qué artefactos produce.

La descripción que sigue está alineada con la implementación real del código actualmente en el repositorio.

Qué es el LDS Bayesiano en este proyecto

En este proyecto, el LDS Bayesiano es un orquestador probabilístico a nivel de caso. No reemplaza a PFM, OBSERVER, PPA ni NPW; los consume como fuentes de evidencia.

Cada motor genera una señal de detección propia:

  • PFM
  • OBSERVER
  • PPA
  • NPW

El LDS Bayesiano toma sus salidas probabilísticas o cuasi-probabilísticas y construye una probabilidad posterior de fuga para el caso evaluado.

En otras palabras:

  • PFM / OBSERVER / PPA / NPW son detectores,
  • el LDS Bayesiano es un fusionador de evidencia.

Dónde se usa

1. En run_test_offline_pipeline.py

Durante la evaluación offline, si existe la sección test_offline_pipeline.lds_bayesian, el script:

  • activa el combinador,
  • toma las salidas de los motores definidos,
  • calcula la probabilidad posterior de fuga,
  • genera una clasificación Bayesiana por caso,
  • y agrega sus métricas al reporte final.

2. En run_lds_bayesian_optimizacion.py

Este script no vuelve a correr los modelos ML ni los motores físicos. Reutiliza resultados offline ya generados y optimiza el combinador LDS Bayes:

  • estima p_i y q_i fijos desde resultados históricos,
  • optimiza alpha_i y el detection_threshold global,
  • y guarda un archivo lds_bayesian_performance_params.yml optimizado.

Conceptos principales

Caso

La unidad de decisión del LDS Bayesiano es el caso completo, no la fila ni la ventana individual.

Cada caso viene de offline_results_cases.json y contiene:

  • si realmente hubo fuga,
  • clasificación de cada motor,
  • probabilidad o señal puntual de cada motor,
  • estado operativo inferido,
  • y la salida Bayes resultante.

Motores

Los motores soportados hoy son:

  • PFM
  • OBSERVER
  • PPA
  • NPW

Cada uno puede estar:

  • configurado o no,
  • habilitado o no,
  • disparado o no,
  • y con probabilidad finita o no.

Solo los motores que realmente aportan evidencia son usados en el cálculo Bayes.

Estado operativo

El combinador usa parámetros distintos según el estado operativo del caso:

  • SS
  • SI
  • TS

Ese estado se infiere desde el nombre del archivo:

  • si contiene _SS_SS
  • si contiene _SI_SI
  • en cualquier otro caso → TS

Esto permite que la calibración Bayes dependa del régimen del sistema.

Variables probabilísticas del modelo

Cada motor i y cada estado operativo tienen tres parámetros:

  • p_i
  • q_i
  • alpha_i

p_i

p_i representa la sensibilidad del motor en ese estado:

p_i = TPR = TP / (TP + FN)

Interpretación:

  • alto p_i → el motor detecta bien casos con fuga,
  • bajo p_i → el motor deja escapar fugas con frecuencia.

q_i

q_i representa la especificidad del motor en ese estado:

q_i = TNR = TN / (TN + FP)

Interpretación:

  • alto q_i → el motor controla bien falsas alarmas,
  • bajo q_i → el motor dispara fugas donde no las hay.

alpha_i

alpha_i es el peso de contribución del motor dentro del combinador Bayesiano.

Interpretación:

  • alpha_i = 0 → el motor no aporta evidencia al posterior,
  • alpha_i = 1 → aporta con peso completo,
  • valores intermedios → atenúan su influencia,
  • valores mayores que cero permiten modular la confianza relativa del motor.

En la implementación actual:

  • p_i y q_i se tratan como calibración histórica del motor,
  • alpha_i se usa como factor de relevancia dentro del ensamble.

Estructura de señal de entrada

El combinador trabaja con BayesianSignal:

  • probability: probabilidad puntual del motor en [0, 1]
  • triggered: si el motor efectivamente declaró fuga
  • enabled: si ese motor estaba disponible/configurado
  • raw_value: valor crudo asociado a la señal

Un motor solo participa si:

  • existe,
  • enabled == True,
  • triggered == True,
  • probability es finita,
  • y alpha_i > 0.

Si un motor está presente pero no dispara, queda registrado en el detalle, pero no aporta evidencia al posterior.

Cómo se construye la probabilidad de cada motor

PFM

Para PFM, el LDS toma la probabilidad puntual del modelo de detección en la ventana relevante:

  • probability = probabilidad de detección del modelo PFM
  • triggered = si PFM declaró fuga en su máquina de estados

OBSERVER

Para OBSERVER, primero se obtiene el flujo predicho y luego se transforma a probabilidad con observer_signal_to_probability().

La transformación actual es:

likelihood_percent = min(100, 60 * abs(flow_pred) / threshold_flujo)
probability = likelihood_percent / 100

Esto significa:

  • el flujo predicho se convierte a una escala porcentual,
  • se recorta a [0, 100],
  • y luego se normaliza a [0, 1].

PPA y NPW

PPA y NPW ya trabajan con una señal tipo “likelihood percent”. El LDS hace:

probability = clip(likelihood_percent / 100, 0, 1)

Entonces:

  • 50 pasa a 0.5,
  • 100 pasa a 1.0,
  • 0 pasa a 0.0.

Fórmula del posterior Bayesiano

El cálculo central está en compute_bayesian_lds_posterior().

El modelo trabaja en espacio de log-odds.

Paso 1: prior

Se parte de una probabilidad a priori de fuga:

P(L=1) = prior_leak_probability

y se transforma a logit:

logit_prior = log(prior / (1 - prior))

Paso 2: contribución de cada motor

Para cada motor válido, con probabilidad y:

log_lr_y1 =
    log(y)
  - log(1 - y)
  + log(p_i)
  - log(1 - q_i)

log_lr_y0 =
    log(1 - y)
  - log(y)
  + log(q_i)
  - log(1 - p_i)

Luego esas contribuciones se ponderan por alpha_i:

log_like_y1 += alpha_i * log_lr_y1
log_like_y0 += alpha_i * log_lr_y0

Paso 3: posterior

El posterior en logit queda:

logit_post = logit_prior + log_like_y1 - log_like_y0

y la probabilidad posterior final es:

posterior = sigmoid(logit_post)

Paso 4: decisión final

La clasificación final se obtiene con:

predicted_leak = posterior >= detection_threshold

Qué devuelve el combinador

Por caso, el LDS produce:

  • posterior_probability
  • predicted_leak
  • detection_threshold
  • prior_leak_probability
  • used_motor_count
  • detalle por motor (motors)

Cada motor en el detalle incluye:

  • probability
  • triggered
  • enabled
  • raw_value
  • used_in_bayes
  • p_i
  • q_i
  • alpha_i

Esto permite inspección completa del razonamiento Bayesiano por caso.

Defaults internos del modelo Bayesiano

Si no se provee un archivo de parámetros Bayes, se usan defaults internos en mlops/lds_bayesian.py.

Resumen de la tabla actual:

PFM

Estado p_i q_i alpha_i
SS 0.99 0.99 1.00
SI 0.98 0.99 1.00
TS 0.98 0.99 1.00

OBSERVER

Estado p_i q_i alpha_i
SS 0.95 0.99 0.98
SI 0.70 0.60 0.10
TS 0.93 0.99 0.98

PPA

Estado p_i q_i alpha_i
SS 0.93 0.99 0.98
SI 0.95 0.99 0.98
TS 0.70 0.60 0.10

NPW

Estado p_i q_i alpha_i
SS 0.93 0.99 0.98
SI 0.93 0.99 0.98
TS 0.70 0.60 0.10

Configuración runtime en test_offline_pipeline

La activación runtime del combinador se hace dentro de:

test_offline_pipeline:
  lds_bayesian:
    prior_leak_probability: 0.001
    detection_threshold: null

Parámetros principales

Parámetro Significado
prior_leak_probability Probabilidad a priori de fuga antes de observar señales de motores.
detection_threshold Umbral final sobre el posterior Bayes para decidir fuga / no fuga.
params_path Archivo opcional YAML/JSON con p_i, q_i, alpha_i y opcionalmente detection_threshold. El artefacto generado también persiste p_leak como metadata del prior usado.

Reglas de activación

  • si la sección lds_bayesian no existe, el combinador no se ejecuta;
  • si existe, el script lo habilita;
  • solo usa motores de detección realmente configurados.

Resolución del threshold seed

El threshold inicial se resuelve en este orden:

  1. lds_bayesian.detection_threshold
  2. lds_bayesian.params_pathdetection_threshold
  3. default interno del código: 0.95

Archivo externo de parámetros Bayesianos

Puedes definir un archivo como configs/lds_bayesian_performance_params.yml con esta estructura:

detection_threshold: 0.55
p_leak: 0.001

PFM:
  SS:
    p_i: 0.98
    q_i: 0.97
    alpha_i: 1.0
  SI:
    p_i: 0.93
    q_i: 0.99
    alpha_i: 0.98

OBSERVER:
  SS:
    p_i: 0.93
    q_i: 0.99
    alpha_i: 0.98

El loader:

  • acepta YAML o JSON,
  • ignora entradas inválidas,
  • normaliza estados a mayúsculas,
  • recorta p_i / q_i a rango válido,
  • y puede almacenar p_leak como metadata explícita del prior usado junto al threshold.

Optimización offline con Optuna

El optimizador vive en run_lds_bayesian_optimizacion.py.

Qué optimiza y qué no optimiza

Esto es fundamental:

  • p_i y q_i no se optimizan directamente en Optuna;
  • se estiman a partir de resultados offline previos;
  • alpha_i y detection_threshold sí se optimizan.

En la implementación actual:

fijos:
  - p_i
  - q_i
  - prior_leak_probability

optimizados:
  - alpha_i
  - detection_threshold

Cómo se estiman p_i y q_i

El optimizador reconstruye casos a partir de resultados offline ya guardados y usa las clasificaciones por motor:

  • classification para PFM
  • observer_classification para OBSERVER
  • ppa_classification para PPA
  • npw_classification para NPW

Con eso estima, por motor y por estado:

p_i = TP / (TP + FN)
q_i = TN / (TN + FP)

Si para un motor/estado no hay suficientes ejemplos:

  • mantiene los valores seed cargados desde params_path o defaults internos.

Qué se optimiza exactamente

alpha_i

Optuna ajusta alpha_i por:

  • motor
  • estado operativo

pero solo para estados con suficientes casos según:

  • min_cases_per_state_to_optimize

Si un estado no tiene suficientes muestras, su alpha_i queda fijo.

detection_threshold

Además, Optuna busca el mejor threshold final del posterior Bayes dentro de:

  • threshold_min
  • threshold_max
  • threshold_step

Función objetivo de la optimización

La optimización usa una función objetivo configurable:

score =
    tpr_weight * TPR
  + tnr_weight * TNR
  + precision_weight * Precision
  - false_negative_penalty * FNR
  - false_positive_penalty * FPR

Este score se calcula sobre la evaluación Bayesiana final del conjunto de casos.

Interpretación

  • subir tpr_weight prioriza detectar fugas reales;
  • subir tnr_weight prioriza evitar falsas alarmas en casos sin fuga;
  • subir precision_weight prioriza que las alarmas emitidas sean confiables;
  • subir false_negative_penalty castiga fuertemente fugas no detectadas;
  • subir false_positive_penalty castiga falsas alarmas.

Configuración del optimizador en YAML

La sección dedicada es:

lds_bayesian_optimization:
  source_report_folder: "reports/offline/supe/v8/1transmitter/diesel"

  score_tpr_weight: 2.0
  score_tnr_weight: 0.5
  score_precision_weight: 0.5
  false_negative_penalty: 2.0
  false_positive_penalty: 0.5

  n_trials: 500
  cv_folds: 3
  timeout: null
  pruner_enabled: true
  pruner_percentile: 50.0
  pruner_n_startup_trials: 10
  pruner_n_warmup_steps: 2
  pruner_interval_steps: 1
  min_improvement: 0.0001
  patience: null
  metric: "score"

  threshold_min: 0.5
  threshold_max: 0.6
  threshold_step: 0.05

  alpha_i_min: 0.0
  alpha_i_max: 1.0
  alpha_i_step: 0.01

Significado de los parámetros

Fuentes de datos

Parámetro Significado
source_report_folder Carpeta única con resultados offline previos.
source_report_folders Lista de carpetas fuente a combinar en una sola optimización.

Score

Parámetro Significado
score_tpr_weight Peso de sensibilidad
score_tnr_weight Peso de especificidad
score_precision_weight Peso de precisión
false_negative_penalty Penalización de fugas no detectadas
false_positive_penalty Penalización de falsas alarmas

Búsqueda Optuna

Parámetro Significado
n_trials Número máximo de trials
cv_folds Cantidad de folds
timeout Límite de tiempo
pruner_enabled Habilita pruning temprano
pruner_percentile Percentil usado por PercentilePruner
pruner_n_startup_trials Trials iniciales sin pruning
pruner_n_warmup_steps Steps mínimos antes de permitir pruning
pruner_interval_steps Frecuencia de evaluación del pruning
min_improvement Mejora mínima para considerar progreso
patience Early stopping por trials sin mejora
metric Métrica objetivo (score, acc, tpr, tnr, precision)

Espacio de búsqueda

Parámetro Significado
threshold_min / threshold_max / threshold_step Rango de búsqueda del threshold final
alpha_i_min / alpha_i_max / alpha_i_step Rango de búsqueda de alpha_i

Cómo se construyen los folds

La optimización usa folds a nivel de caso, no a nivel de ventana.

La construcción actual:

  • separa casos con fuga y sin fuga,
  • distribuye índices en folds de forma balanceada,
  • y evalúa el combinador sobre esos subconjuntos.

Es una validación cruzada ligera a nivel de caso, coherente con el hecho de que el LDS opera sobre resultados agregados por caso.

Sampler y pruner

La implementación usa:

  • TPESampler(multivariate=True)
  • PercentilePruner opcional

Consecuencias:

  • TPE aprende relaciones no triviales entre alpha_i y detection_threshold,
  • el pruning puede cortar trials malos antes de recorrer todos los folds,
  • y el costo computacional se mantiene controlado incluso con muchos trials.

Artefactos de salida

La optimización produce:

1. Archivo optimizado de parámetros

Normalmente:

<model_base_path>/lds_bayesian_performance_params.yml

Ese archivo contiene:

  • detection_threshold
  • p_leak
  • p_i
  • q_i
  • alpha_i

Nota:

  • p_leak se persiste como metadata útil para trazabilidad y portabilidad del artefacto.
  • El prior activo en runtime sigue controlado por test_offline_pipeline.lds_bayesian.prior_leak_probability.

por motor y por estado.

2. Reportes offline actualizados

El optimizador reusa resultados offline y luego vuelve a guardar:

  • offline_results_summary.json
  • offline_results_cases.json
  • offline_report.xlsx si corresponde

pero con la sección lds_bayesian actualizada a la versión optimizada.

3. Metadata de idempotencia

Se actualiza offline_run_metadata.json con:

  • hash de optimización,
  • carpetas fuente,
  • hashes de evaluación fuente,
  • marca de reutilización de resultados cacheados.

Qué significa que “cumple” el modelo Bayesiano teórico

Desde la implementación actual, el modelo cumple estas propiedades:

  • combina evidencia de múltiples motores en forma probabilística;
  • incorpora una probabilidad a priori explícita;
  • permite calibración por estado operativo;
  • usa sensibilidad y especificidad históricas por motor;
  • pondera la evidencia de cada motor mediante alpha_i;
  • decide con un threshold posterior configurable.

Es decir, no es un voto mayoritario ni una suma heurística de scores: es un ensamble Bayesiano en espacio de log-odds con calibración por motor y estado.

Limitaciones y consideraciones

1. Solo motores disparados aportan evidencia

Si un motor no dispara (triggered=False), no suma evidencia al posterior, aunque figure como habilitado.

2. PPA/NPW trabajan con porcentajes

Sus señales se convierten a [0,1] dividiendo por 100, lo que supone que esa escala ya es interpretable como likelihood relativo.

3. OBSERVER usa una transformación específica

observer_signal_to_probability() usa una regla propia basada en el threshold de detección del flujo. Es importante mantener coherencia entre:

  • observer_detection_threshold
  • y la interpretación física del flujo estimado.

4. p_i y q_i dependen del dataset offline fuente

Si cambias fuertemente de dominio, dataset o régimen de operación, conviene recalibrarlos reoptimizando el LDS.

Recomendaciones prácticas

  • Empieza con prior_leak_probability pequeño si las fugas son raras.
  • Ajusta primero el score objetivo antes de ampliar demasiado el espacio de búsqueda.
  • Usa metric: "score" si quieres balance global.
  • Usa metric: "tpr" si tu prioridad absoluta es detectar fugas.
  • Usa múltiples source_report_folders si quieres calibración más robusta sobre varias corridas históricas.
  • Reoptimiza cuando cambien:
  • los modelos base,
  • los thresholds de detección,
  • el dominio operativo,
  • o la mezcla de casos por estado (SS, SI, TS).

Relación con otras docs