Mikecode123 commited on
Commit
3d66b20
·
verified ·
1 Parent(s): da5cfde

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +159 -162
app.py CHANGED
@@ -1,196 +1,193 @@
 
 
 
1
  import torch
2
  import torch.nn as nn
3
- import torchvision.models as models
4
- from fastapi import FastAPI, UploadFile, File
5
- from PIL import Image
6
- import io
7
- import torchvision.transforms as transforms
8
-
9
- # =========================
10
- # APP INIT
11
- # =========================
12
- app = FastAPI(title="Alzheimer Ensemble API")
13
-
14
- # =========================
15
- # DEVICE
16
- # =========================
17
- DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
18
- print("Using device:", DEVICE)
19
-
20
- # =========================
21
- # CLASS LABELS (FIXED)
22
- # =========================
23
- CLASSES = [
24
- "Mild Demented",
25
- "Moderate Demented",
26
- "Non Demented",
27
- "Very Mild Demented"
28
  ]
29
 
30
- # =========================
31
- # IMAGE TRANSFORM
32
- # =========================
33
- transform = transforms.Compose([
34
- transforms.Resize((224, 224)),
35
- transforms.ToTensor(),
36
- transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
37
- ])
38
-
39
- # =========================
40
- # MODEL BUILDER
41
- # =========================
42
- def build_model(version="121"):
43
- if version == "121":
44
- model = models.densenet121(weights=None)
45
- in_features = 1024
46
- elif version == "169":
47
- model = models.densenet169(weights=None)
48
- in_features = 1664
49
- else:
50
- model = models.densenet201(weights=None)
51
- in_features = 1920
52
-
53
- model.classifier = nn.Sequential(
54
- nn.Dropout(0.4),
55
- nn.Linear(in_features, len(CLASSES))
56
- )
57
 
58
- return model
59
 
60
- # =========================
61
- # SAFE MODEL LOADER
62
- # =========================
63
- def load_model(path, version):
64
- model = build_model(version)
65
 
66
- try:
67
- checkpoint = torch.load(path, map_location=DEVICE)
 
 
68
 
69
- if isinstance(checkpoint, dict):
70
- if "state_dict" in checkpoint:
71
- checkpoint = checkpoint["state_dict"]
72
- elif "model_state_dict" in checkpoint:
73
- checkpoint = checkpoint["model_state_dict"]
74
 
75
- model.load_state_dict(checkpoint, strict=False)
76
- print(f"Loaded: {path}")
77
 
78
- except Exception as e:
79
- print(f"Failed loading {path}: {e}")
80
 
81
- model.to(DEVICE)
82
- model.eval()
83
- return model
84
 
85
- # =========================
86
  # LOAD MODELS
87
- # =========================
88
- model_121 = load_model("alzheimers_densenet121.pth", "121")
89
- model_169 = load_model("alzheimers_densenet169.pth", "169")
90
- model_201 = load_model("alzheimers_densenet201.pth", "201")
91
-
92
- # =========================
93
- # IMAGE PROCESSING
94
- # =========================
95
- def process_image(image_bytes):
96
- img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
97
- img = transform(img).unsqueeze(0).to(DEVICE)
98
- return img
99
-
100
- # =========================
101
- # PREDICTION FUNCTION (FIXED)
102
- # =========================
103
- def predict(model, x):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  with torch.no_grad():
105
- output = model(x)
106
- probs = torch.softmax(output, dim=1)[0]
107
-
108
- conf, cls = torch.max(probs, 0)
109
- cls = int(cls.item())
110
-
111
- return {
112
- "prediction": CLASSES[cls],
113
- "class_id": cls,
114
- "confidence": float(conf.item()),
115
- "probabilities": {
116
- CLASSES[i]: float(probs[i].item())
117
- for i in range(len(CLASSES))
118
- }
119
- }
120
 
121
- # =========================
122
- # ROOT ENDPOINT
123
- # =========================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  @app.get("/")
 
125
  def home():
 
126
  return {
127
- "status": "running",
128
- "models": ["121", "169", "201"],
129
- "classes": CLASSES,
130
- "endpoints": [
131
- "/predict/121",
132
- "/predict/169",
133
- "/predict/201",
134
- "/predict/ensemble"
135
- ]
136
  }
137
 
138
- # =========================
139
- # SINGLE MODEL PREDICTIONS
140
- # =========================
141
- @app.post("/predict/121")
142
- async def predict_121(file: UploadFile = File(...)):
143
- img = process_image(await file.read())
144
- result = predict(model_121, img)
145
- result["model"] = "121"
146
- return result
147
 
 
148
 
149
- @app.post("/predict/169")
150
- async def predict_169(file: UploadFile = File(...)):
151
- img = process_image(await file.read())
152
- result = predict(model_169, img)
153
- result["model"] = "169"
154
- return result
155
 
 
156
 
157
- @app.post("/predict/201")
158
- async def predict_201(file: UploadFile = File(...)):
159
- img = process_image(await file.read())
160
- result = predict(model_201, img)
161
- result["model"] = "201"
162
- return result
163
 
164
- # =========================
165
- # ENSEMBLE PREDICTION (FINAL FIXED)
166
- # =========================
167
- @app.post("/predict/ensemble")
168
- async def ensemble(file: UploadFile = File(...)):
169
 
170
- img = process_image(await file.read())
 
 
 
 
171
 
172
- r1 = predict(model_121, img)
173
- r2 = predict(model_169, img)
174
- r3 = predict(model_201, img)
175
 
176
- avg_probs = {}
177
 
178
- for c in CLASSES:
179
- avg_probs[c] = (
180
- r1["probabilities"][c] +
181
- r2["probabilities"][c] +
182
- r3["probabilities"][c]
183
- ) / 3
184
 
185
- final_class = max(avg_probs, key=avg_probs.get)
 
 
 
186
 
187
  return {
188
- "prediction": final_class,
189
- "confidence": avg_probs[final_class],
190
- "probabilities": avg_probs,
191
- "models": {
192
- "121": r1,
193
- "169": r2,
194
- "201": r3
 
 
 
195
  }
196
  }
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel
3
+
4
  import torch
5
  import torch.nn as nn
6
+ import numpy as np
7
+
8
+ app = FastAPI(
9
+ title="NeuroHealth EEG API",
10
+ version="1.0"
11
+ )
12
+
13
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
+
15
+ # =========================================================
16
+ # CLASS NAMES
17
+ # =========================================================
18
+
19
+ AD_CLASSES = [
20
+ "Alzheimer",
21
+ "FTD",
22
+ "Control"
 
 
 
 
 
 
 
 
23
  ]
24
 
25
+ PD_CLASSES = [
26
+ "Parkinson",
27
+ "Control"
28
+ ]
29
+
30
+ # =========================================================
31
+ # MODELS
32
+ # =========================================================
33
+
34
+ INPUT_DIM = 95
35
+
36
+ # =========================================================
37
+ # MLP MODEL
38
+ # =========================================================
39
+
40
+ class MLP(nn.Module):
41
+
42
+ def __init__(self, output_dim):
43
+
44
+ super().__init__()
 
 
 
 
 
 
 
45
 
46
+ self.network = nn.Sequential(
47
 
48
+ nn.Linear(INPUT_DIM, 512),
49
+ nn.BatchNorm1d(512),
50
+ nn.GELU(),
51
+ nn.Dropout(0.3),
 
52
 
53
+ nn.Linear(512, 256),
54
+ nn.BatchNorm1d(256),
55
+ nn.GELU(),
56
+ nn.Dropout(0.3),
57
 
58
+ nn.Linear(256, 128),
59
+ nn.BatchNorm1d(128),
60
+ nn.GELU(),
 
 
61
 
62
+ nn.Linear(128, output_dim)
63
+ )
64
 
65
+ def forward(self, x):
 
66
 
67
+ return self.network(x)
 
 
68
 
69
+ # =========================================================
70
  # LOAD MODELS
71
+ # =========================================================
72
+
73
+ ad_mlp = MLP(3).to(DEVICE)
74
+ pd_mlp = MLP(2).to(DEVICE)
75
+
76
+ ad_mlp.load_state_dict(
77
+ torch.load(
78
+ "AD_MLP.pt",
79
+ map_location=DEVICE
80
+ )
81
+ )
82
+
83
+ pd_mlp.load_state_dict(
84
+ torch.load(
85
+ "PD_MLP.pt",
86
+ map_location=DEVICE
87
+ )
88
+ )
89
+
90
+ ad_mlp.eval()
91
+ pd_mlp.eval()
92
+
93
+ print("Models Loaded Successfully")
94
+
95
+ # =========================================================
96
+ # REQUEST MODEL
97
+ # =========================================================
98
+
99
+ class EEGRequest(BaseModel):
100
+
101
+ features: list
102
+
103
+ # =========================================================
104
+ # UTILITY
105
+ # =========================================================
106
+
107
+ def predict_model(model, features):
108
+
109
+ x = torch.tensor(
110
+ features,
111
+ dtype=torch.float32
112
+ ).unsqueeze(0).to(DEVICE)
113
+
114
  with torch.no_grad():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ outputs = model(x)
117
+
118
+ probs = torch.softmax(
119
+ outputs,
120
+ dim=1
121
+ ).cpu().numpy()[0]
122
+
123
+ pred = int(np.argmax(probs))
124
+
125
+ confidence = float(probs[pred])
126
+
127
+ return pred, confidence, probs.tolist()
128
+
129
+ # =========================================================
130
+ # ROOT
131
+ # =========================================================
132
+
133
  @app.get("/")
134
+
135
  def home():
136
+
137
  return {
138
+ "message": "NeuroHealth EEG API Running"
 
 
 
 
 
 
 
 
139
  }
140
 
141
+ # =========================================================
142
+ # ALZHEIMER PREDICTION
143
+ # =========================================================
144
+
145
+ @app.post("/predict/alzheimer")
 
 
 
 
146
 
147
+ def predict_alzheimer(request: EEGRequest):
148
 
149
+ pred, confidence, probs = predict_model(
150
+ ad_mlp,
151
+ request.features
152
+ )
153
+
154
+ return {
155
 
156
+ "prediction": AD_CLASSES[pred],
157
 
158
+ "confidence": confidence,
 
 
 
 
 
159
 
160
+ "probabilities": {
 
 
 
 
161
 
162
+ AD_CLASSES[i]: float(probs[i])
163
+
164
+ for i in range(len(AD_CLASSES))
165
+ }
166
+ }
167
 
168
+ # =========================================================
169
+ # PARKINSON PREDICTION
170
+ # =========================================================
171
 
172
+ @app.post("/predict/parkinson")
173
 
174
+ def predict_parkinson(request: EEGRequest):
 
 
 
 
 
175
 
176
+ pred, confidence, probs = predict_model(
177
+ pd_mlp,
178
+ request.features
179
+ )
180
 
181
  return {
182
+
183
+ "prediction": PD_CLASSES[pred],
184
+
185
+ "confidence": confidence,
186
+
187
+ "probabilities": {
188
+
189
+ PD_CLASSES[i]: float(probs[i])
190
+
191
+ for i in range(len(PD_CLASSES))
192
  }
193
  }