Mikecode123 commited on
Commit
95fecec
·
verified ·
1 Parent(s): b18a7cf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -54
app.py CHANGED
@@ -6,7 +6,10 @@ from PIL import Image
6
  import io
7
  import torchvision.transforms as transforms
8
 
9
- app = FastAPI(title="Parkinson DATSCAN Ensemble API")
 
 
 
10
 
11
  # =========================
12
  # DEVICE
@@ -15,9 +18,14 @@ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
  print("Using device:", DEVICE)
16
 
17
  # =========================
18
- # LABELS
19
  # =========================
20
- LABELS = ["Control", "Prodromal", "Parkinsons"]
 
 
 
 
 
21
 
22
  # =========================
23
  # IMAGE TRANSFORM
@@ -25,43 +33,47 @@ LABELS = ["Control", "Prodromal", "Parkinsons"]
25
  transform = transforms.Compose([
26
  transforms.Resize((224, 224)),
27
  transforms.ToTensor(),
28
- transforms.Normalize([0.5]*3, [0.5]*3)
29
  ])
30
 
31
  # =========================
32
- # MODEL BUILDER
33
  # =========================
34
- def build_densenet(name="121"):
35
- if name == "121":
36
  model = models.densenet121(weights=None)
37
- in_f = 1024
38
- elif name == "169":
39
  model = models.densenet169(weights=None)
40
- in_f = 1664
41
  else:
42
  model = models.densenet201(weights=None)
43
- in_f = 1920
 
 
 
 
 
44
 
45
- model.classifier = nn.Linear(in_f, len(LABELS))
46
  return model
47
 
48
  # =========================
49
- # SAFE LOADER (IMPORTANT FIX)
50
  # =========================
51
- def load_model(path, name):
52
- model = build_densenet(name)
53
 
54
  try:
55
- checkpoint = torch.load(path, map_location=DEVICE)
56
 
57
- # handle dict checkpoint formats
58
- if isinstance(checkpoint, dict):
59
- if "state_dict" in checkpoint:
60
- checkpoint = checkpoint["state_dict"]
61
- elif "model_state_dict" in checkpoint:
62
- checkpoint = checkpoint["model_state_dict"]
63
 
64
- model.load_state_dict(checkpoint, strict=False)
65
 
66
  print(f"Loaded {path}")
67
 
@@ -75,35 +87,34 @@ def load_model(path, name):
75
  # =========================
76
  # LOAD MODELS
77
  # =========================
78
- model_121 = load_model("densenet121.pth", "121")
79
- model_169 = load_model("densenet169.pth", "169")
80
- model_201 = load_model("densenet201.pth", "201")
81
 
82
  # =========================
83
  # IMAGE PROCESSING
84
  # =========================
85
  def process_image(image_bytes):
86
- image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
87
- tensor = transform(image).unsqueeze(0).to(DEVICE)
88
- return tensor
89
 
90
  # =========================
91
- # PREDICTION FUNCTION (FIXED)
92
  # =========================
93
- def predict(model, image_tensor):
94
  with torch.no_grad():
95
- out = model(image_tensor)
96
  probs = torch.softmax(out, dim=1)[0]
97
-
98
- conf, cls = torch.max(probs, dim=0)
99
 
100
  return {
101
  "class_id": int(cls.item()),
102
- "class_name": LABELS[int(cls.item())],
103
  "confidence": float(conf.item()),
104
  "probabilities": {
105
- LABELS[i]: float(probs[i].item())
106
- for i in range(len(LABELS))
107
  }
108
  }
109
 
@@ -114,30 +125,31 @@ def predict(model, image_tensor):
114
  def home():
115
  return {
116
  "status": "running",
117
- "models": ["121", "169", "201"],
 
118
  "endpoints": ["/predict/121", "/predict/169", "/predict/201", "/predict/ensemble"]
119
  }
120
 
121
  # =========================
122
- # ENDPOINTS
123
  # =========================
124
  @app.post("/predict/121")
125
  async def predict_121(file: UploadFile = File(...)):
126
  img = process_image(await file.read())
127
- return {"model": "densenet121", **predict(model_121, img)}
128
 
129
  @app.post("/predict/169")
130
  async def predict_169(file: UploadFile = File(...)):
131
  img = process_image(await file.read())
132
- return {"model": "densenet169", **predict(model_169, img)}
133
 
134
  @app.post("/predict/201")
135
  async def predict_201(file: UploadFile = File(...)):
136
  img = process_image(await file.read())
137
- return {"model": "densenet201", **predict(model_201, img)}
138
 
139
  # =========================
140
- # ENSEMBLE (FIXED)
141
  # =========================
142
  @app.post("/predict/ensemble")
143
  async def ensemble(file: UploadFile = File(...)):
@@ -148,24 +160,22 @@ async def ensemble(file: UploadFile = File(...)):
148
  r2 = predict(model_169, img)
149
  r3 = predict(model_201, img)
150
 
151
- avg_probs = {}
152
 
153
- for i in range(len(LABELS)):
154
- avg_probs[LABELS[i]] = (
155
- r1["probabilities"][LABELS[i]] +
156
- r2["probabilities"][LABELS[i]] +
157
- r3["probabilities"][LABELS[i]]
158
- ) / 3
159
 
160
- final_class = max(avg_probs, key=avg_probs.get)
161
 
162
  return {
163
  "final_prediction": final_class,
164
- "final_confidence": avg_probs[final_class],
165
- "models": {
166
  "121": r1,
167
  "169": r2,
168
  "201": r3
169
  },
170
- "averaged_probabilities": avg_probs
171
  }
 
6
  import io
7
  import torchvision.transforms as transforms
8
 
9
+ # =========================
10
+ # APP
11
+ # =========================
12
+ app = FastAPI(title="Alzheimer DATSCAN Ensemble API")
13
 
14
  # =========================
15
  # DEVICE
 
18
  print("Using device:", DEVICE)
19
 
20
  # =========================
21
+ # CLASSES (VERY IMPORTANT - YOUR ORIGINAL SETUP)
22
  # =========================
23
+ CLASSES = [
24
+ "Mild Demented",
25
+ "Moderate Demented",
26
+ "Non Demented",
27
+ "Very Mild Demented"
28
+ ]
29
 
30
  # =========================
31
  # IMAGE TRANSFORM
 
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 (MATCH TRAINING)
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, 4)
56
+ )
57
 
 
58
  return model
59
 
60
  # =========================
61
+ # SAFE MODEL LOADER (FIX ALL YOUR ERRORS)
62
  # =========================
63
+ def load_model(path, version):
64
+ model = build_model(version)
65
 
66
  try:
67
+ state = torch.load(path, map_location=DEVICE)
68
 
69
+ # handle different checkpoint formats
70
+ if isinstance(state, dict):
71
+ if "state_dict" in state:
72
+ state = state["state_dict"]
73
+ elif "model_state_dict" in state:
74
+ state = state["model_state_dict"]
75
 
76
+ model.load_state_dict(state, strict=False)
77
 
78
  print(f"Loaded {path}")
79
 
 
87
  # =========================
88
  # LOAD MODELS
89
  # =========================
90
+ model_121 = load_model("alzheimers_densenet121.pth", "121")
91
+ model_169 = load_model("alzheimers_densenet169.pth", "169")
92
+ model_201 = load_model("alzheimers_densenet201.pth", "201")
93
 
94
  # =========================
95
  # IMAGE PROCESSING
96
  # =========================
97
  def process_image(image_bytes):
98
+ img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
99
+ img = transform(img).unsqueeze(0).to(DEVICE)
100
+ return img
101
 
102
  # =========================
103
+ # PREDICT FUNCTION (FIXED)
104
  # =========================
105
+ def predict(model, x):
106
  with torch.no_grad():
107
+ out = model(x)
108
  probs = torch.softmax(out, dim=1)[0]
109
+ conf, cls = torch.max(probs, 0)
 
110
 
111
  return {
112
  "class_id": int(cls.item()),
113
+ "class_name": CLASSES[int(cls.item())],
114
  "confidence": float(conf.item()),
115
  "probabilities": {
116
+ CLASSES[i]: float(probs[i].item())
117
+ for i in range(len(CLASSES))
118
  }
119
  }
120
 
 
125
  def home():
126
  return {
127
  "status": "running",
128
+ "models": ["densenet121", "densenet169", "densenet201"],
129
+ "classes": CLASSES,
130
  "endpoints": ["/predict/121", "/predict/169", "/predict/201", "/predict/ensemble"]
131
  }
132
 
133
  # =========================
134
+ # SINGLE MODEL ROUTES
135
  # =========================
136
  @app.post("/predict/121")
137
  async def predict_121(file: UploadFile = File(...)):
138
  img = process_image(await file.read())
139
+ return {"model": "121", **predict(model_121, img)}
140
 
141
  @app.post("/predict/169")
142
  async def predict_169(file: UploadFile = File(...)):
143
  img = process_image(await file.read())
144
+ return {"model": "169", **predict(model_169, img)}
145
 
146
  @app.post("/predict/201")
147
  async def predict_201(file: UploadFile = File(...)):
148
  img = process_image(await file.read())
149
+ return {"model": "201", **predict(model_201, img)}
150
 
151
  # =========================
152
+ # ENSEMBLE (FINAL FIXED VERSION)
153
  # =========================
154
  @app.post("/predict/ensemble")
155
  async def ensemble(file: UploadFile = File(...)):
 
160
  r2 = predict(model_169, img)
161
  r3 = predict(model_201, img)
162
 
163
+ avg = {}
164
 
165
+ for c in CLASSES:
166
+ avg[c] = (r1["probabilities"][c] +
167
+ r2["probabilities"][c] +
168
+ r3["probabilities"][c]) / 3
 
 
169
 
170
+ final_class = max(avg, key=avg.get)
171
 
172
  return {
173
  "final_prediction": final_class,
174
+ "final_confidence": avg[final_class],
175
+ "individual_models": {
176
  "121": r1,
177
  "169": r2,
178
  "201": r3
179
  },
180
+ "ensemble_probabilities": avg
181
  }