Spaces:
Runtime error
Runtime error
Download core.py from rayeyeow/MedicalClassifier: direct link, hf CLI and curl.
- Browser
- Download file 3.59 kB
-
https://huggingface.co/spaces/rayeyeow/MedicalClassifier/resolve/main/core.py
- Command line
-
hf download hf://spaces/rayeyeow/MedicalClassifier/core.py
-
curl -L -o core.py https://huggingface.co/spaces/rayeyeow/MedicalClassifier/resolve/main/core.py
3.59 kB
| """Pinned model inference and official test split; no training or remote code.""" | |
| import hashlib | |
| import os | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from urllib.request import urlopen | |
| ROOT = Path(__file__).resolve().parent | |
| CACHE = Path(os.environ.get("APP_CACHE", ROOT / ".cache")) | |
| os.environ.setdefault("HF_HOME", str(CACHE / "huggingface")) | |
| os.environ.setdefault("HF_HUB_DISABLE_XET", "1") | |
| import numpy as np | |
| import torch | |
| from PIL import Image, ImageOps | |
| MODEL_ID = "therayelab/pneumoniamnist-resnet18" | |
| REVISION = "a5e0ebebfcf20bee6701e6d528e33c5bf7a33b94" | |
| DATA_URL = "https://zenodo.org/records/10519652/files/pneumoniamnist.npz?download=1" | |
| DATA_MD5 = "28209eda62fecd6e6a2d98b1501bb15f" | |
| LABELS = ["Normal", "Pneumonia"] | |
| torch.set_num_threads(min(4, os.cpu_count() or 1)) | |
| def model_bundle(): | |
| from transformers import AutoImageProcessor, AutoModelForImageClassification | |
| processor = AutoImageProcessor.from_pretrained(MODEL_ID, revision=REVISION, use_fast=False) | |
| model = AutoModelForImageClassification.from_pretrained( | |
| MODEL_ID, revision=REVISION, use_safetensors=True, trust_remote_code=False | |
| ).eval() | |
| if model.config.id2label != {0: "0", 1: "1"}: | |
| raise ValueError("Unexpected model label mapping; review before using this demo.") | |
| return processor, model | |
| def clean_image(image): | |
| if image is None: | |
| raise ValueError("Upload an image first.") | |
| if image.width * image.height > 20_000_000: | |
| raise ValueError("Please use an image smaller than 20 megapixels.") | |
| return ImageOps.exif_transpose(image).convert("RGB") | |
| def predict(images): | |
| processor, model = model_bundle() | |
| inputs = processor(images=[clean_image(i) for i in images], return_tensors="pt") | |
| with torch.inference_mode(): | |
| probabilities = model(**inputs).logits.softmax(dim=-1).cpu().numpy() | |
| return probabilities | |
| def test_data(): | |
| CACHE.mkdir(parents=True, exist_ok=True) | |
| path = CACHE / "pneumoniamnist.npz" | |
| if not path.exists(): | |
| with urlopen(DATA_URL, timeout=120) as response: | |
| data = response.read(20_000_000) | |
| if hashlib.md5(data).hexdigest() != DATA_MD5: | |
| raise ValueError("Dataset checksum failed; please retry later.") | |
| path.write_bytes(data) | |
| if hashlib.md5(path.read_bytes()).hexdigest() != DATA_MD5: | |
| raise ValueError("Dataset checksum failed; remove the cached dataset and retry.") | |
| with np.load(path, allow_pickle=False) as data: | |
| images = data["test_images"].copy() | |
| labels = data["test_labels"].reshape(-1).copy() | |
| assert images.shape == (624, 28, 28) and set(labels) == {0, 1} | |
| return images, labels | |
| def test_predictions(): | |
| images, _ = test_data() | |
| return np.concatenate([predict([Image.fromarray(i) for i in images[s:s+16]]) | |
| for s in range(0, len(images), 16)]) | |
| def metrics(labels, probabilities, threshold): | |
| predicted = (np.asarray(probabilities)[:, 1] >= threshold).astype(int) | |
| labels = np.asarray(labels) | |
| tn = int(np.sum((labels == 0) & (predicted == 0))) | |
| fp = int(np.sum((labels == 0) & (predicted == 1))) | |
| fn = int(np.sum((labels == 1) & (predicted == 0))) | |
| tp = int(np.sum((labels == 1) & (predicted == 1))) | |
| divide = lambda a, b: a / b if b else None | |
| return {"n": len(labels), "accuracy": divide(tp+tn, len(labels)), | |
| "sensitivity": divide(tp, tp+fn), "specificity": divide(tn, tn+fp), | |
| "precision": divide(tp, tp+fp), "tn": tn, "fp": fp, "fn": fn, "tp": tp} | |