Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c592407
Add hyperparameter table generator for the ML4H appendix (WP6)
amrit110 Sep 25, 2026
2d1cd51
Score the label override on the hazard heads (WP1)
amrit110 Sep 25, 2026
c7b5ea6
Channel probes: k versus the poles (WP3 of the ML4H rebuttal plan)
amrit110 Sep 25, 2026
2ba28e0
GEMINI run.sh: alerts-cis, panel-coverage and cohort-counts steps for…
amrit110 Sep 25, 2026
4229135
Pin hazard event heads per run, and write run_pins.json at training time
amrit110 Sep 25, 2026
f55a792
Merge remote-tracking branch 'origin/rebuttal/hazard-override' into r…
amrit110 Sep 25, 2026
2ac3415
Merge remote-tracking branch 'origin/rebuttal/channel-probes' into re…
amrit110 Sep 25, 2026
32408d4
Merge remote-tracking branch 'origin/rebuttal/gemini-stages' into reb…
amrit110 Sep 25, 2026
71662c7
Merge remote-tracking branch 'origin/rebuttal/hparams-table' into reb…
amrit110 Sep 25, 2026
1a47681
Plan: record the three subset-scale RandInt arms under WP4
amrit110 Sep 25, 2026
3b56595
probe_channel: device-safe CTL probes, saved bank, staged JSON writes
amrit110 Sep 25, 2026
69d252e
WP2: paired two-run AUROC deltas on identical landmark rows
amrit110 Sep 25, 2026
fcc3922
Plan: results so far (WP1, WP2, WP3, WP5, WP6) as of Sep 26
amrit110 Sep 26, 2026
c320014
Plan: final WP2 (retrains) and WP4 (RandInt) results
amrit110 Sep 29, 2026
b201e70
probe_channel: annotate the mixed-embedding return for mypy
amrit110 Sep 29, 2026
44e48b0
Plan: paired like-for-like intervals for WP2; VMs stopped
amrit110 Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ uv run python -m odyssey.inference.steering --run-dir <run> --held-out-shard-dir
uv run python -m odyssey.inference.counterfactual --run-dir <run> --held-out-shard-dir <dir>
```

Paired subject-clustered intervals come from `scripts/alerts_cis.py` and `scripts/intervention_cis.py` (`odyssey/inference/uncertainty.py`); `scripts/gbm_feature_ablation.py` refits the GBM with each feature group dropped; `scripts/long_history_compare.py` compares two backbones on identical rows split by truncation; `scripts/concept_atlas.py` renders what each concept promotes and suppresses. The `scripts/make_*_table.py` generators turn banked JSON into the paper's tables. `scripts/eval_run.sh` chains the full evaluation of one run.
Paired subject-clustered intervals come from `scripts/alerts_cis.py` and `scripts/intervention_cis.py` (`odyssey/inference/uncertainty.py`); `scripts/gbm_feature_ablation.py` refits the GBM with each feature group dropped; `scripts/long_history_compare.py` compares two backbones on identical rows split by truncation; `scripts/compare_runs_paired.py` gives the paired hazard-head AUROC delta between two runs' alert row dumps on identical landmark rows (refusing on a row-set mismatch); `scripts/concept_atlas.py` renders what each concept promotes and suppresses. The `scripts/make_*_table.py` generators turn banked JSON into the paper's tables. `scripts/eval_run.sh` chains the full evaluation of one run.

Every run is registered in [`docs/experiments.md`](docs/experiments.md) (host, data, commit, purpose, outcome) and its aggregate outputs are banked under `research_journal/figure_data/<host>/<run>/`. Environment fingerprints and numeric canaries are written with every checkpoint; the landmark protocol is versioned and stamped on every row dump.

Expand Down
91 changes: 87 additions & 4 deletions docs/ml4h2026_rebuttal_plan.md

Large diffs are not rendered by default.

38 changes: 25 additions & 13 deletions odyssey/inference/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,30 @@ def _landmark_mask(
# ---------------------------------------------------------------------------


def position_visit_starts(
sids: torch.Tensor,
vids: torch.Tensor,
times: torch.Tensor,
visit_start: dict[tuple[int, int], float],
) -> torch.Tensor:
"""Visit start hours per position, shaped like ``times``.

Looks up the unique (subject, visit) keys of the chunk once (a few
hundred lookups, not one per token); a key with no visit start maps
to 0. Shared by :func:`collect_model_scores` and the intervention
pass in :mod:`odyssey.inference.interventions`, so both select the
same landmark rows.
"""
keys = torch.stack([sids, vids], dim=-1).reshape(-1, 2)
unique_keys, inverse = torch.unique(keys, dim=0, return_inverse=True)
unique_starts = torch.tensor(
[visit_start.get((int(s), int(v)), 0.0) for s, v in unique_keys.tolist()],
dtype=times.dtype,
device=times.device,
)
return unique_starts[inverse].view_as(times)


def _event_token_mask(
vocab: Vocabulary, alert: AlertEvent, device: str
) -> torch.Tensor:
Expand Down Expand Up @@ -480,19 +504,7 @@ def collect_model_scores(
times = chunk.batch.aux.time_stamps
# Packed-path timestamps are already in the true frame (see
# packed_context._truncate_head): no un-rebasing.
# visit start per position, via the unique (subject, visit)
# keys in this chunk (a few hundred lookups, not one per token)
keys = torch.stack([sids, vids], dim=-1).reshape(-1, 2)
unique_keys, inverse = torch.unique(keys, dim=0, return_inverse=True)
unique_starts = torch.tensor(
[
visit_start.get((int(s), int(v)), 0.0)
for s, v in unique_keys.tolist()
],
dtype=times.dtype,
device=times.device,
)
starts = unique_starts[inverse].view_as(times)
starts = position_visit_starts(sids, vids, times, visit_start)
# Static/demographic tokens build_patient_sequence prepends
# (GENDER etc., stamped with the first real event's time) carry
# visit_id=-1, not a real encounter -- _landmark_mask's own
Expand Down
Loading
Loading