File size: 2,128 Bytes
8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec 745873a 8e23aec | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | """
Pulmo inference examples.
Two ways to use Pulmo:
A) FULL SCAN (recommended) — both stages, raw volume in, findings out.
See `analyze_scan.py` (Stage 1 finds candidates, Stage 2 characterises them).
B) SINGLE PATCH — Stage 2 only, when you already have a candidate location
(e.g. you supply your own detector or use LUNA16 candidates.csv).
That is what this script demonstrates.
Requires: torch, numpy, huggingface_hub (scipy too for the full pipeline)
pip install torch numpy scipy huggingface_hub
"""
import numpy as np
import torch
from huggingface_hub import hf_hub_download
from modeling import load_stage2, crop_stage2_input, explain_malignancy, CONCEPT_NAMES
REPO_ID = "ariyul/Pulmo"
def main():
device = "cuda" if torch.cuda.is_available() else "cpu"
ckpt_path = hf_hub_download(repo_id=REPO_ID, filename="student_2p5d_best.pth")
model = load_stage2(ckpt_path, device=device)
# --- Replace this with a real 64^3 patch cropped around a candidate ---
# (Z, Y, X) raw HU. If you only have a candidate (z, y, x) in a full volume,
# use crop_stage2_input(volume, (z, y, x)) instead of building the patch here.
dummy_patch = np.random.randint(-1000, 400, size=(64, 64, 64)).astype(np.int16)
x = crop_stage2_input(dummy_patch, (32, 32, 32)).to(device) # (1, 7, 64, 64)
with torch.no_grad():
out = model(x)
det_p = torch.softmax(out["detection"][0], 0)[1].item()
mal_p = torch.softmax(out["malignancy"][0], 0)[1].item()
seg = torch.sigmoid(out["segmentation"][0, 0]).cpu().numpy()
print(f"Nodule probability : {det_p:.3f}")
print(f"Malignancy probability : {mal_p:.3f} -> {'MALIGNANT' if mal_p >= 0.5 else 'BENIGN'}")
print(f"Segmented voxels (>0.5): {(seg > 0.5).sum()}")
print("\nPer-concept contribution to the malignancy decision:")
for name, value, contrib in explain_malignancy(model, out):
print(f" {name:18s} value={value:+.2f} contribution={contrib:+.3f}")
print("\nFor full-scan inference (Stage 1 + Stage 2), see analyze_scan.py.")
if __name__ == "__main__":
main()
|