Skip to content

fix(ml): train on few players without crashing on empty splits - #90

Merged
TheMeinerLP merged 2 commits into
mainfrom
fix/ml-empty-split
Oct 16, 2026
Merged

TheMeinerLP merged 2 commits into
mainfrom
fix/ml-empty-split

Conversation

@TheMeinerLP

Copy link
Copy Markdown
Contributor

Problem

A train job on a snapshot with a single player failed with Found array with 0 sample(s) (shape=(0, 12)) while a minimum of 1 is required by IsolationForest. The split is by player; with one player per label stratum the val and test splits are empty, and run_candidate (ml/src/corvus_ml/eval/runner.py) called _eval_metrics -> cand.calibrated_score on the empty test split.

Changes

  • fix(ml): nothing is scored on an empty split. test / val metrics are null with test_skipped / val_skipped (test split empty: needs at least 2 players per label stratum); the confusion table and learning curve are left out. ONNX parity and the benchmark run on the first non-empty split (test, val, train), recorded as onnx.parity_on, so the model can still be registered. train registers it and the job result carries warnings. When no candidate is usable, the JobError names every skip/parity reason. The report renders a "No test metrics" section.
  • feat(ml): new split_by (player default, session) for jobs and run.split_by in eval.toml. Leakage-prone, for smoke tests with few players; recorded in the report (banner) and job result (warning). Refused together with tune.
  • Docs: docs/reference/ml-jobs.md, docs/how-to/evaluate-models.md.

A candidate that crashes in fit on very few windows (not an empty training split) is not turned into a skip; only an empty/one-class training split is, as before.

Tests

tests/test_eval_few_players.py and new cases in tests/test_job_models.py (single player: evaluate, train+register+warning, sequence candidate, no usable candidate, split_by). Synthetic data, fixed seeds, no network.

With one player per label stratum the val and test splits are empty, and
the runner scored the fitted candidate on them (sklearn: 0 samples).

- skip scoring, metrics and confusion on an empty split; report why
  (test_skipped / val_skipped) and leave the metrics out
- run ONNX parity and benchmark on the first non-empty split
  (test, val, train) and record it as parity_on
- train still registers the model; the job result carries warnings
- name every skip and parity reason when no candidate is usable
split_by "session" groups the train/val/test split by session instead of
player, so a smoke test with one or two players gets a test split. It is
leakage-prone (a player lands in train and test) and is recorded in the
report and job result, which carry a warning. Not combinable with tune.
@TheMeinerLP
TheMeinerLP merged commit 1792eec into main Oct 16, 2026
13 checks passed
@TheMeinerLP
TheMeinerLP deleted the fix/ml-empty-split branch October 16, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant