feat: adaptive conformal interval calibration (ACI) - #33
Conversation
|
Dug into this — it's clean, and the hooks are in the right places. The rebase landing right after On your two questions: 1. Batched So at 0.02 the batched Caveat so this doesn't read as alarming: coverage still recovers either way, because the score-buffer quantile ( 2. Everything else looks right — rebase, score threading, opt-in. Once #32 lands and you take this out of draft, happy to do a line-level pass. The |
The batched update stepped alpha_t once per coverage-eval interval, scaling the effective learning rate by 1/N (N = scores per interval) and leaving the per-request-tuned gamma nearly inert at the server cadence. Step per score instead, so gamma carries the same meaning as in the per-request formulation. Addresses review feedback on llm-d#33. Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
Emit {ttft,tpot}_aci_coverage_percent alongside the static coverage and
alpha_t/c_t. Static coverage's deviation from target is the drift signal;
the ACI-adjusted coverage confirms the served interval meets the SLO.
Addresses review feedback on llm-d#33.
Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
|
Thanks for the careful read -- both addressed: 1. Batched alpha_t cadence (the semi-blocker). Fixed in 2. Static vs ACI-adjusted coverage. Added in Still draft until #32 lands; happy to take the line-level pass whenever. Thanks again -- the cadence catch was the difference between gamma being a real knob and inert. |
|
Confirmed — I reproduced both figures independently by replicating
One forward note for whoever tunes this in prod (not a change request): the step is asymmetric by construction — down-step The static + ACI-adjusted split in |
Document the asymmetric alpha_t step (fast widen / slow relax) and that ACI_BUFFER should be sized to the drift timescale of interest. Review follow-up on llm-d#33. Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
|
Thanks for reproducing it -- good to have the figures independently confirmed. On the asymmetry: agreed, it's the intended fast-protect / slow-relax behavior, and the 9:1 is just the target baked in -- down-step Ready for the line-level pass whenever #32 lands and this leaves draft -- appreciate the thorough look. |
|
Nice — the Happy to do the line-level pass once #32 lands and this leaves draft — ping me. |
|
This PR is marked as stale after 21d of inactivity. After an additional 14d of inactivity (7d to become rotten, then 7d more), it will be closed. To prevent this PR from being closed, add a comment or remove the |
Rebased onto the component-directory restructure (llm-d#16) and stacked on the rebased llm-d#32: aci.py moves to training/aci.py, imported as training.aci. One-sided upper ACI (Gibbs & Candes 2021) widens the prediction interval between retrains so SLO coverage holds through detection lag. Opt-in via LATENCY_ACI_ENABLED (default false); exposes aci_alpha, aci_offset_ms, and the ACI-adjusted coverage series in /metrics. Union-merged cleanly with llm-d#32's live-coverage additions (both metric families coexist). Squashes the prior four ACI commits; review history is in PR llm-d#33. Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
c69219b to
7ce4522
Compare
Rebased onto the component-directory restructure (llm-d#16): the module moved to training/, and calibration lives at training/calibration.py imported as training.calibration. Adds a continuous coverage loop that re-evaluates the loaded model between scheduled retrains and fires an early retrain when an EMA of |coverage - target| stays over threshold for k evaluations. The pure CalibrationTrigger carries the decision logic; live coverage lands in its own metric family; the detector releases its pending state on a retrain that concludes without landing a model. Opt-in via LATENCY_COVERAGE_EVAL_INTERVAL_SEC (default 0 = disabled). Squashes the prior four commits on this branch; review history is preserved in PR llm-d#32. Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
Rebased onto the component-directory restructure (llm-d#16) and stacked on the rebased llm-d#32: aci.py moves to training/aci.py, imported as training.aci. One-sided upper ACI (Gibbs & Candes 2021) widens the prediction interval between retrains so SLO coverage holds through detection lag. Opt-in via LATENCY_ACI_ENABLED (default false); exposes aci_alpha, aci_offset_ms, and the ACI-adjusted coverage series in /metrics. Union-merged cleanly with llm-d#32's live-coverage additions (both metric families coexist). Squashes the prior four ACI commits; review history is in PR llm-d#33. Signed-off-by: Greg Neighbors <26003+gkneighb@users.noreply.github.com>
7ce4522 to
e3bac21
Compare
|
This PR is marked as stale after 21d of inactivity. After an additional 14d of inactivity (7d to become rotten, then 7d more), it will be closed. To prevent this PR from being closed, add a comment or remove the |
What does this PR do?
Adds opt-in Adaptive Conformal Inference (Gibbs & Candes 2021) that keeps p90 interval coverage valid between retrains. A one-sided upper offset
c_tis adapted online in the continuous-coverage loop from recent conformity scoress = y - qhat(x)and added to the served quantile inpredict(). The score buffer is rebased on retrain (stale scores against the oldqhatwould otherwise cause transient over-coverage).alpha_tandc_tare exposed in/metrics, doubling as a drift signal. Newaci.pymodule; ~6 small hooks intraining_server.py; disabled by default (ACI_ENABLED=false).Why is this change needed?
From the #19 discussion with @Whatsonyourmind: the static interval under-covers during the detection-to-retrain lag. ACI holds coverage through the lag with one scalar (
gamma), composing with — not replacing — the calibration-triggered retraining (which still sharpens the point model). Validated two ways: an offline demo (coverage held ~90% through the lag vs 45% static, at3.2ms added width; gamma=0.02 sweet spot) and @Whatsonyourmind's independent reproduction of the same result.How was this tested?
tests/test_aci.py(8 tests: update direction, [0,1] clip, offset quantile, rebase, maxlen)/predictreflects it,/metricsexposesalpha_t/c_t, and the retrain-rebase resets the buffer (offset_ms -> 0,alpha_t -> targetexactly when a retrain fires — no stale over-coverage)Checklist
git commit -s) per DCOmake test) — 8 passed, 40 skippedmake lint)Related Issues
Follow-up to #19. Stacked on the continuous-coverage PR — please review/merge that first; this PR's diff is ACI-only against that base.
Notes for review (@Whatsonyourmind)
Two spots I'd value your eyes on, both flagged in the thread:
alpha_tupdate. The server stepsalpha_tonce per coverage-eval interval (vs the per-request prototype), sogammais per-interval — does the cadence look right againstcontinuous_coverage_loop's timing?/metricscoverage is the static (pre-offset) value — useful as the drift signal, but it doesn't reflect the ACI-served coverage. Exposing ACI-adjusted coverage too could be a follow-up; happy to add it here if you'd prefer.