Spaces:
Sleeping
Sleeping
Aadithya commited on
Commit ·
6da8909
1
Parent(s): 08f8258
feat: implement chronos-on-chronos ensemble for multi-resolution drift calibration
Browse files- worker/tasks.py +24 -23
worker/tasks.py
CHANGED
|
@@ -48,18 +48,36 @@ def run_gridops_pipeline(self, dataset_path: str, severity_threshold: float = 0.
|
|
| 48 |
client = get_chronos_client()
|
| 49 |
|
| 50 |
if forecast_horizon <= 14:
|
| 51 |
-
logger.info("Routing to
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
pipeline.train_hourly.values, # pyrefly: ignore[bad-argument-type]
|
| 54 |
prediction_length=forecast_horizon * 24,
|
| 55 |
num_samples=20,
|
| 56 |
)
|
| 57 |
-
import numpy as np
|
| 58 |
|
| 59 |
# Bridge: reshape (horizon*24) -> (horizon, 24) and apply daily median
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
else:
|
| 64 |
logger.info("Routing to Structural Daily Inference Pipeline (horizon > 14)")
|
| 65 |
chronos_result = client.forecast(
|
|
@@ -71,23 +89,6 @@ def run_gridops_pipeline(self, dataset_path: str, severity_threshold: float = 0.
|
|
| 71 |
chronos_p50 = chronos_result['p50']
|
| 72 |
chronos_p90 = chronos_result['p90']
|
| 73 |
|
| 74 |
-
import numpy as np
|
| 75 |
-
|
| 76 |
-
# --- Mean-Matching Ensemble Calibration ---
|
| 77 |
-
# SARIMA (16-yr context) provides the structurally accurate baseline mean
|
| 78 |
-
sarima_mean = np.mean(sarima_fc)
|
| 79 |
-
|
| 80 |
-
# Chronos provides the high-fidelity shape, but has a recency-biased mean
|
| 81 |
-
chronos_mean = np.mean(chronos_p50)
|
| 82 |
-
|
| 83 |
-
# Calculate the bias shift required to anchor Chronos to SARIMA
|
| 84 |
-
bias_shift = sarima_mean - chronos_mean
|
| 85 |
-
|
| 86 |
-
# Apply the shift and mathematically clip to 0 to prevent negative physical loads
|
| 87 |
-
chronos_p10 = np.maximum(chronos_p10 + bias_shift, 0.0)
|
| 88 |
-
chronos_p50 = np.maximum(chronos_p50 + bias_shift, 0.0)
|
| 89 |
-
chronos_p90 = np.maximum(chronos_p90 + bias_shift, 0.0)
|
| 90 |
-
|
| 91 |
assert not isinstance(chronos_p10, list)
|
| 92 |
assert not isinstance(chronos_p50, list)
|
| 93 |
assert not isinstance(chronos_p90, list)
|
|
|
|
| 48 |
client = get_chronos_client()
|
| 49 |
|
| 50 |
if forecast_horizon <= 14:
|
| 51 |
+
logger.info("Routing to Multi-Resolution Chronos Ensemble (horizon <= 14)")
|
| 52 |
+
import numpy as np
|
| 53 |
+
|
| 54 |
+
# 1. Fetch Daily Baseline (Low drift, high structural accuracy)
|
| 55 |
+
daily_chronos_result = client.forecast(
|
| 56 |
+
pipeline.train.values, # pyrefly: ignore[bad-argument-type]
|
| 57 |
+
prediction_length=forecast_horizon,
|
| 58 |
+
num_samples=20,
|
| 59 |
+
)
|
| 60 |
+
daily_chronos_mean = np.mean(daily_chronos_result['p50'])
|
| 61 |
+
|
| 62 |
+
# 2. Fetch Hourly Shape (High-fidelity zig-zags, high drift)
|
| 63 |
+
hourly_chronos_result = client.forecast(
|
| 64 |
pipeline.train_hourly.values, # pyrefly: ignore[bad-argument-type]
|
| 65 |
prediction_length=forecast_horizon * 24,
|
| 66 |
num_samples=20,
|
| 67 |
)
|
|
|
|
| 68 |
|
| 69 |
# Bridge: reshape (horizon*24) -> (horizon, 24) and apply daily median
|
| 70 |
+
hourly_p10 = np.median(np.asarray(hourly_chronos_result['p10']).reshape(forecast_horizon, 24), axis=1)
|
| 71 |
+
hourly_p50 = np.median(np.asarray(hourly_chronos_result['p50']).reshape(forecast_horizon, 24), axis=1)
|
| 72 |
+
hourly_p90 = np.median(np.asarray(hourly_chronos_result['p90']).reshape(forecast_horizon, 24), axis=1)
|
| 73 |
+
|
| 74 |
+
# 3. Ensemble Calibration
|
| 75 |
+
hourly_chronos_mean = np.mean(hourly_p50)
|
| 76 |
+
bias_shift = daily_chronos_mean - hourly_chronos_mean
|
| 77 |
+
|
| 78 |
+
chronos_p10 = np.maximum(hourly_p10 + bias_shift, 0.0)
|
| 79 |
+
chronos_p50 = np.maximum(hourly_p50 + bias_shift, 0.0)
|
| 80 |
+
chronos_p90 = np.maximum(hourly_p90 + bias_shift, 0.0)
|
| 81 |
else:
|
| 82 |
logger.info("Routing to Structural Daily Inference Pipeline (horizon > 14)")
|
| 83 |
chronos_result = client.forecast(
|
|
|
|
| 89 |
chronos_p50 = chronos_result['p50']
|
| 90 |
chronos_p90 = chronos_result['p90']
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
assert not isinstance(chronos_p10, list)
|
| 93 |
assert not isinstance(chronos_p50, list)
|
| 94 |
assert not isinstance(chronos_p90, list)
|