Repository navigation
tools: fit the routing head over --dump-encoder output, so before/after accuracy has an after (engine#1313 item 3) - #2112
Conversation
…er accuracy has an after (engine#1313 item 3)
|
Lane handover note (pi-i753 is being stood down for the release). This PR is complete and self-tested; what it needs is a verdict at For the reviewer, the three things worth checking, in order: (1) that The real before/after still needs the demo's logged decisions and outcome labels (private |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at fbe992b: READY
Fits the routing head over --dump-encoder output, with the reason stated plainly: "--eval scores a classifier; this takes the dump and fits the classifier, so 'before/after routing accuracy' halves use the same cases, encoder and class order."
That equality of halves is the whole value — a before/after number computed with a different encoder or a reordered class list is the classic way a fine-tune reports an improvement it did not achieve. Both halves from one dump, one encoder, one class order removes the degree of freedom.
--self-test registered, per the #1901 rule. This and #2117 are the two halves of #1313's demo row.
#1313 is a release-blocker row (
cl-gameleads,pi-demosupports). Its engine work is three items; items 1 and 2 are already on main — item 1 is--laya-modelswith"model":"auto",X-1bit-Route/-Model/-Ms/-Id,/v1/route/table,/v1/route/decisionsand/metrics; item 2 is the game table itself, whichdocs/laya-models.md:24introduces as "the first table is the game agent's (engine #1313)". Item 3 was the gap: "a routing-head fine-tune path: data format from logged decisions, a CPU training script, and a before/after eval."Two thirds of item 3 exist and this is the missing middle:
--evalscores a classifier (accuracy, per-class recall/precision, confusion, ECE, log loss) and--dump-encoderwrites the head's own input, but nothing took the dump and fitted a head, so "before/after routing accuracy" had no after.What this adds
tools/laya_head_finetune.py DUMP_DIR [--holdout F] [--ridge L] [--pool mean|last] [--json]:dump.json+h.f32+cases.jsonl(the--dump-encoderformat, per-caseoffset/len/label/options);beforeis the table's own decision, taken from each case'slogitsin the same dump — so both arms are scored on the same cases, encoder and class order, and the only variable is the head;afteris a closed-form ridge regression from the pooled encoder state to the class one-hot: deterministic, no framework, exact normal equations — the right tool for a few hundred cases;--jsonfor the log.The design point the self-test forced
The split is by case and stratified by label, not by index — and that was not my first version. A decision log is written in arrival order, and a demo's arrival order is class-contiguous (a stretch of reflex steps, then a planning stretch), so an index split holds out a whole class rather than a sample of each. My own synthetic fixture hit it immediately: 20+20+20 cases with
--holdout 0.5gave train = classes 0 and 1 only, a 1/3 ceiling for any head. The stratifier is in the code with that story in its comment, and--holdout 0reportsin-samplerather than passing as a held-out number.Verified
The fixture is synthesised in the self-test (class-conditional clusters, uninformative table logits), so it needs no dump, no model and no game run.
What this does not claim
The real before/after needs the game's logged decisions and its outcome labels (
labels.json), which live with the demo (cl-game, privatelaya-models). This is the path and the arithmetic; the row iscl-game's to fill with real cases. Not in this PR: training anything, or a served-mode switch to the fitted head — the dump is an offline artifact, and a head with no measured gain should not become a default.