Auditable CT phenotyping through report-derived radiological observations
Python
1
0 commits
updated Aug 24, 2026
Auditable CT phenotyping through report-derived radiological observations
A CT model can phenotype patients for reasons that are not evidence for the phenotype. ACT grounds 3D CT representations in report-derived radiological observations, so the reasons can be read and changed.
Manuscript under submission.
ACT maps a native-resolution chest or abdominal CT volume to similarities with 376,194 report-derived radiological observations, then projects those scores through a large language model (LLM) embedding space. This makes a prediction decomposable into named observations and supports zero-shot annotation, phenotype probing, probe auditing, and observation-bank restriction without retraining the image encoder.
The model was pretrained on volume-report pairs from 38,317 patients (CT-RATE and Merlin) and evaluated in 25,183 held-out patients. It exceeded five vision-language baselines on zero-shot annotation, and CT-CLIP across 221 EHR phenotypes from unseen CT pulmonary angiography, under both zero-shot scoring (0.651 versus 0.572) and linear probing (0.709 versus 0.662). In the paper's restriction analysis, limiting each probe to clinically trusted observations raised mean AUROC across 86 phenotypes from 0.741 to 0.751 (55 phenotypes higher, 31 lower) without touching the encoder.
flowchart LR
A[Chest or abdominal CT] --> B[ACT volume encoder]
B --> C[Similarity to 376,194<br/>radiological observations]
C --> D[LLM semantic projection]
D --> E[Concept-anchored CT embedding]
E --> F[Zero-shot and phenotype probing]
C --> G[Probe-observation auditing]
G --> H[Observation-bank restriction]
model/).model/load_pretrained.py) and the recovered training recipe
(model/run_scripts/train_release_v1.sh).preprocessing/,
analysis/preprocess_new/).analysis/).phenotypes/).ACT requires Python 3.10 or newer. Clone the repository and install the core dependencies in an environment with a PyTorch build appropriate for your hardware:
git clone https://github.com/peterhan91/ACT.git
cd ACT
pip install -r requirements.txt
Optional stacks (concept extraction, figures, external preprocessing,
baselines) are listed as commented extras in requirements.txt. Model weights
are downloaded from Hugging Face on first use and cached locally. The loader
obtains the DINOv2 backbone code through torch.hub; the ACT checkpoint
supplies all model weights.
load_act() downloads the released clip_3d_ctrate_merlin_v1 checkpoint and
returns the model together with the tokenizing text preprocessor:
import sys, torch
sys.path.insert(0, "model")
from load_pretrained import load_act
from train import preprocess_text
device = "cuda" if torch.cuda.is_available() else "cpu"
model, _ = load_act(device=device)
# volume: one preprocessed CT from preprocessing/run_preprocess.py,
# a (160, 224, 224) uint8 array repeated to 3 channels
img = torch.from_numpy(volume[None].repeat(3, 0)).float()[None].to(device)
text = preprocess_text(["pleural effusion", "no pleural effusion"], model).to(device)
with torch.inference_mode():
v = torch.nn.functional.normalize(model.encode_image(img), dim=-1)
t = torch.nn.functional.normalize(model.encode_text(text), dim=-1)
pair_scores = (v @ t.T).softmax(dim=-1)
print(pair_scores) # [[positive_score, negative_score]]
This is a backbone demonstration: it compares one volume with a
positive-negative prompt pair in the shared 768-dimensional space, exactly the
scoring of model/run_test.py. It does not reproduce the full concept
projection, probing, or auditing experiments.
The full published framework follows five stages:
preprocessing/run_preprocess.py (RAS
reorientation, HU clip to [-1000, 1000], aspect-preserving resize and pad
to 160 x 224 x 224, uint8 HDF5).analysis/get_concepts_ct.py (served by vLLM; the prompts are inline in
the script), then embed the deduplicated bank with
analysis/get_embed_ct.py (F2LLM is the paper's embedder).analysis/clear3d/).analysis/run_v1_phenotype_lbfgs_f2llm.sh and the
20-seed runs in analysis/experiments/exp4_confounder_audit/run_pheno_probe.sh;
labels come from phenotypes/extract_inspect_per_ct_phenotypes.py with
the Phecode Map v1.2 manifests.analysis/experiments/exp4_confounder_audit/ (export_f2llm_adam_top25.py,
build_f2llm_natural_clinical_audit.py), the restriction rules in
analysis/trusted_concept_space.py, restricted probes in
analysis/trusted_concept_probe.py, and the evaluation and supplementary
tables in analysis/experiments/supplementary/.The zero-shot benchmark against the vision-language baselines lives in
analysis/experiments/exp1_zeroshot/ (one subdirectory per baseline with its
own faithful preprocessing), retrieval in
analysis/experiments/exp3_concept_retrieval/, and the figure scripts next to
the experiments that produce their data.
The released checkpoint is hosted on Hugging Face:
| File | Contents | Role |
|---|---|---|
clip_3d_ctrate_merlin_v1/best_model.pt | ACT volume encoder and paired text tower | Produces 768-dimensional volume and text features |
Redistributable observation-bank artefacts will accompany the published article on the same repository.
preprocessing/run_preprocess.py. The
CT-RATE validation carve is preprocessing/split_validation.py (seed 42,
summarized in preprocessing/manifests/ctrate_split_summary.json).load_act(revision=...) rather than relying on the repository default.analysis/experiments/exp4_confounder_audit/export_f2llm_adam_top25.py and
build_f2llm_natural_clinical_audit.py. The cosine variant in
analysis/concept_audit_20seeds.py supplies only the top-100 subset
selection for the Figure 6 baseline arm and is marked accordingly.model/run_scripts/train_release_v1.sh..env
(template: .env.example).ACT is released for research on 3D CT representation learning, concept-level analysis, zero-shot evaluation, phenotype probing, model auditing, and interpretable model development. It is not a medical device and is not validated for clinical decision-making. Predictions and observation attributions require independent validation for each population, acquisition setting, and task. They should not be used to diagnose, treat, or triage patients.
Model weights are released for academic research. Review the terms accompanying the Hugging Face assets and the licenses of all upstream models and datasets before reuse.
model/ Volume-report model: architecture, training, inference, HF loader
preprocessing/ Native CT preprocessing, report cleaning, split construction
analysis/ Observation bank, concept representation, probing, audit,
restriction, statistics, and figures (analysis/experiments/)
phenotypes/ 221-phenotype label building and phecode rule manifests
requirements.txt Core dependencies and commented extras
CITATION.cff Machine-readable citation metadata
LICENSE Apache-2.0 license text
A journal reference will be added upon publication. Until then, GitHub reads
the machine-readable CITATION.cff file and offers a
Cite this repository action.
The code in this repository is released under the Apache-2.0 License. Portions
derived from OpenAI CLIP, Meta V-JEPA 2, CheXzero, and OpenCLIP retain their
original MIT copyright notices. The phecode manifests in phenotypes/ are
PheWAS Catalog resources redistributed with attribution. Model weights,
observation-bank assets, upstream models, and datasets may be governed by
separate terms.
Auditable CT phenotyping through report-derived radiological observations
Python
1
0 commits
updated Aug 24, 2026
Auditable CT phenotyping through report-derived radiological observations
A CT model can phenotype patients for reasons that are not evidence for the phenotype. ACT grounds 3D CT representations in report-derived radiological observations, so the reasons can be read and changed.
Manuscript under submission.
ACT maps a native-resolution chest or abdominal CT volume to similarities with 376,194 report-derived radiological observations, then projects those scores through a large language model (LLM) embedding space. This makes a prediction decomposable into named observations and supports zero-shot annotation, phenotype probing, probe auditing, and observation-bank restriction without retraining the image encoder.
The model was pretrained on volume-report pairs from 38,317 patients (CT-RATE and Merlin) and evaluated in 25,183 held-out patients. It exceeded five vision-language baselines on zero-shot annotation, and CT-CLIP across 221 EHR phenotypes from unseen CT pulmonary angiography, under both zero-shot scoring (0.651 versus 0.572) and linear probing (0.709 versus 0.662). In the paper's restriction analysis, limiting each probe to clinically trusted observations raised mean AUROC across 86 phenotypes from 0.741 to 0.751 (55 phenotypes higher, 31 lower) without touching the encoder.
flowchart LR
A[Chest or abdominal CT] --> B[ACT volume encoder]
B --> C[Similarity to 376,194<br/>radiological observations]
C --> D[LLM semantic projection]
D --> E[Concept-anchored CT embedding]
E --> F[Zero-shot and phenotype probing]
C --> G[Probe-observation auditing]
G --> H[Observation-bank restriction]
model/).model/load_pretrained.py) and the recovered training recipe
(model/run_scripts/train_release_v1.sh).preprocessing/,
analysis/preprocess_new/).analysis/).phenotypes/).ACT requires Python 3.10 or newer. Clone the repository and install the core dependencies in an environment with a PyTorch build appropriate for your hardware:
git clone https://github.com/peterhan91/ACT.git
cd ACT
pip install -r requirements.txt
Optional stacks (concept extraction, figures, external preprocessing,
baselines) are listed as commented extras in requirements.txt. Model weights
are downloaded from Hugging Face on first use and cached locally. The loader
obtains the DINOv2 backbone code through torch.hub; the ACT checkpoint
supplies all model weights.
load_act() downloads the released clip_3d_ctrate_merlin_v1 checkpoint and
returns the model together with the tokenizing text preprocessor:
import sys, torch
sys.path.insert(0, "model")
from load_pretrained import load_act
from train import preprocess_text
device = "cuda" if torch.cuda.is_available() else "cpu"
model, _ = load_act(device=device)
# volume: one preprocessed CT from preprocessing/run_preprocess.py,
# a (160, 224, 224) uint8 array repeated to 3 channels
img = torch.from_numpy(volume[None].repeat(3, 0)).float()[None].to(device)
text = preprocess_text(["pleural effusion", "no pleural effusion"], model).to(device)
with torch.inference_mode():
v = torch.nn.functional.normalize(model.encode_image(img), dim=-1)
t = torch.nn.functional.normalize(model.encode_text(text), dim=-1)
pair_scores = (v @ t.T).softmax(dim=-1)
print(pair_scores) # [[positive_score, negative_score]]
This is a backbone demonstration: it compares one volume with a
positive-negative prompt pair in the shared 768-dimensional space, exactly the
scoring of model/run_test.py. It does not reproduce the full concept
projection, probing, or auditing experiments.
The full published framework follows five stages:
preprocessing/run_preprocess.py (RAS
reorientation, HU clip to [-1000, 1000], aspect-preserving resize and pad
to 160 x 224 x 224, uint8 HDF5).analysis/get_concepts_ct.py (served by vLLM; the prompts are inline in
the script), then embed the deduplicated bank with
analysis/get_embed_ct.py (F2LLM is the paper's embedder).analysis/clear3d/).analysis/run_v1_phenotype_lbfgs_f2llm.sh and the
20-seed runs in analysis/experiments/exp4_confounder_audit/run_pheno_probe.sh;
labels come from phenotypes/extract_inspect_per_ct_phenotypes.py with
the Phecode Map v1.2 manifests.analysis/experiments/exp4_confounder_audit/ (export_f2llm_adam_top25.py,
build_f2llm_natural_clinical_audit.py), the restriction rules in
analysis/trusted_concept_space.py, restricted probes in
analysis/trusted_concept_probe.py, and the evaluation and supplementary
tables in analysis/experiments/supplementary/.The zero-shot benchmark against the vision-language baselines lives in
analysis/experiments/exp1_zeroshot/ (one subdirectory per baseline with its
own faithful preprocessing), retrieval in
analysis/experiments/exp3_concept_retrieval/, and the figure scripts next to
the experiments that produce their data.
The released checkpoint is hosted on Hugging Face:
| File | Contents | Role |
|---|---|---|
clip_3d_ctrate_merlin_v1/best_model.pt | ACT volume encoder and paired text tower | Produces 768-dimensional volume and text features |
Redistributable observation-bank artefacts will accompany the published article on the same repository.
preprocessing/run_preprocess.py. The
CT-RATE validation carve is preprocessing/split_validation.py (seed 42,
summarized in preprocessing/manifests/ctrate_split_summary.json).load_act(revision=...) rather than relying on the repository default.analysis/experiments/exp4_confounder_audit/export_f2llm_adam_top25.py and
build_f2llm_natural_clinical_audit.py. The cosine variant in
analysis/concept_audit_20seeds.py supplies only the top-100 subset
selection for the Figure 6 baseline arm and is marked accordingly.model/run_scripts/train_release_v1.sh..env
(template: .env.example).ACT is released for research on 3D CT representation learning, concept-level analysis, zero-shot evaluation, phenotype probing, model auditing, and interpretable model development. It is not a medical device and is not validated for clinical decision-making. Predictions and observation attributions require independent validation for each population, acquisition setting, and task. They should not be used to diagnose, treat, or triage patients.
Model weights are released for academic research. Review the terms accompanying the Hugging Face assets and the licenses of all upstream models and datasets before reuse.
model/ Volume-report model: architecture, training, inference, HF loader
preprocessing/ Native CT preprocessing, report cleaning, split construction
analysis/ Observation bank, concept representation, probing, audit,
restriction, statistics, and figures (analysis/experiments/)
phenotypes/ 221-phenotype label building and phecode rule manifests
requirements.txt Core dependencies and commented extras
CITATION.cff Machine-readable citation metadata
LICENSE Apache-2.0 license text
A journal reference will be added upon publication. Until then, GitHub reads
the machine-readable CITATION.cff file and offers a
Cite this repository action.
The code in this repository is released under the Apache-2.0 License. Portions
derived from OpenAI CLIP, Meta V-JEPA 2, CheXzero, and OpenCLIP retain their
original MIT copyright notices. The phecode manifests in phenotypes/ are
PheWAS Catalog resources redistributed with attribution. Model weights,
observation-bank assets, upstream models, and datasets may be governed by
separate terms.