Spaces:
Running on Zero
Running on Zero
Download src/drdetect/fusion/embedding.py from adarshcod30/drdetect-dr-screening: direct link, hf CLI and curl.
- Browser
- Download file 3.46 kB
-
https://huggingface.co/spaces/adarshcod30/drdetect-dr-screening/resolve/main/src/drdetect/fusion/embedding.py
- Command line
-
hf download hf://spaces/adarshcod30/drdetect-dr-screening/src/drdetect/fusion/embedding.py
-
curl -L -o embedding.py https://huggingface.co/spaces/adarshcod30/drdetect-dr-screening/resolve/main/src/drdetect/fusion/embedding.py
3.46 kB
| """CNN embedding extraction from the canonical grading checkpoint, for | |
| Phase 5's fusion head. | |
| Uses `baseline_effb0_512_fold0` (QWK 0.8930), not the 1024px checkpoint -- | |
| Phase 3's own conclusion was "the baseline stands" (docs/07_PHASE3_RESULTS.md), | |
| and Result 14 retracted the 1024px checkpoint's causal story (its edge over | |
| the baseline is now suspected to be a backend artifact, not resolution). | |
| Building the fusion head on a result the project already walked back would | |
| undercut that finding. | |
| Reuses the checkpoint-loading convention scripts/evaluate.py and | |
| scripts/calibrate.py already established (strip the Lightning `model.` | |
| prefix, load into a bare `FundusGrader`) rather than inventing a new one. | |
| **Images come from the Ben-Graham-preprocessed cache | |
| (`data/processed/aptos_512/`), not the raw APTOS files** -- unlike | |
| `drdetect.fusion.features`, which deliberately uses raw images because the | |
| Phase 4 lesion models were trained on IDRiD's un-enhanced originals. The | |
| grading model was trained on the preprocessed cache, so its embedding is | |
| only meaningful computed on the same distribution it actually learned from. | |
| Two different "the right image for this model" answers for two different | |
| models feeding the same fusion vector -- not an inconsistency. | |
| """ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| import torch | |
| from drdetect.data.dataset import build_transforms | |
| from drdetect.grading.model import FundusGrader, build_model | |
| __all__ = ["load_grading_backbone", "extract_cnn_embedding", "EMBEDDING_DIM"] | |
| EMBEDDING_DIM = 1280 # efficientnet_b0's pre-classifier feature width | |
| def load_grading_backbone( | |
| checkpoint: str, *, backbone: str = "efficientnet_b0", device: torch.device | |
| ) -> FundusGrader: | |
| model = build_model(backbone, num_outputs=5, pretrained=False, freeze_bn=True) | |
| ckpt = torch.load(checkpoint, map_location="cpu", weights_only=False) | |
| state = ckpt.get("state_dict", ckpt) | |
| state = {k.removeprefix("model."): v for k, v in state.items() if k.startswith("model.")} | |
| model.load_state_dict(state) | |
| return model.to(device).eval() | |
| def extract_cnn_embedding( | |
| image_path: str | Path, model: FundusGrader, device: torch.device, *, size: int = 512 | |
| ) -> torch.Tensor: | |
| """The 1280-d pre-classifier pooled feature (`forward_head(..., | |
| pre_logits=True)`), not the 5-way logits `model(x)` would give.""" | |
| embedding, _logits = extract_cnn_embedding_and_logits(image_path, model, device, size=size) | |
| return embedding | |
| def extract_cnn_embedding_and_logits( | |
| image_path: str | Path, model: FundusGrader, device: torch.device, *, size: int = 512 | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| """Both the fusion head's input (the embedding) and the baseline | |
| grading-only prediction (the 5-way logits) from one forward pass -- | |
| used when comparing fusion against grading-alone on the same images, so | |
| the baseline's own prediction doesn't need a second pass over the model.""" | |
| import cv2 | |
| transform = build_transforms(size, train=False) | |
| image = cv2.cvtColor(cv2.imread(str(image_path), cv2.IMREAD_COLOR), cv2.COLOR_BGR2RGB) | |
| tensor = transform(image=image)["image"].unsqueeze(0).to(device) | |
| with torch.no_grad(): | |
| features = model.backbone.forward_features(tensor) | |
| embedding = model.backbone.forward_head(features, pre_logits=True) | |
| logits = model.backbone.forward_head(features) | |
| return embedding.squeeze(0).cpu(), logits.squeeze(0).cpu() | |