RiverRider/srt-cxr14-frozen-probe

Dataset

2

stars

34

commits

1

linked in READMEs

Aug 30, 2026

updated

chest-xray
frozen-features
linear-probe
medical-imaging
representation-analysis

README

Frozen general-purpose features beat a fine-tuned baseline on ChestX-ray14

A linear probe on frozen google/gemma-4-31B-it hidden states, on all 112,120 images of NIH ChestX-ray14, using the official test_list.txt. No fine-tuning, no radiology pretraining, no augmentation. One Linear(d, 14) under BCE, which is fourteen logistic regressions.

ChestX-ray14, official splitmean AUROCmethod
Wang et al. 2017 (dataset authors)0.7451ResNet-50, fine-tuned end to end
this probe0.7590frozen backbone, linear probe
view-position only0.5883shortcut baseline
shuffled labels0.5002refit floor

Ahead on 12 of 14 findings.

Read the split before you compare anything

This is the part that matters and the part that is usually wrong.

The widely quoted numbers, CheXNet 0.8414 and Yao 2017 0.8027, are on a different split. CheXNet section 5: "We randomly split the dataset into training (70%), validation (10%), and test (20%) sets... We ensure that there is no patient overlap between the splits." That is their own random partition, patient-disjoint but not the official list.

Which split is harder is not established, and we are not going to assert it. Wang et al. scored 0.7381 on a random partition and 0.7451 on the official one, so for their model the official split was very slightly easier. The honest statement is only that the two are not comparable, which is why a single split-matched row is the head-to-head above and the rest is context.

Do not compare this 0.7590 to 0.8414. Different test sets.

Per-finding, split-matched

findingthis probeWang (official split)delta
Pleural_Thickening0.73470.6835+0.0512
Mass0.74230.6933+0.0490
Pneumothorax0.84650.7993+0.0472
Emphysema0.86500.8330+0.0320
Nodule0.69560.6687+0.0269
Effusion0.78490.7585+0.0264
Infiltration0.68620.6614+0.0248
Atelectasis0.72480.7003+0.0245
Cardiomegaly0.82210.8100+0.0121
Edema0.81700.8052+0.0118
Consolidation0.71070.7032+0.0075
Pneumonia0.66000.6580+0.0020
Fibrosis0.75380.7859-0.0321
Hernia0.78280.8717-0.0889

Hernia has 227 positives in the entire dataset and 86 in the test split, so that column is thin for everyone and should not carry weight in either direction.

Controls, and why each one is there

Shuffled labels (0.5002). Labels permuted within the training split and the probe refit. Anything above 0.5 on held-out data is leakage or a bug.

View position only (0.5883). Portable AP films are taken of sicker, bedbound patients, so view alone is a real route to a high AUROC that involves no pathology. A finding that does not clear this baseline has not been detected. The baseline is folded (max(vw, 1-vw)): Hernia's raw view-only AUROC is 0.3033, which is 0.6967 of shortcut once flipped, and reporting the raw figure would have flattered the probe.

View position is a single binary feature, so every one of its AUROC comparisons is a tie. Scoring it needs rank averaging within tied groups, or the answer becomes an artefact of the sort order and moves between machines. An earlier release of this card said 0.5896 for that reason.

Patient-level cluster bootstrap. Confidence intervals resample patients, not images. The test split is 25,596 films from 2,797 patients, roughly 9 per patient, and those films are anything but independent. Resampling rows treats correlated images as fresh evidence and yields intervals about 1.5x too narrow.

Patient overlap is asserted to be zero and the script refuses to run otherwise.

Scope

Detection, not early detection. These labels describe what is visible in the image in front of you. Nothing here speaks to catching disease before it is apparent; that needs longitudinal data with outcomes.

One backbone, one probe class, and the number depends on the backbone. The probe is deliberately linear because anything stronger measures the probe rather than the representation. The identical probe and split on other frozen backbones gives Qwen3-Omni-30B-A3B 0.7650 and Aria 0.7080, so the 0.7590 here is a gemma-4 result. Averaging three backbones' logits reaches 0.7774 at no added parameters: see RiverRider/srt-cxr14-pooled-probe.

Labels are NLP-mined from radiology reports by the dataset authors, with their own reported precision and recall. Every model on this dataset inherits that ceiling.

Banked negatives

Kept because they bound the claim.

hypothesisresult
Attention-style pooling beats mean for focal findingsFalsified. Focal mean drops 0.0537 under max-pool and 0.0225 under top16-pool, at every depth tested
Readout depth mattersNo. 0.7600 to 0.7605 across 0.4 / 0.6 / 0.8 of backbone depth

Nine variants (3 depths x 3 poolings) are in results/cxr14_pool_sweep.json.

Contents

pathwhat
states/cxr14_gemma4.npzfrozen pooled hidden states, gemma-4-31B-it, all 112,120 images
states/cxr14_aria.npzthe same 112,120 images through Aria
states/cxr14_qwen3omni.npzthe same 112,120 images through Qwen3-Omni-30B-A3B
manifests/cxr14_manifest.jsonrows with key, labels, patient_id, view, official split
results/cxr14_probe_full112k.jsonper-finding AUROC, CIs, both controls, split-matched comparison
results/cxr14_probe_qwen3omni.jsonthe same, for Qwen3-Omni
results/cxr14_vendor_compare.jsonthe three backbones side by side
results/cxr14_ensemble3.jsonpooling, with the duplicate-vendor capacity control
results/cxr14_transport.jsonprobe from one backbone read on another
results/cxr14_pool_sweep.jsonthe nine pooling / depth variants
scripts/dataset fetch, encoder, probe, sweep, ensemble, transport

All three state files cover the identical 112,120 images in manifest row order, so they can be indexed against each other directly and against the manifest without realignment.

Three backbones, and what falls out of having them

backbone, official splitmean AUROC
Qwen3-Omni-30B-A3B0.7650
gemma-4-31B-it0.7590
Aria0.7080
mean of the three probes' logits0.7774

Against a split-matched 0.7451 (Wang et al. 2017, Table 17, ResNet-50 fine-tuned end to end). Averaging logits adds no parameters, and the paired patient-clustered bootstrap gives +0.0124 with 95% CI [+0.0082, +0.0168].

A probe fitted on one backbone and read on another, through a ridge map fitted on training rows only, scores 0.7511 against 0.7440 native. Transport cost is negative, and four of six cross directions beat the target backbone's own probe. Weights for the pooled probe are at RiverRider/srt-cxr14-pooled-probe.

Reproducing

python scripts/get_cxr14.py --shards 12
python scripts/cxr_probe.py --states states/cxr14_gemma4.npz \
    --manifest manifests/cxr14_manifest.json --out probe.json

Align on the manifest key, never on array position: any row the encoder dropped shifts every later row.

Part of the SRT program, https://github.com/space-bacon/SRT.

Contributors

RiverRider

34 commits

RiverRider/srt-cxr14-frozen-probe

Dataset

2

stars

34

commits

1

linked in READMEs

Aug 30, 2026

updated

chest-xray
frozen-features
linear-probe
medical-imaging
representation-analysis

README

Frozen general-purpose features beat a fine-tuned baseline on ChestX-ray14

A linear probe on frozen google/gemma-4-31B-it hidden states, on all 112,120 images of NIH ChestX-ray14, using the official test_list.txt. No fine-tuning, no radiology pretraining, no augmentation. One Linear(d, 14) under BCE, which is fourteen logistic regressions.

ChestX-ray14, official splitmean AUROCmethod
Wang et al. 2017 (dataset authors)0.7451ResNet-50, fine-tuned end to end
this probe0.7590frozen backbone, linear probe
view-position only0.5883shortcut baseline
shuffled labels0.5002refit floor

Ahead on 12 of 14 findings.

Read the split before you compare anything

This is the part that matters and the part that is usually wrong.

The widely quoted numbers, CheXNet 0.8414 and Yao 2017 0.8027, are on a different split. CheXNet section 5: "We randomly split the dataset into training (70%), validation (10%), and test (20%) sets... We ensure that there is no patient overlap between the splits." That is their own random partition, patient-disjoint but not the official list.

Which split is harder is not established, and we are not going to assert it. Wang et al. scored 0.7381 on a random partition and 0.7451 on the official one, so for their model the official split was very slightly easier. The honest statement is only that the two are not comparable, which is why a single split-matched row is the head-to-head above and the rest is context.

Do not compare this 0.7590 to 0.8414. Different test sets.

Per-finding, split-matched

findingthis probeWang (official split)delta
Pleural_Thickening0.73470.6835+0.0512
Mass0.74230.6933+0.0490
Pneumothorax0.84650.7993+0.0472
Emphysema0.86500.8330+0.0320
Nodule0.69560.6687+0.0269
Effusion0.78490.7585+0.0264
Infiltration0.68620.6614+0.0248
Atelectasis0.72480.7003+0.0245
Cardiomegaly0.82210.8100+0.0121
Edema0.81700.8052+0.0118
Consolidation0.71070.7032+0.0075
Pneumonia0.66000.6580+0.0020
Fibrosis0.75380.7859-0.0321
Hernia0.78280.8717-0.0889

Hernia has 227 positives in the entire dataset and 86 in the test split, so that column is thin for everyone and should not carry weight in either direction.

Controls, and why each one is there

Shuffled labels (0.5002). Labels permuted within the training split and the probe refit. Anything above 0.5 on held-out data is leakage or a bug.

View position only (0.5883). Portable AP films are taken of sicker, bedbound patients, so view alone is a real route to a high AUROC that involves no pathology. A finding that does not clear this baseline has not been detected. The baseline is folded (max(vw, 1-vw)): Hernia's raw view-only AUROC is 0.3033, which is 0.6967 of shortcut once flipped, and reporting the raw figure would have flattered the probe.

View position is a single binary feature, so every one of its AUROC comparisons is a tie. Scoring it needs rank averaging within tied groups, or the answer becomes an artefact of the sort order and moves between machines. An earlier release of this card said 0.5896 for that reason.

Patient-level cluster bootstrap. Confidence intervals resample patients, not images. The test split is 25,596 films from 2,797 patients, roughly 9 per patient, and those films are anything but independent. Resampling rows treats correlated images as fresh evidence and yields intervals about 1.5x too narrow.

Patient overlap is asserted to be zero and the script refuses to run otherwise.

Scope

Detection, not early detection. These labels describe what is visible in the image in front of you. Nothing here speaks to catching disease before it is apparent; that needs longitudinal data with outcomes.

One backbone, one probe class, and the number depends on the backbone. The probe is deliberately linear because anything stronger measures the probe rather than the representation. The identical probe and split on other frozen backbones gives Qwen3-Omni-30B-A3B 0.7650 and Aria 0.7080, so the 0.7590 here is a gemma-4 result. Averaging three backbones' logits reaches 0.7774 at no added parameters: see RiverRider/srt-cxr14-pooled-probe.

Labels are NLP-mined from radiology reports by the dataset authors, with their own reported precision and recall. Every model on this dataset inherits that ceiling.

Banked negatives

Kept because they bound the claim.

hypothesisresult
Attention-style pooling beats mean for focal findingsFalsified. Focal mean drops 0.0537 under max-pool and 0.0225 under top16-pool, at every depth tested
Readout depth mattersNo. 0.7600 to 0.7605 across 0.4 / 0.6 / 0.8 of backbone depth

Nine variants (3 depths x 3 poolings) are in results/cxr14_pool_sweep.json.

Contents

pathwhat
states/cxr14_gemma4.npzfrozen pooled hidden states, gemma-4-31B-it, all 112,120 images
states/cxr14_aria.npzthe same 112,120 images through Aria
states/cxr14_qwen3omni.npzthe same 112,120 images through Qwen3-Omni-30B-A3B
manifests/cxr14_manifest.jsonrows with key, labels, patient_id, view, official split
results/cxr14_probe_full112k.jsonper-finding AUROC, CIs, both controls, split-matched comparison
results/cxr14_probe_qwen3omni.jsonthe same, for Qwen3-Omni
results/cxr14_vendor_compare.jsonthe three backbones side by side
results/cxr14_ensemble3.jsonpooling, with the duplicate-vendor capacity control
results/cxr14_transport.jsonprobe from one backbone read on another
results/cxr14_pool_sweep.jsonthe nine pooling / depth variants
scripts/dataset fetch, encoder, probe, sweep, ensemble, transport

All three state files cover the identical 112,120 images in manifest row order, so they can be indexed against each other directly and against the manifest without realignment.

Three backbones, and what falls out of having them

backbone, official splitmean AUROC
Qwen3-Omni-30B-A3B0.7650
gemma-4-31B-it0.7590
Aria0.7080
mean of the three probes' logits0.7774

Against a split-matched 0.7451 (Wang et al. 2017, Table 17, ResNet-50 fine-tuned end to end). Averaging logits adds no parameters, and the paired patient-clustered bootstrap gives +0.0124 with 95% CI [+0.0082, +0.0168].

A probe fitted on one backbone and read on another, through a ridge map fitted on training rows only, scores 0.7511 against 0.7440 native. Transport cost is negative, and four of six cross directions beat the target backbone's own probe. Weights for the pooled probe are at RiverRider/srt-cxr14-pooled-probe.

Reproducing

python scripts/get_cxr14.py --shards 12
python scripts/cxr_probe.py --states states/cxr14_gemma4.npz \
    --manifest manifests/cxr14_manifest.json --out probe.json

Align on the manifest key, never on array position: any row the encoder dropped shifts every later row.

Part of the SRT program, https://github.com/space-bacon/SRT.

Contributors

RiverRider

34 commits