Spaces:
Runtime error
Runtime error
Update
Browse files- __pycache__/augment.cpython-311.pyc +0 -0
- __pycache__/configs.cpython-310.pyc +0 -0
- __pycache__/configs.cpython-311.pyc +0 -0
- __pycache__/data_loader.cpython-310.pyc +0 -0
- __pycache__/data_loader.cpython-311.pyc +0 -0
- __pycache__/ensemble.cpython-311.pyc +0 -0
- __pycache__/extract.cpython-310.pyc +0 -0
- __pycache__/lime_eval.cpython-310.pyc +0 -0
- __pycache__/models.cpython-310.pyc +0 -0
- __pycache__/models.cpython-311.pyc +0 -0
- __pycache__/predict.cpython-310.pyc +0 -0
- __pycache__/shap.cpython-310.pyc +0 -0
- __pycache__/swa.cpython-311.pyc +0 -0
- __pycache__/weight_averaging.cpython-311.pyc +0 -0
- app.py +7 -40
- compute_mean_std.py +45 -0
- configs.py +4 -269
- convert.py +0 -17
- data-splitting.py β data_splitting.py +0 -0
- ensemble.py +0 -249
- eval.py +0 -190
- eval_orig.py β evaluate.py +4 -11
- extract-ensemble.py +0 -110
- extract.py β extract_gradcam.py +50 -31
- lime_eval.py β extract_lime.py +16 -9
- genetric_algorithm.py +173 -176
- lazy_predict.py +0 -60
- lrp-eval.py +0 -16
- models.py +16 -63
- tuning.py β optuna_unused.py +0 -0
- plot-gradcam.py +0 -65
- plot_structure.py +20 -0
- plot_training_metrics.py +36 -0
- requirements.txt +0 -0
- shap_eval.py +0 -37
- test.py +0 -223
- test-speed.py β test_speed.py +19 -16
- train-svm.py +0 -101
- weight_averaging.py +0 -235
__pycache__/augment.cpython-311.pyc
ADDED
Binary file (3.71 kB). View file
|
|
__pycache__/configs.cpython-310.pyc
ADDED
Binary file (1.94 kB). View file
|
|
__pycache__/configs.cpython-311.pyc
ADDED
Binary file (17.2 kB). View file
|
|
__pycache__/data_loader.cpython-310.pyc
ADDED
Binary file (1.22 kB). View file
|
|
__pycache__/data_loader.cpython-311.pyc
ADDED
Binary file (1.97 kB). View file
|
|
__pycache__/ensemble.cpython-311.pyc
ADDED
Binary file (12.6 kB). View file
|
|
__pycache__/extract.cpython-310.pyc
ADDED
Binary file (1.95 kB). View file
|
|
__pycache__/lime_eval.cpython-310.pyc
ADDED
Binary file (2.15 kB). View file
|
|
__pycache__/models.cpython-310.pyc
ADDED
Binary file (1.14 kB). View file
|
|
__pycache__/models.cpython-311.pyc
ADDED
Binary file (4.38 kB). View file
|
|
__pycache__/predict.cpython-310.pyc
ADDED
Binary file (1.55 kB). View file
|
|
__pycache__/shap.cpython-310.pyc
ADDED
Binary file (2.85 kB). View file
|
|
__pycache__/swa.cpython-311.pyc
ADDED
Binary file (18.5 kB). View file
|
|
__pycache__/weight_averaging.cpython-311.pyc
ADDED
Binary file (9.65 kB). View file
|
|
app.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import gradio as gr
|
2 |
import predict as predict
|
3 |
-
import
|
4 |
-
import
|
5 |
|
6 |
|
7 |
def upload_file(files):
|
@@ -24,33 +24,17 @@ def process_file(
|
|
24 |
result.append(f"{class_label}: {class_prob}%")
|
25 |
result = result[:4]
|
26 |
if gradcam_toggle == True:
|
27 |
-
cam =
|
28 |
result.append("gradcam.jpg")
|
29 |
else:
|
30 |
result.append(None)
|
31 |
if lime_toggle == True:
|
32 |
-
lime =
|
33 |
result.append("lime.jpg")
|
34 |
else:
|
35 |
result.append(None)
|
36 |
return result
|
37 |
-
|
38 |
-
# sorted_classes = predict.predict_image(upload_filepath)
|
39 |
-
# for class_label, class_prob in sorted_classes:
|
40 |
-
# class_prob = class_prob.item().__round__(2)
|
41 |
-
# result.append(f"{class_label}: {class_prob}%")
|
42 |
-
# result = result[:4]
|
43 |
-
# if gradcam_toggle == 1:
|
44 |
-
# cam = extract.extract_gradcam(upload_filepath, save_path="gradcam.jpg")
|
45 |
-
# result.append("gradcam.jpg")
|
46 |
-
# if lime_toggle == 1:
|
47 |
-
# lime = lime_eval.generate_lime(upload_filepath, save_path="lime.jpg")
|
48 |
-
# result.append("lime.jpg")
|
49 |
-
# return result
|
50 |
-
|
51 |
-
|
52 |
-
# Prerun to innitialize the model
|
53 |
-
# process_file(None, r"data\test\Task 1\Dystonia\0c08d2ea-8e1c-4ac6-92db-a752388b30cf.png")
|
54 |
|
55 |
css = """
|
56 |
.block {
|
@@ -108,21 +92,10 @@ with block as demo:
|
|
108 |
with gr.Column():
|
109 |
gr.Label("SpiralSense", elem_id="title-label", show_label=False)
|
110 |
gr.Label(
|
111 |
-
"
|
112 |
elem_id="desc-label",
|
113 |
-
show_label=False
|
114 |
)
|
115 |
-
# gr.Markdown(
|
116 |
-
# """
|
117 |
-
# <h1 style="text-align: center;">SpiralSense</h1>
|
118 |
-
# <h4 style="text-align: center;">Cost-Effective, Portable And Stressless Spiral Drawing Analysing Web Application for Early Detection of Multiple Neurological Disorders with 96% Accuracy</h4>
|
119 |
-
# """
|
120 |
-
# )
|
121 |
-
# gr.Markdown(
|
122 |
-
# """
|
123 |
-
# <h4 style="text-align: center;">------------------------------------------</h4>
|
124 |
-
# """
|
125 |
-
# )
|
126 |
with gr.Row():
|
127 |
image_input = gr.Image(
|
128 |
type="filepath",
|
@@ -147,11 +120,6 @@ with block as demo:
|
|
147 |
with gr.Row():
|
148 |
submit_button = gr.Button(value="Submit")
|
149 |
gr.Markdown("<br>")
|
150 |
-
# cancel_button = gr.Button(value="Cancel")
|
151 |
-
# theme="gradio/soft",
|
152 |
-
# fn=process_file,
|
153 |
-
# title="HANDETECT",
|
154 |
-
# outputs=[
|
155 |
with gr.Row():
|
156 |
prob1_textbox = gr.outputs.Textbox(label="Probability 1")
|
157 |
prob2_textbox = gr.outputs.Textbox(label="Probability 2")
|
@@ -184,7 +152,6 @@ with block as demo:
|
|
184 |
show_progress="minimal",
|
185 |
preprocess=upload_file,
|
186 |
scroll_to_output=True,
|
187 |
-
# cancels=[cancel_button],
|
188 |
)
|
189 |
|
190 |
|
|
|
1 |
import gradio as gr
|
2 |
import predict as predict
|
3 |
+
import extract_gradcam as extract_gradcam
|
4 |
+
import extract_lime as extract_lime
|
5 |
|
6 |
|
7 |
def upload_file(files):
|
|
|
24 |
result.append(f"{class_label}: {class_prob}%")
|
25 |
result = result[:4]
|
26 |
if gradcam_toggle == True:
|
27 |
+
cam = extract_gradcam.extract_gradcam(upload_filepath, save_path="gradcam.jpg")
|
28 |
result.append("gradcam.jpg")
|
29 |
else:
|
30 |
result.append(None)
|
31 |
if lime_toggle == True:
|
32 |
+
lime = extract_lime.generate_lime(upload_filepath, save_path="lime.jpg")
|
33 |
result.append("lime.jpg")
|
34 |
else:
|
35 |
result.append(None)
|
36 |
return result
|
37 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
css = """
|
40 |
.block {
|
|
|
92 |
with gr.Column():
|
93 |
gr.Label("SpiralSense", elem_id="title-label", show_label=False)
|
94 |
gr.Label(
|
95 |
+
"A Stress-free, Portable, and Cost-effective Machine Learning-Powered Web Application for Early Detection of Multiple Neurological Disorders through Spiral Drawing Analysis",
|
96 |
elem_id="desc-label",
|
97 |
+
show_label=False,
|
98 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
with gr.Row():
|
100 |
image_input = gr.Image(
|
101 |
type="filepath",
|
|
|
120 |
with gr.Row():
|
121 |
submit_button = gr.Button(value="Submit")
|
122 |
gr.Markdown("<br>")
|
|
|
|
|
|
|
|
|
|
|
123 |
with gr.Row():
|
124 |
prob1_textbox = gr.outputs.Textbox(label="Probability 1")
|
125 |
prob2_textbox = gr.outputs.Textbox(label="Probability 2")
|
|
|
152 |
show_progress="minimal",
|
153 |
preprocess=upload_file,
|
154 |
scroll_to_output=True,
|
|
|
155 |
)
|
156 |
|
157 |
|
compute_mean_std.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torchvision
|
2 |
+
from torchvision import transforms
|
3 |
+
from torch.utils.data import DataLoader
|
4 |
+
import torch
|
5 |
+
from configs import *
|
6 |
+
|
7 |
+
def main():
|
8 |
+
data_path = COMBINED_DATA_DIR + str(TASK)
|
9 |
+
|
10 |
+
transform_img = transforms.Compose(
|
11 |
+
[
|
12 |
+
transforms.Resize((224, 224)),
|
13 |
+
transforms.ToTensor(), # Convert to tensor
|
14 |
+
transforms.Grayscale(num_output_channels=3), # Convert to 3 channels
|
15 |
+
]
|
16 |
+
)
|
17 |
+
|
18 |
+
image_data = torchvision.datasets.ImageFolder(root=data_path, transform=transform_img)
|
19 |
+
|
20 |
+
batch_size = BATCH_SIZE
|
21 |
+
|
22 |
+
loader = DataLoader(image_data, batch_size=batch_size, num_workers=1)
|
23 |
+
|
24 |
+
def batch_mean_and_sd(loader):
|
25 |
+
cnt = 0
|
26 |
+
fst_moment = torch.empty(3)
|
27 |
+
snd_moment = torch.empty(3)
|
28 |
+
|
29 |
+
for images, _ in loader:
|
30 |
+
b, c, h, w = images.shape
|
31 |
+
nb_pixels = b * h * w
|
32 |
+
sum_ = torch.sum(images, dim=[0, 2, 3])
|
33 |
+
sum_of_square = torch.sum(images**2, dim=[0, 2, 3])
|
34 |
+
fst_moment = (cnt * fst_moment + sum_) / (cnt + nb_pixels)
|
35 |
+
snd_moment = (cnt * snd_moment + sum_of_square) / (cnt + nb_pixels)
|
36 |
+
cnt += nb_pixels
|
37 |
+
|
38 |
+
mean, std = fst_moment, torch.sqrt(snd_moment - fst_moment**2)
|
39 |
+
return mean, std
|
40 |
+
|
41 |
+
mean, std = batch_mean_and_sd(loader)
|
42 |
+
print("mean and std: \n", mean, std)
|
43 |
+
|
44 |
+
if __name__ == '__main__':
|
45 |
+
main()
|
configs.py
CHANGED
@@ -1,39 +1,7 @@
|
|
1 |
-
import os
|
2 |
import torch
|
3 |
from torchvision import transforms
|
4 |
from torch.utils.data import Dataset
|
5 |
from models import *
|
6 |
-
import torch.nn as nn
|
7 |
-
from torchvision.models import (
|
8 |
-
squeezenet1_0,
|
9 |
-
SqueezeNet1_0_Weights,
|
10 |
-
squeezenet1_1,
|
11 |
-
SqueezeNet1_1_Weights,
|
12 |
-
shufflenet_v2_x2_0,
|
13 |
-
ShuffleNet_V2_X2_0_Weights,
|
14 |
-
mobilenet_v3_small,
|
15 |
-
MobileNet_V3_Small_Weights,
|
16 |
-
efficientnet_v2_s,
|
17 |
-
EfficientNet_V2_S_Weights,
|
18 |
-
efficientnet_b0,
|
19 |
-
EfficientNet_B0_Weights,
|
20 |
-
efficientnet_b1,
|
21 |
-
EfficientNet_B1_Weights,
|
22 |
-
efficientnet_b2,
|
23 |
-
EfficientNet_B2_Weights,
|
24 |
-
efficientnet_b3,
|
25 |
-
EfficientNet_B3_Weights,
|
26 |
-
mobilenet_v3_small,
|
27 |
-
MobileNet_V3_Small_Weights,
|
28 |
-
mobilenet_v3_large,
|
29 |
-
MobileNet_V3_Large_Weights,
|
30 |
-
googlenet,
|
31 |
-
GoogLeNet_Weights,
|
32 |
-
MobileNet_V2_Weights,
|
33 |
-
mobilenet_v2,
|
34 |
-
)
|
35 |
-
|
36 |
-
import torch.nn.functional as F
|
37 |
|
38 |
# Constants
|
39 |
RANDOM_SEED = 123
|
@@ -44,8 +12,8 @@ LEARNING_RATE = 0.0001
|
|
44 |
STEP_SIZE = 10
|
45 |
GAMMA = 0.3
|
46 |
CUTMIX_ALPHA = 0.3
|
47 |
-
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
48 |
-
|
49 |
NUM_PRINT = 100
|
50 |
TASK = 1
|
51 |
WARMUP_EPOCHS = 5
|
@@ -69,235 +37,13 @@ CLASSES = [
|
|
69 |
]
|
70 |
|
71 |
|
72 |
-
|
73 |
-
def __init__(self, channel, reduction=16):
|
74 |
-
super(SE_Block, self).__init__()
|
75 |
-
self.avg_pool = nn.AdaptiveAvgPool2d(1)
|
76 |
-
self.fc = nn.Sequential(
|
77 |
-
nn.Linear(channel, channel // reduction, bias=False),
|
78 |
-
nn.ReLU(inplace=True),
|
79 |
-
nn.Linear(channel // reduction, channel, bias=False),
|
80 |
-
nn.Sigmoid(), # Sigmoid activation to produce attention scores
|
81 |
-
)
|
82 |
-
|
83 |
-
def forward(self, x):
|
84 |
-
b, c, _, _ = x.size()
|
85 |
-
y = self.avg_pool(x).view(b, c)
|
86 |
-
y = self.fc(y).view(b, c, 1, 1)
|
87 |
-
return x * y.expand_as(x)
|
88 |
-
|
89 |
-
|
90 |
-
class SqueezeNet1_0WithSE(nn.Module):
|
91 |
-
def __init__(self, num_classes, dropout_prob=0.5):
|
92 |
-
super(SqueezeNet1_0WithSE, self).__init__()
|
93 |
-
squeezenet = squeezenet1_0(weights=SqueezeNet1_0_Weights.DEFAULT)
|
94 |
-
self.features = squeezenet.features
|
95 |
-
self.classifier = nn.Sequential(
|
96 |
-
nn.Conv2d(512, num_classes, kernel_size=1),
|
97 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
98 |
-
nn.ReLU(inplace=True),
|
99 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
100 |
-
)
|
101 |
-
self.dropout = nn.Dropout(
|
102 |
-
dropout_prob
|
103 |
-
) # Add dropout layer with the specified probability
|
104 |
-
|
105 |
-
# Adjust channel for SqueezeNet1.0 (original SqueezeNet1.0 has 1000 classes)
|
106 |
-
num_classes_squeezenet1_0 = 7
|
107 |
-
|
108 |
-
# Add Squeeze-and-Excitation block
|
109 |
-
self.se_block = SE_Block(
|
110 |
-
channel=num_classes_squeezenet1_0
|
111 |
-
) # Adjust channel as needed
|
112 |
-
|
113 |
-
def forward(self, x):
|
114 |
-
x = self.features(x)
|
115 |
-
x = self.classifier(x)
|
116 |
-
# x = self.se_block(x) # Apply the SE block
|
117 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
118 |
-
x = torch.flatten(x, 1)
|
119 |
-
return x
|
120 |
-
|
121 |
-
|
122 |
-
class SqueezeNet1_1WithSE(nn.Module):
|
123 |
-
def __init__(self, num_classes, dropout_prob=0.2):
|
124 |
-
super(SqueezeNet1_1WithSE, self).__init__()
|
125 |
-
squeezenet = squeezenet1_1(weights=SqueezeNet1_1_Weights.DEFAULT)
|
126 |
-
self.features = squeezenet.features
|
127 |
-
self.classifier = nn.Sequential(
|
128 |
-
nn.Conv2d(512, num_classes, kernel_size=1),
|
129 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
130 |
-
nn.ReLU(inplace=True),
|
131 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
132 |
-
)
|
133 |
-
self.dropout = nn.Dropout(
|
134 |
-
dropout_prob
|
135 |
-
) # Add dropout layer with the specified probability
|
136 |
-
|
137 |
-
# Add Squeeze-and-Excitation block
|
138 |
-
self.se_block = SE_Block(channel=num_classes) # Adjust channel as needed
|
139 |
-
|
140 |
-
def forward(self, x):
|
141 |
-
x = self.features(x)
|
142 |
-
x = self.classifier(x)
|
143 |
-
x = self.se_block(x) # Apply the SE block
|
144 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
145 |
-
x = torch.flatten(x, 1)
|
146 |
-
return x
|
147 |
-
|
148 |
-
|
149 |
-
class EfficientNetB2WithDropout(nn.Module):
|
150 |
-
# 0.00022015769999619205
|
151 |
-
def __init__(self, num_classes, dropout_prob=0.2):
|
152 |
-
super(EfficientNetB2WithDropout, self).__init__()
|
153 |
-
efficientnet = efficientnet_b2(weights=EfficientNet_B2_Weights.DEFAULT)
|
154 |
-
self.features = efficientnet.features
|
155 |
-
self.classifier = nn.Sequential(
|
156 |
-
nn.Conv2d(1408, num_classes, kernel_size=1),
|
157 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
158 |
-
nn.ReLU(inplace=True),
|
159 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
160 |
-
)
|
161 |
-
self.dropout = nn.Dropout(
|
162 |
-
dropout_prob
|
163 |
-
) # Add dropout layer with the specified probability
|
164 |
-
|
165 |
-
def forward(self, x):
|
166 |
-
x = self.features(x)
|
167 |
-
x = self.classifier(x)
|
168 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
169 |
-
x = torch.flatten(x, 1)
|
170 |
-
return x
|
171 |
-
|
172 |
-
|
173 |
-
class EfficientNetB3WithDropout(nn.Module):
|
174 |
-
def __init__(self, num_classes, dropout_prob=0.2):
|
175 |
-
super(EfficientNetB3WithDropout, self).__init__()
|
176 |
-
efficientnet = efficientnet_b3(weights=EfficientNet_B3_Weights.DEFAULT)
|
177 |
-
self.features = efficientnet.features
|
178 |
-
self.classifier = nn.Sequential(
|
179 |
-
nn.Conv2d(1536, num_classes, kernel_size=1),
|
180 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
181 |
-
nn.ReLU(inplace=True),
|
182 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
183 |
-
)
|
184 |
-
self.dropout = nn.Dropout(
|
185 |
-
dropout_prob
|
186 |
-
) # Add dropout layer with the specified probability
|
187 |
-
|
188 |
-
def forward(self, x):
|
189 |
-
x = self.features(x)
|
190 |
-
x = self.classifier(x)
|
191 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
192 |
-
x = torch.flatten(x, 1)
|
193 |
-
return x
|
194 |
-
|
195 |
-
|
196 |
-
class ResNet18WithNorm(nn.Module):
|
197 |
-
def __init__(self, num_classes=1000):
|
198 |
-
super(ResNet18WithNorm, self).__init__()
|
199 |
-
resnet = resnet18(pretrained=False)
|
200 |
-
|
201 |
-
# Remove the last block (Block 4)
|
202 |
-
self.features = nn.Sequential(
|
203 |
-
*list(resnet.children())[:-1] # Exclude the last block
|
204 |
-
)
|
205 |
-
|
206 |
-
self.classifier = nn.Sequential(
|
207 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
208 |
-
nn.Flatten(),
|
209 |
-
nn.Linear(
|
210 |
-
512, num_classes
|
211 |
-
), # Adjust input size for the fully connected layer
|
212 |
-
nn.BatchNorm1d(num_classes), # Add batch normalization
|
213 |
-
)
|
214 |
-
|
215 |
-
def forward(self, x):
|
216 |
-
x = self.features(x)
|
217 |
-
x = self.classifier(x)
|
218 |
-
x = torch.flatten(x, 1)
|
219 |
-
return x
|
220 |
-
|
221 |
-
|
222 |
-
class MobileNetV3LargeWithDropout(nn.Module):
|
223 |
-
def __init__(self, num_classes, dropout_prob=0.2):
|
224 |
-
super(MobileNetV3LargeWithDropout, self).__init__()
|
225 |
-
mobilenet = mobilenet_v3_large(weights=MobileNet_V3_Large_Weights.DEFAULT)
|
226 |
-
self.features = mobilenet.features
|
227 |
-
self.classifier = nn.Sequential(
|
228 |
-
nn.Conv2d(960, num_classes, kernel_size=1),
|
229 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
230 |
-
nn.ReLU(inplace=True),
|
231 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
232 |
-
)
|
233 |
-
self.dropout = nn.Dropout(
|
234 |
-
dropout_prob
|
235 |
-
) # Add dropout layer with the specified probability
|
236 |
-
|
237 |
-
def forward(self, x):
|
238 |
-
x = self.features(x)
|
239 |
-
x = self.classifier(x)
|
240 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
241 |
-
x = torch.flatten(x, 1)
|
242 |
-
return x
|
243 |
-
|
244 |
-
|
245 |
-
class GoogLeNetWithSE(nn.Module):
|
246 |
-
def __init__(self, num_classes):
|
247 |
-
super(GoogLeNetWithSE, self).__init__()
|
248 |
-
googlenet = googlenet(weights=GoogLeNet_Weights.DEFAULT)
|
249 |
-
# self.features = googlenet.features
|
250 |
-
self.classifier = nn.Sequential(
|
251 |
-
nn.Conv2d(1024, num_classes, kernel_size=1),
|
252 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
253 |
-
nn.ReLU(inplace=True),
|
254 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
255 |
-
)
|
256 |
-
|
257 |
-
# Add Squeeze-and-Excitation block
|
258 |
-
self.se_block = SE_Block(channel=num_classes) # Adjust channel as needed
|
259 |
-
|
260 |
-
def forward(self, x):
|
261 |
-
# x = self.features(x)
|
262 |
-
x = self.classifier(x)
|
263 |
-
x = self.se_block(x) # Apply the SE block
|
264 |
-
x = torch.flatten(x, 1)
|
265 |
-
return x
|
266 |
-
|
267 |
-
|
268 |
-
class MobileNetV2WithDropout(nn.Module):
|
269 |
-
def __init__(self, num_classes, dropout_prob=0.2):
|
270 |
-
super(MobileNetV2WithDropout, self).__init__()
|
271 |
-
mobilenet = mobilenet_v2(weights=MobileNet_V2_Weights.DEFAULT)
|
272 |
-
self.features = mobilenet.features
|
273 |
-
self.classifier = nn.Sequential(
|
274 |
-
nn.Conv2d(1280, num_classes, kernel_size=1),
|
275 |
-
nn.BatchNorm2d(num_classes), # add batch normalization
|
276 |
-
nn.ReLU(inplace=True),
|
277 |
-
nn.AdaptiveAvgPool2d((1, 1)),
|
278 |
-
)
|
279 |
-
self.dropout = nn.Dropout(
|
280 |
-
dropout_prob
|
281 |
-
) # Add dropout layer with the specified probability
|
282 |
-
|
283 |
-
def forward(self, x):
|
284 |
-
x = self.features(x)
|
285 |
-
x = self.classifier(x)
|
286 |
-
x = F.dropout(x, training=self.training) # Apply dropout during training
|
287 |
-
x = torch.flatten(x, 1)
|
288 |
-
return x
|
289 |
-
|
290 |
-
|
291 |
-
MODEL = EfficientNetB3WithDropout(num_classes=NUM_CLASSES)
|
292 |
MODEL_SAVE_PATH = r"output/checkpoints/" + MODEL.__class__.__name__ + ".pth"
|
293 |
-
# MODEL_SAVE_PATH = r"C:\Users\User\Downloads\bestsqueezenetSE.pth"
|
294 |
preprocess = transforms.Compose(
|
295 |
[
|
296 |
transforms.Resize((224, 224)),
|
297 |
transforms.ToTensor(), # Convert to tensor
|
298 |
-
transforms.
|
299 |
-
# Normalize 3 channels
|
300 |
-
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
|
301 |
]
|
302 |
)
|
303 |
|
@@ -313,14 +59,3 @@ class CustomDataset(Dataset):
|
|
313 |
def __getitem__(self, idx):
|
314 |
img, label = self.data[idx]
|
315 |
return img, label
|
316 |
-
|
317 |
-
|
318 |
-
def ensemble_predictions(models, image):
|
319 |
-
all_predictions = []
|
320 |
-
|
321 |
-
with torch.no_grad():
|
322 |
-
for model in models:
|
323 |
-
output = model(image)
|
324 |
-
all_predictions.append(output)
|
325 |
-
|
326 |
-
return torch.stack(all_predictions, dim=0).mean(dim=0)
|
|
|
|
|
1 |
import torch
|
2 |
from torchvision import transforms
|
3 |
from torch.utils.data import Dataset
|
4 |
from models import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
# Constants
|
7 |
RANDOM_SEED = 123
|
|
|
12 |
STEP_SIZE = 10
|
13 |
GAMMA = 0.3
|
14 |
CUTMIX_ALPHA = 0.3
|
15 |
+
# DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
16 |
+
DEVICE = torch.device("cpu")
|
17 |
NUM_PRINT = 100
|
18 |
TASK = 1
|
19 |
WARMUP_EPOCHS = 5
|
|
|
37 |
]
|
38 |
|
39 |
|
40 |
+
MODEL = EfficientNetB3WithNorm(num_classes=NUM_CLASSES)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
MODEL_SAVE_PATH = r"output/checkpoints/" + MODEL.__class__.__name__ + ".pth"
|
|
|
42 |
preprocess = transforms.Compose(
|
43 |
[
|
44 |
transforms.Resize((224, 224)),
|
45 |
transforms.ToTensor(), # Convert to tensor
|
46 |
+
transforms.Normalize(0.8289, 0.2006),
|
|
|
|
|
47 |
]
|
48 |
)
|
49 |
|
|
|
59 |
def __getitem__(self, idx):
|
60 |
img, label = self.data[idx]
|
61 |
return img, label
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
convert.py
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
import torch
|
2 |
-
import onnx2tf
|
3 |
-
from configs import *
|
4 |
-
|
5 |
-
torch.onnx.export(
|
6 |
-
model=model,
|
7 |
-
args=torch.randn(1, 3, 64, 64),
|
8 |
-
f="output/checkpoints/model.onnx",
|
9 |
-
verbose=True,
|
10 |
-
input_names=["input"],
|
11 |
-
output_names=["output"],
|
12 |
-
)
|
13 |
-
|
14 |
-
onnx2tf.convert(
|
15 |
-
input_onnx_file_path="output/checkpoints/model.onnx",
|
16 |
-
output_folder_path="output/checkpoints/converted/",
|
17 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data-splitting.py β data_splitting.py
RENAMED
File without changes
|
ensemble.py
DELETED
@@ -1,249 +0,0 @@
|
|
1 |
-
import matplotlib.pyplot as plt
|
2 |
-
from torch.optim.lr_scheduler import CosineAnnealingLR
|
3 |
-
import torch
|
4 |
-
import torch.nn as nn
|
5 |
-
from torchvision.datasets import ImageFolder
|
6 |
-
from torch.utils.data import DataLoader
|
7 |
-
from data_loader import load_data, load_test_data
|
8 |
-
from configs import *
|
9 |
-
import numpy as np
|
10 |
-
|
11 |
-
torch.cuda.empty_cache()
|
12 |
-
|
13 |
-
#
|
14 |
-
|
15 |
-
|
16 |
-
class MLP(nn.Module):
|
17 |
-
def __init__(self, num_classes, num_models):
|
18 |
-
super(MLP, self).__init__()
|
19 |
-
self.layers = nn.Sequential(
|
20 |
-
nn.Linear(num_classes * num_models, 1024),
|
21 |
-
nn.LayerNorm(1024),
|
22 |
-
nn.LeakyReLU(negative_slope=0.01, inplace=True),
|
23 |
-
nn.Dropout(0.8),
|
24 |
-
nn.Linear(1024, 2048),
|
25 |
-
nn.LeakyReLU(negative_slope=0.01, inplace=True),
|
26 |
-
nn.Dropout(0.5),
|
27 |
-
nn.Linear(2048, 2048),
|
28 |
-
nn.LeakyReLU(negative_slope=0.01, inplace=True),
|
29 |
-
nn.Dropout(0.5),
|
30 |
-
nn.Linear(2048, num_classes),
|
31 |
-
)
|
32 |
-
|
33 |
-
def forward(self, x):
|
34 |
-
x = x.view(x.size(0), -1)
|
35 |
-
x = self.layers(x)
|
36 |
-
return x
|
37 |
-
|
38 |
-
|
39 |
-
def mlp_meta(num_classes, num_models):
|
40 |
-
model = MLP(num_classes, num_models)
|
41 |
-
return model
|
42 |
-
|
43 |
-
|
44 |
-
# Hyperparameters
|
45 |
-
input_dim = 3 * 224 * 224 # Modify this based on your input size
|
46 |
-
hidden_dim = 256
|
47 |
-
output_dim = NUM_CLASSES
|
48 |
-
|
49 |
-
# Create the data loaders using your data_loader functions50
|
50 |
-
train_loader, val_loader = load_data(COMBINED_DATA_DIR + "1", preprocess, BATCH_SIZE)
|
51 |
-
test_loader = load_test_data("data/test/Task 1", preprocess, BATCH_SIZE)
|
52 |
-
|
53 |
-
model_paths = [
|
54 |
-
"output/checkpoints/bestsqueezenetSE3.pth",
|
55 |
-
"output/checkpoints/EfficientNetB3WithDropout.pth",
|
56 |
-
"output/checkpoints/MobileNetV2WithDropout2.pth",
|
57 |
-
]
|
58 |
-
|
59 |
-
|
60 |
-
# Define a function to load pretrained models
|
61 |
-
def load_pretrained_model(path, model):
|
62 |
-
model.load_state_dict(torch.load(path))
|
63 |
-
return model.to(DEVICE)
|
64 |
-
|
65 |
-
|
66 |
-
def rand_bbox(size, lam):
|
67 |
-
W = size[2]
|
68 |
-
H = size[3]
|
69 |
-
cut_rat = np.sqrt(1.0 - lam)
|
70 |
-
cut_w = np.int_(W * cut_rat)
|
71 |
-
cut_h = np.int_(H * cut_rat)
|
72 |
-
|
73 |
-
# uniform
|
74 |
-
cx = np.random.randint(W)
|
75 |
-
cy = np.random.randint(H)
|
76 |
-
|
77 |
-
bbx1 = np.clip(cx - cut_w // 2, 0, W)
|
78 |
-
bby1 = np.clip(cy - cut_h // 2, 0, H)
|
79 |
-
bbx2 = np.clip(cx + cut_w // 2, 0, W)
|
80 |
-
bby2 = np.clip(cy + cut_h // 2, 0, H)
|
81 |
-
|
82 |
-
return bbx1, bby1, bbx2, bby2
|
83 |
-
|
84 |
-
|
85 |
-
def cutmix_data(input, target, alpha=1.0):
|
86 |
-
if alpha > 0:
|
87 |
-
lam = np.random.beta(alpha, alpha)
|
88 |
-
else:
|
89 |
-
lam = 1
|
90 |
-
|
91 |
-
batch_size = input.size()[0]
|
92 |
-
index = torch.randperm(batch_size)
|
93 |
-
rand_index = torch.randperm(input.size()[0])
|
94 |
-
|
95 |
-
bbx1, bby1, bbx2, bby2 = rand_bbox(input.size(), lam)
|
96 |
-
input[:, :, bbx1:bbx2, bby1:bby2] = input[rand_index, :, bbx1:bbx2, bby1:bby2]
|
97 |
-
|
98 |
-
lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (input.size()[-1] * input.size()[-2]))
|
99 |
-
targets_a = target
|
100 |
-
targets_b = target[rand_index]
|
101 |
-
|
102 |
-
return input, targets_a, targets_b, lam
|
103 |
-
|
104 |
-
|
105 |
-
def cutmix_criterion(criterion, outputs, targets_a, targets_b, lam):
|
106 |
-
return lam * criterion(outputs, targets_a) + (1 - lam) * criterion(
|
107 |
-
outputs, targets_b
|
108 |
-
)
|
109 |
-
|
110 |
-
|
111 |
-
# Load pretrained models
|
112 |
-
model1 = load_pretrained_model(
|
113 |
-
model_paths[0], SqueezeNet1_0WithSE(num_classes=NUM_CLASSES)
|
114 |
-
).to(DEVICE)
|
115 |
-
model2 = load_pretrained_model(
|
116 |
-
model_paths[1], EfficientNetB3WithDropout(num_classes=NUM_CLASSES)
|
117 |
-
).to(DEVICE)
|
118 |
-
model3 = load_pretrained_model(
|
119 |
-
model_paths[2], MobileNetV2WithDropout(num_classes=NUM_CLASSES)
|
120 |
-
).to(DEVICE)
|
121 |
-
|
122 |
-
models = [model1, model2, model3]
|
123 |
-
|
124 |
-
# Create the meta learner
|
125 |
-
meta_learner_model = mlp_meta(NUM_CLASSES, len(models)).to(DEVICE)
|
126 |
-
meta_optimizer = torch.optim.Adam(meta_learner_model.parameters(), lr=0.001)
|
127 |
-
meta_loss_fn = torch.nn.CrossEntropyLoss()
|
128 |
-
|
129 |
-
# Define the Cosine Annealing Learning Rate Scheduler
|
130 |
-
scheduler = CosineAnnealingLR(
|
131 |
-
meta_optimizer, T_max=700
|
132 |
-
) # T_max is the number of epochs for the cosine annealing.
|
133 |
-
|
134 |
-
# Define loss function and optimizer for the meta learner
|
135 |
-
criterion = nn.CrossEntropyLoss().to(DEVICE)
|
136 |
-
|
137 |
-
# Record learning rate
|
138 |
-
lr_hist = []
|
139 |
-
|
140 |
-
# Training loop
|
141 |
-
num_epochs = 160
|
142 |
-
for epoch in range(num_epochs):
|
143 |
-
print("[Epoch: {}]".format(epoch + 1))
|
144 |
-
print("Total number of batches: {}".format(len(train_loader)))
|
145 |
-
for batch_idx, data in enumerate(train_loader, 0):
|
146 |
-
print("Batch: {}".format(batch_idx + 1))
|
147 |
-
inputs, labels = data
|
148 |
-
inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)
|
149 |
-
inputs, targets_a, targets_b, lam = cutmix_data(inputs, labels, alpha=1)
|
150 |
-
|
151 |
-
# Forward pass through the three pretrained models
|
152 |
-
features1 = model1(inputs)
|
153 |
-
features2 = model2(inputs)
|
154 |
-
features3 = model3(inputs)
|
155 |
-
|
156 |
-
# Stack the features from the three models
|
157 |
-
stacked_features = torch.cat((features1, features2, features3), dim=1).to(
|
158 |
-
DEVICE
|
159 |
-
)
|
160 |
-
|
161 |
-
# Forward pass through the meta learner
|
162 |
-
meta_output = meta_learner_model(stacked_features)
|
163 |
-
|
164 |
-
# Compute the loss
|
165 |
-
loss = cutmix_criterion(criterion, meta_output, targets_a, targets_b, lam)
|
166 |
-
|
167 |
-
# Compute the accuracy
|
168 |
-
_, predicted = torch.max(meta_output, 1)
|
169 |
-
total = labels.size(0)
|
170 |
-
correct = (predicted == labels).sum().item()
|
171 |
-
|
172 |
-
# Backpropagation and optimization
|
173 |
-
meta_optimizer.zero_grad()
|
174 |
-
loss.backward()
|
175 |
-
meta_optimizer.step()
|
176 |
-
|
177 |
-
lr_hist.append(meta_optimizer.param_groups[0]["lr"])
|
178 |
-
|
179 |
-
scheduler.step()
|
180 |
-
|
181 |
-
print("Train Loss: {}".format(loss.item()))
|
182 |
-
print("Train Accuracy: {}%".format(100 * correct / total))
|
183 |
-
|
184 |
-
# Validation
|
185 |
-
meta_learner_model.eval()
|
186 |
-
correct = 0
|
187 |
-
total = 0
|
188 |
-
val_loss = 0
|
189 |
-
with torch.no_grad():
|
190 |
-
for data in val_loader:
|
191 |
-
inputs, labels = data
|
192 |
-
inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)
|
193 |
-
features1 = model1(inputs)
|
194 |
-
features2 = model2(inputs)
|
195 |
-
features3 = model3(inputs)
|
196 |
-
stacked_features = torch.cat((features1, features2, features3), dim=1).to(
|
197 |
-
DEVICE
|
198 |
-
)
|
199 |
-
outputs = meta_learner_model(stacked_features)
|
200 |
-
loss = criterion(outputs, labels) # Use the validation loss
|
201 |
-
val_loss += loss.item()
|
202 |
-
_, predicted = torch.max(outputs, 1)
|
203 |
-
total += labels.size(0)
|
204 |
-
correct += (predicted == labels).sum().item()
|
205 |
-
|
206 |
-
print(
|
207 |
-
"Validation Loss: {}".format(val_loss / len(val_loader))
|
208 |
-
) # Calculate the average loss
|
209 |
-
print("Validation Accuracy: {}%".format(100 * correct / total))
|
210 |
-
|
211 |
-
|
212 |
-
print("Finished Training")
|
213 |
-
|
214 |
-
# Test the ensemble
|
215 |
-
print("Testing the ensemble")
|
216 |
-
meta_learner_model.eval()
|
217 |
-
correct = 0
|
218 |
-
total = 0
|
219 |
-
with torch.no_grad():
|
220 |
-
for data in test_loader:
|
221 |
-
inputs, labels = data
|
222 |
-
inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)
|
223 |
-
features1 = model1(inputs)
|
224 |
-
features2 = model2(inputs)
|
225 |
-
features3 = model3(inputs)
|
226 |
-
stacked_features = torch.cat((features1, features2, features3), dim=1)
|
227 |
-
outputs = meta_learner_model(stacked_features)
|
228 |
-
_, predicted = torch.max(outputs, 1)
|
229 |
-
total += labels.size(0)
|
230 |
-
correct += (predicted == labels).sum().item()
|
231 |
-
|
232 |
-
print(
|
233 |
-
"Accuracy of the ensemble network on the test images: {}%".format(
|
234 |
-
100 * correct / total
|
235 |
-
)
|
236 |
-
)
|
237 |
-
|
238 |
-
|
239 |
-
# Plot the learning rate history
|
240 |
-
|
241 |
-
plt.plot(lr_hist)
|
242 |
-
plt.xlabel("Iterations")
|
243 |
-
plt.ylabel("Learning Rate")
|
244 |
-
plt.title("Learning Rate History")
|
245 |
-
plt.show()
|
246 |
-
|
247 |
-
|
248 |
-
# Save the model
|
249 |
-
torch.save(meta_learner_model.state_dict(), "output/checkpoints/ensemble.pth")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval.py
DELETED
@@ -1,190 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import torch
|
3 |
-
import numpy as np
|
4 |
-
import pathlib
|
5 |
-
from PIL import Image
|
6 |
-
import matplotlib.pyplot as plt
|
7 |
-
from sklearn.metrics import (
|
8 |
-
classification_report,
|
9 |
-
precision_recall_curve,
|
10 |
-
accuracy_score,
|
11 |
-
f1_score,
|
12 |
-
confusion_matrix,
|
13 |
-
ConfusionMatrixDisplay,
|
14 |
-
)
|
15 |
-
from sklearn.preprocessing import label_binarize
|
16 |
-
from torchvision import transforms
|
17 |
-
from configs import *
|
18 |
-
|
19 |
-
# EfficientNet: 0.901978973407545
|
20 |
-
# MobileNet: 0.8731189445475158
|
21 |
-
# SquuezeNet: 0.8559218559218559
|
22 |
-
|
23 |
-
|
24 |
-
# Constants
|
25 |
-
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
26 |
-
NUM_AUGMENTATIONS = 10 # Number of augmentations to perform
|
27 |
-
|
28 |
-
model2 = EfficientNetB2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
29 |
-
model2.load_state_dict(torch.load("output/checkpoints/EfficientNetB2WithDropout.pth"))
|
30 |
-
model1 = SqueezeNet1_0WithSE(num_classes=NUM_CLASSES).to(DEVICE)
|
31 |
-
model1.load_state_dict(torch.load("output/checkpoints/SqueezeNet1_0WithSE.pth"))
|
32 |
-
model3 = MobileNetV2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
33 |
-
model3.load_state_dict(torch.load("output\checkpoints\MobileNetV2WithDropout.pth"))
|
34 |
-
|
35 |
-
best_weights = [0.901978973407545, 0.8731189445475158, 0.8559218559218559]
|
36 |
-
|
37 |
-
# Load the model
|
38 |
-
model = WeightedVoteEnsemble([model1, model2, model3], best_weights)
|
39 |
-
# model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=DEVICE))
|
40 |
-
model.load_state_dict(torch.load('output/checkpoints/WeightedVoteEnsemble.pth', map_location=DEVICE))
|
41 |
-
model.eval()
|
42 |
-
|
43 |
-
# define augmentations for TTA
|
44 |
-
tta_transforms = transforms.Compose(
|
45 |
-
[
|
46 |
-
transforms.RandomHorizontalFlip(p=0.5),
|
47 |
-
transforms.RandomVerticalFlip(p=0.5),
|
48 |
-
]
|
49 |
-
)
|
50 |
-
|
51 |
-
|
52 |
-
def perform_tta(model, image, tta_transforms):
|
53 |
-
augmented_predictions = []
|
54 |
-
augmented_scores = []
|
55 |
-
|
56 |
-
for _ in range(NUM_AUGMENTATIONS):
|
57 |
-
augmented_image = tta_transforms(image)
|
58 |
-
output = model(augmented_image)
|
59 |
-
predicted_class = torch.argmax(output, dim=1).item()
|
60 |
-
augmented_predictions.append(predicted_class)
|
61 |
-
augmented_scores.append(output.softmax(dim=1).cpu().numpy())
|
62 |
-
|
63 |
-
# max voting
|
64 |
-
final_predicted_class_max = max(
|
65 |
-
set(augmented_predictions), key=augmented_predictions.count
|
66 |
-
)
|
67 |
-
|
68 |
-
# average probabilities
|
69 |
-
final_predicted_scores_avg = np.mean(np.array(augmented_scores), axis=0)
|
70 |
-
|
71 |
-
# rotate and average probabilities
|
72 |
-
rotation_transforms = [
|
73 |
-
transforms.RandomRotation(degrees=i) for i in range(0, 360, 30)
|
74 |
-
]
|
75 |
-
rotated_scores = []
|
76 |
-
for rotation_transform in rotation_transforms:
|
77 |
-
augmented_image = rotation_transform(image)
|
78 |
-
output = model(augmented_image)
|
79 |
-
rotated_scores.append(output.softmax(dim=1).cpu().numpy())
|
80 |
-
|
81 |
-
final_predicted_scores_rotation = np.mean(np.array(rotated_scores), axis=0)
|
82 |
-
|
83 |
-
return (
|
84 |
-
final_predicted_class_max,
|
85 |
-
final_predicted_scores_avg,
|
86 |
-
final_predicted_scores_rotation,
|
87 |
-
)
|
88 |
-
|
89 |
-
|
90 |
-
def predict_image_with_tta(image_path, model, transform, tta_transforms):
|
91 |
-
model.eval()
|
92 |
-
correct_predictions = 0
|
93 |
-
true_classes = []
|
94 |
-
predicted_labels_max = []
|
95 |
-
predicted_labels_avg = []
|
96 |
-
predicted_labels_rotation = []
|
97 |
-
|
98 |
-
with torch.no_grad():
|
99 |
-
images = list(pathlib.Path(image_path).rglob("*.png"))
|
100 |
-
total_predictions = len(images)
|
101 |
-
|
102 |
-
for image_file in images:
|
103 |
-
true_class = CLASSES.index(image_file.parts[-2])
|
104 |
-
|
105 |
-
original_image = Image.open(image_file).convert("RGB")
|
106 |
-
original_image = transform(original_image).unsqueeze(0)
|
107 |
-
original_image = original_image.to(DEVICE)
|
108 |
-
|
109 |
-
# Perform TTA with different strategies
|
110 |
-
final_predicted_class_max, _, _ = perform_tta(
|
111 |
-
model, original_image, tta_transforms
|
112 |
-
)
|
113 |
-
_, final_predicted_scores_avg, _ = perform_tta(
|
114 |
-
model, original_image, tta_transforms
|
115 |
-
)
|
116 |
-
_, _, final_predicted_scores_rotation = perform_tta(
|
117 |
-
model, original_image, tta_transforms
|
118 |
-
)
|
119 |
-
|
120 |
-
true_classes.append(true_class)
|
121 |
-
predicted_labels_max.append(final_predicted_class_max)
|
122 |
-
predicted_labels_avg.append(np.argmax(final_predicted_scores_avg))
|
123 |
-
predicted_labels_rotation.append(np.argmax(final_predicted_scores_rotation))
|
124 |
-
|
125 |
-
if final_predicted_class_max == true_class:
|
126 |
-
correct_predictions += 1
|
127 |
-
|
128 |
-
# accuracy for each strategy
|
129 |
-
accuracy_max = accuracy_score(true_classes, predicted_labels_max)
|
130 |
-
accuracy_avg = accuracy_score(true_classes, predicted_labels_avg)
|
131 |
-
accuracy_rotation = accuracy_score(true_classes, predicted_labels_rotation)
|
132 |
-
|
133 |
-
print("Accuracy (Max Voting):", accuracy_max)
|
134 |
-
print("Accuracy (Average Probabilities):", accuracy_avg)
|
135 |
-
print("Accuracy (Rotation and Average):", accuracy_rotation)
|
136 |
-
|
137 |
-
# final prediction using ensemble (choose the strategy with the highest accuracy)
|
138 |
-
final_predicted_labels = []
|
139 |
-
for i in range(len(true_classes)):
|
140 |
-
max_strategy_accuracy = max(accuracy_max, accuracy_avg, accuracy_rotation)
|
141 |
-
if accuracy_max == max_strategy_accuracy:
|
142 |
-
final_predicted_labels.append(predicted_labels_max[i])
|
143 |
-
elif accuracy_avg == max_strategy_accuracy:
|
144 |
-
final_predicted_labels.append(predicted_labels_avg[i])
|
145 |
-
else:
|
146 |
-
final_predicted_labels.append(predicted_labels_rotation[i])
|
147 |
-
|
148 |
-
# calculate accuracy and f1 score(ensemble)
|
149 |
-
accuracy_ensemble = accuracy_score(true_classes, final_predicted_labels)
|
150 |
-
f1_ensemble = f1_score(true_classes, final_predicted_labels, average="weighted")
|
151 |
-
|
152 |
-
print("Ensemble Accuracy:", accuracy_ensemble)
|
153 |
-
print("Ensemble Weighted F1 Score:", f1_ensemble)
|
154 |
-
|
155 |
-
# Classification report
|
156 |
-
class_names = [str(cls) for cls in range(NUM_CLASSES)]
|
157 |
-
report = classification_report(
|
158 |
-
true_classes, final_predicted_labels, target_names=class_names
|
159 |
-
)
|
160 |
-
print("Classification Report of", MODEL.__class__.__name__, ":\n", report)
|
161 |
-
|
162 |
-
# confusion matrix and classification report for the ensemble
|
163 |
-
conf_matrix_ensemble = confusion_matrix(true_classes, final_predicted_labels)
|
164 |
-
ConfusionMatrixDisplay(
|
165 |
-
confusion_matrix=conf_matrix_ensemble, display_labels=range(NUM_CLASSES)
|
166 |
-
).plot(cmap=plt.cm.Blues)
|
167 |
-
plt.title("Confusion Matrix (Ensemble)")
|
168 |
-
plt.show()
|
169 |
-
|
170 |
-
class_names = [str(cls) for cls in range(NUM_CLASSES)]
|
171 |
-
report_ensemble = classification_report(
|
172 |
-
true_classes, final_predicted_labels, target_names=class_names
|
173 |
-
)
|
174 |
-
print("Classification Report (Ensemble):\n", report_ensemble)
|
175 |
-
|
176 |
-
# Calculate precision and recall for each class
|
177 |
-
true_classes_binary = label_binarize(true_classes, classes=range(NUM_CLASSES))
|
178 |
-
precision, recall, _ = precision_recall_curve(
|
179 |
-
true_classes_binary.ravel(), np.array(final_predicted_scores_rotation).ravel()
|
180 |
-
)
|
181 |
-
|
182 |
-
# Plot precision-recall curve
|
183 |
-
plt.figure(figsize=(10, 6))
|
184 |
-
plt.plot(recall, precision)
|
185 |
-
plt.title("Precision-Recall Curve")
|
186 |
-
plt.xlabel("Recall")
|
187 |
-
plt.ylabel("Precision")
|
188 |
-
plt.show()
|
189 |
-
|
190 |
-
predict_image_with_tta("data/test/Task 1/", model, preprocess, tta_transforms)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eval_orig.py β evaluate.py
RENAMED
@@ -1,6 +1,3 @@
|
|
1 |
-
import os
|
2 |
-
import torchvision
|
3 |
-
import shap
|
4 |
import torch
|
5 |
import numpy as np
|
6 |
import pathlib
|
@@ -19,14 +16,10 @@ from sklearn.metrics import (
|
|
19 |
auc,
|
20 |
average_precision_score,
|
21 |
cohen_kappa_score,
|
|
|
22 |
)
|
23 |
from sklearn.preprocessing import label_binarize
|
24 |
from configs import *
|
25 |
-
from data_loader import load_data # Import the load_data function
|
26 |
-
|
27 |
-
# MobileNet: 0.8731189445475158
|
28 |
-
# EfficientNet: 0.873118944547516
|
29 |
-
# SquuezeNet: 0.8865856365856365
|
30 |
|
31 |
|
32 |
rcParams["font.family"] = "Times New Roman"
|
@@ -126,7 +119,7 @@ def predict_image(image_path, model, transform):
|
|
126 |
plt.title("Confusion Matrix")
|
127 |
manager = plt.get_current_fig_manager()
|
128 |
manager.full_screen_toggle()
|
129 |
-
plt.savefig("docs/
|
130 |
plt.show()
|
131 |
|
132 |
# Classification report
|
@@ -170,7 +163,7 @@ def predict_image(image_path, model, transform):
|
|
170 |
"AUC-PRC = {:.3f}".format(auc_prc),
|
171 |
bbox=dict(boxstyle="round", facecolor="white", alpha=0.8),
|
172 |
)
|
173 |
-
plt.savefig("docs/
|
174 |
plt.show()
|
175 |
|
176 |
# Plot ROC curve
|
@@ -186,7 +179,7 @@ def predict_image(image_path, model, transform):
|
|
186 |
"AUC-ROC = {:.3f}".format(auc_roc),
|
187 |
bbox=dict(boxstyle="round", facecolor="white", alpha=0.8),
|
188 |
)
|
189 |
-
plt.savefig("docs/
|
190 |
plt.show()
|
191 |
|
192 |
|
|
|
|
|
|
|
|
|
1 |
import torch
|
2 |
import numpy as np
|
3 |
import pathlib
|
|
|
16 |
auc,
|
17 |
average_precision_score,
|
18 |
cohen_kappa_score,
|
19 |
+
|
20 |
)
|
21 |
from sklearn.preprocessing import label_binarize
|
22 |
from configs import *
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
|
25 |
rcParams["font.family"] = "Times New Roman"
|
|
|
119 |
plt.title("Confusion Matrix")
|
120 |
manager = plt.get_current_fig_manager()
|
121 |
manager.full_screen_toggle()
|
122 |
+
plt.savefig("docs/evaluation/confusion_matrix.png")
|
123 |
plt.show()
|
124 |
|
125 |
# Classification report
|
|
|
163 |
"AUC-PRC = {:.3f}".format(auc_prc),
|
164 |
bbox=dict(boxstyle="round", facecolor="white", alpha=0.8),
|
165 |
)
|
166 |
+
plt.savefig("docs/evaluation/prc.png")
|
167 |
plt.show()
|
168 |
|
169 |
# Plot ROC curve
|
|
|
179 |
"AUC-ROC = {:.3f}".format(auc_roc),
|
180 |
bbox=dict(boxstyle="round", facecolor="white", alpha=0.8),
|
181 |
)
|
182 |
+
plt.savefig("docs/evaluation/roc.png")
|
183 |
plt.show()
|
184 |
|
185 |
|
extract-ensemble.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
from pytorch_grad_cam import GradCAMPlusPlus
|
2 |
-
from pytorch_grad_cam.utils.image import show_cam_on_image, preprocess_image
|
3 |
-
import cv2
|
4 |
-
import numpy as np
|
5 |
-
import torch
|
6 |
-
import torch.nn as nn # Replace with your model
|
7 |
-
from configs import *
|
8 |
-
|
9 |
-
# Load your model (change this according to your model definition)
|
10 |
-
model2 = EfficientNetB2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
11 |
-
model2.load_state_dict(torch.load("output/checkpoints/EfficientNetB2WithDropout.pth"))
|
12 |
-
model1 = SqueezeNet1_0WithSE(num_classes=NUM_CLASSES).to(DEVICE)
|
13 |
-
model1.load_state_dict(torch.load("output/checkpoints/SqueezeNet1_0WithSE.pth"))
|
14 |
-
model3 = MobileNetV2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
15 |
-
model3.load_state_dict(torch.load("output\checkpoints\MobileNetV2WithDropout.pth"))
|
16 |
-
|
17 |
-
model1.eval()
|
18 |
-
model2.eval()
|
19 |
-
model3.eval()
|
20 |
-
|
21 |
-
|
22 |
-
# Find the target layer (modify this based on your model architecture)
|
23 |
-
# EfficientNetB2WithDropout - model.features[-1]
|
24 |
-
# SqueezeNet1_0WithSE - model.features
|
25 |
-
# MobileNetV2WithDropout - model.features[-1]
|
26 |
-
|
27 |
-
target_layer_efficientnet = None
|
28 |
-
for child in model2.features[-1]:
|
29 |
-
if isinstance(child, nn.Conv2d):
|
30 |
-
target_layer_efficientnet = child
|
31 |
-
|
32 |
-
if target_layer_efficientnet is None:
|
33 |
-
raise ValueError(
|
34 |
-
"Invalid EfficientNet layer name: {}".format(target_layer_efficientnet)
|
35 |
-
)
|
36 |
-
|
37 |
-
target_layer_squeezenet = None
|
38 |
-
for child in model1.features:
|
39 |
-
if isinstance(child, nn.Conv2d):
|
40 |
-
target_layer_squeezenet = child
|
41 |
-
|
42 |
-
if target_layer_squeezenet is None:
|
43 |
-
raise ValueError(
|
44 |
-
"Invalid SqueezeNet layer name: {}".format(target_layer_squeezenet)
|
45 |
-
)
|
46 |
-
|
47 |
-
target_layer_mobilenet = None
|
48 |
-
for child in model3.features[-1]:
|
49 |
-
if isinstance(child, nn.Conv2d):
|
50 |
-
target_layer_mobilenet = child
|
51 |
-
|
52 |
-
if target_layer_mobilenet is None:
|
53 |
-
raise ValueError("Invalid MobileNet layer name: {}".format(target_layer_mobilenet))
|
54 |
-
|
55 |
-
# Load and preprocess the image
|
56 |
-
image_path = r"data\test\Task 1\Cerebral Palsy\89.png"
|
57 |
-
rgb_img = cv2.imread(image_path, 1)
|
58 |
-
rgb_img = np.float32(rgb_img) / 255
|
59 |
-
input_tensor = preprocess_image(rgb_img, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
|
60 |
-
input_tensor = input_tensor.to(DEVICE)
|
61 |
-
input_tensor.requires_grad = True # Enable gradients for the input tensor
|
62 |
-
|
63 |
-
|
64 |
-
# Create a GradCAMPlusPlus object
|
65 |
-
efficientnet_cam = GradCAMPlusPlus(model=model2, target_layers=[target_layer_efficientnet], use_cuda=True)
|
66 |
-
squeezenet_cam = GradCAMPlusPlus(model=model1, target_layers=[target_layer_squeezenet], use_cuda=True)
|
67 |
-
mobilenet_cam = GradCAMPlusPlus(model=model3, target_layers=[target_layer_mobilenet], use_cuda=True)
|
68 |
-
|
69 |
-
|
70 |
-
efficientnet_grayscale_cam = efficientnet_cam(input_tensor=input_tensor)[0]
|
71 |
-
squeezenet_grayscale_cam = squeezenet_cam(input_tensor=input_tensor)[0]
|
72 |
-
mobilenet_grayscale_cam = mobilenet_cam(input_tensor=input_tensor)[0]
|
73 |
-
|
74 |
-
# Apply a colormap to the grayscale heatmap
|
75 |
-
efficientnet_heatmap_colored = cv2.applyColorMap(np.uint8(255 * efficientnet_grayscale_cam), cv2.COLORMAP_JET)
|
76 |
-
squeezenet_heatmap_colored = cv2.applyColorMap(np.uint8(255 * squeezenet_grayscale_cam), cv2.COLORMAP_JET)
|
77 |
-
mobilenet_heatmap_colored = cv2.applyColorMap(np.uint8(255 * mobilenet_grayscale_cam), cv2.COLORMAP_JET)
|
78 |
-
|
79 |
-
# normalized_efficientnet_heatmap = efficientnet_heatmap_colored / np.max(efficientnet_heatmap_colored)
|
80 |
-
# normalized_squeezenet_heatmap = squeezenet_heatmap_colored / np.max(squeezenet_heatmap_colored)
|
81 |
-
# normalized_mobilenet_heatmap = mobilenet_heatmap_colored / np.max(mobilenet_heatmap_colored)
|
82 |
-
|
83 |
-
# # Ensure heatmap_colored has the same dtype as rgb_img
|
84 |
-
# normalized_efficientnet_heatmap = normalized_efficientnet_heatmap.astype(np.float32) / 255
|
85 |
-
# normalized_squeezenet_heatmap = normalized_squeezenet_heatmap.astype(np.float32) / 255
|
86 |
-
# normalized_mobilenet_heatmap = normalized_mobilenet_heatmap.astype(np.float32) / 255
|
87 |
-
|
88 |
-
efficientnet_heatmap_colored = efficientnet_heatmap_colored.astype(np.float32) / 255
|
89 |
-
squeezenet_heatmap_colored = squeezenet_heatmap_colored.astype(np.float32) / 255
|
90 |
-
mobilenet_heatmap_colored = mobilenet_heatmap_colored.astype(np.float32) / 255
|
91 |
-
|
92 |
-
# Adjust the alpha value to control transparency
|
93 |
-
alpha = (
|
94 |
-
0.1 # You can change this value to make the original image more or less transparent
|
95 |
-
)
|
96 |
-
|
97 |
-
|
98 |
-
# [0.38, 0.34, 0.28]
|
99 |
-
weighted_heatmap = (
|
100 |
-
efficientnet_heatmap_colored * 0.38
|
101 |
-
+ squeezenet_heatmap_colored * 0.34
|
102 |
-
+ mobilenet_heatmap_colored * 0.28
|
103 |
-
)
|
104 |
-
|
105 |
-
|
106 |
-
# Overlay the colored heatmap on the original image
|
107 |
-
final_output = cv2.addWeighted(rgb_img, 0.3, weighted_heatmap, 0.7, 0)
|
108 |
-
|
109 |
-
# Save the final output
|
110 |
-
cv2.imwrite("cam.jpg", (final_output * 255).astype(np.uint8))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extract.py β extract_gradcam.py
RENAMED
@@ -3,6 +3,7 @@ from pytorch_grad_cam.utils.image import show_cam_on_image, preprocess_image
|
|
3 |
import cv2
|
4 |
import numpy as np
|
5 |
import torch
|
|
|
6 |
import torch.nn as nn # Replace with your model
|
7 |
from configs import *
|
8 |
import os, random
|
@@ -21,30 +22,36 @@ for child in model.features[-1]:
|
|
21 |
if target_layer is None:
|
22 |
raise ValueError("Invalid layer name: {}".format(target_layer))
|
23 |
|
24 |
-
|
25 |
|
26 |
def extract_gradcam(image_path=None, save_path=None):
|
27 |
if image_path is None:
|
28 |
for disease in CLASSES:
|
29 |
print("Processing", disease)
|
30 |
-
for image_path in os.listdir(r
|
31 |
print("Processing", image_path)
|
32 |
-
image_path = r
|
33 |
-
image_name = image_path.split(
|
34 |
-
print("Processing", image_name)
|
35 |
rgb_img = cv2.imread(image_path, 1)
|
36 |
rgb_img = np.float32(rgb_img) / 255
|
37 |
-
input_tensor = preprocess_image(
|
|
|
|
|
38 |
input_tensor = input_tensor.to(DEVICE)
|
|
|
39 |
|
40 |
# Create a GradCAMPlusPlus object
|
41 |
-
cam = GradCAMPlusPlus(
|
|
|
|
|
42 |
|
43 |
# Generate the GradCAM heatmap
|
44 |
grayscale_cam = cam(input_tensor=input_tensor)[0]
|
45 |
|
46 |
# Apply a colormap to the grayscale heatmap
|
47 |
-
heatmap_colored = cv2.applyColorMap(
|
|
|
|
|
48 |
|
49 |
# Ensure heatmap_colored has the same dtype as rgb_img
|
50 |
heatmap_colored = heatmap_colored.astype(np.float32) / 255
|
@@ -56,34 +63,46 @@ def extract_gradcam(image_path=None, save_path=None):
|
|
56 |
final_output = cv2.addWeighted(rgb_img, 0.3, heatmap_colored, 0.7, 0)
|
57 |
|
58 |
# Save the final output
|
59 |
-
os.makedirs(f
|
60 |
-
cv2.imwrite(
|
|
|
|
|
|
|
61 |
else:
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
-
|
68 |
-
|
|
|
|
|
69 |
|
70 |
-
|
71 |
-
|
72 |
|
73 |
-
|
74 |
-
|
75 |
|
76 |
-
|
77 |
-
|
78 |
|
79 |
-
|
80 |
-
|
81 |
|
82 |
-
|
83 |
-
final_output = cv2.addWeighted(rgb_img, 0.3, heatmap_colored, 0.7, 0)
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
3 |
import cv2
|
4 |
import numpy as np
|
5 |
import torch
|
6 |
+
import time
|
7 |
import torch.nn as nn # Replace with your model
|
8 |
from configs import *
|
9 |
import os, random
|
|
|
22 |
if target_layer is None:
|
23 |
raise ValueError("Invalid layer name: {}".format(target_layer))
|
24 |
|
25 |
+
print(target_layer)
|
26 |
|
27 |
def extract_gradcam(image_path=None, save_path=None):
|
28 |
if image_path is None:
|
29 |
for disease in CLASSES:
|
30 |
print("Processing", disease)
|
31 |
+
for image_path in os.listdir(r"data\test\Task 1\{}".format(disease)):
|
32 |
print("Processing", image_path)
|
33 |
+
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
34 |
+
image_name = image_path.split(".")[0].split("\\")[-1]
|
|
|
35 |
rgb_img = cv2.imread(image_path, 1)
|
36 |
rgb_img = np.float32(rgb_img) / 255
|
37 |
+
input_tensor = preprocess_image(
|
38 |
+
rgb_img, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]
|
39 |
+
)
|
40 |
input_tensor = input_tensor.to(DEVICE)
|
41 |
+
input_tensor.requires_grad = True
|
42 |
|
43 |
# Create a GradCAMPlusPlus object
|
44 |
+
cam = GradCAMPlusPlus(
|
45 |
+
model=model, target_layers=[target_layer], use_cuda=True
|
46 |
+
)
|
47 |
|
48 |
# Generate the GradCAM heatmap
|
49 |
grayscale_cam = cam(input_tensor=input_tensor)[0]
|
50 |
|
51 |
# Apply a colormap to the grayscale heatmap
|
52 |
+
heatmap_colored = cv2.applyColorMap(
|
53 |
+
np.uint8(255 * grayscale_cam), cv2.COLORMAP_JET
|
54 |
+
)
|
55 |
|
56 |
# Ensure heatmap_colored has the same dtype as rgb_img
|
57 |
heatmap_colored = heatmap_colored.astype(np.float32) / 255
|
|
|
63 |
final_output = cv2.addWeighted(rgb_img, 0.3, heatmap_colored, 0.7, 0)
|
64 |
|
65 |
# Save the final output
|
66 |
+
os.makedirs(f"docs/evaluation/gradcam/{disease}", exist_ok=True)
|
67 |
+
cv2.imwrite(
|
68 |
+
f"docs/evaluation/gradcam/{disease}/{image_name}.jpg",
|
69 |
+
(final_output * 255).astype(np.uint8),
|
70 |
+
)
|
71 |
else:
|
72 |
+
rgb_img = cv2.imread(image_path, 1)
|
73 |
+
rgb_img = np.float32(rgb_img) / 255
|
74 |
+
input_tensor = preprocess_image(
|
75 |
+
rgb_img, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]
|
76 |
+
)
|
77 |
+
input_tensor = input_tensor.to(DEVICE)
|
78 |
+
input_tensor.requires_grad = True
|
79 |
+
|
80 |
+
# Create a GradCAMPlusPlus object
|
81 |
+
cam = GradCAMPlusPlus(model=model, target_layers=[target_layer])
|
82 |
+
|
83 |
+
# Generate the GradCAM heatmap
|
84 |
+
grayscale_cam = cam(input_tensor=input_tensor)[0]
|
85 |
|
86 |
+
# Apply a colormap to the grayscale heatmap
|
87 |
+
heatmap_colored = cv2.applyColorMap(
|
88 |
+
np.uint8(255 * grayscale_cam), cv2.COLORMAP_JET
|
89 |
+
)
|
90 |
|
91 |
+
# Ensure heatmap_colored has the same dtype as rgb_img
|
92 |
+
heatmap_colored = heatmap_colored.astype(np.float32) / 255
|
93 |
|
94 |
+
# Adjust the alpha value to control transparency
|
95 |
+
alpha = 0.3 # You can change this value to make the original image more or less transparent
|
96 |
|
97 |
+
# Overlay the colored heatmap on the original image
|
98 |
+
final_output = cv2.addWeighted(rgb_img, 0.3, heatmap_colored, 0.7, 0)
|
99 |
|
100 |
+
# Save the final output
|
101 |
+
cv2.imwrite(save_path, (final_output * 255).astype(np.uint8))
|
102 |
|
103 |
+
return save_path
|
|
|
104 |
|
105 |
+
# start = time.time()
|
106 |
+
# extract_gradcam()
|
107 |
+
# end = time.time()
|
108 |
+
# print("Time taken:", end - start)
|
|
lime_eval.py β extract_lime.py
RENAMED
@@ -1,12 +1,11 @@
|
|
|
|
1 |
import numpy as np
|
2 |
from lime.lime_image import LimeImageExplainer
|
3 |
from PIL import Image
|
4 |
import torch
|
5 |
-
import torchvision.transforms as transforms
|
6 |
import matplotlib.pyplot as plt
|
7 |
-
from matplotlib.colors import Normalize
|
8 |
from configs import *
|
9 |
-
|
10 |
|
11 |
|
12 |
model = MODEL.to(DEVICE)
|
@@ -34,7 +33,6 @@ def generate_lime(image_path=None, save_path=None):
|
|
34 |
print("Processing", image_path)
|
35 |
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
36 |
image_name = image_path.split(".")[0].split("\\")[-1]
|
37 |
-
print("Processing", image_name)
|
38 |
image = Image.open(image_path).convert("RGB")
|
39 |
image = preprocess(image)
|
40 |
image = image.unsqueeze(0) # Add batch dimension
|
@@ -67,9 +65,9 @@ def generate_lime(image_path=None, save_path=None):
|
|
67 |
image = (image - np.min(image)) / (np.max(image) - np.min(image))
|
68 |
|
69 |
# image = Image.fromarray(image)
|
70 |
-
os.makedirs(f"docs/
|
71 |
-
# image.save(f'docs/
|
72 |
-
plt.imsave(f"docs/
|
73 |
|
74 |
else:
|
75 |
image = None
|
@@ -103,6 +101,15 @@ def generate_lime(image_path=None, save_path=None):
|
|
103 |
image = (image - np.min(image)) / (np.max(image) - np.min(image))
|
104 |
|
105 |
# image = Image.fromarray(image)
|
106 |
-
# os.makedirs(f"docs/
|
107 |
-
# image.save(f'docs/
|
108 |
plt.imsave(save_path, image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
import numpy as np
|
3 |
from lime.lime_image import LimeImageExplainer
|
4 |
from PIL import Image
|
5 |
import torch
|
|
|
6 |
import matplotlib.pyplot as plt
|
|
|
7 |
from configs import *
|
8 |
+
import time
|
9 |
|
10 |
|
11 |
model = MODEL.to(DEVICE)
|
|
|
33 |
print("Processing", image_path)
|
34 |
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
35 |
image_name = image_path.split(".")[0].split("\\")[-1]
|
|
|
36 |
image = Image.open(image_path).convert("RGB")
|
37 |
image = preprocess(image)
|
38 |
image = image.unsqueeze(0) # Add batch dimension
|
|
|
65 |
image = (image - np.min(image)) / (np.max(image) - np.min(image))
|
66 |
|
67 |
# image = Image.fromarray(image)
|
68 |
+
os.makedirs(f"docs/evaluation/lime/{disease}", exist_ok=True)
|
69 |
+
# image.save(f'docs/evaluation/lime/{disease}/{image_name}.jpg')
|
70 |
+
plt.imsave(f"docs/evaluation/lime/{disease}/{image_name}.jpg", image)
|
71 |
|
72 |
else:
|
73 |
image = None
|
|
|
101 |
image = (image - np.min(image)) / (np.max(image) - np.min(image))
|
102 |
|
103 |
# image = Image.fromarray(image)
|
104 |
+
# os.makedirs(f"docs/evaluation/lime/{disease}", exist_ok=True)
|
105 |
+
# image.save(f'docs/evaluation/lime/{disease}/{image_name}.jpg')
|
106 |
plt.imsave(save_path, image)
|
107 |
+
|
108 |
+
|
109 |
+
# start = time.time()
|
110 |
+
|
111 |
+
# generate_lime()
|
112 |
+
|
113 |
+
# end = time.time()
|
114 |
+
|
115 |
+
# print("Time taken:", end - start)
|
genetric_algorithm.py
CHANGED
@@ -1,34 +1,38 @@
|
|
1 |
import os
|
2 |
import optuna
|
3 |
-
from optuna.trial import TrialState
|
4 |
import torch
|
5 |
import torch.nn as nn
|
6 |
import torch.optim as optim
|
|
|
7 |
from configs import *
|
8 |
import data_loader
|
9 |
from torch.utils.tensorboard import SummaryWriter
|
|
|
10 |
import numpy as np
|
11 |
-
import
|
12 |
-
import pygad.torchga
|
13 |
|
14 |
torch.cuda.empty_cache()
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
)
|
23 |
-
NUM_GENERATIONS = 10
|
24 |
-
SOL_PER_POP = 10 # Number of solutions in the population
|
25 |
-
NUM_GENES = 2
|
26 |
-
NUM_PARENTS_MATING = 4
|
27 |
|
|
|
|
|
|
|
28 |
# Create a TensorBoard writer
|
29 |
writer = SummaryWriter(log_dir="output/tensorboard/tuning")
|
30 |
|
31 |
-
|
32 |
# Function to create or modify data loaders with the specified batch size
|
33 |
def create_data_loaders(batch_size):
|
34 |
train_loader, valid_loader = data_loader.load_data(
|
@@ -38,211 +42,204 @@ def create_data_loaders(batch_size):
|
|
38 |
)
|
39 |
return train_loader, valid_loader
|
40 |
|
|
|
|
|
|
|
|
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
train_loader, valid_loader = create_data_loaders(batch_size)
|
48 |
-
|
49 |
-
lr = trial.suggest_float("lr", 1e-5, 1e-3, log=True)
|
50 |
optimizer = optim.Adam(model.parameters(), lr=lr)
|
51 |
criterion = nn.CrossEntropyLoss()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
-
|
54 |
-
|
|
|
55 |
|
56 |
-
|
|
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
print(f"{study.best_trial.params}")
|
62 |
|
63 |
-
|
64 |
-
print(f"Batch Size: {batch_size}")
|
65 |
-
print(f"Learning Rate: {lr}")
|
66 |
-
print(f"Gamma: {gamma}\n")
|
67 |
|
68 |
-
|
69 |
-
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
for epoch in range(EPOCHS):
|
72 |
model.train()
|
73 |
for batch_idx, (data, target) in enumerate(train_loader, 0):
|
74 |
data, target = data.to(DEVICE), target.to(DEVICE)
|
75 |
optimizer.zero_grad()
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
78 |
loss.backward()
|
79 |
optimizer.step()
|
80 |
|
81 |
-
|
82 |
-
|
83 |
model.eval()
|
84 |
correct = 0
|
85 |
with torch.no_grad():
|
86 |
for batch_idx, (data, target) in enumerate(valid_loader, 0):
|
87 |
data, target = data.to(DEVICE), target.to(DEVICE)
|
|
|
88 |
output = model(data)
|
89 |
pred = output.argmax(dim=1, keepdim=True)
|
90 |
correct += pred.eq(target.view_as(pred)).sum().item()
|
91 |
|
92 |
accuracy = correct / len(valid_loader.dataset)
|
93 |
|
94 |
-
# Log
|
95 |
-
writer.add_scalar("Accuracy", accuracy,
|
96 |
-
|
97 |
-
|
98 |
-
{"accuracy": accuracy},
|
99 |
-
)
|
100 |
-
|
101 |
-
print(f"[EPOCH {epoch + 1}] Accuracy: {accuracy:.4f}")
|
102 |
-
|
103 |
-
trial.report(accuracy, epoch)
|
104 |
-
|
105 |
-
if accuracy > best_accuracy:
|
106 |
-
best_accuracy = accuracy
|
107 |
-
early_stopping_counter = 0
|
108 |
-
else:
|
109 |
-
early_stopping_counter += 1
|
110 |
-
|
111 |
-
# Early stopping check
|
112 |
-
if early_stopping_counter >= EARLY_STOPPING_PATIENCE:
|
113 |
-
print(f"\nEarly stopping at epoch {epoch + 1}")
|
114 |
-
break
|
115 |
-
|
116 |
-
if trial.number > 10 and trial.params["lr"] < 1e-3 and best_accuracy < 0.7:
|
117 |
-
return float("inf")
|
118 |
-
|
119 |
-
past_trials += 1
|
120 |
-
|
121 |
-
return best_accuracy
|
122 |
-
|
123 |
-
|
124 |
-
# Custom genetic algorithm
|
125 |
-
def run_genetic_algorithm(fitness_func):
|
126 |
-
# Initial population
|
127 |
-
population = np.random.rand(SOL_PER_POP, NUM_GENES) # Random initialization
|
128 |
-
|
129 |
-
# Run for a fixed number of generations
|
130 |
-
for generation in range(NUM_GENERATIONS):
|
131 |
-
# Calculate fitness for each solution in the population
|
132 |
-
fitness = np.array(
|
133 |
-
[fitness_func(solution, idx) for idx, solution in enumerate(population)]
|
134 |
-
)
|
135 |
-
|
136 |
-
# Get the index of the best solution
|
137 |
-
best_idx = np.argmax(fitness)
|
138 |
-
best_solution = population[best_idx]
|
139 |
-
best_fitness = fitness[best_idx]
|
140 |
-
|
141 |
-
# Print the best solution and fitness for this generation
|
142 |
-
print(f"Generation {generation + 1}:")
|
143 |
-
print("Best Solution:")
|
144 |
-
print("Learning Rate = {lr}".format(lr=best_solution[0]))
|
145 |
-
print("Gamma = {gamma}".format(gamma=best_solution[1]))
|
146 |
-
print("Best Fitness = {fitness}".format(fitness=best_fitness))
|
147 |
-
|
148 |
-
# Perform selection and crossover to create the next generation
|
149 |
-
population = selection_and_crossover(population, fitness)
|
150 |
-
|
151 |
-
|
152 |
-
# Selection and crossover logic
|
153 |
-
def selection_and_crossover(population, fitness):
|
154 |
-
# Perform tournament selection
|
155 |
-
parents = []
|
156 |
-
for _ in range(SOL_PER_POP):
|
157 |
-
tournament_idxs = np.random.choice(range(SOL_PER_POP), NUM_PARENTS_MATING)
|
158 |
-
tournament_fitness = [fitness[idx] for idx in tournament_idxs]
|
159 |
-
selected_parent_idx = tournament_idxs[np.argmax(tournament_fitness)]
|
160 |
-
parents.append(population[selected_parent_idx])
|
161 |
-
|
162 |
-
# Perform single-point crossover
|
163 |
-
offspring = []
|
164 |
-
for i in range(0, SOL_PER_POP, 2):
|
165 |
-
if i + 1 < SOL_PER_POP:
|
166 |
-
crossover_point = np.random.randint(0, NUM_GENES)
|
167 |
-
offspring.extend(
|
168 |
-
[
|
169 |
-
np.concatenate(
|
170 |
-
(parents[i][:crossover_point], parents[i + 1][crossover_point:])
|
171 |
-
)
|
172 |
-
]
|
173 |
-
)
|
174 |
-
offspring.extend(
|
175 |
-
[
|
176 |
-
np.concatenate(
|
177 |
-
(parents[i + 1][:crossover_point], parents[i][crossover_point:])
|
178 |
-
)
|
179 |
-
]
|
180 |
-
)
|
181 |
-
|
182 |
-
return np.array(offspring)
|
183 |
-
|
184 |
-
|
185 |
-
# Modify callback function to log best accuracy
|
186 |
-
def callback_generation(ga_instance):
|
187 |
-
global study
|
188 |
-
|
189 |
-
# Fetch the parameters of the best solution
|
190 |
-
solution, solution_fitness, _ = ga_instance.best_solution()
|
191 |
-
best_learning_rate, best_gamma = solution
|
192 |
-
|
193 |
-
# Report the best accuracy to Optuna study
|
194 |
-
study.set_user_attr("best_accuracy", solution_fitness)
|
195 |
-
|
196 |
-
# Print generation number and best fitness
|
197 |
-
print(
|
198 |
-
"Generation = {generation}".format(generation=ga_instance.generations_completed)
|
199 |
-
)
|
200 |
-
print("Best Fitness = {fitness}".format(fitness=solution_fitness))
|
201 |
-
print("Best Learning Rate = {lr}".format(lr=best_learning_rate))
|
202 |
-
print("Best Gamma = {gamma}".format(gamma=best_gamma))
|
203 |
|
|
|
|
|
204 |
|
205 |
if __name__ == "__main__":
|
206 |
-
global study
|
207 |
pruner = optuna.pruners.HyperbandPruner()
|
|
|
|
|
208 |
study = optuna.create_study(
|
209 |
direction="maximize",
|
210 |
pruner=pruner,
|
211 |
-
study_name="
|
|
|
212 |
)
|
213 |
|
214 |
-
|
215 |
-
# You need to populate these with your own data
|
216 |
|
217 |
-
|
218 |
-
loss_function = nn.CrossEntropyLoss()
|
219 |
|
220 |
-
|
221 |
-
global data_inputs, data_outputs, model, loss_function
|
222 |
|
223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
|
225 |
-
|
226 |
-
optimizer = torch.optim.SGD(
|
227 |
-
model.parameters(), lr=learning_rate, momentum=momentum
|
228 |
-
)
|
229 |
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
)
|
234 |
-
model.load_state_dict(model_weights_dict)
|
235 |
|
236 |
-
|
237 |
-
predictions = model(data_inputs)
|
238 |
|
239 |
-
|
240 |
-
|
241 |
|
242 |
-
|
243 |
-
solution_fitness = 1.0 / (loss.detach().numpy() + 1e-8)
|
244 |
|
245 |
-
|
|
|
|
|
|
|
246 |
|
247 |
-
|
248 |
-
|
|
|
|
1 |
import os
|
2 |
import optuna
|
|
|
3 |
import torch
|
4 |
import torch.nn as nn
|
5 |
import torch.optim as optim
|
6 |
+
import torch.utils.data
|
7 |
from configs import *
|
8 |
import data_loader
|
9 |
from torch.utils.tensorboard import SummaryWriter
|
10 |
+
import time
|
11 |
import numpy as np
|
12 |
+
import random
|
|
|
13 |
|
14 |
torch.cuda.empty_cache()
|
15 |
+
RANDOM_SEED1=42
|
16 |
+
random.seed(RANDOM_SEED1)
|
17 |
+
torch.cuda.manual_seed(RANDOM_SEED1)
|
18 |
+
torch.manual_seed(RANDOM_SEED1)
|
19 |
+
print("PyTorch Seed:", torch.initial_seed())
|
20 |
+
print("Random Seed:", random.getstate()[1][0])
|
21 |
+
print("PyTorch CUDA Seed:", torch.cuda.initial_seed())
|
22 |
+
|
23 |
|
24 |
+
# Define the constants for genetic algorithm
|
25 |
+
POPULATION_SIZE = 5
|
26 |
+
MUTATION_RATE = 0.05
|
27 |
+
CROSSOVER_RATE = 0.7
|
28 |
+
NUM_GENERATIONS = 5
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
EPOCHS = 5
|
31 |
+
|
32 |
+
EARLY_STOPPING_PATIENCE = 4
|
33 |
# Create a TensorBoard writer
|
34 |
writer = SummaryWriter(log_dir="output/tensorboard/tuning")
|
35 |
|
|
|
36 |
# Function to create or modify data loaders with the specified batch size
|
37 |
def create_data_loaders(batch_size):
|
38 |
train_loader, valid_loader = data_loader.load_data(
|
|
|
42 |
)
|
43 |
return train_loader, valid_loader
|
44 |
|
45 |
+
# Create a TensorBoard writer
|
46 |
+
writer = SummaryWriter(log_dir="output/tensorboard/tuning")
|
47 |
+
model = MODEL.to(DEVICE)
|
48 |
+
# model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=DEVICE))
|
49 |
|
50 |
+
def fitness_function(individual,model):
|
51 |
+
batch_size, lr,= individual
|
52 |
+
|
53 |
+
# Assuming you have a model, optimizer, and loss function defined
|
54 |
+
model = model.to(DEVICE)
|
|
|
|
|
|
|
55 |
optimizer = optim.Adam(model.parameters(), lr=lr)
|
56 |
criterion = nn.CrossEntropyLoss()
|
57 |
+
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=NUM_EPOCHS)
|
58 |
+
# Define your data loaders using the given batch_size
|
59 |
+
train_loader, valid_loader = create_data_loaders(batch_size)
|
60 |
+
|
61 |
+
# Training loop
|
62 |
+
for epoch in range(EPOCHS):
|
63 |
+
model.train()
|
64 |
+
for batch_idx, (data, target) in enumerate(train_loader, 0):
|
65 |
+
data, target = data.to(DEVICE), target.to(DEVICE)
|
66 |
+
optimizer.zero_grad()
|
67 |
+
if model.__class__.__name__ == "GoogLeNet":
|
68 |
+
output = model(data).logits
|
69 |
+
else:
|
70 |
+
output = model(data)
|
71 |
+
loss = criterion(output, target)
|
72 |
+
loss.backward()
|
73 |
+
optimizer.step()
|
74 |
+
scheduler.step()
|
75 |
+
# Validation loop
|
76 |
+
model.eval()
|
77 |
+
correct = 0
|
78 |
+
with torch.no_grad():
|
79 |
+
for batch_idx, (data, target) in enumerate(valid_loader, 0):
|
80 |
+
data, target = data.to(DEVICE), target.to(DEVICE)
|
81 |
+
data, targets_a, targets_b, lam = cutmix_data(data, target, alpha=1)
|
82 |
+
output = model(data)
|
83 |
+
pred = output.argmax(dim=1, keepdim=True)
|
84 |
+
correct += pred.eq(target.view_as(pred)).sum().item()
|
85 |
+
|
86 |
+
accuracy = correct / len(valid_loader.dataset)
|
87 |
+
print(f"Epoch {epoch + 1}/{EPOCHS}, Accuracy: {accuracy:.4f}")
|
88 |
+
return accuracy,
|
89 |
+
|
90 |
+
def rand_bbox(size, lam):
|
91 |
+
W = size[2]
|
92 |
+
H = size[3]
|
93 |
+
cut_rat = np.sqrt(1.0 - lam)
|
94 |
+
cut_w = np.int_(W * cut_rat)
|
95 |
+
cut_h = np.int_(H * cut_rat)
|
96 |
+
|
97 |
+
# uniform
|
98 |
+
cx = np.random.randint(W)
|
99 |
+
cy = np.random.randint(H)
|
100 |
+
|
101 |
+
bbx1 = np.clip(cx - cut_w // 2, 0, W)
|
102 |
+
bby1 = np.clip(cy - cut_h // 2, 0, H)
|
103 |
+
bbx2 = np.clip(cx + cut_w // 2, 0, W)
|
104 |
+
bby2 = np.clip(cy + cut_h // 2, 0, H)
|
105 |
+
|
106 |
+
return bbx1, bby1, bbx2, bby2
|
107 |
+
|
108 |
+
def cutmix_data(input, target, alpha=1.0):
|
109 |
+
if alpha > 0:
|
110 |
+
lam = np.random.beta(alpha, alpha)
|
111 |
+
else:
|
112 |
+
lam = 1
|
113 |
|
114 |
+
batch_size = input.size()[0]
|
115 |
+
index = torch.randperm(batch_size)
|
116 |
+
rand_index = torch.randperm(input.size()[0])
|
117 |
|
118 |
+
bbx1, bby1, bbx2, bby2 = rand_bbox(input.size(), lam)
|
119 |
+
input[:, :, bbx1:bbx2, bby1:bby2] = input[rand_index, :, bbx1:bbx2, bby1:bby2]
|
120 |
|
121 |
+
lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (input.size()[-1] * input.size()[-2]))
|
122 |
+
targets_a = target
|
123 |
+
targets_b = target[rand_index]
|
|
|
124 |
|
125 |
+
return input, targets_a, targets_b, lam
|
|
|
|
|
|
|
126 |
|
127 |
+
def cutmix_criterion(criterion, outputs, targets_a, targets_b, lam):
|
128 |
+
return lam * criterion(outputs, targets_a) + (1 - lam) * criterion(outputs, targets_b)
|
129 |
|
130 |
+
# Function to create or modify data loaders with the specified batch size
|
131 |
+
def create_data_loaders(batch_size):
|
132 |
+
print(f"Batch Size (before conversion): {batch_size}")
|
133 |
+
batch_size = int(batch_size) # Ensure batch_size is an integer
|
134 |
+
print(f"Batch Size (after conversion): {batch_size}")
|
135 |
+
train_loader, valid_loader = data_loader.load_data(
|
136 |
+
COMBINED_DATA_DIR + "1",
|
137 |
+
preprocess,
|
138 |
+
batch_size=batch_size,
|
139 |
+
)
|
140 |
+
return train_loader, valid_loader
|
141 |
+
|
142 |
+
# Genetic algorithm initialization functions
|
143 |
+
def create_individual():
|
144 |
+
lr = abs(np.random.uniform(0.0006, 0.0009))
|
145 |
+
print(f"Generated lr: {lr}")
|
146 |
+
return creator.Individual([
|
147 |
+
int(np.random.choice([32])), # Choose a valid batch size
|
148 |
+
lr, # lr in log scale between 1e-4 and 1e-2
|
149 |
+
])
|
150 |
+
# Genetic algorithm evaluation function
|
151 |
+
def evaluate_individual(individual, model=MODEL):
|
152 |
+
batch_size, lr, = individual
|
153 |
+
lr=abs(lr)
|
154 |
+
# Assuming you have a model, optimizer, and loss function defined
|
155 |
+
model = model.to(DEVICE)
|
156 |
+
optimizer = optim.Adam(model.parameters(), lr=lr)
|
157 |
+
criterion = nn.CrossEntropyLoss()
|
158 |
+
|
159 |
+
# Define your data loaders using the given batch_size
|
160 |
+
train_loader, valid_loader = create_data_loaders(batch_size)
|
161 |
+
|
162 |
+
# Training loop
|
163 |
for epoch in range(EPOCHS):
|
164 |
model.train()
|
165 |
for batch_idx, (data, target) in enumerate(train_loader, 0):
|
166 |
data, target = data.to(DEVICE), target.to(DEVICE)
|
167 |
optimizer.zero_grad()
|
168 |
+
# Apply CutMix
|
169 |
+
data, targets_a, targets_b, lam = cutmix_data(data, target, alpha=1)
|
170 |
+
if model.__class__.__name__ == "GoogLeNet":
|
171 |
+
output = model(data).logits
|
172 |
+
else:
|
173 |
+
output = model(data)
|
174 |
+
loss = cutmix_criterion(criterion, output, targets_a, targets_b, lam)
|
175 |
loss.backward()
|
176 |
optimizer.step()
|
177 |
|
178 |
+
# Validation loop
|
|
|
179 |
model.eval()
|
180 |
correct = 0
|
181 |
with torch.no_grad():
|
182 |
for batch_idx, (data, target) in enumerate(valid_loader, 0):
|
183 |
data, target = data.to(DEVICE), target.to(DEVICE)
|
184 |
+
data, targets_a, targets_b, lam = cutmix_data(data, target, alpha=1)
|
185 |
output = model(data)
|
186 |
pred = output.argmax(dim=1, keepdim=True)
|
187 |
correct += pred.eq(target.view_as(pred)).sum().item()
|
188 |
|
189 |
accuracy = correct / len(valid_loader.dataset)
|
190 |
|
191 |
+
# Log accuracy or other metrics as needed
|
192 |
+
writer.add_scalar("Accuracy", accuracy, epoch)
|
193 |
+
|
194 |
+
print(f"Epoch {epoch + 1}/{EPOCHS}, Accuracy: {accuracy:.4f}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
+
# Return the accuracy (or any other metric you want to optimize)
|
197 |
+
return (accuracy,)
|
198 |
|
199 |
if __name__ == "__main__":
|
|
|
200 |
pruner = optuna.pruners.HyperbandPruner()
|
201 |
+
|
202 |
+
start_time = time.time()
|
203 |
study = optuna.create_study(
|
204 |
direction="maximize",
|
205 |
pruner=pruner,
|
206 |
+
study_name="hyperparameter_optimization",
|
207 |
+
storage="sqlite:///" + MODEL.__class__.__name__ + ".sqlite3",
|
208 |
)
|
209 |
|
210 |
+
from deap import base, creator, tools, algorithms
|
|
|
211 |
|
212 |
+
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
|
|
|
213 |
|
214 |
+
creator.create("Individual", list, fitness=creator.FitnessMax)
|
|
|
215 |
|
216 |
+
toolbox = base.Toolbox()
|
217 |
+
toolbox.register("individual", create_individual)
|
218 |
+
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
|
219 |
+
toolbox.register("evaluate", fitness_function, model=model)
|
220 |
+
toolbox.register("mate", tools.cxTwoPoint)
|
221 |
+
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=0.1, indpb=MUTATION_RATE)
|
222 |
+
toolbox.register("select", tools.selTournament, tournsize=3)
|
223 |
|
224 |
+
population = toolbox.population(n=POPULATION_SIZE)
|
|
|
|
|
|
|
225 |
|
226 |
+
for ind in population:
|
227 |
+
print(type(ind))
|
228 |
+
fitness_value = evaluate_individual(ind, model)
|
229 |
+
ind.fitness.values = (fitness_value[0],)
|
|
|
230 |
|
231 |
+
algorithms.eaSimple(population, toolbox, cxpb=CROSSOVER_RATE, mutpb=MUTATION_RATE, ngen=NUM_GENERATIONS, stats=None, halloffame=None, verbose=True)
|
|
|
232 |
|
233 |
+
best_individual = tools.selBest(population, 1)[0]
|
234 |
+
best_batch_size, best_lr = best_individual
|
235 |
|
236 |
+
best_accuracy = evaluate_individual(best_individual, model)
|
|
|
237 |
|
238 |
+
print("Best Hyperparameters:")
|
239 |
+
print(f"Batch Size: {best_batch_size}")
|
240 |
+
print(f"Learning Rate: {best_lr}")
|
241 |
+
print(f"Best Accuracy: {best_accuracy[0]}")
|
242 |
|
243 |
+
end_time = time.time()
|
244 |
+
tuning_duration = end_time - start_time
|
245 |
+
print(f"Hyperparameter optimization took {tuning_duration:.2f} seconds.")
|
lazy_predict.py
DELETED
@@ -1,60 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import torch
|
3 |
-
import torch.nn as nn
|
4 |
-
import torch.optim as optim
|
5 |
-
import matplotlib.pyplot as plt
|
6 |
-
from models import *
|
7 |
-
from torch.utils.tensorboard import SummaryWriter
|
8 |
-
from configs import *
|
9 |
-
import data_loader
|
10 |
-
import numpy as np
|
11 |
-
from lazypredict.Supervised import LazyClassifier
|
12 |
-
from sklearn.utils import shuffle
|
13 |
-
|
14 |
-
def extract_features_labels(loader):
|
15 |
-
data = []
|
16 |
-
labels = []
|
17 |
-
for inputs, labels_batch in loader:
|
18 |
-
for img in inputs:
|
19 |
-
data.append(img.view(-1).numpy())
|
20 |
-
labels.extend(labels_batch.numpy())
|
21 |
-
return np.array(data), np.array(labels)
|
22 |
-
|
23 |
-
def load_and_preprocess_data():
|
24 |
-
train_loader, valid_loader = data_loader.load_data(
|
25 |
-
RAW_DATA_DIR + str(TASK),
|
26 |
-
AUG_DATA_DIR + str(TASK),
|
27 |
-
EXTERNAL_DATA_DIR + str(TASK),
|
28 |
-
preprocess,
|
29 |
-
)
|
30 |
-
return train_loader, valid_loader
|
31 |
-
|
32 |
-
def initialize_model_optimizer_scheduler(train_loader, valid_loader):
|
33 |
-
model = MODEL.to(DEVICE)
|
34 |
-
criterion = nn.CrossEntropyLoss()
|
35 |
-
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
|
36 |
-
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=NUM_EPOCHS)
|
37 |
-
return model, criterion, optimizer, scheduler
|
38 |
-
|
39 |
-
# Load and preprocess data
|
40 |
-
train_loader, valid_loader = load_and_preprocess_data()
|
41 |
-
|
42 |
-
# Initialize the model, criterion, optimizer, and scheduler
|
43 |
-
model, criterion, optimizer, scheduler = initialize_model_optimizer_scheduler(train_loader, valid_loader)
|
44 |
-
|
45 |
-
# Extract features and labels
|
46 |
-
X_train, y_train = extract_features_labels(train_loader)
|
47 |
-
X_valid, y_valid = extract_features_labels(valid_loader)
|
48 |
-
|
49 |
-
# LazyClassifier
|
50 |
-
clf = LazyClassifier(verbose=0, ignore_warnings=True, custom_metric=None)
|
51 |
-
models, predictions = clf.fit(X_train, X_valid, y_train, y_valid)
|
52 |
-
|
53 |
-
print("Models:", models)
|
54 |
-
print("Predictions:", predictions)
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lrp-eval.py
DELETED
@@ -1,16 +0,0 @@
|
|
1 |
-
import torch
|
2 |
-
from torchvision.models import vgg16, VGG16_Weights
|
3 |
-
from src.lrp import LRPModel
|
4 |
-
from configs import *
|
5 |
-
from PIL import Image
|
6 |
-
|
7 |
-
|
8 |
-
image = Image.open(r'data\test\Task 1\Alzheimer Disease\0d846ee1-c90d-4ed5-8467-3550dd653858.png').convert("RGB")
|
9 |
-
image = preprocess(image).unsqueeze(0)
|
10 |
-
image = image.to(DEVICE)
|
11 |
-
model = MODEL.to(DEVICE)
|
12 |
-
print(dict(model.named_modules()))
|
13 |
-
model.load_state_dict(torch.load(MODEL_SAVE_PATH, map_location=DEVICE))
|
14 |
-
model.eval()
|
15 |
-
lrp_model = LRPModel(model)
|
16 |
-
r = lrp_model.forward(image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
models.py
CHANGED
@@ -1,69 +1,22 @@
|
|
1 |
-
|
2 |
-
# This file stores all the models used in the project.#
|
3 |
-
#######################################################
|
4 |
-
|
5 |
-
# Import all models from torchvision.models
|
6 |
-
from torchvision.models import resnet50
|
7 |
-
from torchvision.models import resnet18
|
8 |
-
from torchvision.models import squeezenet1_0
|
9 |
-
from torchvision.models import vgg16
|
10 |
-
from torchvision.models import alexnet
|
11 |
-
from torchvision.models import densenet121
|
12 |
-
from torchvision.models import googlenet
|
13 |
-
from torchvision.models import inception_v3
|
14 |
-
from torchvision.models import mobilenet_v2
|
15 |
-
from torchvision.models import mobilenet_v3_small
|
16 |
-
from torchvision.models import mobilenet_v3_large
|
17 |
-
from torchvision.models import shufflenet_v2_x0_5
|
18 |
-
from torchvision.models import vgg11
|
19 |
-
from torchvision.models import vgg11_bn
|
20 |
-
from torchvision.models import vgg13
|
21 |
-
from torchvision.models import vgg13_bn
|
22 |
-
from torchvision.models import vgg16_bn
|
23 |
-
from torchvision.models import vgg19_bn
|
24 |
-
from torchvision.models import vgg19
|
25 |
-
from torchvision.models import wide_resnet50_2
|
26 |
-
from torchvision.models import wide_resnet101_2
|
27 |
-
from torchvision.models import mnasnet0_5
|
28 |
-
from torchvision.models import mnasnet0_75
|
29 |
-
from torchvision.models import mnasnet1_0
|
30 |
-
from torchvision.models import mnasnet1_3
|
31 |
-
from torchvision.models import resnext50_32x4d
|
32 |
-
from torchvision.models import resnext101_32x8d
|
33 |
-
from torchvision.models import shufflenet_v2_x1_0
|
34 |
-
from torchvision.models import shufflenet_v2_x1_5
|
35 |
-
from torchvision.models import shufflenet_v2_x2_0
|
36 |
-
from torchvision.models import squeezenet1_1
|
37 |
-
from torchvision.models import efficientnet_v2_s
|
38 |
-
from torchvision.models import efficientnet_v2_m
|
39 |
-
from torchvision.models import efficientnet_v2_l
|
40 |
-
from torchvision.models import efficientnet_b0
|
41 |
-
from torchvision.models import efficientnet_b1
|
42 |
import torch
|
43 |
import torch.nn as nn
|
44 |
|
45 |
-
class WeightedVoteEnsemble(nn.Module):
|
46 |
-
def __init__(self, models, weights):
|
47 |
-
super(WeightedVoteEnsemble, self).__init__()
|
48 |
-
self.models = models
|
49 |
-
self.weights = weights
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
)
|
56 |
-
avg_predictions = weighted_predictions.sum(dim=0)
|
57 |
-
return avg_predictions
|
58 |
-
|
59 |
-
|
60 |
-
def ensemble_predictions(models, image):
|
61 |
-
all_predictions = []
|
62 |
-
|
63 |
-
with torch.no_grad():
|
64 |
-
for model in models:
|
65 |
-
output = model(image)
|
66 |
-
all_predictions.append(output)
|
67 |
-
|
68 |
-
return torch.stack(all_predictions, dim=0).mean(dim=0)
|
69 |
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from torchvision.models import efficientnet_b3, EfficientNet_B3_Weights
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import torch
|
3 |
import torch.nn as nn
|
4 |
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
+
class EfficientNetB3WithNorm(nn.Module):
|
7 |
+
def __init__(self, num_classes):
|
8 |
+
super(EfficientNetB3WithNorm, self).__init__()
|
9 |
+
efficientnet = efficientnet_b3(weights=EfficientNet_B3_Weights.DEFAULT)
|
10 |
+
self.features = efficientnet.features
|
11 |
+
self.classifier = nn.Sequential(
|
12 |
+
nn.Conv2d(1536, num_classes, kernel_size=1),
|
13 |
+
nn.BatchNorm2d(num_classes), # add batch normalization
|
14 |
+
nn.ReLU(inplace=True),
|
15 |
+
nn.AdaptiveAvgPool2d((1, 1)),
|
16 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
def forward(self, x):
|
19 |
+
x = self.features(x)
|
20 |
+
x = self.classifier(x)
|
21 |
+
x = torch.flatten(x, 1)
|
22 |
+
return x
|
tuning.py β optuna_unused.py
RENAMED
File without changes
|
plot-gradcam.py
DELETED
@@ -1,65 +0,0 @@
|
|
1 |
-
# Plot a table, each column is a test image, separate to 7 tables (one for each disease), each column have 4 rows, one is disease name, one is gradcam, one is lime, one is original image
|
2 |
-
|
3 |
-
import os
|
4 |
-
import cv2
|
5 |
-
import numpy as np
|
6 |
-
import torch
|
7 |
-
import torchvision.transforms as transforms
|
8 |
-
import matplotlib.pyplot as plt
|
9 |
-
from matplotlib.colors import Normalize
|
10 |
-
from configs import *
|
11 |
-
from sklearn.preprocessing import minmax_scale
|
12 |
-
|
13 |
-
plt.rcParams["font.family"] = "Times New Roman"
|
14 |
-
|
15 |
-
# Plot a table, each column is a test image, separate to 7 plot (one for each disease), each column have 4 rows, one is disease name, one is gradcam, one is lime, one is original image, the images are in 'docs/efficientnet/gradcam' and 'docs/efficientnet/lime' and 'data/test/Task 1'
|
16 |
-
|
17 |
-
|
18 |
-
def plot_table():
|
19 |
-
diseases = CLASSES
|
20 |
-
diseases.sort()
|
21 |
-
# diseases = ["Atelectasis", "Cardiomegaly", "Consolidation", "Edema", "Effusion", "Emphysema", "Fibrosis", "Hernia", "Infiltration", "Mass", "Nodule", "Pleural_Thickening", "Pneumonia", "Pneumothorax"]
|
22 |
-
print(diseases)
|
23 |
-
fig, axs = plt.subplots(4, 14, figsize=(20, 10))
|
24 |
-
fig.tight_layout()
|
25 |
-
for i, disease in enumerate(diseases):
|
26 |
-
# Create a new plot
|
27 |
-
print("Processing", disease)
|
28 |
-
axs[0, i].axis("off")
|
29 |
-
axs[0, i].set_title(disease)
|
30 |
-
axs[1, i].axis("off")
|
31 |
-
axs[1, i].set_title("GradCAM")
|
32 |
-
axs[2, i].axis("off")
|
33 |
-
axs[2, i].set_title("LIME")
|
34 |
-
axs[3, i].axis("off")
|
35 |
-
axs[3, i].set_title("Original")
|
36 |
-
# For each image in test folder, there are corresponding ones in gradcam folder and lime folder, plot it accordingly
|
37 |
-
for j, image_path in enumerate(os.listdir(r"data\test\Task 1\{}".format(disease))):
|
38 |
-
print("Processing", image_path)
|
39 |
-
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
40 |
-
image_name = image_path.split(".")[0].split("\\")[-1]
|
41 |
-
print("Processing", image_name)
|
42 |
-
# Plot the original image
|
43 |
-
image = cv2.imread(image_path, 1)
|
44 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
45 |
-
axs[3, i].imshow(image)
|
46 |
-
# Plot the gradcam image
|
47 |
-
image = cv2.imread(
|
48 |
-
f"docs/efficientnet/gradcam/{disease}/{image_name}.jpg", 1
|
49 |
-
)
|
50 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
51 |
-
axs[1, i].imshow(image)
|
52 |
-
# Plot the lime image
|
53 |
-
image = cv2.imread(
|
54 |
-
f"docs/efficientnet/lime/{disease}/{image_name}.jpg", 1
|
55 |
-
)
|
56 |
-
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
57 |
-
axs[2, i].imshow(image)
|
58 |
-
# # Plot the disease name
|
59 |
-
# axs[0, i].text(0.5, 0.5, disease, horizontalalignment="center")
|
60 |
-
plt.savefig("docs/efficientnet/table.png")
|
61 |
-
plt.show()
|
62 |
-
|
63 |
-
if __name__ == "__main__":
|
64 |
-
plot_table()
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plot_structure.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# from torchview import draw_graph
|
2 |
+
from torchviz import make_dot
|
3 |
+
from configs import *
|
4 |
+
import os
|
5 |
+
# import graphviz
|
6 |
+
|
7 |
+
# when running on VSCode run the below command
|
8 |
+
# svg format on vscode does not give desired result
|
9 |
+
# graphviz.set_jupyter_format("png")
|
10 |
+
|
11 |
+
model = EfficientNetB3WithNorm(num_classes=7)
|
12 |
+
|
13 |
+
batch_size = 2
|
14 |
+
# device='meta' -> no memory is consumed for visualization
|
15 |
+
# model_graph = draw_graph(model, input_size=(32, 3, 224, 224), save_graph=True, filename="model_graph.png")
|
16 |
+
# model_graph.visual_graph
|
17 |
+
|
18 |
+
model_graph = make_dot(
|
19 |
+
model(torch.randn(batch_size, 3, 224, 224)), params=dict(model.named_parameters())
|
20 |
+
).render("torchviz", format="png")
|
plot_training_metrics.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import matplotlib.pyplot as plt
|
3 |
+
|
4 |
+
# Load data from the CSV file
|
5 |
+
df = pd.read_csv('training_metrics.csv')
|
6 |
+
|
7 |
+
# Extract data
|
8 |
+
epochs = df['Epoch']
|
9 |
+
train_loss = df['Train Loss']
|
10 |
+
train_accuracy = df['Train Accuracy']
|
11 |
+
validation_loss = df['Validation Loss']
|
12 |
+
validation_accuracy = df['Validation Accuracy']
|
13 |
+
|
14 |
+
# Create subplots for loss and accuracy
|
15 |
+
plt.figure(figsize=(12, 5))
|
16 |
+
|
17 |
+
# Loss subplot
|
18 |
+
plt.subplot(1, 2, 1)
|
19 |
+
plt.plot(epochs, train_loss, label='Train Loss', marker='o')
|
20 |
+
plt.plot(epochs, validation_loss, label='Validation Loss', marker='o')
|
21 |
+
plt.title('Training and Validation Loss')
|
22 |
+
plt.xlabel('Epoch')
|
23 |
+
plt.ylabel('Loss')
|
24 |
+
plt.legend()
|
25 |
+
|
26 |
+
# Accuracy subplot
|
27 |
+
plt.subplot(1, 2, 2)
|
28 |
+
plt.plot(epochs, train_accuracy, label='Train Accuracy', marker='o')
|
29 |
+
plt.plot(epochs, validation_accuracy, label='Validation Accuracy', marker='o')
|
30 |
+
plt.title('Training and Validation Accuracy')
|
31 |
+
plt.xlabel('Epoch')
|
32 |
+
plt.ylabel('Accuracy')
|
33 |
+
plt.legend()
|
34 |
+
|
35 |
+
plt.tight_layout()
|
36 |
+
plt.show()
|
requirements.txt
CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
|
|
shap_eval.py
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
# Import necessary libraries
|
2 |
-
import shap
|
3 |
-
import torch
|
4 |
-
import numpy as np
|
5 |
-
|
6 |
-
# Load your EfficientNetB3 model
|
7 |
-
from torchvision import models
|
8 |
-
|
9 |
-
# Load your test data
|
10 |
-
from data_loader import load_test_data # Replace with your actual data loader function
|
11 |
-
from configs import *
|
12 |
-
|
13 |
-
# Define your EfficientNetB3 model and load its pre-trained weights
|
14 |
-
model = MODEL
|
15 |
-
|
16 |
-
# Set your model to evaluation mode
|
17 |
-
model.eval()
|
18 |
-
|
19 |
-
# Load your test data using your data loader
|
20 |
-
test_loader = load_test_data(TEST_DATA_DIR + "1", preprocess) # Replace with your test data loader
|
21 |
-
|
22 |
-
# Choose a specific image from the test dataset
|
23 |
-
image, _ = next(iter(test_loader))
|
24 |
-
|
25 |
-
# Make sure your model and input data are on the same device (CPU or GPU)
|
26 |
-
device = DEVICE
|
27 |
-
model = model.to(device)
|
28 |
-
image = image.to(device)
|
29 |
-
|
30 |
-
# Initialize an explainer for your model using SHAP's DeepExplainer
|
31 |
-
explainer = shap.DeepExplainer(model, data=test_loader)
|
32 |
-
|
33 |
-
# Calculate SHAP values for your chosen image
|
34 |
-
shap_values = explainer(image)
|
35 |
-
|
36 |
-
# Summarize the feature importance for the specific image
|
37 |
-
shap.summary_plot(shap_values, image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test.py
DELETED
@@ -1,223 +0,0 @@
|
|
1 |
-
import sys
|
2 |
-
import torch
|
3 |
-
import torch.nn as nn
|
4 |
-
from PIL import Image
|
5 |
-
import os
|
6 |
-
from configs import *
|
7 |
-
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
|
8 |
-
import matplotlib.pyplot as plt
|
9 |
-
import random
|
10 |
-
from itertools import product
|
11 |
-
|
12 |
-
random.seed(RANDOM_SEED)
|
13 |
-
torch.cuda.manual_seed(RANDOM_SEED)
|
14 |
-
torch.manual_seed(RANDOM_SEED)
|
15 |
-
print("PyTorch Seed:", torch.initial_seed())
|
16 |
-
print("Random Seed:", random.getstate()[1][0])
|
17 |
-
print("PyTorch CUDA Seed:", torch.cuda.initial_seed())
|
18 |
-
|
19 |
-
# Define your model paths
|
20 |
-
# Load your pre-trained models
|
21 |
-
model2 = EfficientNetB2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
22 |
-
model2.load_state_dict(torch.load("output/checkpoints/EfficientNetB2WithDropout.pth"))
|
23 |
-
model1 = SqueezeNet1_0WithSE(num_classes=NUM_CLASSES).to(DEVICE)
|
24 |
-
model1.load_state_dict(torch.load("output/checkpoints/SqueezeNet1_0WithSE.pth"))
|
25 |
-
model3 = MobileNetV2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
26 |
-
model3.load_state_dict(torch.load("output\checkpoints\MobileNetV2WithDropout.pth"))
|
27 |
-
|
28 |
-
# Define the class labels
|
29 |
-
class_labels = CLASSES
|
30 |
-
|
31 |
-
# Define your test data folder path
|
32 |
-
test_data_folder = "data/test/Task 1/"
|
33 |
-
|
34 |
-
|
35 |
-
# Put models in evaluation mode
|
36 |
-
def set_models_eval(models):
|
37 |
-
for model in models:
|
38 |
-
model.eval()
|
39 |
-
|
40 |
-
|
41 |
-
# Define the ensemble model using a list of models
|
42 |
-
class WeightedVoteEnsemble(nn.Module):
|
43 |
-
def __init__(self, models, weights):
|
44 |
-
super(WeightedVoteEnsemble, self).__init__()
|
45 |
-
self.models = models
|
46 |
-
self.weights = weights
|
47 |
-
|
48 |
-
def forward(self, x):
|
49 |
-
predictions = [model(x) for model in self.models]
|
50 |
-
weighted_predictions = torch.stack(
|
51 |
-
[w * pred for w, pred in zip(self.weights, predictions)], dim=0
|
52 |
-
)
|
53 |
-
avg_predictions = weighted_predictions.sum(dim=0)
|
54 |
-
return avg_predictions
|
55 |
-
|
56 |
-
|
57 |
-
def ensemble_predictions(models, image):
|
58 |
-
all_predictions = []
|
59 |
-
|
60 |
-
with torch.no_grad():
|
61 |
-
for model in models:
|
62 |
-
output = model(image)
|
63 |
-
all_predictions.append(output)
|
64 |
-
|
65 |
-
return torch.stack(all_predictions, dim=0).mean(dim=0)
|
66 |
-
|
67 |
-
|
68 |
-
# Load a single image and make predictions
|
69 |
-
def evaluate_image(models, image_path, transform=preprocess):
|
70 |
-
image = Image.open(image_path).convert("RGB")
|
71 |
-
image = transform(image).unsqueeze(0)
|
72 |
-
image = image.to(DEVICE)
|
73 |
-
outputs = ensemble_predictions(models, image)
|
74 |
-
|
75 |
-
return outputs.argmax(dim=1).item()
|
76 |
-
|
77 |
-
|
78 |
-
# Evaluate and plot a confusion matrix for an ensemble of models
|
79 |
-
def evaluate_and_plot_confusion_matrix(models, test_data_folder):
|
80 |
-
all_predictions = []
|
81 |
-
true_labels = []
|
82 |
-
|
83 |
-
with torch.no_grad():
|
84 |
-
for class_label in class_labels:
|
85 |
-
class_path = os.path.join(test_data_folder, class_label)
|
86 |
-
for image_file in os.listdir(class_path):
|
87 |
-
image_path = os.path.join(class_path, image_file)
|
88 |
-
# print(image_path)
|
89 |
-
predicted_label = evaluate_image(models, image_path, preprocess)
|
90 |
-
all_predictions.append(predicted_label)
|
91 |
-
true_labels.append(class_labels.index(class_label))
|
92 |
-
|
93 |
-
# Print accuracy
|
94 |
-
accuracy = (
|
95 |
-
(torch.tensor(all_predictions) == torch.tensor(true_labels)).float().mean()
|
96 |
-
)
|
97 |
-
print("Accuracy:", accuracy)
|
98 |
-
|
99 |
-
# Create the confusion matrix
|
100 |
-
cm = confusion_matrix(true_labels, all_predictions)
|
101 |
-
|
102 |
-
# Plot the confusion matrix
|
103 |
-
display = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=class_labels)
|
104 |
-
display.plot(cmap=plt.cm.Blues, values_format="d")
|
105 |
-
|
106 |
-
# Show the plot
|
107 |
-
plt.show()
|
108 |
-
|
109 |
-
return accuracy
|
110 |
-
|
111 |
-
# Set the models to evaluation mode
|
112 |
-
set_models_eval([model1, model2, model3])
|
113 |
-
|
114 |
-
# Define different weight configurations
|
115 |
-
# [SqueezeNet, EfficientNetB2WithDropout, MobileNetV2WithDropout]
|
116 |
-
weights_configurations = [
|
117 |
-
# Random set of weights using random.random() and all weights sum to 1
|
118 |
-
[
|
119 |
-
random.randrange(1, 10) / 10,
|
120 |
-
random.randrange(1, 10) / 10,
|
121 |
-
random.randrange(1, 10) / 10,
|
122 |
-
],
|
123 |
-
]
|
124 |
-
|
125 |
-
|
126 |
-
## NOTE OF PREVIOUS WEIGHTS
|
127 |
-
# Best weights: [0.2, 0.3, 0.5] with accuracy: 0.9428571462631226 at iteration: 15 with torch seed: 28434738589300 and random seed: 3188652458777471118 and torch cuda seed: None
|
128 |
-
|
129 |
-
|
130 |
-
best_weights = {
|
131 |
-
"weights": 0,
|
132 |
-
"accuracy": 0,
|
133 |
-
"iteration": 0,
|
134 |
-
"torch_seed": 0,
|
135 |
-
"random_seed": 0,
|
136 |
-
"torch_cuda_seed": 0,
|
137 |
-
}
|
138 |
-
|
139 |
-
i = 0
|
140 |
-
|
141 |
-
# weights_hist = []
|
142 |
-
|
143 |
-
target_sum = 1.0
|
144 |
-
number_of_numbers = 3
|
145 |
-
lower_limit = 0.20
|
146 |
-
upper_limit = 0.9
|
147 |
-
step = 0.1
|
148 |
-
|
149 |
-
valid_combinations = []
|
150 |
-
|
151 |
-
# Generate all unique combinations of three numbers with values to two decimal places
|
152 |
-
range_values = list(range(int(lower_limit * 100), int(upper_limit * 100) + 1))
|
153 |
-
for combo in product(range_values, repeat=number_of_numbers):
|
154 |
-
combo_float = [x / 100.0 for x in combo]
|
155 |
-
|
156 |
-
# Check if the sum of the numbers is equal to 1
|
157 |
-
if sum(combo_float) == target_sum:
|
158 |
-
valid_combinations.append(combo_float)
|
159 |
-
|
160 |
-
# Calculate the total number of possibilities
|
161 |
-
total_possibilities = len(valid_combinations)
|
162 |
-
|
163 |
-
print("Total number of possibilities:", total_possibilities)
|
164 |
-
|
165 |
-
valid_combinations = [[0.37, 0.34, 0.29]]
|
166 |
-
|
167 |
-
for weights in valid_combinations:
|
168 |
-
# while True:
|
169 |
-
print("---------------------------")
|
170 |
-
print("Iteration:", i)
|
171 |
-
# Should iterate until all possible weights are exhausted
|
172 |
-
# Create an ensemble model with weighted voting
|
173 |
-
|
174 |
-
random.seed(RANDOM_SEED)
|
175 |
-
torch.cuda.manual_seed(RANDOM_SEED)
|
176 |
-
torch.manual_seed(RANDOM_SEED)
|
177 |
-
# print("PyTorch Seed:", torch.initial_seed())
|
178 |
-
# weights_hist.append(weights)
|
179 |
-
weighted_vote_ensemble_model = WeightedVoteEnsemble(
|
180 |
-
# [model1, model2, model3], weights
|
181 |
-
[model1, model2, model3],
|
182 |
-
weights,
|
183 |
-
)
|
184 |
-
# print("Weights:", weights)
|
185 |
-
print("Weights:", weights)
|
186 |
-
# Call the evaluate_and_plot_confusion_matrix function with your models and test data folder
|
187 |
-
accuracy = evaluate_and_plot_confusion_matrix(
|
188 |
-
[weighted_vote_ensemble_model], test_data_folder
|
189 |
-
)
|
190 |
-
# Convert tensor to float
|
191 |
-
accuracy = accuracy.item()
|
192 |
-
if accuracy > best_weights["accuracy"]:
|
193 |
-
# best_weights["weights"] = weights
|
194 |
-
best_weights["weights"] = weights
|
195 |
-
best_weights["accuracy"] = accuracy
|
196 |
-
best_weights["iteration"] = i
|
197 |
-
best_weights["torch_seed"] = torch.initial_seed()
|
198 |
-
seed = random.randrange(sys.maxsize)
|
199 |
-
rng = random.Random(seed)
|
200 |
-
best_weights["random_seed"] = seed
|
201 |
-
best_weights["torch_cuda_seed"] = torch.cuda.initial_seed()
|
202 |
-
|
203 |
-
print(
|
204 |
-
"Best weights:",
|
205 |
-
best_weights["weights"],
|
206 |
-
"with accuracy:",
|
207 |
-
best_weights["accuracy"],
|
208 |
-
"at iteration:",
|
209 |
-
best_weights["iteration"],
|
210 |
-
"with torch seed:",
|
211 |
-
best_weights["torch_seed"],
|
212 |
-
"and random seed:",
|
213 |
-
best_weights["random_seed"],
|
214 |
-
"and torch cuda seed:",
|
215 |
-
best_weights["torch_cuda_seed"],
|
216 |
-
)
|
217 |
-
i += 1
|
218 |
-
|
219 |
-
|
220 |
-
torch.save(
|
221 |
-
weighted_vote_ensemble_model.state_dict(),
|
222 |
-
"output/checkpoints/WeightedVoteEnsemble.pth",
|
223 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test-speed.py β test_speed.py
RENAMED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
from gradio_client import Client
|
2 |
import time
|
3 |
import csv
|
@@ -6,7 +7,7 @@ from matplotlib import rcParams
|
|
6 |
from configs import *
|
7 |
from PIL import Image
|
8 |
|
9 |
-
client = Client("https://cycool29-
|
10 |
|
11 |
list_of_times = []
|
12 |
|
@@ -25,33 +26,35 @@ for disease in CLASSES:
|
|
25 |
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
26 |
start_time = time.time()
|
27 |
result = client.predict(
|
28 |
-
|
29 |
-
|
|
|
|
|
30 |
)
|
31 |
time_taken = time.time() - start_time
|
32 |
list_of_times.append(time_taken)
|
33 |
print("Time taken:", time_taken)
|
34 |
|
35 |
# Log to csv
|
36 |
-
with open(
|
37 |
writer = csv.writer(file)
|
38 |
writer.writerow([disease])
|
39 |
writer.writerow([image_path])
|
40 |
writer.writerow([time_taken])
|
41 |
-
|
42 |
|
43 |
-
|
|
|
44 |
print("Max time taken:", max(list_of_times))
|
45 |
print("Min time taken:", min(list_of_times))
|
46 |
print("Total time taken:", sum(list_of_times))
|
47 |
-
print("Median time taken:", sorted(list_of_times)[len(list_of_times)//2])
|
48 |
|
49 |
# Plot the histogram
|
50 |
plt.hist(list_of_times, bins=10)
|
51 |
plt.xlabel("Time taken (s)")
|
52 |
plt.ylabel("Frequency")
|
53 |
-
plt.title("Time
|
54 |
-
plt.savefig("docs/
|
55 |
|
56 |
|
57 |
# Now is local
|
@@ -72,22 +75,22 @@ for disease in CLASSES:
|
|
72 |
print("Time taken:", time_taken)
|
73 |
|
74 |
# Log to csv
|
75 |
-
with open(
|
76 |
writer = csv.writer(file)
|
77 |
writer.writerow([disease])
|
78 |
writer.writerow([image_path])
|
79 |
writer.writerow([time_taken])
|
80 |
|
81 |
|
82 |
-
print("Average time taken local:", sum(list_of_times)/len(list_of_times))
|
83 |
print("Max time taken local:", max(list_of_times))
|
84 |
print("Min time taken local:", min(list_of_times))
|
85 |
print("Total time taken local:", sum(list_of_times))
|
86 |
-
print("Median time taken local:", sorted(list_of_times)[len(list_of_times)//2])
|
87 |
|
88 |
# Plot the histogram
|
89 |
plt.hist(list_of_times, bins=10)
|
90 |
-
plt.xlabel("Time taken (s)
|
91 |
-
plt.ylabel("Frequency
|
92 |
-
plt.title("Time taken to
|
93 |
-
plt.savefig("docs/
|
|
|
1 |
+
import os
|
2 |
from gradio_client import Client
|
3 |
import time
|
4 |
import csv
|
|
|
7 |
from configs import *
|
8 |
from PIL import Image
|
9 |
|
10 |
+
client = Client("https://cycool29-spiralsense.hf.space/")
|
11 |
|
12 |
list_of_times = []
|
13 |
|
|
|
26 |
image_path = r"data\test\Task 1\{}\{}".format(disease, image_path)
|
27 |
start_time = time.time()
|
28 |
result = client.predict(
|
29 |
+
image_path,
|
30 |
+
False,
|
31 |
+
False,
|
32 |
+
fn_index=0,
|
33 |
)
|
34 |
time_taken = time.time() - start_time
|
35 |
list_of_times.append(time_taken)
|
36 |
print("Time taken:", time_taken)
|
37 |
|
38 |
# Log to csv
|
39 |
+
with open("log.csv", "a", newline="") as file:
|
40 |
writer = csv.writer(file)
|
41 |
writer.writerow([disease])
|
42 |
writer.writerow([image_path])
|
43 |
writer.writerow([time_taken])
|
|
|
44 |
|
45 |
+
|
46 |
+
print("Average time taken:", sum(list_of_times) / len(list_of_times))
|
47 |
print("Max time taken:", max(list_of_times))
|
48 |
print("Min time taken:", min(list_of_times))
|
49 |
print("Total time taken:", sum(list_of_times))
|
50 |
+
print("Median time taken:", sorted(list_of_times)[len(list_of_times) // 2])
|
51 |
|
52 |
# Plot the histogram
|
53 |
plt.hist(list_of_times, bins=10)
|
54 |
plt.xlabel("Time taken (s)")
|
55 |
plt.ylabel("Frequency")
|
56 |
+
plt.title("Time Taken to Process Each Image (Web)")
|
57 |
+
plt.savefig("docs/evaluation/time_taken_for_web.png")
|
58 |
|
59 |
|
60 |
# Now is local
|
|
|
75 |
print("Time taken:", time_taken)
|
76 |
|
77 |
# Log to csv
|
78 |
+
with open("log.csv", "a", newline="") as file:
|
79 |
writer = csv.writer(file)
|
80 |
writer.writerow([disease])
|
81 |
writer.writerow([image_path])
|
82 |
writer.writerow([time_taken])
|
83 |
|
84 |
|
85 |
+
print("Average time taken local:", sum(list_of_times) / len(list_of_times))
|
86 |
print("Max time taken local:", max(list_of_times))
|
87 |
print("Min time taken local:", min(list_of_times))
|
88 |
print("Total time taken local:", sum(list_of_times))
|
89 |
+
print("Median time taken local:", sorted(list_of_times)[len(list_of_times) // 2])
|
90 |
|
91 |
# Plot the histogram
|
92 |
plt.hist(list_of_times, bins=10)
|
93 |
+
plt.xlabel("Time taken (s)")
|
94 |
+
plt.ylabel("Frequency")
|
95 |
+
plt.title("Time taken to Process Each Image (Local)")
|
96 |
+
plt.savefig("docs/evaluation/time_taken_for_local.png")
|
train-svm.py
DELETED
@@ -1,101 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import numpy as np
|
3 |
-
from sklearn import svm
|
4 |
-
from sklearn.model_selection import train_test_split
|
5 |
-
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
|
6 |
-
from skimage.io import imread
|
7 |
-
from skimage.transform import resize
|
8 |
-
from sklearn.model_selection import train_test_split, RandomizedSearchCV
|
9 |
-
from scipy.stats import uniform
|
10 |
-
from configs import *
|
11 |
-
|
12 |
-
# Set the path to your dataset folder, where each subfolder represents a class
|
13 |
-
dataset_path = COMBINED_DATA_DIR + str(1)
|
14 |
-
|
15 |
-
|
16 |
-
# Function to load, resize, and convert images to grayscale
|
17 |
-
def load_resize_and_convert_to_gray(folder, target_size=(100, 100)):
|
18 |
-
images = []
|
19 |
-
for filename in os.listdir(folder):
|
20 |
-
img_path = os.path.join(folder, filename)
|
21 |
-
if os.path.isfile(img_path):
|
22 |
-
img = imread(img_path, as_gray=True)
|
23 |
-
img = resize(img, target_size, anti_aliasing=True)
|
24 |
-
images.append(img)
|
25 |
-
return images
|
26 |
-
|
27 |
-
|
28 |
-
# Load, resize, and convert images to grayscale from folders
|
29 |
-
X = [] # List to store images
|
30 |
-
y = [] # List to store corresponding labels
|
31 |
-
|
32 |
-
class_folders = os.listdir(dataset_path)
|
33 |
-
class_folders.sort() # Sort the class folders to ensure consistent class ordering
|
34 |
-
|
35 |
-
for class_folder in class_folders:
|
36 |
-
class_path = os.path.join(dataset_path, class_folder)
|
37 |
-
if os.path.isdir(class_path):
|
38 |
-
images = load_resize_and_convert_to_gray(class_path)
|
39 |
-
X.extend(images)
|
40 |
-
y.extend([class_folder] * len(images)) # Assign labels based on folder name
|
41 |
-
|
42 |
-
# Convert data to NumPy arrays
|
43 |
-
X = np.array(X)
|
44 |
-
y = np.array(y)
|
45 |
-
|
46 |
-
# Split the dataset into training and testing sets
|
47 |
-
X_train, X_test, y_train, y_test = train_test_split(
|
48 |
-
X, y, test_size=0.2, random_state=42
|
49 |
-
)
|
50 |
-
|
51 |
-
# Define the parameter distributions for random search
|
52 |
-
param_dist = {
|
53 |
-
"C": uniform(loc=0, scale=10), # Randomly sample from [0, 10]
|
54 |
-
"kernel": ["linear", "rbf", "poly"],
|
55 |
-
"gamma": uniform(loc=0.001, scale=0.1), # Randomly sample from [0.001, 0.1]
|
56 |
-
}
|
57 |
-
|
58 |
-
# Flatten the images to a 1D array
|
59 |
-
X_train_flat = X_train.reshape(X_train.shape[0], -1)
|
60 |
-
X_test_flat = X_test.reshape(X_test.shape[0], -1)
|
61 |
-
|
62 |
-
# Create an SVM classifier
|
63 |
-
svm_classifier = svm.SVC()
|
64 |
-
|
65 |
-
# Perform Randomized Search with cross-validation
|
66 |
-
random_search = RandomizedSearchCV(
|
67 |
-
svm_classifier,
|
68 |
-
param_distributions=param_dist,
|
69 |
-
n_iter=50,
|
70 |
-
cv=5,
|
71 |
-
n_jobs=-1,
|
72 |
-
verbose=2,
|
73 |
-
random_state=42,
|
74 |
-
)
|
75 |
-
|
76 |
-
# Fit the Randomized Search on the training data
|
77 |
-
random_search.fit(X_train_flat, y_train)
|
78 |
-
|
79 |
-
# Print the best hyperparameters
|
80 |
-
print("Best Hyperparameters:")
|
81 |
-
print(random_search.best_params_)
|
82 |
-
|
83 |
-
# Get the best SVM model with the tuned hyperparameters
|
84 |
-
best_svm_model = random_search.best_estimator_
|
85 |
-
|
86 |
-
# Evaluate the best model on the test set
|
87 |
-
y_pred = best_svm_model.predict(X_test_flat)
|
88 |
-
|
89 |
-
# Calculate accuracy and other metrics
|
90 |
-
accuracy = accuracy_score(y_test, y_pred)
|
91 |
-
print("Accuracy:", accuracy)
|
92 |
-
|
93 |
-
# Confusion Matrix
|
94 |
-
conf_matrix = confusion_matrix(y_test, y_pred)
|
95 |
-
print("Confusion Matrix:\n", conf_matrix)
|
96 |
-
|
97 |
-
# You can also print other classification metrics like precision, recall, and F1-score
|
98 |
-
from sklearn.metrics import classification_report
|
99 |
-
|
100 |
-
report = classification_report(y_test, y_pred)
|
101 |
-
print("Classification Report:\n", report)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
weight_averaging.py
DELETED
@@ -1,235 +0,0 @@
|
|
1 |
-
import sys
|
2 |
-
import torch
|
3 |
-
import torch.nn as nn
|
4 |
-
from PIL import Image
|
5 |
-
import os
|
6 |
-
from configs import *
|
7 |
-
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
|
8 |
-
import matplotlib.pyplot as plt
|
9 |
-
import random
|
10 |
-
from itertools import product
|
11 |
-
|
12 |
-
random.seed(RANDOM_SEED)
|
13 |
-
torch.cuda.manual_seed(RANDOM_SEED)
|
14 |
-
torch.manual_seed(RANDOM_SEED)
|
15 |
-
print("PyTorch Seed:", torch.initial_seed())
|
16 |
-
print("Random Seed:", random.getstate()[1][0])
|
17 |
-
print("PyTorch CUDA Seed:", torch.cuda.initial_seed())
|
18 |
-
|
19 |
-
print("DEVICE:", DEVICE)
|
20 |
-
|
21 |
-
# Define your model paths
|
22 |
-
# Load your pre-trained models
|
23 |
-
model2 = EfficientNetB3WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
24 |
-
model2.load_state_dict(torch.load("output/checkpoints/EfficientNetB3WithDropout.pth"))
|
25 |
-
model1 = SqueezeNet1_0WithSE(num_classes=NUM_CLASSES).to(DEVICE)
|
26 |
-
model1.load_state_dict(torch.load("output/checkpoints/SqueezeNet1_0WithSE.pth"))
|
27 |
-
model3 = MobileNetV2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
28 |
-
model3.load_state_dict(torch.load("output\checkpoints\MobileNetV2WithDropout.pth"))
|
29 |
-
model4 = EfficientNetB2WithDropout(num_classes=NUM_CLASSES).to(DEVICE)
|
30 |
-
model4.load_state_dict(torch.load("output\checkpoints\EfficientNetB2WithDropout.pth"))
|
31 |
-
|
32 |
-
models = [model1, model2, model3, model4]
|
33 |
-
|
34 |
-
# Define the class labels
|
35 |
-
class_labels = CLASSES
|
36 |
-
|
37 |
-
# Define your test data folder path
|
38 |
-
test_data_folder = "data/test/Task 1/"
|
39 |
-
|
40 |
-
|
41 |
-
# Put models in evaluation mode
|
42 |
-
def set_models_eval(models):
|
43 |
-
for model in models:
|
44 |
-
model.eval()
|
45 |
-
|
46 |
-
|
47 |
-
# Define the ensemble model using a list of models
|
48 |
-
class WeightedVoteEnsemble(nn.Module):
|
49 |
-
def __init__(self, models, weights):
|
50 |
-
super(WeightedVoteEnsemble, self).__init__()
|
51 |
-
self.models = models
|
52 |
-
self.weights = weights
|
53 |
-
|
54 |
-
def forward(self, x):
|
55 |
-
predictions = [model(x) for model in self.models]
|
56 |
-
weighted_predictions = torch.stack(
|
57 |
-
[w * pred for w, pred in zip(self.weights, predictions)], dim=0
|
58 |
-
)
|
59 |
-
avg_predictions = weighted_predictions.sum(dim=0)
|
60 |
-
return avg_predictions
|
61 |
-
|
62 |
-
|
63 |
-
def ensemble_predictions(models, image):
|
64 |
-
all_predictions = []
|
65 |
-
|
66 |
-
with torch.no_grad():
|
67 |
-
for model in models:
|
68 |
-
output = model(image)
|
69 |
-
all_predictions.append(output)
|
70 |
-
|
71 |
-
return torch.stack(all_predictions, dim=0).mean(dim=0)
|
72 |
-
|
73 |
-
|
74 |
-
# Load a single image and make predictions
|
75 |
-
def evaluate_image(models, image_path, transform=preprocess):
|
76 |
-
image = Image.open(image_path).convert("RGB")
|
77 |
-
image = transform(image).unsqueeze(0)
|
78 |
-
image = image.to(DEVICE)
|
79 |
-
outputs = ensemble_predictions(models, image)
|
80 |
-
|
81 |
-
return outputs.argmax(dim=1).item()
|
82 |
-
|
83 |
-
|
84 |
-
# Evaluate and plot a confusion matrix for an ensemble of models
|
85 |
-
def evaluate_and_plot_confusion_matrix(models, test_data_folder):
|
86 |
-
all_predictions = []
|
87 |
-
true_labels = []
|
88 |
-
|
89 |
-
with torch.no_grad():
|
90 |
-
for class_label in class_labels:
|
91 |
-
class_path = os.path.join(test_data_folder, class_label)
|
92 |
-
for image_file in os.listdir(class_path):
|
93 |
-
image_path = os.path.join(class_path, image_file)
|
94 |
-
# print(image_path)
|
95 |
-
predicted_label = evaluate_image(models, image_path, preprocess)
|
96 |
-
all_predictions.append(predicted_label)
|
97 |
-
true_labels.append(class_labels.index(class_label))
|
98 |
-
|
99 |
-
# Print accuracy
|
100 |
-
accuracy = (
|
101 |
-
(torch.tensor(all_predictions) == torch.tensor(true_labels)).float().mean()
|
102 |
-
)
|
103 |
-
print("Accuracy:", accuracy)
|
104 |
-
|
105 |
-
# Create the confusion matrix
|
106 |
-
# cm = confusion_matrix(true_labels, all_predictions)
|
107 |
-
|
108 |
-
# # Plot the confusion matrix
|
109 |
-
# display = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=class_labels)
|
110 |
-
# display.plot(cmap=plt.cm.Blues, values_format="d")
|
111 |
-
|
112 |
-
# # Show the plot
|
113 |
-
# plt.show()
|
114 |
-
|
115 |
-
return accuracy
|
116 |
-
|
117 |
-
# Set the models to evaluation mode
|
118 |
-
set_models_eval(models)
|
119 |
-
|
120 |
-
# Define different weight configurations
|
121 |
-
# [SqueezeNet, EfficientNetB2WithDropout, MobileNetV2WithDropout]
|
122 |
-
weights_configurations = [
|
123 |
-
# Random set of weights using random.random() and all weights sum to 1
|
124 |
-
[
|
125 |
-
random.randrange(1, 10) / 10,
|
126 |
-
random.randrange(1, 10) / 10,
|
127 |
-
random.randrange(1, 10) / 10,
|
128 |
-
],
|
129 |
-
]
|
130 |
-
|
131 |
-
|
132 |
-
## NOTE OF PREVIOUS WEIGHTS
|
133 |
-
# Best weights: [0.2, 0.3, 0.5] with accuracy: 0.9428571462631226 at iteration: 15 with torch seed: 28434738589300 and random seed: 3188652458777471118 and torch cuda seed: None
|
134 |
-
|
135 |
-
|
136 |
-
best_weights = {
|
137 |
-
"weights": 0,
|
138 |
-
"accuracy": 0,
|
139 |
-
"iteration": 0,
|
140 |
-
"torch_seed": 0,
|
141 |
-
"random_seed": 0,
|
142 |
-
"torch_cuda_seed": 0,
|
143 |
-
}
|
144 |
-
|
145 |
-
i = 0
|
146 |
-
|
147 |
-
# weights_hist = []
|
148 |
-
|
149 |
-
target_sum = 1.0
|
150 |
-
number_of_numbers = 4
|
151 |
-
lower_limit = 0.2
|
152 |
-
upper_limit = 0.8
|
153 |
-
step = 0.01
|
154 |
-
|
155 |
-
valid_combinations = []
|
156 |
-
|
157 |
-
# Generate all unique combinations of four numbers with values to two decimal places
|
158 |
-
for combination in product(
|
159 |
-
*[range(int(lower_limit * 100), int(upper_limit * 100) + 1)] * number_of_numbers
|
160 |
-
):
|
161 |
-
# Convert the combination to a list of floats
|
162 |
-
combination = [float(number) / 100 for number in combination]
|
163 |
-
# Check if the sum of the combination is equal to the target sum
|
164 |
-
if sum(combination) == target_sum:
|
165 |
-
# Add the combination to the list of valid combinations
|
166 |
-
valid_combinations.append(combination)
|
167 |
-
|
168 |
-
|
169 |
-
# Calculate the total number of possibilities
|
170 |
-
total_possibilities = len(valid_combinations)
|
171 |
-
|
172 |
-
print("Total number of possibilities:", total_possibilities)
|
173 |
-
|
174 |
-
# valid_combinations = [[0.3, 0.5, 0.2]]
|
175 |
-
# 0.38 for SqueezeNet, 0.34 for EfficientNetB2WithDropout, 0.28 for MobileNetV2WithDropout
|
176 |
-
best_weighted_vote_ensemble_model = None
|
177 |
-
|
178 |
-
for weights in valid_combinations:
|
179 |
-
# while True:
|
180 |
-
print("---------------------------")
|
181 |
-
print("Iteration:", i)
|
182 |
-
# Should iterate until all possible weights are exhausted
|
183 |
-
# Create an ensemble model with weighted voting
|
184 |
-
|
185 |
-
random.seed(RANDOM_SEED)
|
186 |
-
torch.cuda.manual_seed(RANDOM_SEED)
|
187 |
-
torch.manual_seed(RANDOM_SEED)
|
188 |
-
# print("PyTorch Seed:", torch.initial_seed())
|
189 |
-
# weights_hist.append(weights)
|
190 |
-
weighted_vote_ensemble_model = WeightedVoteEnsemble(
|
191 |
-
# [model1, model2, model3], weights
|
192 |
-
models,
|
193 |
-
weights,
|
194 |
-
)
|
195 |
-
# print("Weights:", weights)
|
196 |
-
print("Weights:", weights)
|
197 |
-
# Call the evaluate_and_plot_confusion_matrix function with your models and test data folder
|
198 |
-
accuracy = evaluate_and_plot_confusion_matrix(
|
199 |
-
[weighted_vote_ensemble_model], test_data_folder
|
200 |
-
)
|
201 |
-
# Convert tensor to float
|
202 |
-
accuracy = accuracy.item()
|
203 |
-
if accuracy > best_weights["accuracy"]:
|
204 |
-
# best_weights["weights"] = weights
|
205 |
-
best_weights["weights"] = weights
|
206 |
-
best_weights["accuracy"] = accuracy
|
207 |
-
best_weights["iteration"] = i
|
208 |
-
best_weights["torch_seed"] = torch.initial_seed()
|
209 |
-
seed = random.randrange(sys.maxsize)
|
210 |
-
rng = random.Random(seed)
|
211 |
-
best_weights["random_seed"] = seed
|
212 |
-
best_weights["torch_cuda_seed"] = torch.cuda.initial_seed()
|
213 |
-
best_weighted_vote_ensemble_model = weighted_vote_ensemble_model
|
214 |
-
|
215 |
-
print(
|
216 |
-
"Best weights:",
|
217 |
-
best_weights["weights"],
|
218 |
-
"with accuracy:",
|
219 |
-
best_weights["accuracy"],
|
220 |
-
"at iteration:",
|
221 |
-
best_weights["iteration"],
|
222 |
-
"with torch seed:",
|
223 |
-
best_weights["torch_seed"],
|
224 |
-
"and random seed:",
|
225 |
-
best_weights["random_seed"],
|
226 |
-
"and torch cuda seed:",
|
227 |
-
best_weights["torch_cuda_seed"],
|
228 |
-
)
|
229 |
-
i += 1
|
230 |
-
|
231 |
-
|
232 |
-
torch.save(
|
233 |
-
best_weighted_vote_ensemble_model.state_dict(),
|
234 |
-
"output/checkpoints/WeightedVoteEnsemble.pth",
|
235 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|