Skip to content

tabicl_baseline: thread offload_mode/batch_size/disk_offload_dir through - #213

Merged
amrit110 merged 7 commits into
mainfrom
tabicl-offload-memory-fix
Aug 27, 2026
Merged

tabicl_baseline: thread offload_mode/batch_size/disk_offload_dir through#213
amrit110 merged 7 commits into
mainfrom
tabicl-offload-memory-fix

Conversation

@amrit110

Copy link
Copy Markdown
Member

Summary

  • TabICL comparator runs have only ever scored the basic 17-feature panel, never the strong 609-feature panel the GBM baseline uses, because a naive fit at the standard row/estimator settings costs an estimated ~70GB per predict_proba call and reliably OOM-killed the host (three confirmed kills, 2026-08-23).
  • TabICLClassifier already exposes offload_mode/batch_size/disk_offload_dir to manage exactly this memory bottleneck, but none of it was wired through this project's tabicl_baseline.py. This threads those three params through fit_tabicl_baselines -> _fit_one_tabicl -> the classifier, and narrows check_inference_cost's RAM-budget gate to skip only for offload_mode="disk" with a real disk_offload_dir, since that combination changes the actual constraint from resident memory to disk I/O (which the existing GB estimate doesn't model, and shouldn't be trusted to block).
  • Adds docs/tabicl_strong_feature_comparison.md: the first TabICL run on the strong feature panel this unblocks, with bootstrap CIs against the tuned GBM on all 12 MIMIC alert cells, plus an honest account of two failed configurations before a one-model-at-a-time loop completed cleanly.

Test plan

  • uv run ruff check / uv run ruff format --check clean on both changed files
  • uv run mypy odyssey/inference/tabicl_baseline.py clean
  • uv run pytest tests/odyssey/inference/test_tabicl_baseline.py — 20/20 passed locally
  • Empirically validated on the GPU host (odyssey-cbm-a100): all 12 (event, horizon) cells fit and scored without OOM using offload_mode="cpu", n_estimators=1, reduced row cap; see docs/tabicl_strong_feature_comparison.md for full results and caveats

🤖 Generated with Claude Code

Every prior TabICL comparator run in this project scored the basic
17-feature panel, never the strong 609-feature one the GBM baseline uses,
because a naive fit at the standard row/estimator settings costs an
estimated ~70GB per predict_proba call and reliably OOM-killed the host.

TabICLClassifier already exposes offload_mode/batch_size/disk_offload_dir
to manage exactly this (the column-wise embedding tensor is the documented
memory bottleneck), but none of it was wired through. This threads those
three params from fit_tabicl_baselines through to the classifier, and
narrows check_inference_cost's RAM-budget gate to skip only when
offload_mode="disk" with a real disk_offload_dir set, since that
combination changes the actual constraint from resident memory to disk
I/O, which the existing GB estimate does not model.

docs/tabicl_strong_feature_comparison.md documents the first strong-panel
TabICL run this unblocks, including two failed configurations along the
way (a disk-offload I/O stall, and a resident-memory OOM from fitting all
12 models up front) before a one-model-at-a-time loop completed cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.03%. Comparing base (fd11be2) to head (a02c1b3).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #213   +/-   ##
=======================================
  Coverage   89.03%   89.03%           
=======================================
  Files          57       57           
  Lines        8491     8493    +2     
=======================================
+ Hits         7560     7562    +2     
  Misses        931      931           
Files with missing lines Coverage Δ
odyssey/inference/tabicl_baseline.py 98.03% <100.00%> (+0.03%) ⬆️

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

amrit110 and others added 6 commits August 26, 2026 03:58
…igation

Amrit asked whether TabICL could be run at its actual full capability
(8 estimators, 50,000-row context) on the 609-feature panel instead of the
reduced config (1 estimator, 20,000 rows) the existing report used.

Found and worked around a real gap: this project's offload_mode/
disk_offload_dir params only cover TabICL's column-wise embedding stage
(COL_CONFIG), per the library's own docstring. A synthetic-data test with
offload_mode="disk" plus inference_config={"COL_CONFIG": {"disk_dtype":
torch.float16}} confirmed disk offload genuinely works at full scale: a
real ~7.4GB-per-estimator memmap file lands in the configured directory,
one estimator's file is written/used/deleted before the next starts, and
fit completes in 27 seconds without any memory error.

But it is not usable: a predict_proba call for 200 query rows against
that context took 728.9 seconds, and a call for 8,192 rows (this
project's real per-batch size) did not finish inside a 15-minute timeout,
because the entire context is re-read from disk on every call with no
caching. Extrapolated conservatively, a single real (event, horizon) cell
would take multiple hours and the full 12-cell comparison would take at
least a day of sequential compute -- not a memory problem, a throughput
one, and not achievable in a reasonable session on this hardware.

No code changes: the working offload_mode="disk" plumbing already merged
is sufficient to reproduce this finding; the inference_config/disk_dtype
override that made disk offload actually land in the right place did not
translate into a practically usable configuration, so it is documented
rather than wired into the module as unused, unvalidated plumbing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Migrated the GPU host to a2-ultragpu-1g (170GB RAM, A100-80GB,
odyssey-cbm-a100-ultra, us-central1-a) to test whether more RAM (not disk)
fixes the throughput wall found in the prior disk-offload investigation.

It does: RAM-resident full capability (offload_mode="cpu", n_estimators=8,
50,000-row context) fits and scores without crashing or hanging, peak RSS
~98GB, well inside 165GB free RAM.

One full cell run end to end (vasopressor_start@8h, fit 109s for all 3
horizons, predict 1854s for this horizon): TabICL(strong, full) scores
0.915 [0.896, 0.936] vs GBM's 0.934 [0.916, 0.950], CIs now overlapping,
vs the reduced config's 0.859 [0.832, 0.887] which was a real, separated
loss. Full capability closes most of the gap.

Extrapolated from the measured per-row rate, the full 12-cell sweep would
take on the order of 5 hours of sequential compute. Not run to completion
in this session; stopped deliberately rather than run unattended on an
hourly-billed A100-80GB host without asking first. VM stopped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reproduces the full-capability run (n_estimators=8, TABICL_MAX_ROWS=50,000,
this project's real TabICL defaults, not the reduced config the 12-cell
report table uses) so the comparison in docs/tabicl_strong_feature_comparison.md
can be rerun or extended without recreating the driver from scratch --
the reduced-config comparator script was left disposable, this one isn't,
since full capability requires enough RAM (170GB+) that it's worth being
able to repeat deliberately rather than by accident.

Reuses the existing GBM(strong) scores from alerts_rows_v3.parquet instead
of refitting, fits/scores/drops one TabICL model at a time (the discipline
that fixed the earlier cross-cell memory accumulation incident), and writes
results incrementally so a partial run isn't a lost run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All 12 core (event, horizon) cells completed on the migrated a2-ultragpu-1g
host (170GB RAM, one A100-80GB): TabICL(strong) at its real full capability
(n_estimators=8, 50,000-row context, no reduction), ~6h sequential compute.

Headline change from the reduced-config table: TabICL is statistically
indistinguishable from the tuned GBM on 9 of 12 cells (all of death, ICU
admission, vasopressor start). Only acute kidney injury keeps a real,
CI-separated gap across all three horizons. The reduced config (1 estimator,
20,000 rows) was a genuine handicap, not a formality -- it cost real,
measurable performance across the board, not just on cells that already
looked weak.

Also documents a real caveat found after the sweep completed: the AKI label
in the reused alerts dump predates the KDIGO staging completeness fix
(3d7ecbb, same day, ~9h after the dump was generated), so every AKI number
here (both models) is against a known-incomplete label. Flagged, not yet
corrected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@amrit110
amrit110 merged commit 351d613 into main Aug 27, 2026
7 checks passed
@amrit110
amrit110 deleted the tabicl-offload-memory-fix branch August 27, 2026 12:10
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