Skip to content

ML4H Submission Final: Code + Experiment Run Results - #52

Open
will-pang wants to merge 65 commits into
mainfrom
ml4h-merge-tranche-1-wp-20260830
Open

ML4H Submission Final: Code + Experiment Run Results#52
will-pang wants to merge 65 commits into
mainfrom
ml4h-merge-tranche-1-wp-20260830

Conversation

@will-pang

@will-pang will-pang commented Sep 4, 2026

Copy link
Copy Markdown

Description

Overview of Changes

Results

Seed 1

Labs (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes + CXR (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Seed 2

Labs (Will)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes (Will)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN 0.84 0.98 0.98 0.77 NVIDIA A100-SXM4-40GB 738.41 MB 5443.22s (~90.72 min) Link
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes + CXR (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Seed 3

Labs (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Labs + Notes + CXR (Rian)

Backbone Val PR-AUC Val ROC-AUC Val Accuracy Val F1 Compute VRAM Runtime WandB Link
MLP
RNN
Transformer
Bottleneck Transformer
EHRMamba
JambaEHR

Logiquo and others added 30 commits April 12, 2026 12:19
* rename arg name for chefer

* Initial attempts to fix the interpretability target_class_idx

* Support negative prediction for interpretability metric.

* Fix tests

* Fix more tests

* Revert "Support negative prediction for interpretability metric."

This reverts commit fe8c8ad.

* Reapply "Support all samples for interpretability metric"

* Initial attempt for the filter

* Fixup

* Fix sample_class handling

* fixup

* fix test

* Fix arg name

* Add example

* fix docs
…sunlabuiuc#927)

* small fix + bump to pyproject.toml ver. for bug fixed release on pypi

* We don't really have someone qualified for a second review, and this broken CI is leading to a lot of issues here.

Will revert if it doesn't resolve here.
Add test_rnn.py with 12 test cases covering:

TestRNN (8 tests):
- Model initialization with correct attributes
- Forward pass output structure and shapes
- Backward pass gradient propagation
- Embedding extraction via embed=True
- Custom hyperparameters (embedding_dim, hidden_dim)
- LSTM cell type variant
- Vanilla RNN cell type variant
- Bidirectional RNN variant

TestMultimodalRNN (4 tests):
- Initialization with correct sequential/non-sequential classification
- Forward pass with mixed modalities (sequence + multi_hot + tensor)
- Backward pass gradient propagation
- Embedding extraction with correct mixed-modality dimensions

Follows the established test pattern from test_mlp.py and test_tcn.py
using create_sample_dataset with synthetic data.

Ref sunlabuiuc#425
* Fixed repo to be able to run TUEV/TUAB + updated example scripts

* Args need to be passed correctly

* Minor fixes and precomputed STFT logic

* Fix the test files to reflect codebase changes

* Args update

* test script fixes

* dataset path update

* fix contrawr - small change

* divide by 0 error

* Incorporate tfm logic

* Fix label stuff

* tuab fixes

* fix metrics

* aggregate alphas

* Fix splitting and add tfm weights

* fix tfm+tuab

* updates scripts and haoyu splitter

* fix conflict

* Remove weightfiles from tracking and add to .gitignore

Weight files are large binaries distributed separately; untrack all
existing .pth files under weightfiles/ and add weightfiles/ to
.gitignore so they are excluded from future commits and the PR.

Made-with: Cursor

* normalization = 95%

* temporarily re-add weight files

* 16 workers

* tuab sanity check

* consistent log outputs

* test tuab

* change back to multiclass

* update conformal scripts

* remove weightfiles

* oops

* fix tests
* feat: migrate GRASP model from PyHealth 1.0 to 2.0 API

Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* feat: add GRASP mortality prediction notebook and fix cluster_num

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* Restore code_mapping support in SequenceProcessor for PyHealth 2.0

Adds optional code_mapping parameter to SequenceProcessor that maps
granular medical codes to grouped vocabularies (e.g. ICD9CM→CCSCM)
before building the embedding table. Resolves the functional gap
from the 1.x→2.0 rewrite where code_mapping was removed. Ref sunlabuiuc#535

Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>

* Add RNN baseline and code_mapping comparison notebooks for MIMIC-III

Two identical notebooks for A/B testing code_mapping impact on mortality
prediction. Only difference is the schema override in Step 2. Both use
seed=42 for reproducible splits.

Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>

* fix(tasks): extract NDC codes instead of drug names for prescription mapping

event.drug returns drug names (e.g. "Aspirin") which produce zero matches
in CrossMap NDC→ATC; event.ndc returns actual NDC codes enabling 3/3
feature mapping for mortality and readmission tasks.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* test(tasks): add tests verifying NDC extraction in drug tasks

Checks that mortality and readmission task processors build vocabulary
from NDC codes (numeric strings) rather than drug names (e.g. "Aspirin"),
confirming the event.drug -> event.ndc fix works correctly.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* fix(tasks): fix missed MortalityPredictionMIMIC4 event.drug and update docs

- Fix event.drug -> event.ndc in MortalityPredictionMIMIC4 (line 282)
- Update readmission task docstrings to reflect NDC extraction

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* fix(tasks): fix DrugRecommendationMIMIC3 to extract NDC codes

DrugRecommendationMIMIC3 used prescriptions/drug (drug names) via Polars
column select; changed to prescriptions/ndc to match MIMIC-4 variant and
enable NDC->ATC code mapping.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* fix(models): guard RNNLayer and ConCare against zero-length sequences

RNNLayer: clamp sequence lengths to min 1 so pack_padded_sequence
does not crash on all-zero masks, matching TCNLayer (tcn.py:186).

ConCare: guard covariance divisor with max(n-1, 1) to prevent
ZeroDivisionError when attention produces single-element features.

Both edge cases are triggered when code_mapping collapses vocabularies
and some patients have all codes map to <unk>, producing all-zero
embeddings and all-zero masks.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* docs: add docstrings to SequenceProcessor class and fit method

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* docs: add docstrings, type hints, and fix test dims for GRASP module

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* feat: add GRASP mortality prediction notebooks for baseline and code_mapping

Baseline notebook runs GRASP with raw ICD-9/NDC codes. Code_mapping
notebook collapses vocab via ICD9CM→CCSCM, ICD9PROC→CCSPROC, NDC→ATC
for trainable embeddings on full MIMIC-III.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* fix(models): guard ConCare and GRASP against batch_size=1 crashes

- ConCare FinalAttentionQKV: bare .squeeze() removed batch dim when
  batch_size=1, causing IndexError in softmax. Use .squeeze(-1) and
  .squeeze(1) to target only the intended dimensions.
- ConCare cov(): division by zero when x.size(1)==1. Guard with max().
- GRASP grasp_encoder: remove stale torch.squeeze(hidden_t, 0) that
  collapsed [1, hidden] to [hidden] with batch_size=1. Both RNNLayer
  and ConCareLayer already return [batch, hidden].
- GRASP random_init: clamp num_centers to num_points to prevent
  ValueError when cluster_num > batch_size.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* feat: add GRASP mortality prediction notebooks for baseline and code_mapping

Baseline notebook runs GRASP with raw ICD-9/NDC codes. Code_mapping
notebook collapses vocab via ICD9CM→CCSCM, ICD9PROC→CCSPROC, NDC→ATC
for trainable embeddings on full MIMIC-III.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* Add code_mapping as task __init__ argument

Allow tasks to accept a code_mapping dict that upgrades input_schema
entries so SequenceProcessor maps raw codes (e.g. ICD9CM) to grouped
vocabularies (e.g. CCSCM) at fit/process time. This avoids manual
schema manipulation after task construction.

- Add code_mapping parameter to BaseTask.__init__()
- Thread **kwargs + super().__init__() through all task subclasses
  with existing __init__ methods (4 readmission tasks, 1 multimodal
  mortality task)
- Add 17 tests covering SequenceProcessor mapping and task-level
  code_mapping initialization

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* Update code_mapping notebook to use task init argument

Replace manual task.input_schema override with the new
code_mapping parameter on MortalityPredictionMIMIC3().

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* feat(examples): add ConCare hyperparameter grid sweep script

Mirrors the GRASP+ConCare mortality notebook pipeline exactly
(same tables, split, seed, metrics) but sweeps 72 configurations
of embedding_dim, hidden_dim, cluster_num, lr, and weight_decay.

Results are logged to sweep_results.csv. Supports --root for
pointing at local MIMIC-III, --code-mapping, --dev, and --monitor.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* chore(sweep): increase early stopping patience from 10 to 15 epochs

Smaller ConCare configs (embedding_dim=8/16) may learn slower and
need more epochs before plateauing.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* Initial plan

* fix: filter falsy NDCs, guard None tokens in process(), fix NDC regex

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-authored-by: ddhangdd <43976109+ddhangdd@users.noreply.github.com>

* refactor(sweep): rename and generalize sweep script for all backbones

Rename sweep_concare_grasp.py → sweep_grasp.py. Now supports
--block GRU|ConCare|LSTM with per-backbone default grids, --resume
for crash recovery, --grid JSON override, auto-dated output dirs
(sweep/{BLOCK}_{YYYYMMDD}_{HHMMSS}_{mapping}/), and config.json
saved alongside results for reproducibility.

Co-Authored-By: Colton Loew <colton.loew@gmail.com>
Co-Authored-By: lookman-olowo <lookmanolowo@hotmail.com>
Co-Authored-By: christiana-beard <christyanamarie116@gmail.com>
Co-Authored-By: ddhangdd <dfung2@wisc.edu>

* test(sweep): add unit and integration tests for sweep_grasp utilities

Covers grid building, combo hashing, CSV resume parsing, output
directory naming, and end-to-end single-config runs for GRU and ConCare
on synthetic data (13 tests, all passing).

Co-Authored-By: Colton Loew <loewcx@illinois.edu>
Co-Authored-By: lookman-olowo <lookman-olowo@github.com>
Co-Authored-By: christiana-beard <christiana-beard@github.com>
Co-Authored-By: ddhangdd <ddhangdd@github.com>

* docs(sweep): add tmux copy-paste instructions for each paper run

Co-Authored-By: Colton Loew <loewcx@illinois.edu>
Co-Authored-By: lookman-olowo <lookman-olowo@github.com>
Co-Authored-By: christiana-beard <christiana-beard@github.com>
Co-Authored-By: ddhangdd <ddhangdd@github.com>

* chore(examples): adds cleans examples, removes util script

* Delete tests/core/test_grasp.py

we removed grasp script from examples, dropped test

* Revert "Delete tests/core/test_grasp.py"

This reverts commit 0d95758.

* fix: remove orphaned sweep test, restore grasp tests

* feat(grasp): add static_key support for demographic features with tests

* fix(test): add valid NDC to test prescriptions so readmit test produces both labels

---------

Co-authored-by: lookman-olowo <lookmanolowo@hotmail.com>
Co-authored-by: christiana-beard <christyanamarie116@gmail.com>
Co-authored-by: ddhangdd <dfung2@wisc.edu>
Co-authored-by: Lookman Olowo <42081779+lookman-olowo@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ddhangdd <43976109+ddhangdd@users.noreply.github.com>
Co-authored-by: ddhangdd <desmondfung123@gmail.com>
Co-authored-by: Colton Loew <loewcx@illinois.edu>
Co-authored-by: lookman-olowo <lookman-olowo@github.com>
Co-authored-by: christiana-beard <christiana-beard@github.com>
Co-authored-by: ddhangdd <ddhangdd@github.com>
Co-authored-by: lookman-olowo <lookman-olowo@users.noreply.github.com>
* dl4h final project kobeguo2 - CaliForest

* Update CaliForest to require explicit fit before inference

* Remove unused logit_scale from CaliForest
* Fix Drug Recommandation NDC/ATC3 code

* Fix padding behaviour

* remove .codex file

* Change test from FakePatient to demo dataset
…IUC purge (sunlabuiuc#1143)

literally just updating the examples/ no need to waste reviewer time.
* add back backups of original tutorials

* Backup lost tutorials

* generate new tokenizer tutorial

* update with pip install d4rl install pyhealth and rename

* update colab references
* Add synthetic-EHR generative evaluation metrics

Adds pyhealth/metrics/generative/, a subpackage for evaluating synthetic
EHR data along privacy, utility, and statistical-fidelity axes:

- privacy.py: NNAAR, membership inference attack, discriminator privacy
- utility.py: machine learning efficacy (TRTR vs TSTR), code-prevalence
  similarity (R2, Pearson, RMSE)
- utils.py: shared data prep, an LSTM classifier, and a random-forest
  baseline
- evaluate_synthetic_ehr(): convenience orchestrator for the full suite

These functions are ported from a standalone evaluation script. The
MIMIC-specific data-loading/CLI glue is dropped; the metrics work on any
flat EHR dataframe. Public functions are re-exported from
pyhealth.metrics. Adds unit tests in tests/core/test_generative_metrics.py
and Sphinx docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add synthetic-EHR generative evaluation metrics

Adds pyhealth/metrics/generative/, a subpackage for evaluating synthetic
EHR data along privacy, utility, and statistical-fidelity axes:

- privacy.py: NNAAR, membership inference attack, discriminator privacy
- utility.py: machine learning efficacy (TRTR vs TSTR), code-prevalence
  similarity (R2, Pearson, RMSE)
- utils.py: shared data prep, an LSTM classifier, and a random-forest
  baseline
- evaluate_synthetic_ehr(): convenience orchestrator for the full suite

These functions are ported from a standalone evaluation script. The
MIMIC-specific data-loading/CLI glue is dropped; the metrics work on any
flat EHR dataframe. Public functions are re-exported from
pyhealth.metrics. Adds unit tests in tests/core/test_generative_metrics.py
and Sphinx docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* add baselines

* removed halo save file and updated promptehr to be more paper accurate

* update docs

* update docs

* Update pyhealth.models.HALO.rst

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* transfer FHIR pipeline to branch

* fix

* fix unit test using fast json readers

* Replace editdistance with rapidfuzz for Python 3.13 compatibility

editdistance 0.8.1 only ships cp311 wheels and has no Python 3.13
binary, causing CI installs to fail on Linux. rapidfuzz>=3.0.0
ships wheels for all major platforms including cp313 and provides
an equivalent Levenshtein.distance() API.

https://claude.ai/code/session_01L5qHpvAZQSgmZyc6tMTX6d

* copilot fixes

* revert ignore error change

---------

Co-authored-by: Claude <noreply@anthropic.com>
sunlabuiuc#1003)

* Add MedFuse multimodal model for EHR+CXR fusion

* Address PR feedback: docstring, mask doc, test cleanup, paper citations
…unlabuiuc#1158)

* Attention Rollout skeleton

* attention_rollout.py done

* tests/core/test_attention_rollout.py done

* attention rollout integrated into example scripts

* attention rollout docs

* attention rollout docs/interpret/pyhealth.interpret.methods.attention_rollout.rst added

* Stip trailing whitespace and rename example keys to rollout

* attention rollout: doc and style changes

* attention rollout: module docstring header
…nlabuiuc#1176)

Any PR touching pyhealth/**/*.py must also update docs/ and examples/,
keep added/modified lines free of ruff lint violations, and give new or
modified top-level public classes/functions a '>>>' docstring example.

Co-authored-by: Claude <noreply@anthropic.com>
* feat: add EEGBCI helper functions

* feat: add EEGBCI dataset

* feat: add EEGBCI tasks

* test: add opt-in EEGBCI real-data smoke test

* docs: add EEGBCI pattern discovery example

* docs: add EEGBCI API docs

* chore: record EEGBCI verification

* docs: refine EEGBCI moment report design

* Add EEGBCI moment report constants

* Add EEGBCI rest baseline helpers

* Add EEGBCI state scoring helpers

* Add EEGBCI task state quality helpers

* Add EEGBCI moment row annotation

* Add EEGBCI representative windows

* Render EEGBCI moment summary

* Wire EEGBCI moment report main flow

* Document EEGBCI moment report outputs

* Fix EEGBCI moment report review findings

* Polish EEGBCI report artifact

* Exclude EEG pattern discovery notes

* fix: address EEGBCI review feedback

* fix: address EEGBCI review feedback

* fix: satisfy PR contribution rules
* Covariate CP fixes

* small edits to pass checks
* Add missing citations for TFMTokenizer, EHRMambaCEHR, CEHR embeddings, comprehensiveness/sufficiency metrics, MLE, and NNAAR

* to pass the pr checks
…labuiuc#1179)

* feat(datasets): add Parquet scan path to BaseDataset

Route .parquet/.pq files, globs, and directories through a typed
_scan_parquet scanner; keep CSV/TSV(.gz) on the existing path. Add a
datetime fast-path in load_table that skips the string round-trip and
casts to datetime64[ms], preserving NaT for static events.

* feat(datasets): add MEDSDataset for the Medical Event Data Standard

Declarative YAML wrapper over the shared Parquet scan path, with
split_source subset selection (metadata or directory layout), distinct
processing caches per subset, and a construction-time Parquet footer
schema guard that rejects missing, non-timestamp, or timezone-aware
time columns.

* test(datasets): add MEDS synthetic and demo smoke tests

Deterministic sharded Parquet fixtures cover nested splits, subset
filtering, cache isolation, set_task smoke, and construction-time
schema-guard TypeErrors. Demo smoke stays skip-gated behind
MEDS_DEMO_ROOT / test-resources/meds_demo (gitignored).

* docs(examples): add MEDS example and API docs

Document MEDSDataset in the API reference and add an end-to-end
examples/meds_demo.py against the public PhysioNet MIMIC-IV MEDS demo.

* feat(tasks): add InHospitalMortalityMEDS

MEDS-native in-hospital mortality task: one sample per completed stay,
reconstructed by joining HOSPITAL_ADMISSION/HOSPITAL_DISCHARGE events on
hadm_id. Half-open [admit, prediction_time) observation window (full_stay
default; first_hours early-warning variant), label from the
HOSPITAL_DISCHARGE//DIED discharge code. Discharge and MEDS_DEATH events
are excluded from features to prevent label leakage.

hadm_id is dataset-specific (not part of the core MEDS schema), so it is
exposed via a bundled configs/meds_with_hadm.yaml rather than the default
config.

Verified on the public MIMIC-IV demo in MEDS: 12 positive / 238 stays
(rate 0.0504); set_task sample count 238.

- pyhealth/tasks/in_hospital_mortality_meds.py (+ __init__ export)
- pyhealth/datasets/configs/meds_with_hadm.yaml
- tests/core/test_in_hospital_mortality_meds.py
- examples/verify_meds_mortality.py
- docs/api/tasks/pyhealth.tasks.InHospitalMortalityMEDS.rst (+ tasks.rst toctree)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tasks): re-export InHospitalMortalityMEDS with explicit alias

Satisfies ruff F401 on the newly added __init__ line under
tools/check_pr_rules scoped lint (same pattern as eegbci).

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: link MEDS schema docs for subject_splits mapping

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: rename _reconstruct_stays to _group_stays and clarify summarize docstring

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: add end-to-end RNN training to MEDS demo

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: link subject_splits in MEDSDataset API page; qualify demo metrics output

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop summarize helper from InHospitalMortalityMEDS

Co-authored-by: Cursor <cursoragent@cursor.com>

* style: modernize typing annotations, drop unused noqa (UP006/UP035/UP045/RUF100)

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…buiuc#1189)

* Implement real APS and add dynamic score_type to conformal methods

Adds pyhealth/calib/predictionset/scores.py, a shared score module
implementing genuine Adaptive Prediction Sets (Romano, Sesia, and
Candes 2020): nonconformity score = cumulative sum of predicted
probabilities for classes ranked above the target, plus a randomized
U*p(target) term (U ~ Uniform(0,1), one draw per example, shared
across all candidate classes). Previously, BaseConformal's
score_type="aps" was silently just an alias for "threshold" and did
not implement APS at all.

Threads a new score_type parameter ("threshold" [default, unchanged
behavior] or "aps") through BaseConformal, LABEL, ClusterLabel,
CovariateLabel, and NeighborhoodLabel, each with an optional
random_state for reproducible APS randomization. SCRIB and FavMac are
intentionally excluded since their calibration isn't a score-then-
quantile pattern.

Verified via numpy-only synthetic tests: both score types hit ~90%
empirical coverage at alpha=0.1, for marginal and class-conditional
coverage, in both nonconformity and conformity sign conventions.

* Add scores.py doctests, aps usage examples, tests, and docs

Adds >>> usage examples to the 4 public functions in
pyhealth/calib/predictionset/scores.py (verified against real computed
output). Adds a score_type="aps" usage example to the docstrings of
BaseConformal, LABEL, ClusterLabel, CovariateLabel, and
NeighborhoodLabel.

Adds tests/core/test_scores.py covering both score types: threshold
backward-compatibility, the APS formula's hand-computable non-randomized
case, monotonicity, reproducibility under a seeded RNG, nonconformity/
conformity complementarity, and empirical marginal coverage at the
target alpha. Extends test_cluster_label.py, test_covariate_label.py,
and test_neighborhood_label.py with score_type="aps" end-to-end cases.

Documents the score_type argument and adds the previously-missing
BaseConformal entry to docs/api/calib/pyhealth.calib.predictionset.rst.

* Fix ruff lint violations flagged by CI (UP/RUF rules)

CI's ruff install (pip install 'ruff~=0.15') resolves to the latest
0.x release under PEP 440 compatible-release semantics, which enabled
more pyupgrade/ruff-specific default rules than the older cached ruff
used for local verification. Fixes all 13 flagged violations:
Optional[X]/Union[X, Y] -> X | Y, typing.Dict -> dict (including the
now-modernized pre-existing forward() return annotations this forced),
an unused unpacked variable, and an unsorted __all__.

Verified by reproducing the CI's exact environment: a clean venv with
`pip install 'ruff~=0.15'` (which also resolves to 0.16.3), confirming
`tools/check_pr_rules.py` now passes.
will-pang and others added 26 commits August 26, 2026 15:45
Replace the trailing -1 in .view()/.reshape() calls for the text and
  image branches with the known embedding dim (or an explicit empty
  mask), since torch can't infer -1 for a 0-element tensor and raises
  "cannot reshape tensor of 0 elements ... dimension size -1 is
  ambiguous" when a batch has zero note or image slots.
…single per-sample clock.

  Previously, time offsets were computed inconsistently across modalities within a multi-admission sample:
  - ICD codes: hours since the previous admission (a delta, reset to 0 for the first admission).
  - Labs, notes, CXR: hours since that admission's own start (reset to 0 at every admission).

  Since labs, admission_note_times, and cxr_image_times concatenate events from every admission into one sequence per patient,
  the per-admission reset caused values from different admissions to collide — e.g., a lab drawn 6h into stay 2 sorted
  identically to one drawn 6h into stay 1, even though the two are actually days apart.
Five small changes on top of cdeb3e0, plus the paper launchers.

1. lab_standardizer.py + wiring. Per-feature z-score fit on the training
   split only; missing values stay missing. UnifiedMultimodalEmbeddingModel
   already accepted numeric_standardizers, so this is only the fit + the
   hand-off. --no-lab-standardization runs raw labs as an ablation, so the
   default is a choice you can turn off rather than a commitment.
   Measured on EHRMamba/labs+notes/seed 1: 0.7473 without, 0.8011 with.

2. write_run_config. metrics_history.json records what a run scored but not
   the conditions that produced it, and it records resolved values rather
   than raw flags -- that distinction is what surfaced a per-model optimizer
   override where run_config stored adam_eps: null while the optimizer used
   1e-6. Also records source_sha256 so a table can be shown to come from one
   build.

3. eval_split. The inference loader fell back test-or-val-or-train, so a run
   without a test split reported TRAINING metrics as test with nothing saying
   so. The split is now named, warned about, and recorded.

4. exp_name includes the task. It was {model}_seed{seed}, so labs and
   notes_labs at one seed wrote to the same directory and the second run
   silently destroyed the first. This matters immediately: the plan is 36
   paired cells.

5. Restore emitted_data_version. cdeb3e0 removed it. It is part of vars(task),
   which is what the task-cache uuid5 key is built from, so without it a cache
   built before an emitted-data change is silently reused -- and cdeb3e0
   changes every event timestamp, which is exactly when the bump is needed.

Deliberately not included: the time_origin fix. cdeb3e0 already does it, and
by inspection it is identical to ours (same _hours_since helper, same anchor
on admissions_to_process[0].timestamp). No need to revert it.

scripts/paper: common.sh holds the protocol; will.sh and rian.sh add only the
data roots and the CPU tuning for their machine. rian.sh pins OMP threads and
uses loader workers because those nodes run several cells at once -- unpinned,
four concurrent cells put ~800 threads on 128 cores and epoch time went 191s
to 8600s with the GPUs at 0-1%. will.sh keeps num_workers=4 and no pinning.

Verified on a dev split before and after: output dir goes mlp_seed1 ->
notes_labs_mlp_seed1, run_config.json appears with eval_split=test, and the
fitted mean/std/count buffers land in the checkpoint with --no-lab-
standardization correctly removing them.
… onto a single per-sample clock."

This reverts commit cdeb3e0.
…imic4.py onto a single per-sample clock.""

This reverts commit 6ad4726.
@will-pang will-pang changed the title Ml4h merge tranche 1 wp 20260830 ML4H Submission Final: Code + Experiment Run Results Sep 4, 2026
Rian354 and others added 3 commits September 4, 2026 16:43
…ough

--loader-num-workers and --persistent-workers do not exist on this branch, so
every rian cell died at argparse before doing any work. This runner has no
dataloader-worker control at all: --num-workers feeds the dataset build only,
and thread pinning is what actually keeps concurrent cells off each other.

Also forward "$@" so callers can add flags (--wandb, --observation-window-hours)
without editing the launcher.
Four small fixes found while running the Tranche 1 sweep.

1. Test evaluation was gated on wandb. `if wandb_logger.enabled and
   test_loader is not None` meant a run without --wandb never computed test
   metrics at all -- not merely unlogged, never calculated. Ungated.

2. Test metrics are now written to test_metrics.json. metrics_history.json
   carries validation only and log.txt has no test lines, so the numbers that
   go in a paper previously lived nowhere on disk: only in stdout and W&B,
   recoverable afterwards only by re-scoring predictions_*.csv by hand.

3. Per-epoch CPU accounting alongside the existing VRAM and epoch_time_s:
   train_cpu_seconds and train_cpu_util_pct. Counts dataloader workers, since
   self-only time badly understates a data-loading-bound run. psutil is
   already present via wandb, with a resource fallback.

4. exp_name and the W&B run name now include the observation window. An
   observation-window arm is a different experiment from the full-stay run at
   the same task/model/seed, but both resolved to the same name -- so they
   shared an output directory and collided in W&B. Runs also now set W&B
   group (arm) and job_type (backbone) so a many-cell sweep is navigable.

Also: create_directory used `if not exists: makedirs`, which two processes
importing pyhealth for the first time can both pass, leaving one to die on
FileExistsError. Seen on a shared cluster home with two concurrent jobs.
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.