Skip to content

Point-forecast skill floor: production does not reliably beat seasonal-naive (verdict pre-committed, ~2026-09-27) #29

Description

@ducroq

What

Every comparison in this project is relativeeval_log.jsonl scores LightGBM vs ARF, experiments/registry.jsonl scores variants vs production, metrics.diebold_mariano compares two candidates. None can see the case where every arm is worse than a trivial baseline; that reads as a healthy log with a clear winner.

Measured 2026-09-06 against seasonal-naive ("same clock hour, last day available at t0"):

Live record — horizon-matched, production p50, 24 eval days, t0 ∈ [2026-07-30, 2026-08-24]:

horizon n LGBM p50 naive winner
1–24h 576 26.92 23.34 naive
25–48h 576 33.87 28.94 naive
49–72h 552 36.92 32.71 naive

Offline, 260 vintages (EXP-035, re-scoring stored EXP-018/EXP-021 predictions):

arm MAE naive skill DM p
chronos_bolt_base 22.72 30.31 +0.250 <1e-6
lgbm_drop_rolling 27.07 30.31 +0.107 3.2e-5
lgbm_full (production) 28.76 30.31 +0.051 0.033
lgbm_drop_calendar 30.82 30.31 −0.017 0.720

The precise claim

Not "the model has no skill" — across the full year it edges naive by 5%. The claim is narrower and worse: the edge is regime-dependent and collapses where a forecast is worth having. lgbm_full is below naive in 2 of 9 months, and in August 2026 (the current live regime, mean 131.9 EUR/MWh) all eight LightGBM variants are below naive. Chronos-bolt-base is above naive in 9 of 9 months.

Shipped

evaluate_shadow.py now logs n_naive_hours, naive_mae, lightgbm_mae_on_naive_hours, lightgbm_skill_vs_naive, naive_min/max_horizon_h per eval row (89d4ac1). update_shadow.py records t0_utc per prediction so the anchor is never re-derived.

Open

Verdict is pre-committed in docs/hypothesis-log.md [2026-09-06] on ≥21 rows carrying non-null lightgbm_skill_vs_naive (≈2026-09-27), with buckets at skill <0, [0, 0.05], >0.05. EXP-035 does not discharge it — same offline vintages EXP-021 used, and its LightGBM arms are harness rebuilds rather than the deployed model.

Untested alternative, and the cheapest next check: Augur exists for load shifting, where ranking the cheap hours matters more than the level. A rank-correlation or cheapest-k-hours hit-rate metric might show the model ahead of naive where MAE says otherwise — that would make this issue's verdict narrow rather than wrong, and would mean the dashboard should be scored on that instead.

Reproduce: python scripts/exp035_naive_floor.py

Related: #15 (the model class that clears the floor), #19 (calibration is the second problem), #28 (drop_rolling is the best arm against the floor)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions