Framework improvements to generalize use of external model harness - #115
Conversation
cd049e2 to
b97ad65
Compare
anagainaru
left a comment
There was a problem hiding this comment.
I believe we don't need max_val_batches and drift_event_count so I would remove this and make changes in the model harness/toml to include them.
I need to test the other model harnesses examples before we merge this
|
Thanks — both done.
The counter — removed. One clarification: Checkpointing — yes, reverted in Also: trimmed the comment on the lazy
What's left: batch-size fix, logger ordering, KSWIN seed, lazy evidently import, per-name validation metrics, profiler guard. CI green. I made the matching change in #116 so the stack stays consistent. No results changed — the cap was never binding. |
Co-authored-by: Ana Gainaru <ana.gainaru@gmail.com>
Co-authored-by: Ana Gainaru <ana.gainaru@gmail.com>
Co-authored-by: Ana Gainaru <ana.gainaru@gmail.com>
Co-authored-by: Ana Gainaru <ana.gainaru@gmail.com>
541ff25 to
9443ac7
Compare
Six small fixes to the framework, found by trying to run a model harness that APEIRON had never been asked to run before. Together they help support some functionalities of a different type of model harness such as the MATEY foundation model, for which another PR is being prepared with an example in PR #116; these changes don't affect the behaviour for the bundled MNIST/CIFAR/ImageNet examples.
57 lines of source across nine files. Each commit is one fix and can be read on its own.
What was wrong
The framework quietly assumed certain patterns which might not be the case for all model harnesses:
A target is a bare
Tensor.eval()weighted its metrics withy.size(0), which requiressizeto be a callable with Tensor's semantics:numpy's
.sizeis an element count, not a method; a structured target has none at all..shapeis a plain attribute all three already expose, so reading it asks a custom batch type for one attribute rather than for Tensor's whole interface.Eval metrics are self-describing. They are not, and the code already said so:
eval()returns a bare list of floats, so the caller loggedmetrics[0]astest_curr_acc— genuinely accuracy for MNIST, electron-density error for a harness reporting seven metrics. The labels were never lost:eval_metricsis an ordered dict thateval()itself walks to build the list, so zipping its keys back on recovers every name. The pre-adaptation validation was also only printed, never recorded, so a finished run's CSV held post-adaptation numbers alone and no before/after comparison could be made from it.Whoever calls
get_logger()first sets the configuration.main()built the harness before configuring the logger, andget_logger()ignores its arguments once an instance exists — so a harness that logs during construction pinned the default backend and a null CSV path, andvisualization.inputwas silently dropped for the whole run. No file, no error. Nothing inget_example()needs the logger, so configuring first is the entire fix.Every profiled block does tensor work. With zero ATen events the FLOP profiler built a column on an empty DataFrame, which raises instead of recording zero. Reachable from stock config:
update_mode = "none"does no work by design.KSWIN can be replayed. It samples its reference window at random — the only one of the three detectors that samples anything. Three runs over identical data, before and after:
evidentlyis importable everywhere.model_performance_detector.pyimported it at module scope, anddrift_detection/__init__.pyimports that module — soimport apeironfailed outright wherever evidently has no wheel, taking the three river-backed detectors down with it even though none of them needs it. It is now imported inside the single function that uses it. The public API is unchanged where evidently is installed.New configuration
Documented in
docs/configurations.md.Summary
Any harness with a non-Tensor target, more than one eval metric, a no-op update mode, a need for reproducible detection, or a deployment without evidently hits at least one of these.
Testing
ruff checkandruff format --checkclean. 207 passed.One pre-existing failure,
test_valiadation_tests.py::test_mnist_first_drift_losses_match_reference, reproduces identically onmain— both branches produce[6.85099196434021, 5.589590549468994]against a reference of[7.514564037322998, 5.5480828285217285]. It needs the MNIST raw files present locally, so it silently skips without them; that is why it is easy to miss. This branch changes no numerics.Two of the six ship without a dedicated test — the batch-size fix and the logger ordering — after the test simplification in review. Flagging it rather than letting it be found.
Note on an earlier revision
This branch was roughly six times larger before review. Removed since:
[eval] max_val_batches(the per-window evaluation cap belongs in the harness's loader, which builds it and can bound it there), a drift-check counter and the run-summary lines it fed, a duplicate checkpointing implementation, and a test file that accounted for 88% of total suite wall time.continuous_monitor.pyandtest_continuous_monitor.pyare now identical tomain.