Repository navigation
Conversation
Test CoverageTotal: 70.76% (19698 / 27838 lines)
|
solarsys
force-pushed
the
feat/autoimprove-feature-search
branch
2 times, most recently
from
October 23, 2026 09:18
e0eedf5 to
1f4f810
Compare
…ion, report) A run now ends with a report that explains the selected model and how the search got there, built from the decision log alone. - Decision log: every trial records the proposer's rationale and expectation (written before it runs), the trial it was changed from and what changed. LLM proposers are asked for both. - Selection: among the trials that beat the baseline consistently across folds, the most interpretable one within one paired SE of the best (the one-SE rule, select_simplest). Models sit on an interpretability ladder (SearchSpace.rung): logistic regression, depth-1 XGBoost (additive, now in the space), shallow trees and MLPs, then deeper trees and sequence models. one_se=False keeps the highest-lower-bound rule. - Plain words: every model has a description and every setting a meaning (Param.meaning, SearchSpace.explain). - AutoImprover.attribute(): reverts each changed setting of the selected trial one at a time and scores it on the same folds. - evaluate_on_test() also scores the highest-scoring candidate when a simpler one was selected, and saves test.json. - AutoImprover.report() / load_run(): HTML, Markdown and JSON with the summary, task, model and why (candidates, ladder, price of interpretability, attribution), test comparison and reasoning trace (timeline, every trial with hypothesis, change, result and verdict, single-setting comparisons, lessons). Statements are labelled measured, tested or hypothesis; numbers in lessons that match nothing in the log are flagged (unverified_numbers). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
A run's report now explains how the selected model decides and how it would perform in clinical use, and lists what a clinician must decide. - pyhealth.tasks.TaskCard and the optional BaseTask.task_card() hook: the task's design choices with their rationale, and a feature dictionary (meaning, clinical group, expected direction of effect). - pyhealth.metrics.clinical: alert_rate_metrics (sensitivity, PPV and number needed to evaluate when the top 5/10/20% of risk is flagged), net_benefit (decision curve), calibration_bins, subgroup_metrics and clinical_summary; counts below 11 patients are suppressed. - AutoImprover(task_card=...); explain(): exact TreeSHAP for trees, integrated gradients for torch models (exact for logistic regression); importance by clinical group, top features with effect shapes in bins of at least 11 patients, learned vs expected direction, and the top-feature overlap of the fold models. - evaluate_on_test(subgroups=...) adds the clinical summary per config. - flags() and approve(): a missing card, undescribed top features, effects opposite to the expected direction, poor calibration and subgroup gaps; decisions are kept in review.json. - The report gains How it decides, Performance in clinical terms, Clinician review and a TRIPOD+AI reporting checklist; load_run reads the new files. The eICU example has a task card and age/sex subgroups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
…d test) New inputs are written as JSON specs over PyHealth's event log, never as code. FeatureSpace validates a spec against a fixed set of operators (count, exists, days_since, stat, n_distinct, trend, blocks_with, ratio, diff, band, ge, any, cross, column), compiles it at each sample's prediction time t with windows that end strictly before t, and writes its column name and plain-language definition from the spec itself. Every spec carries its clinical rationale and expected direction; invalid specs are refused with a reason. - The future-blind test computes each spec on the full record and on the record as known at t (later rows removed, values known only later blanked via known_at, derived events rebuilt); any difference rejects it. - FeatureSpace.check reports definition, coverage and the test result; augment adds an "engineered" tensor field (EngineeredDataset) whose imputation and scaling run_config_cv refits on each fold's training part (fold_parts). - Optional BaseTask hooks: time_key, feature_vocabulary() and derived_events(patient, t) with derived_event_types. Class-level defaults, so set_task cache keys are unchanged. - examples/autoimprove_features_mimic3.py checks one hand-written feature on synthetic MIMIC-III and measures its paired CV gain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
…g, review gate) With a FeatureSpace, AutoImprover searches inputs as well as settings. - A trial is a config plus a feature set; trials.jsonl records the specs, the group a trial added, whether it was accepted, and any suspicion. - Cycles alternate (feature_cycles=(2, 1)): feature cycles hold the selected model and propose groups of specs, accepted only if their paired gain over the accepted set clears the lower bound (forward selection); model cycles hold the accepted features. Specs failing the future-blind test are refused before training; rejected groups never become the result. - Proposers: LLMProposer.propose_features (operator menu, task card, accepted features, earlier groups), random specs without a rationale, and QueueProposer for a ranked list without an LLM. - confirm(): the leading candidates are re-scored on new folds with several seeds and must clear confirm_se (2 SE) there. - prune(): accepted groups whose removal costs under one SE are dropped; the pruned selection is the result, and the ablation records what each group added. - Suspicion check (a feature that alone has ROC-AUC above suspicion_auc), review gate (features with no rationale, an opposite direction or a suspicion are held back until approved; the test also scores the model without them) and early_stop for clearly worse trials. - The report gains Features and Confirmation sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
After confirmation, the "Why this model" sentence compared the selection with the top candidate on the search folds, although the one-SE rule chose on the confirmation folds. A run could then read "costs +0.022 ± 0.006 (better in 5/5 folds), which is within noise". It now quotes the confirmation scores, says why the rule preferred the selection (a lower rung or fewer engineered features), and names the trial it was pruned from. The trace verdicts follow the same scores. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
solarsys
force-pushed
the
feat/autoimprove-feature-search
branch
from
October 25, 2026 15:20
1f4f810 to
a3752fb
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1319, then #1318, #1317 and #1316. Review the last commit,
autoimprove: feature search.Why
#1319 lets you write and check features by hand. This PR lets the improver search for them. Searching features as well as settings brings three risks:
Each part below addresses one of these.
What changes
A trial is a config plus a feature set.
trials.jsonlrecords the full spec list, the group the trial added, whether it was accepted, and any suspicion.Alternating cycles (
feature_cycles=(2, 1)):With one side fixed, every accepted change has a gain of its own.
Forward selection. A group joins the accepted set only if its paired gain over the accepted set clears the lower bound. Specs that fail the future-blind test are refused before training, and the reason goes back to the proposer. A rejected group never becomes the result.
Proposers:
LLMProposer.propose_featuressees the operator menu, the task card, the accepted features and the earlier groups.RandomProposerproposes random valid specs, labelled as having no rationale.QueueProposertries a ranked list in order, with no LLM.confirm()(confirm=True). The leading candidates are re-scored on new folds with 3 training seeds, and must clear a stricter bound,confirm_se=2. Candidates picked as the best of many on the same folds are partly lucky.prune(). Accepted groups whose removal costs less than one SE are dropped, and the pruned set is the result. The ablation records what each group added.Safeguards:
early_stop: a trial that is clearly worse is stopped after 2 folds.The report gains Features (each feature's definition, rationale, expected direction, contribution and review status) and Confirmation (confirmed scores, the ablation and what was pruned).
What the tests showed, and what changed because of it
The planted-data tests found two statistical weaknesses. Both are fixed here:
Tests
tests/core/test_autoimprove_search.pycovers the proposal's done-when list with 6 tests on a synthetic event log, where the outcome depends on recent visits, with 15% label noise:trials.jsonl, and early stop works.All 49 autoimprove tests pass, as do the doctests, ruff and
tools/check_pr_rules.py.🤖 Generated with Claude Code
https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T