File size: 2,732 Bytes
179372e | 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 59 60 61 62 63 64 65 66 | """SonoCNN demo: predict the bundled samples, a few held-out test images,
and — if the sibling ViewConsensus library is importable — an 8-view
consensus panel with a defer-to-human verdict on one test image.
This is a mechanics demo on a 28x28 benchmark model, not a clinical tool.
"""
import os
import sys
import numpy as np
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
HERE = os.path.dirname(os.path.abspath(__file__))
PKG = os.path.dirname(HERE)
from sonocnn import load_model, predict # noqa: E402
model = load_model(os.path.join(PKG, "model.safetensors"))
print("== bundled demo samples (256x256 ultrasound stills, resized to 28x28) ==")
for name in ("benign", "malignant"):
r = predict(model, os.path.join(PKG, "assets", "demo_samples", f"{name}.png"))
print(f"{name}.png -> predicted {r['label']} "
f"(confidence {r['confidence']:.1%}; benign {r['probabilities']['benign']:.3f}, "
f"malignant {r['probabilities']['malignant']:.3f})")
print("\n== held-out BreastMNIST test images ==")
d = np.load(os.path.join(PKG, "assets", "breastmnist.npz"))
x_test = d["test_images"]
y_test = d["test_labels"].ravel()
labels = ("benign", "malignant")
for cls in (0, 1):
idx = int(np.where(y_test == cls)[0][0])
r = predict(model, x_test[idx])
print(f"test[{idx}] true={labels[cls]} -> predicted {r['label']} "
f"(confidence {r['confidence']:.1%})")
print("\n== ViewConsensus panel on the malignant test image (optional) ==")
try:
sys.path.insert(0, os.path.join(PKG, "..", "..", "libraries", "consensuskit", "src"))
from viewconsensus import consensus_from_probabilities
import torch
from sonocnn.infer import TRAIN_MEAN, TRAIN_STD
img = x_test[int(np.where(y_test == 1)[0][0])].astype(np.float32) / 255.0
def shift(a, dy, dx):
return np.roll(np.roll(a, dy, axis=0), dx, axis=1)
views = [img, img[:, ::-1], img[::-1, :], shift(img, 0, 2), shift(img, 0, -2),
shift(img, 2, 0), shift(img, -2, 0), shift(img, 2, 2)[:, ::-1]]
batch = torch.from_numpy(
np.stack([((v - TRAIN_MEAN) / TRAIN_STD).astype(np.float32) for v in views]))[:, None]
with torch.no_grad():
probs = torch.softmax(model(batch), 1).numpy()
res = consensus_from_probabilities(probs, labels=list(labels))
verdict = getattr(res.verdict, "value", res.verdict)
print(f"consensus: {res.label} at {res.probability:.1%} "
f"(+/-{res.uncertainty:.1%} across 8 views) -> verdict: {verdict}, "
f"should_defer: {res.should_defer}")
except ImportError:
print("viewconsensus not importable here — install the sibling ViewConsensus "
"library to see the consensus panel.")
|