JobenTan commited on
Commit
a8a0987
·
verified ·
1 Parent(s): ff702b5

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -0
app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ 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__()
25
+ self.model = smp.Unet(
26
+ encoder_name="resnet34",
27
+ encoder_weights=None,
28
+ in_channels=1,
29
+ classes=1
30
+ )
31
+
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__()
39
+ base_model = densenet121(weights=None)
40
+ in_features = base_model.classifier.in_features
41
+ base_model.classifier = nn.Linear(in_features, num_classes)
42
+ self.model = base_model
43
+
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) < 100:
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)),
111
+ transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)
112
+ ])
113
+
114
+ train_entries, val_entries = train_test_split(
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)
132
+ loss = criterion(out, labels)
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
+ }
146
+
147
+ # Gradio interface
148
+ demo = gr.Interface(
149
+ fn=trigger_incremental_train,
150
+ inputs=[],
151
+ outputs="json",
152
+ title="COVID X-ray Incremental Trainer",
153
+ description="Trigger training on new validated X-ray samples from MongoDB"
154
+ )
155
+
156
+ if __name__ == "__main__":
157
+ demo.launch()