Mikecode123 commited on
Commit
83238b7
·
verified ·
1 Parent(s): d4dcb72

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -43
app.py CHANGED
@@ -12,35 +12,36 @@ app = FastAPI(title="Alzheimer Ensemble API")
12
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
13
 
14
  # -----------------------------
15
- # LABELS (YOUR REAL ONES)
16
  # -----------------------------
17
  LABELS = [
18
  "Mild Demented",
19
  "Moderate Demented",
20
  "Non Demented",
21
- "Very Mild Demented"
22
  ]
23
 
24
  # -----------------------------
25
- # IMAGE PREPROCESSING
26
  # -----------------------------
27
  transform = transforms.Compose([
28
  transforms.Resize((224, 224)),
29
  transforms.ToTensor(),
30
- transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
31
  ])
32
 
33
  # -----------------------------
34
- # MODEL ARCHITECTURE (4-LAYER HEAD)
35
  # -----------------------------
36
  def build_model():
37
  model = models.densenet121(weights=None)
38
 
39
  model.classifier = nn.Sequential(
40
- nn.Linear(1024, 512),
 
41
  nn.ReLU(),
42
  nn.Dropout(0.3),
43
- nn.Linear(512, 4)
44
  )
45
 
46
  return model
@@ -52,68 +53,115 @@ def load_model(path):
52
  model = build_model()
53
  state = torch.load(path, map_location=DEVICE)
54
 
55
- # safe load for HF spaces
56
- model.load_state_dict(state, strict=False)
57
 
 
58
  model.to(DEVICE)
59
  model.eval()
60
  return model
61
 
62
  # -----------------------------
63
- # MODEL PATHS
64
  # -----------------------------
 
65
 
66
-
67
- model_121 = "alzheimers_densenet121.pth"
68
- model_169 = "alzheimers_densenet169.pth"
69
- model_201 = "alzheimers_densenet201.pth"
70
 
71
  # -----------------------------
72
- # SINGLE MODEL PREDICTION
73
  # -----------------------------
74
- def predict_single(model, image_tensor):
75
  with torch.no_grad():
76
  out = model(image_tensor)
77
- probs = torch.softmax(out, dim=1)
78
- return probs[0]
 
 
 
 
 
 
 
 
 
 
79
 
80
  # -----------------------------
81
- # ENSEMBLE
82
  # -----------------------------
83
- def ensemble_predict(image_bytes):
84
-
85
  image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
86
- image = transform(image).unsqueeze(0).to(DEVICE)
 
 
 
 
 
 
 
 
87
 
88
- p1 = predict_single(model_121, image)
89
- p2 = predict_single(model_169, image)
90
- p3 = predict_single(model_201, image)
 
91
 
92
- avg = (p1 + p2 + p3) / 3
 
 
 
93
 
94
- confidence, cls = torch.max(avg, dim=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  return {
97
- "prediction": LABELS[cls.item()],
98
- "class_id": cls.item(),
99
- "confidence": round(confidence.item() * 100, 2),
100
- "probabilities": {
101
- LABELS[i]: round(avg[i].item() * 100, 2)
102
- for i in range(len(LABELS))
103
- }
 
 
 
104
  }
105
 
106
  # -----------------------------
107
- # ROUTES
108
  # -----------------------------
109
  @app.get("/")
110
  def home():
111
  return {
112
- "message": "Alzheimer Ensemble API Running",
113
- "classes": LABELS
114
- }
115
-
116
- @app.post("/predict")
117
- async def predict(file: UploadFile = File(...)):
118
- image_bytes = await file.read()
119
- return ensemble_predict(image_bytes)
 
 
12
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
13
 
14
  # -----------------------------
15
+ # LABELS
16
  # -----------------------------
17
  LABELS = [
18
  "Mild Demented",
19
  "Moderate Demented",
20
  "Non Demented",
21
+ "Very Very Mild Demented"
22
  ]
23
 
24
  # -----------------------------
25
+ # TRANSFORM
26
  # -----------------------------
27
  transform = transforms.Compose([
28
  transforms.Resize((224, 224)),
29
  transforms.ToTensor(),
30
+ transforms.Normalize([0.5]*3, [0.5]*3)
31
  ])
32
 
33
  # -----------------------------
34
+ # MODEL BUILDER (MATCH YOUR TRAINING)
35
  # -----------------------------
36
  def build_model():
37
  model = models.densenet121(weights=None)
38
 
39
  model.classifier = nn.Sequential(
40
+ nn.Dropout(0.5),
41
+ nn.Linear(1024, 256),
42
  nn.ReLU(),
43
  nn.Dropout(0.3),
44
+ nn.Linear(256, 4)
45
  )
46
 
47
  return model
 
53
  model = build_model()
54
  state = torch.load(path, map_location=DEVICE)
55
 
56
+ if isinstance(state, dict) and "model_state_dict" in state:
57
+ state = state["model_state_dict"]
58
 
59
+ model.load_state_dict(state, strict=False)
60
  model.to(DEVICE)
61
  model.eval()
62
  return model
63
 
64
  # -----------------------------
65
+ # LOAD ALL MODELS
66
  # -----------------------------
67
+ BASE = Path("saved_models")
68
 
69
+ model_121 = load_model(BASE / "alzheimers_densenet121.pth")
70
+ model_169 = load_model(BASE / "alzheimers_densenet169.pth")
71
+ model_201 = load_model(BASE / "alzheimers_densenet201.pth")
 
72
 
73
  # -----------------------------
74
+ # SINGLE PREDICT FUNCTION
75
  # -----------------------------
76
+ def predict(model, image_tensor):
77
  with torch.no_grad():
78
  out = model(image_tensor)
79
+ probs = torch.softmax(out, dim=1)[0]
80
+ conf, cls = torch.max(probs, dim=0)
81
+
82
+ return {
83
+ "class_id": cls.item(),
84
+ "class_name": LABELS[cls.item()],
85
+ "confidence": float(conf.item()),
86
+ "probabilities": {
87
+ LABELS[i]: float(probs[i].item())
88
+ for i in range(len(LABELS))
89
+ }
90
+ }
91
 
92
  # -----------------------------
93
+ # IMAGE PROCESSING
94
  # -----------------------------
95
+ def process_image(image_bytes):
 
96
  image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
97
+ return transform(image).unsqueeze(0).to(DEVICE)
98
+
99
+ # -----------------------------
100
+ # INDIVIDUAL ENDPOINTS
101
+ # -----------------------------
102
+ @app.post("/predict/121")
103
+ async def predict_121(file: UploadFile = File(...)):
104
+ img = process_image(await file.read())
105
+ return {"model": "densenet121", **predict(model_121, img)}
106
 
107
+ @app.post("/predict/169")
108
+ async def predict_169(file: UploadFile = File(...)):
109
+ img = process_image(await file.read())
110
+ return {"model": "densenet169", **predict(model_169, img)}
111
 
112
+ @app.post("/predict/201")
113
+ async def predict_201(file: UploadFile = File(...)):
114
+ img = process_image(await file.read())
115
+ return {"model": "densenet201", **predict(model_201, img)}
116
 
117
+ # -----------------------------
118
+ # ENSEMBLE PREDICTION (UPGRADED)
119
+ # -----------------------------
120
+ @app.post("/predict/ensemble")
121
+ async def ensemble(file: UploadFile = File(...)):
122
+
123
+ img = process_image(await file.read())
124
+
125
+ r1 = predict(model_121, img)
126
+ r2 = predict(model_169, img)
127
+ r3 = predict(model_201, img)
128
+
129
+ # average probabilities
130
+ avg_probs = {}
131
+ for i in range(len(LABELS)):
132
+ avg_probs[LABELS[i]] = (
133
+ r1["probabilities"][LABELS[i]] +
134
+ r2["probabilities"][LABELS[i]] +
135
+ r3["probabilities"][LABELS[i]]
136
+ ) / 3
137
+
138
+ final_class = max(avg_probs, key=avg_probs.get)
139
 
140
  return {
141
+ "final_prediction": final_class,
142
+ "final_confidence": avg_probs[final_class],
143
+
144
+ "ensemble_breakdown": {
145
+ "model_121": r1,
146
+ "model_169": r2,
147
+ "model_201": r3
148
+ },
149
+
150
+ "averaged_probabilities": avg_probs
151
  }
152
 
153
  # -----------------------------
154
+ # ROOT
155
  # -----------------------------
156
  @app.get("/")
157
  def home():
158
  return {
159
+ "status": "running",
160
+ "models": ["121", "169", "201"],
161
+ "endpoints": [
162
+ "/predict/121",
163
+ "/predict/169",
164
+ "/predict/201",
165
+ "/predict/ensemble"
166
+ ]
167
+ }