kuechenpassagent / src /cv /predict.py
lederyou's picture
Upload folder using huggingface_hub
db662ea verified
Raw
History Blame Contribute Delete
2.36 kB
"""Inference wrapper around the persisted CV model."""
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from src.runtime import configure_runtime # noqa: E402
configure_runtime()
import torch
from PIL import Image
from torchvision import transforms
from src.config import CV_CLASSES_PATH, CV_MODEL_PATH # noqa: E402
from src.cv.train import IMAGENET_MEAN, IMAGENET_STD, build_model # noqa: E402
class DishClassifier:
def __init__(self, model_path: Path | None = None) -> None:
model_path = model_path or CV_MODEL_PATH
if not model_path.exists():
raise FileNotFoundError(
f"CV model missing at {model_path}. Run 'python -m src.cv.train'."
)
checkpoint = torch.load(model_path, map_location="cpu")
self.classes: list[str] = checkpoint["classes"]
self.model_name: str = checkpoint["model_name"]
# temperature scaling factor (set by src.cv.calibrate); 1.0 = uncalibrated
self.temperature: float = float(checkpoint.get("temperature") or 1.0)
self.model = build_model(self.model_name, len(self.classes))
self.model.load_state_dict(checkpoint["state_dict"])
self.model.eval()
self.tf = transforms.Compose(
[
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD),
]
)
@torch.no_grad()
def predict(self, image: Image.Image, topk: int = 3) -> list[dict[str, Any]]:
if image.mode != "RGB":
image = image.convert("RGB")
x = self.tf(image).unsqueeze(0)
logits = self.model(x)
if self.temperature and self.temperature > 0:
logits = logits / self.temperature
probs = torch.softmax(logits, dim=1).squeeze(0)
k = min(topk, len(self.classes))
top_probs, top_idx = probs.topk(k)
return [
{"label": self.classes[int(i)], "confidence": float(p)}
for p, i in zip(top_probs, top_idx)
]
def load_classes() -> list[str]:
if CV_CLASSES_PATH.exists():
return json.loads(CV_CLASSES_PATH.read_text())
return []