JobenTan commited on
Commit
a65c43b
·
verified ·
1 Parent(s): c1bac62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -76
app.py CHANGED
@@ -4,21 +4,22 @@ import torch.optim as optim
4
  from torchvision import transforms
5
  from torch.utils.data import Dataset, DataLoader
6
  from PIL import Image
7
- import segmentation_models_pytorch as smp
8
- from torchvision.models import densenet121
9
  from pymongo import MongoClient
10
  from sklearn.model_selection import train_test_split
11
  from sklearn.metrics import accuracy_score, f1_score
 
 
12
  import gradio as gr
 
13
 
14
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
 
16
- # MongoDB connection
17
  client = MongoClient("mongodb+srv://xcovidinsight:FYP25S108@cluster0.vqesy.mongodb.net/")
18
  db = client["covid_system"]
19
  collection = db["validated_xrays"]
20
 
21
- # UNet model
22
  class UNet(nn.Module):
23
  def __init__(self):
24
  super().__init__()
@@ -32,7 +33,6 @@ class UNet(nn.Module):
32
  def forward(self, x):
33
  return self.model(x)
34
 
35
- # Classifier model
36
  class MyClassifier(nn.Module):
37
  def __init__(self, num_classes=4):
38
  super().__init__()
@@ -44,67 +44,63 @@ class MyClassifier(nn.Module):
44
  def forward(self, x):
45
  return self.model(x)
46
 
47
- # Dataset class
48
- class LungXrayDataset(Dataset):
49
- def __init__(self, entries, transform, m1, device):
50
- self.entries = entries
51
- self.transform = transform
52
- self.m1 = m1
53
- self.device = device
54
- self.label_map = {label: i for i, label in enumerate(sorted(set(s["true_label"] for s in entries)))}
55
-
56
- def __len__(self):
57
- return len(self.entries)
58
-
59
- def __getitem__(self, idx):
60
- entry = self.entries[idx]
61
- image = Image.open(entry["image_path"]).convert("L").resize((256, 256))
62
- image_tensor = transforms.ToTensor()(image).unsqueeze(0).to(self.device)
63
-
64
- with torch.no_grad():
65
- mask = self.m1(image_tensor).sigmoid()
66
- mask = (mask > 0.5).float()
67
- masked = image_tensor * mask
68
-
69
- masked_rgb = masked.squeeze(0).repeat(3, 1, 1).cpu()
70
-
71
- if self.transform:
72
- masked_rgb = self.transform(masked_rgb)
73
-
74
- label = self.label_map[entry["true_label"]]
75
- return masked_rgb, label
76
-
77
- def evaluate(model, loader):
78
- model.eval()
79
- y_true, y_pred = [], []
80
- with torch.no_grad():
81
- for imgs, labels in loader:
82
- imgs = imgs.to(device)
83
- outputs = model(imgs)
84
- y_pred.extend(outputs.argmax(1).cpu().numpy())
85
- y_true.extend(labels.numpy())
86
- return {
87
- "accuracy": round(accuracy_score(y_true, y_pred), 4),
88
- "f1_macro": round(f1_score(y_true, y_pred, average="macro"), 4),
89
- }
90
 
91
  def trigger_incremental_train():
92
  samples = list(collection.find())
93
  samples = [s for s in samples if "image_path" in s and "true_label" in s]
94
 
95
- if len(samples) < 1:
96
- return "Not enough validated samples (need at least 100)."
97
 
98
- m1 = UNet().to(device)
99
  m1.load_state_dict(torch.load("Segmentation Model.pth", map_location=device))
100
- m1.eval()
101
 
102
- m2_old = MyClassifier().to(device)
103
  m2_old.load_state_dict(torch.load("Classification Model.pth", map_location=device))
104
- m2_old.eval()
105
 
106
- m2 = MyClassifier().to(device)
107
  m2.load_state_dict(torch.load("Classification Model.pth", map_location=device))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
  transform = transforms.Compose([
110
  transforms.Resize((224, 224)),
@@ -115,17 +111,17 @@ def trigger_incremental_train():
115
  samples, test_size=0.2, stratify=[s["true_label"] for s in samples], random_state=42
116
  )
117
 
118
- train_ds = LungXrayDataset(train_entries, transform, m1, device)
119
- val_ds = LungXrayDataset(val_entries, transform, m1, device)
120
-
121
  train_loader = DataLoader(train_ds, batch_size=16, shuffle=True)
122
  val_loader = DataLoader(val_ds, batch_size=16)
123
 
124
- optimizer = optim.Adam(m2.parameters(), lr=1e-4)
125
  criterion = nn.CrossEntropyLoss()
 
126
 
127
  for epoch in range(5):
128
  m2.train()
 
129
  for imgs, labels in train_loader:
130
  imgs, labels = imgs.to(device), labels.to(device)
131
  out = m2(imgs)
@@ -133,26 +129,41 @@ def trigger_incremental_train():
133
  optimizer.zero_grad()
134
  loss.backward()
135
  optimizer.step()
136
-
137
- eval_old = evaluate(m2_old, val_loader)
138
- eval_new = evaluate(m2, val_loader)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  torch.save(m2.state_dict(), "New Classification Model.pth")
141
 
142
  return {
143
- "Old Model": eval_old,
144
- "New Model": eval_new,
145
- "status": "Training completed successfully"
146
  }
147
 
148
- # Gradio interface
149
- demo = gr.Interface(
150
- fn=trigger_incremental_train,
151
- inputs=[],
152
- outputs="json",
153
- title="COVID X-ray Incremental Trainer",
154
- description="Trigger training on new validated X-ray samples from MongoDB"
155
- )
156
-
157
- if __name__ == "__main__":
158
- demo.launch()
 
4
  from torchvision import transforms
5
  from torch.utils.data import Dataset, DataLoader
6
  from PIL import Image
7
+ import requests
8
+ from io import BytesIO
9
  from pymongo import MongoClient
10
  from sklearn.model_selection import train_test_split
11
  from sklearn.metrics import accuracy_score, f1_score
12
+ import segmentation_models_pytorch as smp
13
+ from torchvision.models import densenet121
14
  import gradio as gr
15
+ import json
16
 
17
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
18
 
 
19
  client = MongoClient("mongodb+srv://xcovidinsight:FYP25S108@cluster0.vqesy.mongodb.net/")
20
  db = client["covid_system"]
21
  collection = db["validated_xrays"]
22
 
 
23
  class UNet(nn.Module):
24
  def __init__(self):
25
  super().__init__()
 
33
  def forward(self, x):
34
  return self.model(x)
35
 
 
36
  class MyClassifier(nn.Module):
37
  def __init__(self, num_classes=4):
38
  super().__init__()
 
44
  def forward(self, x):
45
  return self.model(x)
46
 
47
+ def fetch_image(url):
48
+ try:
49
+ response = requests.get(url)
50
+ image = Image.open(BytesIO(response.content)).convert("L").resize((256, 256))
51
+ return image
52
+ except Exception as e:
53
+ print(f"Failed to fetch image from {url}: {e}")
54
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  def trigger_incremental_train():
57
  samples = list(collection.find())
58
  samples = [s for s in samples if "image_path" in s and "true_label" in s]
59
 
60
+ if not samples or len(samples) < 100:
61
+ return {"error": "Not enough validated samples (minimum 100 required)."}
62
 
63
+ m1 = UNet()
64
  m1.load_state_dict(torch.load("Segmentation Model.pth", map_location=device))
65
+ m1.to(device).eval()
66
 
67
+ m2_old = MyClassifier()
68
  m2_old.load_state_dict(torch.load("Classification Model.pth", map_location=device))
69
+ m2_old.to(device).eval()
70
 
71
+ m2 = MyClassifier()
72
  m2.load_state_dict(torch.load("Classification Model.pth", map_location=device))
73
+ m2.to(device)
74
+
75
+ class LungXrayDataset(Dataset):
76
+ def __init__(self, entries, transform):
77
+ self.entries = entries
78
+ self.transform = transform
79
+ self.label_map = {label: i for i, label in enumerate(sorted(set(s["true_label"] for s in entries)))}
80
+
81
+ def __len__(self):
82
+ return len(self.entries)
83
+
84
+ def __getitem__(self, idx):
85
+ entry = self.entries[idx]
86
+ image = fetch_image(entry["image_path"])
87
+ if image is None:
88
+ raise RuntimeError(f"Image at {entry['image_path']} could not be loaded.")
89
+
90
+ image_tensor = transforms.ToTensor()(image).unsqueeze(0).to(device)
91
+
92
+ with torch.no_grad():
93
+ mask = m1(image_tensor).sigmoid()
94
+ mask = (mask > 0.5).float()
95
+ masked = image_tensor * mask
96
+
97
+ masked_rgb = masked.squeeze(0).repeat(3, 1, 1).cpu()
98
+
99
+ if self.transform:
100
+ masked_rgb = self.transform(masked_rgb)
101
+
102
+ label = self.label_map[entry["true_label"]]
103
+ return masked_rgb, label
104
 
105
  transform = transforms.Compose([
106
  transforms.Resize((224, 224)),
 
111
  samples, test_size=0.2, stratify=[s["true_label"] for s in samples], random_state=42
112
  )
113
 
114
+ train_ds = LungXrayDataset(train_entries, transform)
115
+ val_ds = LungXrayDataset(val_entries, transform)
 
116
  train_loader = DataLoader(train_ds, batch_size=16, shuffle=True)
117
  val_loader = DataLoader(val_ds, batch_size=16)
118
 
 
119
  criterion = nn.CrossEntropyLoss()
120
+ optimizer = optim.Adam(m2.parameters(), lr=1e-4)
121
 
122
  for epoch in range(5):
123
  m2.train()
124
+ total_loss, correct = 0, 0
125
  for imgs, labels in train_loader:
126
  imgs, labels = imgs.to(device), labels.to(device)
127
  out = m2(imgs)
 
129
  optimizer.zero_grad()
130
  loss.backward()
131
  optimizer.step()
132
+ total_loss += loss.item()
133
+ correct += (out.argmax(1) == labels).sum().item()
134
+ acc = correct / len(train_ds)
135
+ print(f"Epoch {epoch+1} ➤ Loss: {total_loss:.4f} | Accuracy: {acc:.4f}")
136
+
137
+ def evaluate(model, loader, version):
138
+ model.eval()
139
+ y_true, y_pred = [], []
140
+ with torch.no_grad():
141
+ for imgs, labels in loader:
142
+ imgs = imgs.to(device)
143
+ outputs = model(imgs)
144
+ y_pred.extend(outputs.argmax(1).cpu().numpy())
145
+ y_true.extend(labels.numpy())
146
+ return {
147
+ "version": version,
148
+ "accuracy": round(accuracy_score(y_true, y_pred), 4),
149
+ "f1_macro": round(f1_score(y_true, y_pred, average="macro"), 4),
150
+ }
151
+
152
+ eval_old = evaluate(m2_old, val_loader, "Old")
153
+ eval_new = evaluate(m2, val_loader, "New")
154
 
155
  torch.save(m2.state_dict(), "New Classification Model.pth")
156
 
157
  return {
158
+ "old_model": eval_old,
159
+ "new_model": eval_new
 
160
  }
161
 
162
+ with gr.Blocks() as demo:
163
+ with gr.Row():
164
+ train_button = gr.Button("Start Incremental Training")
165
+ output_json = gr.JSON(label="Training Result")
166
+
167
+ train_button.click(fn=trigger_incremental_train, inputs=[], outputs=output_json)
168
+
169
+ demo.launch(server_name="0.0.0.0", server_port=7860)