Spaces:
Sleeping
Sleeping
| import torch | |
| import torch.nn as nn | |
| import torchvision.models as models | |
| from fastapi import FastAPI, UploadFile, File | |
| from PIL import Image | |
| import io | |
| import torchvision.transforms as transforms | |
| # ========================= | |
| # APP | |
| # ========================= | |
| app = FastAPI(title="Alzheimer DATSCAN Ensemble API") | |
| # ========================= | |
| # DEVICE | |
| # ========================= | |
| DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| print("Using device:", DEVICE) | |
| # ========================= | |
| # CLASSES (VERY IMPORTANT - YOUR ORIGINAL SETUP) | |
| # ========================= | |
| CLASSES = [ | |
| "Mild Demented", | |
| "Moderate Demented", | |
| "Non Demented", | |
| "Very Mild Demented" | |
| ] | |
| # ========================= | |
| # IMAGE TRANSFORM | |
| # ========================= | |
| transform = transforms.Compose([ | |
| transforms.Resize((224, 224)), | |
| transforms.ToTensor(), | |
| transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) | |
| ]) | |
| # ========================= | |
| # MODEL BUILDER (MATCH TRAINING) | |
| # ========================= | |
| def build_model(version="121"): | |
| if version == "121": | |
| model = models.densenet121(weights=None) | |
| in_features = 1024 | |
| elif version == "169": | |
| model = models.densenet169(weights=None) | |
| in_features = 1664 | |
| else: | |
| model = models.densenet201(weights=None) | |
| in_features = 1920 | |
| model.classifier = nn.Sequential( | |
| nn.Dropout(0.4), | |
| nn.Linear(in_features, 4) | |
| ) | |
| return model | |
| # ========================= | |
| # SAFE MODEL LOADER (FIX ALL YOUR ERRORS) | |
| # ========================= | |
| def load_model(path, version): | |
| model = build_model(version) | |
| try: | |
| state = torch.load(path, map_location=DEVICE) | |
| # handle different checkpoint formats | |
| if isinstance(state, dict): | |
| if "state_dict" in state: | |
| state = state["state_dict"] | |
| elif "model_state_dict" in state: | |
| state = state["model_state_dict"] | |
| model.load_state_dict(state, strict=False) | |
| print(f"Loaded {path}") | |
| except Exception as e: | |
| print(f"Failed loading {path}: {e}") | |
| model.to(DEVICE) | |
| model.eval() | |
| return model | |
| # ========================= | |
| # LOAD MODELS | |
| # ========================= | |
| model_121 = load_model("alzheimers_densenet121.pth", "121") | |
| model_169 = load_model("alzheimers_densenet169.pth", "169") | |
| model_201 = load_model("alzheimers_densenet201.pth", "201") | |
| # ========================= | |
| # IMAGE PROCESSING | |
| # ========================= | |
| def process_image(image_bytes): | |
| img = Image.open(io.BytesIO(image_bytes)).convert("RGB") | |
| img = transform(img).unsqueeze(0).to(DEVICE) | |
| return img | |
| # ========================= | |
| # PREDICT FUNCTION (FIXED) | |
| # ========================= | |
| def predict(model, x): | |
| with torch.no_grad(): | |
| out = model(x) | |
| probs = torch.softmax(out, dim=1)[0] | |
| conf, cls = torch.max(probs, 0) | |
| return { | |
| "class_id": int(cls.item()), | |
| "class_name": CLASSES[int(cls.item())], | |
| "confidence": float(conf.item()), | |
| "probabilities": { | |
| CLASSES[i]: float(probs[i].item()) | |
| for i in range(len(CLASSES)) | |
| } | |
| } | |
| # ========================= | |
| # ROOT | |
| # ========================= | |
| def home(): | |
| return { | |
| "status": "running", | |
| "models": ["densenet121", "densenet169", "densenet201"], | |
| "classes": CLASSES, | |
| "endpoints": ["/predict/121", "/predict/169", "/predict/201", "/predict/ensemble"] | |
| } | |
| # ========================= | |
| # SINGLE MODEL ROUTES | |
| # ========================= | |
| async def predict_121(file: UploadFile = File(...)): | |
| img = process_image(await file.read()) | |
| return {"model": "121", **predict(model_121, img)} | |
| async def predict_169(file: UploadFile = File(...)): | |
| img = process_image(await file.read()) | |
| return {"model": "169", **predict(model_169, img)} | |
| async def predict_201(file: UploadFile = File(...)): | |
| img = process_image(await file.read()) | |
| return {"model": "201", **predict(model_201, img)} | |
| # ========================= | |
| # ENSEMBLE (FINAL FIXED VERSION) | |
| # ========================= | |
| async def ensemble(file: UploadFile = File(...)): | |
| img = process_image(await file.read()) | |
| r1 = predict(model_121, img) | |
| r2 = predict(model_169, img) | |
| r3 = predict(model_201, img) | |
| avg = {} | |
| for c in CLASSES: | |
| avg[c] = (r1["probabilities"][c] + | |
| r2["probabilities"][c] + | |
| r3["probabilities"][c]) / 3 | |
| final_class = max(avg, key=avg.get) | |
| return { | |
| "final_prediction": final_class, | |
| "final_confidence": avg[final_class], | |
| "individual_models": { | |
| "121": r1, | |
| "169": r2, | |
| "201": r3 | |
| }, | |
| "ensemble_probabilities": avg | |
| } |