LDS Bayesiano¶
Esta guía documenta en detalle el combinador LDS Bayesiano implementado en:
scripts/run_test_offline_pipeline.pyscripts/run_lds_bayesian_optimizacion.pymlops/lds_bayesian.py
El objetivo de esta documentación es explicar:
- qué problema resuelve el LDS Bayesiano dentro del pipeline,
- cómo combina señales de múltiples motores de detección,
- qué significan sus parámetros probabilísticos,
- cómo se configura en
test_offline_pipeline, - cómo se optimiza offline con Optuna,
- 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:
PFMOBSERVERPPANPW
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_iyq_ifijos desde resultados históricos, - optimiza
alpha_iy eldetection_thresholdglobal, - y guarda un archivo
lds_bayesian_performance_params.ymloptimizado.
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:
PFMOBSERVERPPANPW
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:
SSSITS
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_iq_ialpha_i
p_i¶
p_i representa la sensibilidad del motor en ese estado:
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:
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_iyq_ise tratan como calibración histórica del motor,alpha_ise 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ó fugaenabled: si ese motor estaba disponible/configuradoraw_value: valor crudo asociado a la señal
Un motor solo participa si:
- existe,
enabled == True,triggered == True,probabilityes 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 PFMtriggered= 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:
Entonces:
50pasa a0.5,100pasa a1.0,0pasa a0.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:
y se transforma a logit:
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:
Paso 3: posterior¶
El posterior en logit queda:
y la probabilidad posterior final es:
Paso 4: decisión final¶
La clasificación final se obtiene con:
Qué devuelve el combinador¶
Por caso, el LDS produce:
posterior_probabilitypredicted_leakdetection_thresholdprior_leak_probabilityused_motor_count- detalle por motor (
motors)
Cada motor en el detalle incluye:
probabilitytriggeredenabledraw_valueused_in_bayesp_iq_ialpha_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:
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_bayesianno 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:
lds_bayesian.detection_thresholdlds_bayesian.params_path→detection_threshold- 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_ia rango válido, - y puede almacenar
p_leakcomo 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_iyq_ino se optimizan directamente en Optuna;- se estiman a partir de resultados offline previos;
alpha_iydetection_thresholdsí se optimizan.
En la implementación actual:
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:
classificationparaPFMobserver_classificationparaOBSERVERppa_classificationparaPPAnpw_classificationparaNPW
Con eso estima, por motor y por estado:
Si para un motor/estado no hay suficientes ejemplos:
- mantiene los valores seed cargados desde
params_patho 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_minthreshold_maxthreshold_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_weightprioriza detectar fugas reales; - subir
tnr_weightprioriza evitar falsas alarmas en casos sin fuga; - subir
precision_weightprioriza que las alarmas emitidas sean confiables; - subir
false_negative_penaltycastiga fuertemente fugas no detectadas; - subir
false_positive_penaltycastiga 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)PercentilePruneropcional
Consecuencias:
- TPE aprende relaciones no triviales entre
alpha_iydetection_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:
Ese archivo contiene:
detection_thresholdp_leakp_iq_ialpha_i
Nota:
p_leakse 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.jsonoffline_results_cases.jsonoffline_report.xlsxsi 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_probabilitypequeñ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_folderssi 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¶
- Ver Test offline para entender cómo se generan los resultados fuente del LDS.
- Ver Pipelines Overview para la navegación general de pipelines.