Evgueni Poloukarov Claude commited on
Commit
069627f
·
1 Parent(s): ff9fbcf

feat: implement past-only covariate masking for volatility capture

Browse files

Past-Only Masking Implementation (Chronos-2 v1.4.0):
- Include ALL 3,043 features in future_df with automatic masking
- Known-future: weather, generation, load (615 features)
- Past-only masked: CNEC outages, volatility, flows (~2,428 features)
- Leverages Chronos-2's mask-based attention mechanism

Batch Size Optimization:
- Increased from 32 to 128 for better temporal attention
- Enables longer-range dependencies within single batch
- Faster inference (fewer forward passes)

Architecture Rationale:
- Past-only features have NaN future values → Chronos-2 sets mask=0
- Model learns cross-feature correlations from historical context
- Attention mechanism uses dimensional structure even when masked
- Enables learning CNEC/volatility patterns without future knowledge

Expected Impact:
- Better hourly volatility capture (especially hours 15-21)
- Cross-feature learning from masked constraint indicators
- No accuracy degradation risk (worst case: features ignored)

Files Modified:
- src/forecasting/dynamic_forecast.py: Include all feature categories
- src/forecasting/chronos_inference.py: batch_size=128, update docs
- app.py: Update feature count documentation (615 → 3,043)

Co-Authored-By: Claude <[email protected]>

app.py CHANGED
@@ -140,10 +140,12 @@ with gr.Blocks(title="FBMC Chronos-2 Forecasting") as demo:
140
  generalizes directly to FBMC cross-border flows using historical patterns and future covariates.
141
 
142
  **Features**:
143
- - 2,553 engineered features (weather, CNEC constraints, load forecasts, LTA)
 
 
144
  - 24-month historical context (Oct 2023 - Oct 2025)
145
  - Time-aware extraction (prevents data leakage)
146
- - Probabilistic forecasts (10th/50th/90th percentiles)
147
 
148
  **Performance**:
149
  - Smoke test: ~30 seconds (1 border × 168 hours)
 
140
  generalizes directly to FBMC cross-border flows using historical patterns and future covariates.
141
 
142
  **Features**:
143
+ - 3,043 engineered features using past-only covariate masking
144
+ - Known-future: weather, generation, load forecasts (615 features)
145
+ - Past-only masked: CNEC outages, volatility, flows (~2,428 features)
146
  - 24-month historical context (Oct 2023 - Oct 2025)
147
  - Time-aware extraction (prevents data leakage)
148
+ - Probabilistic forecasts (9 quantiles: 1st/5th/10th/25th/50th/75th/90th/95th/99th)
149
 
150
  **Performance**:
151
  - Smoke test: ~30 seconds (1 border × 168 hours)
src/forecasting/chronos_inference.py CHANGED
@@ -1,9 +1,9 @@
1
  #!/usr/bin/env python3
2
  """
3
- Chronos-2 Inference Pipeline with Covariate Support
4
  Standalone inference script for HuggingFace Space deployment.
5
- Uses predict_df() API to enable multivariate forecasting with weather, generation, CNEC outages.
6
- FORCE REBUILD: v1.3.0 - Context reduced to 128h for memory
7
  """
8
 
9
  import os
@@ -29,8 +29,10 @@ from .feature_availability import FeatureAvailability
29
 
30
  class ChronosInferencePipeline:
31
  """
32
- Production inference pipeline for Chronos-2 zero-shot forecasting WITH COVARIATES.
33
- Uses predict_df() API to leverage all 615 collected features (weather, generation, outages, etc.)
 
 
34
  Designed for deployment as API endpoint on HuggingFace Spaces.
35
  """
36
 
@@ -170,10 +172,11 @@ class ChronosInferencePipeline:
170
 
171
  total_start = time.time()
172
 
173
- # PER-BORDER INFERENCE WITH COVARIATES
174
- # Using predict_df() API to leverage all 615 features (weather, generation, CNEC outages, etc.)
175
- print(f"\n[COVARIATE FORECAST] Running inference for {len(forecast_borders)} borders with 615 features...")
176
- print(f" Features: weather per zone, generation per zone, CNEC outages, LTA, load forecasts")
 
177
 
178
  for i, border in enumerate(forecast_borders, 1):
179
  # Clear GPU cache BEFORE each border to prevent memory accumulation
@@ -194,7 +197,7 @@ class ChronosInferencePipeline:
194
  )
195
 
196
  print(f" Context shape: {context_data.shape}, Future shape: {future_data.shape}", flush=True)
197
- print(f" Using {len(future_data.columns)-2} future covariates for multivariate forecast", flush=True)
198
 
199
  # Run covariate-informed inference using DataFrame API
200
  # Note: predict_df() returns quantiles directly
@@ -203,12 +206,12 @@ class ChronosInferencePipeline:
203
  with torch.inference_mode():
204
  forecasts_df = pipeline.predict_df(
205
  context_data, # Historical data with ALL features
206
- future_df=future_data, # Future covariates (615 features)
207
  prediction_length=prediction_hours,
208
  id_column='border',
209
  timestamp_column='timestamp',
210
  target='target',
211
- batch_size=32, # Reduce from default 256 to save GPU memory
212
  quantile_levels=[0.01, 0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95, 0.99] # 9 quantiles for volatility
213
  )
214
 
@@ -267,7 +270,7 @@ class ChronosInferencePipeline:
267
  'num_features': len(future_data.columns) - 2 # Exclude border and timestamp
268
  }
269
 
270
- print(f" [OK] Complete in {inference_time:.1f}s (WITH {len(future_data.columns)-2} covariates)", flush=True)
271
 
272
  except Exception as e:
273
  import traceback
 
1
  #!/usr/bin/env python3
2
  """
3
+ Chronos-2 Inference Pipeline with Past-Only Covariate Masking
4
  Standalone inference script for HuggingFace Space deployment.
5
+ Uses predict_df() API with ALL 3,043 features leveraging Chronos-2's mask-based attention.
6
+ FORCE REBUILD: v1.4.0 - Past-only covariates + batch_size=128 for volatility capture
7
  """
8
 
9
  import os
 
29
 
30
  class ChronosInferencePipeline:
31
  """
32
+ Production inference pipeline for Chronos-2 zero-shot forecasting WITH PAST-ONLY MASKING.
33
+ Uses predict_df() API with ALL 3,043 features (known-future + past-only covariates).
34
+ Past-only covariates (CNEC, volatility, historical flows) are masked in future → model
35
+ learns cross-feature correlations from historical context via attention mechanism.
36
  Designed for deployment as API endpoint on HuggingFace Spaces.
37
  """
38
 
 
172
 
173
  total_start = time.time()
174
 
175
+ # PER-BORDER INFERENCE WITH PAST-ONLY COVARIATE MASKING
176
+ # Using predict_df() API with ALL 3,043 features (known-future + past-only masked)
177
+ print(f"\n[PAST-ONLY MASKING] Running inference for {len(forecast_borders)} borders with 3,043 features...")
178
+ print(f" Known-future: weather, generation, load forecasts (615 features)")
179
+ print(f" Past-only masked: CNEC outages, volatility, historical flows (~2,428 features)")
180
 
181
  for i, border in enumerate(forecast_borders, 1):
182
  # Clear GPU cache BEFORE each border to prevent memory accumulation
 
197
  )
198
 
199
  print(f" Context shape: {context_data.shape}, Future shape: {future_data.shape}", flush=True)
200
+ print(f" Using {len(future_data.columns)-2} features (known-future + past-only masked)", flush=True)
201
 
202
  # Run covariate-informed inference using DataFrame API
203
  # Note: predict_df() returns quantiles directly
 
206
  with torch.inference_mode():
207
  forecasts_df = pipeline.predict_df(
208
  context_data, # Historical data with ALL features
209
+ future_df=future_data, # All 3,043 features (past-only masked)
210
  prediction_length=prediction_hours,
211
  id_column='border',
212
  timestamp_column='timestamp',
213
  target='target',
214
+ batch_size=128, # Increased from 32 for better temporal attention + faster inference
215
  quantile_levels=[0.01, 0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95, 0.99] # 9 quantiles for volatility
216
  )
217
 
 
270
  'num_features': len(future_data.columns) - 2 # Exclude border and timestamp
271
  }
272
 
273
+ print(f" [OK] Complete in {inference_time:.1f}s ({len(future_data.columns)-2} features with past-only masking)", flush=True)
274
 
275
  except Exception as e:
276
  import traceback
src/forecasting/dynamic_forecast.py CHANGED
@@ -9,7 +9,16 @@ Key Concepts:
9
  - run_date: When the forecast is made (e.g., "2025-09-30 23:00")
10
  - forecast_horizon: Always 14 days (D+1 to D+14, fixed at 336 hours)
11
  - context_window: Historical data before run_date (typically 512 hours)
12
- - future_covariates: Features available for forecasting (603 full + 12 partial)
 
 
 
 
 
 
 
 
 
13
  """
14
 
15
  from typing import Dict, Tuple, Optional
@@ -156,9 +165,16 @@ class DynamicForecast:
156
  """
157
  Extract future covariate data for D+1 to D+14.
158
 
159
- Future covariates include:
160
- - Full-horizon D+14: 603 features (always available)
161
- - Partial D+1: 12 features (load forecasts, will be masked D+2-D+14)
 
 
 
 
 
 
 
162
 
163
  Args:
164
  run_date: Forecast run timestamp
@@ -179,10 +195,12 @@ class DynamicForecast:
179
  (pl.col('timestamp') <= forecast_end)
180
  )
181
 
182
- # Select only future covariate features (603 full + 12 partial)
 
183
  future_features = (
184
- self.categories['full_horizon_d14'] +
185
- self.categories['partial_d1']
 
186
  )
187
 
188
  # Build future DataFrame
 
9
  - run_date: When the forecast is made (e.g., "2025-09-30 23:00")
10
  - forecast_horizon: Always 14 days (D+1 to D+14, fixed at 336 hours)
11
  - context_window: Historical data before run_date (typically 512 hours)
12
+ - future_covariates: ALL 3,043 features (leveraging Chronos-2 past-only masking)
13
+ * 603 full-horizon (known future)
14
+ * 12 partial D+1 (masked D+2-D+14)
15
+ * ~2,428 historical-only (masked as past-only covariates)
16
+
17
+ Chronos-2 Past-Only Covariate Masking:
18
+ - Historical-only features have NaN future values → Chronos-2 sets mask=0
19
+ - Model learns cross-feature correlations from historical context
20
+ - Attention mechanism uses dimensional structure even when values masked
21
+ - Enables learning of CNEC/volatility patterns without future knowledge
22
  """
23
 
24
  from typing import Dict, Tuple, Optional
 
165
  """
166
  Extract future covariate data for D+1 to D+14.
167
 
168
+ Future covariates include ALL 3,043 features using Chronos-2's past-only masking:
169
+ - Full-horizon D+14: 603 features (known future values)
170
+ - Partial D+1: 12 features (load forecasts, masked D+2-D+14)
171
+ - Historical-only: ~2,428 features (MASKED as past-only covariates)
172
+
173
+ Past-only covariates leverage Chronos-2's mask-based attention:
174
+ - Future values are NaN (unknown)
175
+ - Chronos-2 sets mask=0 for these dimensions
176
+ - Model learns cross-feature correlations from historical context
177
+ - Attention mechanism uses structure even when future values masked
178
 
179
  Args:
180
  run_date: Forecast run timestamp
 
195
  (pl.col('timestamp') <= forecast_end)
196
  )
197
 
198
+ # Include ALL features (3,043 total) to leverage past-only covariate masking
199
+ # Historical-only features will be NaN in future → Chronos-2 masks them automatically
200
  future_features = (
201
+ self.categories['full_horizon_d14'] + # 603 known-future
202
+ self.categories['partial_d1'] + # 12 partial
203
+ self.categories['historical_only'] # ~2,428 past-only (MASKED!)
204
  )
205
 
206
  # Build future DataFrame