JobenTan commited on
Commit
49d3f8d
·
verified ·
1 Parent(s): e822203

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -23
app.py CHANGED
@@ -13,14 +13,9 @@ import segmentation_models_pytorch as smp
13
  from torchvision.models import densenet121, DenseNet121_Weights
14
  import gradio as gr
15
  import os
16
- import torch
17
- import gradio as gr
18
- from PIL import Image
19
- import numpy as np
20
  import albumentations as A
21
  from albumentations.pytorch import ToTensorV2
22
  from torchvision import transforms
23
- import torch.nn as nn
24
  import cv2
25
  import matplotlib.cm as cm
26
 
@@ -47,7 +42,6 @@ m2.classifier = nn.Linear(1024, 4)
47
  m2.load_state_dict(torch.load("Classification_Model.pth", map_location=device))
48
  m2.eval().to(device)
49
 
50
- classes = ["COVID", "Lung_Opacity", "Normal", "Viral Pneumonia"]
51
 
52
  unet_transform = A.Compose([
53
  A.Resize(256, 256),
@@ -119,24 +113,23 @@ gradio_ui = gr.Interface(
119
  app = gr.mount_gradio_app(app, gradio_ui, path="/")
120
 
121
  # ================== TRAINING API ==================
 
122
  class LungXrayDataset(Dataset):
123
- def __init__(self, entries, transform, unet):
124
  self.entries = entries
125
  self.transform = transform
126
  self.label_map = {label: i for i, label in enumerate(sorted(set(s["true_label"] for s in entries)))}
127
- self.unet = unet
128
 
129
  def __len__(self):
130
  return len(self.entries)
131
 
132
  def __getitem__(self, idx):
133
  entry = self.entries[idx]
134
- response = requests.get(entry["image_path"])
135
- image = Image.open(BytesIO(response.content)).convert("L").resize((256, 256))
136
-
137
  image_tensor = transforms.ToTensor()(image).unsqueeze(0).to(device)
 
138
  with torch.no_grad():
139
- mask = self.unet(image_tensor).sigmoid()
140
  mask = (mask > 0.5).float()
141
  masked = image_tensor * mask
142
 
@@ -155,28 +148,37 @@ async def trigger_train(request: Request):
155
 
156
  if not samples or len(samples) < 100:
157
  return {"error": "Not enough validated samples (minimum 100 required)."}
158
-
159
- m2_new = MyClassifier()
160
- m2_new.load_state_dict(torch.load("weights/Classification_Model.pth", map_location=device))
 
 
 
 
 
 
 
161
  m2_new.to(device)
162
 
163
  transform = transforms.Compose([
164
  transforms.Resize((224, 224)),
165
- transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)
 
166
  ])
167
 
168
  train_entries, val_entries = train_test_split(
169
  samples, test_size=0.2, stratify=[s["true_label"] for s in samples], random_state=42
170
  )
171
 
172
- train_ds = LungXrayDataset(train_entries, transform, m1)
173
- val_ds = LungXrayDataset(val_entries, transform, m1)
174
  train_loader = DataLoader(train_ds, batch_size=16, shuffle=True)
175
  val_loader = DataLoader(val_ds, batch_size=16)
176
 
177
  criterion = nn.CrossEntropyLoss()
178
  optimizer = optim.Adam(m2_new.parameters(), lr=1e-4)
179
 
 
180
  for epoch in range(5):
181
  m2_new.train()
182
  for imgs, labels in train_loader:
@@ -200,14 +202,25 @@ async def trigger_train(request: Request):
200
  "accuracy": round(accuracy_score(y_true, y_pred), 4),
201
  "f1_macro": round(f1_score(y_true, y_pred, average="macro"), 4),
202
  }
203
-
204
  eval_old = evaluate(m2, val_loader)
205
  eval_new = evaluate(m2_new, val_loader)
206
-
207
- torch.save(m2_new.state_dict(), "weights/Classification_Model.pth")
208
-
 
 
 
 
 
 
209
  return {
210
  "old_model": eval_old,
211
  "new_model": eval_new,
212
- "updated_model_path": "weights/Classification_Model.pth"
 
213
  }
 
 
 
 
 
13
  from torchvision.models import densenet121, DenseNet121_Weights
14
  import gradio as gr
15
  import os
 
 
 
 
16
  import albumentations as A
17
  from albumentations.pytorch import ToTensorV2
18
  from torchvision import transforms
 
19
  import cv2
20
  import matplotlib.cm as cm
21
 
 
42
  m2.load_state_dict(torch.load("Classification_Model.pth", map_location=device))
43
  m2.eval().to(device)
44
 
 
45
 
46
  unet_transform = A.Compose([
47
  A.Resize(256, 256),
 
113
  app = gr.mount_gradio_app(app, gradio_ui, path="/")
114
 
115
  # ================== TRAINING API ==================
116
+ # Data prepare
117
  class LungXrayDataset(Dataset):
118
+ def __init__(self, entries, transform):
119
  self.entries = entries
120
  self.transform = transform
121
  self.label_map = {label: i for i, label in enumerate(sorted(set(s["true_label"] for s in entries)))}
 
122
 
123
  def __len__(self):
124
  return len(self.entries)
125
 
126
  def __getitem__(self, idx):
127
  entry = self.entries[idx]
128
+ image = Image.open(entry["image_path"]).convert("L").resize((256, 256))
 
 
129
  image_tensor = transforms.ToTensor()(image).unsqueeze(0).to(device)
130
+
131
  with torch.no_grad():
132
+ mask = m1(image_tensor).sigmoid()
133
  mask = (mask > 0.5).float()
134
  masked = image_tensor * mask
135
 
 
148
 
149
  if not samples or len(samples) < 100:
150
  return {"error": "Not enough validated samples (minimum 100 required)."}
151
+
152
+ # Classification Model
153
+ m2 = densenet121(weights=DenseNet121_Weights.IMAGENET1K_V1)
154
+ m2.classifier = nn.Linear(1024, 4)
155
+ m2.load_state_dict(torch.load("Classification_Model.pth", map_location=device))
156
+ m2.eval().to(device)
157
+
158
+ m2_new = densenet121(weights=DenseNet121_Weights.IMAGENET1K_V1)
159
+ m2_new.classifier = nn.Linear(1024, 4)
160
+ m2_new.load_state_dict(torch.load("Classification_Model.pth", map_location=device))
161
  m2_new.to(device)
162
 
163
  transform = transforms.Compose([
164
  transforms.Resize((224, 224)),
165
+ transforms.ToTensor(),
166
+ transforms.Normalize(mean=[0.41]*3, std=[0.16]*3)
167
  ])
168
 
169
  train_entries, val_entries = train_test_split(
170
  samples, test_size=0.2, stratify=[s["true_label"] for s in samples], random_state=42
171
  )
172
 
173
+ train_ds = LungXrayDataset(train_entries, transform)
174
+ val_ds = LungXrayDataset(val_entries, transform)
175
  train_loader = DataLoader(train_ds, batch_size=16, shuffle=True)
176
  val_loader = DataLoader(val_ds, batch_size=16)
177
 
178
  criterion = nn.CrossEntropyLoss()
179
  optimizer = optim.Adam(m2_new.parameters(), lr=1e-4)
180
 
181
+ print(f"Training triggered with {len(samples)} samples by API")
182
  for epoch in range(5):
183
  m2_new.train()
184
  for imgs, labels in train_loader:
 
202
  "accuracy": round(accuracy_score(y_true, y_pred), 4),
203
  "f1_macro": round(f1_score(y_true, y_pred, average="macro"), 4),
204
  }
205
+
206
  eval_old = evaluate(m2, val_loader)
207
  eval_new = evaluate(m2_new, val_loader)
208
+
209
+ if eval_new["f1_macro"] > eval_old["f1_macro"]:
210
+ torch.save(m2_new.state_dict(), "Classification_Model.pth")
211
+ model_used = "new"
212
+ print("[NOTE] New model used and saved based on higher F1 score.")
213
+ else:
214
+ model_used = "old"
215
+ print("[NOTE] Old model retained (new model did not improve F1 score).")
216
+
217
  return {
218
  "old_model": eval_old,
219
  "new_model": eval_new,
220
+ "model_used": model_used,
221
+ "updated_model_path": "Classification_Model.pth" if model_used == "new" else "unchanged"
222
  }
223
+
224
+ if __name__ == "__main__":
225
+ import uvicorn
226
+ uvicorn.run(app, host="0.0.0.0", port=7860)