Handling Supervision Scarcity in Chest X-ray Classification: Long-Tailed and Zero-Shot Learning
Paper β’ 2602.13430 β’ Published β’ 1
How to use hieuphamha/cxrlt2026-task1-convnextv2 with timm:
import timm
model = timm.create_model("hf_hub:hieuphamha/cxrlt2026-task1-convnextv2", pretrained=True)Top-1 submission for Task 1 (Long-tailed Multi-label Chest X-ray Classification) of the CXR-LT 2026 Challenge.
| Task | Model | Recommended use | Weights |
|---|---|---|---|
| Task 1 | ConvNeXtV2-Base + CSRA-style (DB-CAS) | Best score for single-model inference | convnextv2_base_mimic-cxr_padchest_csra_dbcas.safetensors |
| Task 1 | ConvNeXtV2-Base + standard MLP | Reproducible baseline / ensemble candidate | convnextv2_base_padchest_standard_mlp.safetensors |
Recommendation: choose CSRA-style + DB-CAS when using a single model; it achieves a higher score than the standard MLP model.
Terminology: this implementation uses a CSRA-style class-specific residual attention head. It is inspired by, but not identical to, the original CSRA formulation.
ConvNeXtV2-Base (timm, global_pool="", drop_path_rate=0.2)
βββ spatial feature map (B, 1024, H, W)
βββ BatchNorm2d(1024)
βββ CSRA-style head (Ξ»=0.1)
βββ GAP branch : Linear(1024 β 30) β logit_gap
βββ Attention : Conv2d(1024β30, 1Γ1) β Softmax
β class-wise weighted pool β logit_csra
βββ output : logit_gap + 0.1 Γ logit_csra
ConvNeXtV2-Base (timm, global pooling, drop_path_rate=0.2)
βββ BatchNorm1d(1024)
βββ Dropout(0.3)
βββ Linear(1024 β 512) β ReLU
βββ BatchNorm1d(512) β Dropout(0.3)
βββ Linear(512 β 30)
Stage 1 β Pre-train on MIMIC-CXR (14 classes) [FC/MLP head]
ConvNeXtV2-Base (ImageNet-22k init)
+ head: BN β Dropout β Linear(1024β512) β ReLU β BN β Dropout β Linear(512β14)
βββ Phase 1: head-only warm-up (LR 1e-3, 3 epochs)
βββ Phase 2: full fine-tune (backbone LR 1e-5 / head LR 1e-4)
Loss: AsymmetricLoss + LogitAdjustment, EMA decay 0.9999
Stage 2 β DB-CAS fine-tune on PadChest (30 classes) [FC head β CSRA-style head]
Backbone weights resumed from Stage 1; FC head discarded; new CSRA-style head initialized
βββ Distribution-Balanced(DB) sampling: balances label frequency + co-occurrence
βββ Class-Aware Sampling (CAS): leverages CSRA-style spatial attention
Loss: AsymmetricLoss, EMA decay 0.9999
| File | Size | Description |
|---|---|---|
convnextv2_base_mimic-cxr_padchest_csra_dbcas.safetensors |
~351 MB | CSRA-style weights only (recommended for single-model inference) |
convnextv2_base_mimic-cxr_padchest_csra_dbcas.pth |
~1.05 GB | Full CSRA-style checkpoint including training states |
convnextv2_base_padchest_standard_mlp.safetensors |
~353 MB | Standard MLP weights only (baseline / ensemble candidate) |
convnextv2_base_padchest_standard_mlp.pth |
~1.06 GB | Full standard MLP checkpoint including training states |
model.py |
β | timm-compatible CSRA-style model registration |
Install the required packages:
pip install torch timm safetensors huggingface_hub
Use this model for the strongest single-model score.
from huggingface_hub import hf_hub_download
import importlib.util
import sys
import timm
# Download and register the custom CSRA-style architecture.
path = hf_hub_download("hieuphamha/cxrlt2026-task1-convnextv2", "model.py")
spec = importlib.util.spec_from_file_location("cxrlt", path)
module = importlib.util.module_from_spec(spec)
sys.modules["cxrlt"] = module
spec.loader.exec_module(module)
model = timm.create_model("cxrlt2026_task1_csra_dbcas", pretrained=True)
model.eval()
Use this model as a reproducible baseline or an ensemble candidate.
import torch
import torch.nn as nn
import timm
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
class ConvNeXtV2MLP(nn.Module):
def __init__(self, num_classes=30, drop_path_rate=0.2):
super().__init__()
self.backbone = timm.create_model(
"convnextv2_base",
pretrained=False,
num_classes=0,
drop_path_rate=drop_path_rate,
)
nf = self.backbone.num_features
self.classifier = nn.Sequential(
nn.BatchNorm1d(nf),
nn.Dropout(0.3),
nn.Linear(nf, 512),
nn.ReLU(inplace=True),
nn.BatchNorm1d(512),
nn.Dropout(0.3),
nn.Linear(512, num_classes),
)
def forward(self, x):
return self.classifier(self.backbone(x))
weights_path = hf_hub_download(
"hieuphamha/cxrlt2026-task1-convnextv2",
"convnextv2_base_padchest_standard_mlp.safetensors",
)
model = ConvNeXtV2MLP(num_classes=30)
model.load_state_dict(load_file(weights_path), strict=True)
model.eval()
import cv2, numpy as np, torch
import torchvision.transforms as T
CLASS_NAMES = ['Normal', 'aortic elongation', 'cardiomegaly', 'pleural effusion', 'Nodule', 'atelectasis', 'pleural thickening', 'aortic atheromatosis', 'Support Devices', 'alveolar pattern', 'fracture', 'Hernia', 'Emphysema', 'azygos lobe', 'Hydropneumothorax', 'Kyphosis', 'Mass', 'Pneumothorax', 'Subcutaneous Emphysema', 'pneumoperitoneo', 'vascular hilar enlargement', 'vertebral degenerative changes', 'hyperinflated lung', 'interstitial pattern', 'central venous catheter', 'hypoexpansion', 'bronchiectasis', 'hemidiaphragm elevation', 'sternotomy', 'calcified densities']
transform = T.Compose([
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
def predict(image_path):
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (512, 512))
x = transform(np.stack([img] * 3, axis=-1).astype(np.float32) / 255.0)
x = x.unsqueeze(0).to(device)
with torch.no_grad():
probs = torch.sigmoid(model(x)).squeeze(0).cpu().numpy()
return dict(zip(CLASS_NAMES, probs.tolist()))
results = predict("chest_xray.png")
for cls, p in sorted(results.items(), key=lambda x: -x[1])[:5]:
print(f"{p:.3f} {cls}")
Normalaortic elongationcardiomegalypleural effusionNoduleatelectasispleural thickeningaortic atheromatosisSupport Devicesalveolar patternfractureHerniaEmphysemaazygos lobeHydropneumothoraxKyphosisMassPneumothoraxSubcutaneous Emphysemapneumoperitoneovascular hilar enlargementvertebral degenerative changeshyperinflated lunginterstitial patterncentral venous catheterhypoexpansionbronchiectasishemidiaphragm elevationsternotomycalcified densitiesmean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]@article{Pham2026HandlingSS,
title = {Handling Supervision Scarcity in Chest X-ray Classification:
Long-Tailed and Zero-Shot Learning},
author = {Ha-Hieu Pham and Hai-Dang Nguyen and Thanh-Huy Nguyen and
Min Xu and Ulas Bagci and Trung-Nghia Le and Huy-Hieu Pham},
journal = {ArXiv},
year = {2026},
volume = {abs/2602.13430},
url = {https://arxiv.org/abs/2602.13430}
}