update models, output, examples
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README.md +0 -13
- app.py +142 -80
- crop.pt +1 -0
- examples/10043.png +0 -0
- examples/2639.png +0 -0
- examples/8888.png +0 -0
- fold0.ckpt +0 -3
- fold1.ckpt +0 -3
- fold2.ckpt +0 -3
- greulich_and_pyle_ages.json +65 -0
- net0.pt +1 -0
- net1.pt +1 -0
- net2.pt +1 -0
- ref_img.png +0 -0
- requirements.txt +6 -5
- skp/__pycache__/utils.cpython-312.pyc +0 -0
- skp/configs/__init__.py +21 -0
- skp/configs/__pycache__/__init__.cpython-312.pyc +0 -0
- skp/configs/__pycache__/base.cpython-312.pyc +0 -0
- skp/configs/base.py +21 -0
- skp/configs/boneage/__pycache__/cfg_baseline.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_crop.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_crop_simple_resize.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_MIL.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_MIL_lstm.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_MIL_transformer.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_clip_outliers_aug.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_match_hist.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_with_cls.cpython-312.pyc +0 -0
- skp/configs/boneage/__pycache__/cfg_female_channel_with_cls_clip_outliers.cpython-312.pyc +0 -0
- skp/configs/boneage/cfg_baseline.py +117 -0
- skp/configs/boneage/cfg_crop.py +123 -0
- skp/configs/boneage/cfg_crop_simple_resize.py +117 -0
- skp/configs/boneage/cfg_female_channel.py +114 -0
- skp/configs/boneage/cfg_female_channel_MIL.py +113 -0
- skp/configs/boneage/cfg_female_channel_MIL_lstm.py +116 -0
- skp/configs/boneage/cfg_female_channel_MIL_transformer.py +117 -0
- skp/configs/boneage/cfg_female_channel_reg_cls.py +115 -0
- skp/configs/boneage/cfg_female_channel_reg_cls_clip_outliers_aug.py +119 -0
- skp/configs/boneage/cfg_female_channel_reg_cls_match_hist.py +116 -0
- skp/configs/boneage/cfg_female_channel_with_cls.py +115 -0
- skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers.py +117 -0
- skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers_aug.py +119 -0
- skp/models/MIL/__pycache__/net2d_attn.cpython-312.pyc +0 -0
- skp/models/MIL/__pycache__/net2d_basic_attn.cpython-312.pyc +0 -0
- skp/models/MIL/net2d_attn.py +286 -0
- skp/models/MIL/net2d_basic_attn.py +284 -0
- skp/models/__pycache__/modules.cpython-312.pyc +0 -0
README.md
DELETED
@@ -1,13 +0,0 @@
|
|
1 |
-
---
|
2 |
-
title: Deep Learning Model for Pediatric Bone Age
|
3 |
-
emoji: 💻
|
4 |
-
colorFrom: red
|
5 |
-
colorTo: blue
|
6 |
-
sdk: gradio
|
7 |
-
sdk_version: 3.8.2
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
-
license: apache-2.0
|
11 |
-
---
|
12 |
-
|
13 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
@@ -1,106 +1,168 @@
|
|
|
|
1 |
import gradio as gr
|
2 |
-
import
|
3 |
-
import
|
|
|
4 |
import torch.nn as nn
|
5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
-
def change_num_input_channels(model, in_channels=1):
|
8 |
-
"""
|
9 |
-
Assumes number of input channels in model is 3.
|
10 |
-
"""
|
11 |
-
for i, m in enumerate(model.modules()):
|
12 |
-
if isinstance(m, (nn.Conv2d,nn.Conv3d)) and m.in_channels == 3:
|
13 |
-
m.in_channels = in_channels
|
14 |
-
# First, sum across channels
|
15 |
-
W = m.weight.sum(1, keepdim=True)
|
16 |
-
# Then, divide by number of channels
|
17 |
-
W = W / in_channels
|
18 |
-
# Then, repeat by number of channels
|
19 |
-
size = [1] * W.ndim
|
20 |
-
size[1] = in_channels
|
21 |
-
W = W.repeat(size)
|
22 |
-
m.weight = nn.Parameter(W)
|
23 |
-
break
|
24 |
-
return model
|
25 |
-
|
26 |
-
|
27 |
-
class Net2D(nn.Module):
|
28 |
-
|
29 |
-
def __init__(self, weights):
|
30 |
-
super().__init__()
|
31 |
-
self.backbone = timm.create_model("tf_efficientnetv2_s", pretrained=False, global_pool="", num_classes=0)
|
32 |
-
self.backbone = change_num_input_channels(self.backbone, 2)
|
33 |
-
self.pool_layer = nn.AdaptiveAvgPool2d(1)
|
34 |
-
self.dropout = nn.Dropout(0.2)
|
35 |
-
self.classifier = nn.Linear(1280, 1)
|
36 |
-
self.load_state_dict(weights)
|
37 |
-
|
38 |
-
def forward(self, x):
|
39 |
-
x = self.backbone(x)
|
40 |
-
x = self.pool_layer(x).view(x.size(0), -1)
|
41 |
-
x = self.dropout(x)
|
42 |
-
x = self.classifier(x)
|
43 |
-
return x[:, 0] if x.size(1) == 1 else x
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
def __init__(self, model_list):
|
49 |
super().__init__()
|
50 |
-
self.
|
51 |
-
|
52 |
-
def forward(self, x):
|
53 |
-
return torch.stack([model(x) for model in self.model_list]).mean(0)
|
54 |
|
|
|
|
|
55 |
|
56 |
-
checkpoints = ["fold0.ckpt", "fold1.ckpt", "fold2.ckpt"]
|
57 |
-
weights = [torch.load(ckpt, map_location=torch.device("cpu"))["state_dict"] for ckpt in checkpoints]
|
58 |
-
weights = [{k.replace("model.", "") : v for k, v in wt.items()} for wt in weights]
|
59 |
-
models = [Net2D(wt) for wt in weights]
|
60 |
-
ensemble = Ensemble(models).eval()
|
61 |
|
62 |
-
def
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
img = img - 0.5
|
67 |
-
img = img * 2.0
|
68 |
-
if Sex == 1:
|
69 |
-
img = torch.cat([img, torch.zeros_like(img) + 1], dim=1)
|
70 |
-
else:
|
71 |
-
img = torch.cat([img, torch.zeros_like(img) - 1], dim=1)
|
72 |
-
with torch.no_grad():
|
73 |
-
bone_age = ensemble(img.float())[0].item()
|
74 |
-
total_months = bone_age * 12
|
75 |
-
years = int(total_months // 12)
|
76 |
-
months = total_months - years * 12
|
77 |
months = round(months)
|
78 |
-
|
|
|
79 |
years += 1
|
80 |
months = 0
|
|
|
81 |
if years == 0:
|
82 |
str_output = f"{months} months" if months != 1 else "1 month"
|
83 |
else:
|
84 |
-
months = round(months)
|
85 |
if months == 0:
|
86 |
str_output = f"{years} years" if years != 1 else "1 year"
|
87 |
else:
|
88 |
-
str_output =
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
|
|
|
|
|
91 |
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
sex = gr.Radio(["Male", "Female"], type="index")
|
94 |
-
|
|
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
)
|
101 |
|
|
|
|
|
|
|
|
|
|
|
102 |
|
103 |
-
|
104 |
-
|
|
|
105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
import gradio as gr
|
3 |
+
import json
|
4 |
+
import numpy as np
|
5 |
+
import torch
|
6 |
import torch.nn as nn
|
7 |
|
8 |
+
from einops import rearrange
|
9 |
+
from importlib import import_module
|
10 |
+
from pytorch_grad_cam import GradCAM
|
11 |
+
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
|
12 |
+
from skimage.exposure import match_histograms
|
13 |
+
from skp.utils import load_model_from_config, load_kfold_ensemble_as_list
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
+
class ModelForGradCAM(nn.Module):
|
17 |
+
def __init__(self, model):
|
|
|
|
|
18 |
super().__init__()
|
19 |
+
self.model = model
|
|
|
|
|
|
|
20 |
|
21 |
+
def forward(self, x):
|
22 |
+
return self.model({"x": x})["logits1"]
|
23 |
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
+
def convert_bone_age_to_string(bone_age: float):
|
26 |
+
# bone_age in months
|
27 |
+
years = round(bone_age // 12)
|
28 |
+
months = bone_age - (years * 12)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
months = round(months)
|
30 |
+
|
31 |
+
if months == 12:
|
32 |
years += 1
|
33 |
months = 0
|
34 |
+
|
35 |
if years == 0:
|
36 |
str_output = f"{months} months" if months != 1 else "1 month"
|
37 |
else:
|
|
|
38 |
if months == 0:
|
39 |
str_output = f"{years} years" if years != 1 else "1 year"
|
40 |
else:
|
41 |
+
str_output = (
|
42 |
+
f"{years} years, {months} months"
|
43 |
+
if months != 1
|
44 |
+
else f"{years} years, 1 month"
|
45 |
+
)
|
46 |
+
return str_output
|
47 |
+
|
48 |
+
|
49 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
50 |
+
|
51 |
+
cfg_crop = import_module("skp.configs.boneage.cfg_crop_simple_resize").cfg
|
52 |
+
crop_model = load_model_from_config(
|
53 |
+
cfg_crop, weights_path="crop.pt", device=device, eval_mode=True
|
54 |
+
)
|
55 |
|
56 |
+
cfg = import_module("skp.configs.boneage.cfg_female_channel_reg_cls_match_hist").cfg
|
57 |
+
cfg.backbone = "convnextv2_tiny"
|
58 |
|
59 |
+
model_list = load_kfold_ensemble_as_list(
|
60 |
+
cfg, [f"net{i}.pt" for i in range(3)], device=device, eval_mode=True
|
61 |
+
)
|
62 |
+
|
63 |
+
ref_img = rearrange(cv2.imread("ref_img.png", 0), "h w -> h w 1 ")
|
64 |
+
|
65 |
+
with open("greulich_and_pyle_ages.json", "r") as f:
|
66 |
+
greulich_and_pyle_ages = json.load(f)["bone_ages"]
|
67 |
+
|
68 |
+
greulich_and_pyle_ages = {k: np.asarray(v) for k, v in greulich_and_pyle_ages.items()}
|
69 |
+
|
70 |
+
model_grad_cam = ModelForGradCAM(model_list[0])
|
71 |
+
target_layers = [model_grad_cam.model.backbone.stages[-1]]
|
72 |
+
|
73 |
+
def predict_bone_age(Radiograph, Sex):
|
74 |
+
x0 = rearrange(Radiograph, "h w -> h w 1")
|
75 |
+
x = cfg_crop.val_transforms(image=x0)["image"]
|
76 |
+
x = torch.from_numpy(x)
|
77 |
+
x = rearrange(x, "h w c -> 1 c h w")
|
78 |
+
# crop
|
79 |
+
with torch.inference_mode():
|
80 |
+
box = crop_model({"x": x.to(device).float()}, return_loss=False)["logits"][
|
81 |
+
0
|
82 |
+
].cpu()
|
83 |
+
box[[0, 2]] = box[[0, 2]] * x0.shape[1]
|
84 |
+
box[[1, 3]] = box[[1, 3]] * x0.shape[0]
|
85 |
+
box = box.numpy().astype("int")
|
86 |
+
x, y, w, h = box
|
87 |
+
x0 = x0[y : y + h, x : x + w]
|
88 |
+
# histogram matching
|
89 |
+
x0 = match_histograms(x0, ref_img)
|
90 |
+
x = cfg.val_transforms(image=x0)["image"]
|
91 |
+
# create image channel for female/male
|
92 |
+
ch = np.zeros_like(x)
|
93 |
+
if Sex: # 0- male, 1- female
|
94 |
+
ch[...] = 255
|
95 |
+
x = np.concatenate([x, ch], axis=-1)
|
96 |
+
x = torch.from_numpy(x)
|
97 |
+
x = rearrange(x, "h w c -> 1 c h w")
|
98 |
+
with torch.inference_mode():
|
99 |
+
bone_age = []
|
100 |
+
for each_model in model_list:
|
101 |
+
pred = each_model({"x": x.to(device).float()}, return_loss=False)[
|
102 |
+
"logits1"
|
103 |
+
][0].cpu()
|
104 |
+
pred = (pred.softmax(0) * torch.arange(240)).sum().numpy()
|
105 |
+
bone_age.append(pred)
|
106 |
+
bone_age = np.mean(bone_age)
|
107 |
+
|
108 |
+
gp_ages = greulich_and_pyle_ages["female" if Sex else "male"]
|
109 |
+
diffs_gp = np.abs(bone_age - gp_ages)
|
110 |
+
diffs_gp = np.argsort(diffs_gp)
|
111 |
+
closest1 = gp_ages[diffs_gp[0]]
|
112 |
+
closest2 = gp_ages[diffs_gp[1]]
|
113 |
+
|
114 |
+
bone_age_str = convert_bone_age_to_string(bone_age)
|
115 |
+
closest1 = convert_bone_age_to_string(closest1)
|
116 |
+
closest2 = convert_bone_age_to_string(closest2)
|
117 |
+
|
118 |
+
targets = [ClassifierOutputTarget(round(bone_age))]
|
119 |
+
with GradCAM(model=model_grad_cam, target_layers=target_layers) as cam:
|
120 |
+
grayscale_cam = cam(input_tensor=x.to(device).float(), targets=targets, eigen_smooth=True)
|
121 |
+
|
122 |
+
heatmap = cv2.applyColorMap((grayscale_cam[0] * 255).astype("uint8"), cv2.COLORMAP_JET)
|
123 |
+
image = cv2.cvtColor(x[0, 0].cpu().numpy().astype("uint8"), cv2.COLOR_GRAY2RGB)
|
124 |
+
image_weight = 0.6
|
125 |
+
grad_cam_image = (1 - image_weight) * heatmap[..., ::-1] + image_weight * image
|
126 |
+
grad_cam_image = grad_cam_image.astype("uint8")
|
127 |
+
|
128 |
+
return f"Predicted bone age: {bone_age_str}\n\nThe closest Greulich & Pyle bone ages are:\n 1) {closest1}\n 2) {closest2}", grad_cam_image
|
129 |
+
|
130 |
+
|
131 |
+
image = gr.Image(image_mode="L")
|
132 |
sex = gr.Radio(["Male", "Female"], type="index")
|
133 |
+
textbox = gr.Textbox(show_label=True, label="Result")
|
134 |
+
grad_cam_image = gr.Image(image_mode="RGB", label="Heatmap")
|
135 |
|
136 |
+
with gr.Blocks() as demo:
|
137 |
+
gr.Markdown(
|
138 |
+
"""
|
139 |
+
# Deep Learning Model for Pediatric Bone Age
|
|
|
140 |
|
141 |
+
This model predicts the bone age from a single frontal view hand radiograph.
|
142 |
+
The model was trained on the publicly available
|
143 |
+
[RSNA Pediatric Bone Age Challenge](https://www.rsna.org/rsnai/ai-image-challenge/rsna-pediatric-bone-age-challenge-2017) dataset.
|
144 |
+
The model achieves a mean absolute error of 4.26 months on the original test set comprising 200 multi-annotated hand radiographs,
|
145 |
+
which is competitive with [top solutions](https://pubs.rsna.org/doi/10.1148/radiol.2018180736) from the original challenge.
|
146 |
|
147 |
+
This model is for demonstration purposes only and has NOT been approved by any regulatory agency for clinical use. The user assumes
|
148 |
+
any and all responsibility regarding their own use of this model and its outputs. Do NOT upload any images containing protected
|
149 |
+
health information, as this demonstration is not compliant with patient privacy laws.
|
150 |
|
151 |
+
Created by: Ian Pan, <https://ianpan.me>
|
152 |
+
|
153 |
+
Last updated: December 15, 2024
|
154 |
+
"""
|
155 |
+
)
|
156 |
+
gr.Interface(
|
157 |
+
fn=predict_bone_age,
|
158 |
+
inputs=[image, sex],
|
159 |
+
outputs=[textbox, grad_cam_image],
|
160 |
+
examples=[
|
161 |
+
["examples/2639.png", "Female"],
|
162 |
+
["examples/10043.png", "Female"],
|
163 |
+
["examples/8888.png", "Female"],
|
164 |
+
],
|
165 |
+
)
|
166 |
|
167 |
+
if __name__ == "__main__":
|
168 |
+
demo.launch(share=True)
|
crop.pt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
../../experiments/boneage/boneage.cfg_crop_simple_resize/8b59fed7/fold0/checkpoints/last.ckpt
|
examples/10043.png
ADDED
![]() |
examples/2639.png
ADDED
![]() |
examples/8888.png
ADDED
![]() |
fold0.ckpt
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:2db6d3fb26a05b916341574c83683017e4a04a1c0df8fda4a97ad2314b33f109
|
3 |
-
size 81642981
|
|
|
|
|
|
|
|
fold1.ckpt
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:8c806c2ccd21cb4f1d1102e86d8716ed67583f561d4eea6a1761ac4f9bf6a60b
|
3 |
-
size 81642981
|
|
|
|
|
|
|
|
fold2.ckpt
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:cabdc105bb4c3239d1a57ceaaca4306096a017763c1ec1d23adacf6d8c0713ab
|
3 |
-
size 81642981
|
|
|
|
|
|
|
|
greulich_and_pyle_ages.json
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"bone_ages": {
|
3 |
+
"female": [
|
4 |
+
0,
|
5 |
+
3,
|
6 |
+
6,
|
7 |
+
9,
|
8 |
+
12,
|
9 |
+
15,
|
10 |
+
18,
|
11 |
+
24,
|
12 |
+
36,
|
13 |
+
42,
|
14 |
+
50,
|
15 |
+
60,
|
16 |
+
69,
|
17 |
+
82,
|
18 |
+
94,
|
19 |
+
106,
|
20 |
+
120,
|
21 |
+
132,
|
22 |
+
144,
|
23 |
+
156,
|
24 |
+
162,
|
25 |
+
168,
|
26 |
+
180,
|
27 |
+
192,
|
28 |
+
204,
|
29 |
+
216
|
30 |
+
],
|
31 |
+
"male": [
|
32 |
+
0,
|
33 |
+
3,
|
34 |
+
6,
|
35 |
+
9,
|
36 |
+
12,
|
37 |
+
15,
|
38 |
+
18,
|
39 |
+
24,
|
40 |
+
30,
|
41 |
+
32,
|
42 |
+
36,
|
43 |
+
42,
|
44 |
+
48,
|
45 |
+
54,
|
46 |
+
60,
|
47 |
+
72,
|
48 |
+
84,
|
49 |
+
96,
|
50 |
+
108,
|
51 |
+
120,
|
52 |
+
132,
|
53 |
+
138,
|
54 |
+
150,
|
55 |
+
156,
|
56 |
+
162,
|
57 |
+
168,
|
58 |
+
180,
|
59 |
+
192,
|
60 |
+
204,
|
61 |
+
216,
|
62 |
+
228
|
63 |
+
]
|
64 |
+
}
|
65 |
+
}
|
net0.pt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
../../experiments/boneage/boneage.cfg_female_channel_reg_cls_match_hist/fa77ff59/fold0/checkpoints/last.ckpt
|
net1.pt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
../../experiments/boneage/boneage.cfg_female_channel_reg_cls_match_hist/fa77ff59/fold1/checkpoints/last.ckpt
|
net2.pt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
../../experiments/boneage/boneage.cfg_female_channel_reg_cls_match_hist/fa77ff59/fold2/checkpoints/last.ckpt
|
ref_img.png
ADDED
![]() |
requirements.txt
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
|
|
|
|
4 |
timm
|
5 |
-
torch
|
6 |
-
|
|
|
1 |
+
albumentations
|
2 |
+
einops
|
3 |
+
grad-cam
|
4 |
+
gradio
|
5 |
+
scikit-image
|
6 |
timm
|
7 |
+
torch
|
|
skp/__pycache__/utils.cpython-312.pyc
ADDED
Binary file (2.51 kB). View file
|
|
skp/configs/__init__.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from types import SimpleNamespace
|
2 |
+
|
3 |
+
|
4 |
+
class Config(SimpleNamespace):
|
5 |
+
|
6 |
+
def __getattribute__(self, value):
|
7 |
+
# If attribute not specified in config,
|
8 |
+
# return None instead of raise error
|
9 |
+
try:
|
10 |
+
return super().__getattribute__(value)
|
11 |
+
except AttributeError:
|
12 |
+
return None
|
13 |
+
|
14 |
+
def __str__(self):
|
15 |
+
# pretty print
|
16 |
+
string = ["config"]
|
17 |
+
string.append("=" * len(string[0]))
|
18 |
+
longest_param_name = max([len(k) for k in [*self.__dict__]])
|
19 |
+
for k, v in self.__dict__.items():
|
20 |
+
string.append(f"{k.ljust(longest_param_name)} : {v}")
|
21 |
+
return "\n".join(string)
|
skp/configs/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (1.44 kB). View file
|
|
skp/configs/__pycache__/base.cpython-312.pyc
ADDED
Binary file (1.44 kB). View file
|
|
skp/configs/base.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from types import SimpleNamespace
|
2 |
+
|
3 |
+
|
4 |
+
class Config(SimpleNamespace):
|
5 |
+
|
6 |
+
def __getattribute__(self, value):
|
7 |
+
# If attribute not specified in config,
|
8 |
+
# return None instead of raise error
|
9 |
+
try:
|
10 |
+
return super().__getattribute__(value)
|
11 |
+
except AttribuateError:
|
12 |
+
return None
|
13 |
+
|
14 |
+
def __str__(self):
|
15 |
+
# pretty print
|
16 |
+
string = ["config"]
|
17 |
+
string.append("=" * len(string[0]))
|
18 |
+
longest_param_name = max([len(k) for k in [*self.__dict__]])
|
19 |
+
for k, v in self.__dict__.items():
|
20 |
+
string.append(f"{k.ljust(longest_param_name)} : {v}")
|
21 |
+
return "\n".join(string)
|
skp/configs/boneage/__pycache__/cfg_baseline.cpython-312.pyc
ADDED
Binary file (4.08 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_crop.cpython-312.pyc
ADDED
Binary file (4.18 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_crop_simple_resize.cpython-312.pyc
ADDED
Binary file (4.06 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel.cpython-312.pyc
ADDED
Binary file (3.98 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_MIL.cpython-312.pyc
ADDED
Binary file (4.02 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_MIL_lstm.cpython-312.pyc
ADDED
Binary file (4.12 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_MIL_transformer.cpython-312.pyc
ADDED
Binary file (4.2 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls.cpython-312.pyc
ADDED
Binary file (4.08 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_clip_outliers_aug.cpython-312.pyc
ADDED
Binary file (4.27 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_match_hist.cpython-312.pyc
ADDED
Binary file (4.25 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_with_cls.cpython-312.pyc
ADDED
Binary file (4.11 kB). View file
|
|
skp/configs/boneage/__pycache__/cfg_female_channel_with_cls_clip_outliers.cpython-312.pyc
ADDED
Binary file (4.22 kB). View file
|
|
skp/configs/boneage/cfg_baseline.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_var_embed"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.embed_num_classes = 2
|
18 |
+
cfg.embed_dim = 32
|
19 |
+
cfg.pretrained = True
|
20 |
+
cfg.num_input_channels = 1
|
21 |
+
cfg.pool = "gem"
|
22 |
+
cfg.pool_params = {"p": 3}
|
23 |
+
cfg.dropout = 0.1
|
24 |
+
cfg.num_classes = 1
|
25 |
+
cfg.normalization = "-1_1"
|
26 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
27 |
+
cfg.backbone_img_size = False
|
28 |
+
|
29 |
+
cfg.fold = 0
|
30 |
+
cfg.dataset = "simple2d"
|
31 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
32 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
33 |
+
cfg.inputs = "imgfile0"
|
34 |
+
cfg.targets = ["bone_age_years"]
|
35 |
+
cfg.vars = "female"
|
36 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
37 |
+
cfg.num_workers = 16
|
38 |
+
cfg.pin_memory = True
|
39 |
+
cfg.persistent_workers = True
|
40 |
+
cfg.sampler = "IterationBasedSampler"
|
41 |
+
cfg.num_iterations_per_epoch = 1000
|
42 |
+
|
43 |
+
cfg.loss = "classification.L1Loss"
|
44 |
+
cfg.loss_params = {}
|
45 |
+
|
46 |
+
cfg.batch_size = 32
|
47 |
+
cfg.num_epochs = 10
|
48 |
+
cfg.optimizer = "AdamW"
|
49 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
50 |
+
|
51 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
52 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
53 |
+
cfg.scheduler_interval = "step"
|
54 |
+
|
55 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
56 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
57 |
+
cfg.val_metric = "mae_mean"
|
58 |
+
cfg.val_track = "min"
|
59 |
+
|
60 |
+
cfg.image_height = 512
|
61 |
+
cfg.image_width = 512
|
62 |
+
|
63 |
+
resize_transforms = [
|
64 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
65 |
+
A.PadIfNeeded(
|
66 |
+
min_height=cfg.image_height,
|
67 |
+
min_width=cfg.image_width,
|
68 |
+
border_mode=cv2.BORDER_CONSTANT,
|
69 |
+
p=1,
|
70 |
+
),
|
71 |
+
]
|
72 |
+
|
73 |
+
cfg.train_transforms = A.Compose(
|
74 |
+
resize_transforms
|
75 |
+
+ [
|
76 |
+
A.VerticalFlip(p=0.5),
|
77 |
+
A.HorizontalFlip(p=0.5),
|
78 |
+
A.SomeOf(
|
79 |
+
[
|
80 |
+
A.ShiftScaleRotate(
|
81 |
+
shift_limit=0.2,
|
82 |
+
scale_limit=0.0,
|
83 |
+
rotate_limit=0,
|
84 |
+
border_mode=cv2.BORDER_CONSTANT,
|
85 |
+
p=1,
|
86 |
+
),
|
87 |
+
A.ShiftScaleRotate(
|
88 |
+
shift_limit=0.0,
|
89 |
+
scale_limit=0.2,
|
90 |
+
rotate_limit=0,
|
91 |
+
border_mode=cv2.BORDER_CONSTANT,
|
92 |
+
p=1,
|
93 |
+
),
|
94 |
+
A.ShiftScaleRotate(
|
95 |
+
shift_limit=0.0,
|
96 |
+
scale_limit=0.0,
|
97 |
+
rotate_limit=30,
|
98 |
+
border_mode=cv2.BORDER_CONSTANT,
|
99 |
+
p=1,
|
100 |
+
),
|
101 |
+
A.GaussianBlur(p=1),
|
102 |
+
A.GaussNoise(p=1),
|
103 |
+
A.RandomBrightnessContrast(
|
104 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
105 |
+
),
|
106 |
+
A.RandomBrightnessContrast(
|
107 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
108 |
+
),
|
109 |
+
],
|
110 |
+
n=3,
|
111 |
+
p=0.9,
|
112 |
+
replace=False,
|
113 |
+
),
|
114 |
+
]
|
115 |
+
)
|
116 |
+
|
117 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_crop.py
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d"
|
16 |
+
cfg.backbone = "mobilenetv3_small_100"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 1
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 4
|
23 |
+
cfg.normalization = "-1_1"
|
24 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
25 |
+
cfg.backbone_img_size = False
|
26 |
+
cfg.model_activation_fn = "sigmoid"
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "crop2d"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/train/"
|
31 |
+
cfg.annotations_file = (
|
32 |
+
"/mnt/stor/datasets/bone-age/train_with_bounding_box_crop_coords_kfold.csv"
|
33 |
+
)
|
34 |
+
cfg.inputs = "imgfile"
|
35 |
+
cfg.targets = ["x1", "y1", "w", "h"]
|
36 |
+
cfg.normalize_crop_coords = True
|
37 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
38 |
+
cfg.num_workers = 16
|
39 |
+
cfg.pin_memory = True
|
40 |
+
cfg.persistent_workers = True
|
41 |
+
cfg.sampler = "IterationBasedSampler"
|
42 |
+
cfg.num_iterations_per_epoch = 100
|
43 |
+
|
44 |
+
cfg.loss = "classification.L1Loss"
|
45 |
+
cfg.loss_params = {}
|
46 |
+
|
47 |
+
cfg.batch_size = 16
|
48 |
+
cfg.num_epochs = 10
|
49 |
+
cfg.optimizer = "AdamW"
|
50 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
51 |
+
|
52 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
53 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
54 |
+
cfg.scheduler_interval = "step"
|
55 |
+
|
56 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
57 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
58 |
+
cfg.val_metric = "mae_mean"
|
59 |
+
cfg.val_track = "min"
|
60 |
+
|
61 |
+
cfg.image_height = 512
|
62 |
+
cfg.image_width = 512
|
63 |
+
|
64 |
+
bbox_params = A.BboxParams(format="coco")
|
65 |
+
resize_transforms = [
|
66 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
67 |
+
A.PadIfNeeded(
|
68 |
+
min_height=cfg.image_height,
|
69 |
+
min_width=cfg.image_width,
|
70 |
+
border_mode=cv2.BORDER_CONSTANT,
|
71 |
+
p=1,
|
72 |
+
),
|
73 |
+
]
|
74 |
+
|
75 |
+
cfg.train_transforms = A.Compose(
|
76 |
+
resize_transforms
|
77 |
+
+ [
|
78 |
+
A.VerticalFlip(p=0.5),
|
79 |
+
A.HorizontalFlip(p=0.5),
|
80 |
+
A.SomeOf(
|
81 |
+
[
|
82 |
+
A.ShiftScaleRotate(
|
83 |
+
shift_limit=0.2,
|
84 |
+
scale_limit=0.0,
|
85 |
+
rotate_limit=0,
|
86 |
+
border_mode=cv2.BORDER_CONSTANT,
|
87 |
+
p=1,
|
88 |
+
),
|
89 |
+
A.ShiftScaleRotate(
|
90 |
+
shift_limit=0.0,
|
91 |
+
scale_limit=0.2,
|
92 |
+
rotate_limit=0,
|
93 |
+
border_mode=cv2.BORDER_CONSTANT,
|
94 |
+
p=1,
|
95 |
+
),
|
96 |
+
A.ShiftScaleRotate(
|
97 |
+
shift_limit=0.0,
|
98 |
+
scale_limit=0.0,
|
99 |
+
rotate_limit=30,
|
100 |
+
border_mode=cv2.BORDER_CONSTANT,
|
101 |
+
p=1,
|
102 |
+
),
|
103 |
+
A.GaussianBlur(p=1),
|
104 |
+
A.GaussNoise(p=1),
|
105 |
+
A.RandomBrightnessContrast(
|
106 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
107 |
+
),
|
108 |
+
A.RandomBrightnessContrast(
|
109 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
110 |
+
),
|
111 |
+
],
|
112 |
+
n=3,
|
113 |
+
p=0.9,
|
114 |
+
replace=False,
|
115 |
+
),
|
116 |
+
],
|
117 |
+
bbox_params=bbox_params,
|
118 |
+
)
|
119 |
+
|
120 |
+
cfg.val_transforms = A.Compose(
|
121 |
+
resize_transforms,
|
122 |
+
bbox_params=bbox_params,
|
123 |
+
)
|
skp/configs/boneage/cfg_crop_simple_resize.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d"
|
16 |
+
cfg.backbone = "mobilenetv3_small_100"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 1
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 4
|
23 |
+
cfg.normalization = "-1_1"
|
24 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
25 |
+
cfg.backbone_img_size = False
|
26 |
+
cfg.model_activation_fn = "sigmoid"
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "crop2d"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/train/"
|
31 |
+
cfg.annotations_file = (
|
32 |
+
"/mnt/stor/datasets/bone-age/train_with_bounding_box_crop_coords_kfold.csv"
|
33 |
+
)
|
34 |
+
cfg.inputs = "imgfile"
|
35 |
+
cfg.targets = ["x1", "y1", "w", "h"]
|
36 |
+
cfg.normalize_crop_coords = True
|
37 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
38 |
+
cfg.num_workers = 16
|
39 |
+
cfg.pin_memory = True
|
40 |
+
cfg.persistent_workers = True
|
41 |
+
cfg.sampler = "IterationBasedSampler"
|
42 |
+
cfg.num_iterations_per_epoch = 200
|
43 |
+
|
44 |
+
cfg.loss = "classification.L1Loss"
|
45 |
+
cfg.loss_params = {}
|
46 |
+
|
47 |
+
cfg.batch_size = 16
|
48 |
+
cfg.num_epochs = 10
|
49 |
+
cfg.optimizer = "AdamW"
|
50 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
51 |
+
|
52 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
53 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
54 |
+
cfg.scheduler_interval = "step"
|
55 |
+
|
56 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
57 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
58 |
+
cfg.val_metric = "mae_mean"
|
59 |
+
cfg.val_track = "min"
|
60 |
+
|
61 |
+
cfg.image_height = 512
|
62 |
+
cfg.image_width = 512
|
63 |
+
|
64 |
+
bbox_params = A.BboxParams(format="coco")
|
65 |
+
resize_transforms = [
|
66 |
+
A.Resize(height=cfg.image_height, width=cfg.image_width, p=1)
|
67 |
+
]
|
68 |
+
|
69 |
+
cfg.train_transforms = A.Compose(
|
70 |
+
resize_transforms
|
71 |
+
+ [
|
72 |
+
A.VerticalFlip(p=0.5),
|
73 |
+
A.HorizontalFlip(p=0.5),
|
74 |
+
A.SomeOf(
|
75 |
+
[
|
76 |
+
A.ShiftScaleRotate(
|
77 |
+
shift_limit=0.2,
|
78 |
+
scale_limit=0.0,
|
79 |
+
rotate_limit=0,
|
80 |
+
border_mode=cv2.BORDER_CONSTANT,
|
81 |
+
p=1,
|
82 |
+
),
|
83 |
+
A.ShiftScaleRotate(
|
84 |
+
shift_limit=0.0,
|
85 |
+
scale_limit=0.2,
|
86 |
+
rotate_limit=0,
|
87 |
+
border_mode=cv2.BORDER_CONSTANT,
|
88 |
+
p=1,
|
89 |
+
),
|
90 |
+
A.ShiftScaleRotate(
|
91 |
+
shift_limit=0.0,
|
92 |
+
scale_limit=0.0,
|
93 |
+
rotate_limit=30,
|
94 |
+
border_mode=cv2.BORDER_CONSTANT,
|
95 |
+
p=1,
|
96 |
+
),
|
97 |
+
A.GaussianBlur(p=1),
|
98 |
+
A.GaussNoise(p=1),
|
99 |
+
A.RandomBrightnessContrast(
|
100 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
101 |
+
),
|
102 |
+
A.RandomBrightnessContrast(
|
103 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
104 |
+
),
|
105 |
+
],
|
106 |
+
n=3,
|
107 |
+
p=0.9,
|
108 |
+
replace=False,
|
109 |
+
),
|
110 |
+
],
|
111 |
+
bbox_params=bbox_params,
|
112 |
+
)
|
113 |
+
|
114 |
+
cfg.val_transforms = A.Compose(
|
115 |
+
resize_transforms,
|
116 |
+
bbox_params=bbox_params,
|
117 |
+
)
|
skp/configs/boneage/cfg_female_channel.py
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 1
|
23 |
+
cfg.normalization = "-1_1"
|
24 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
25 |
+
cfg.backbone_img_size = False
|
26 |
+
|
27 |
+
cfg.fold = 0
|
28 |
+
cfg.dataset = "boneage.female_channel"
|
29 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
30 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
31 |
+
cfg.inputs = "imgfile0"
|
32 |
+
cfg.targets = ["bone_age_years"]
|
33 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
34 |
+
cfg.num_workers = 16
|
35 |
+
cfg.pin_memory = True
|
36 |
+
cfg.persistent_workers = True
|
37 |
+
cfg.sampler = "IterationBasedSampler"
|
38 |
+
cfg.num_iterations_per_epoch = 1000
|
39 |
+
|
40 |
+
cfg.loss = "classification.L1Loss"
|
41 |
+
cfg.loss_params = {}
|
42 |
+
|
43 |
+
cfg.batch_size = 32
|
44 |
+
cfg.num_epochs = 10
|
45 |
+
cfg.optimizer = "AdamW"
|
46 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
47 |
+
|
48 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
49 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
50 |
+
cfg.scheduler_interval = "step"
|
51 |
+
|
52 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
53 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
54 |
+
cfg.val_metric = "mae_mean"
|
55 |
+
cfg.val_track = "min"
|
56 |
+
|
57 |
+
cfg.image_height = 512
|
58 |
+
cfg.image_width = 512
|
59 |
+
|
60 |
+
resize_transforms = [
|
61 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
62 |
+
A.PadIfNeeded(
|
63 |
+
min_height=cfg.image_height,
|
64 |
+
min_width=cfg.image_width,
|
65 |
+
border_mode=cv2.BORDER_CONSTANT,
|
66 |
+
p=1,
|
67 |
+
),
|
68 |
+
]
|
69 |
+
|
70 |
+
cfg.train_transforms = A.Compose(
|
71 |
+
resize_transforms
|
72 |
+
+ [
|
73 |
+
A.VerticalFlip(p=0.5),
|
74 |
+
A.HorizontalFlip(p=0.5),
|
75 |
+
A.SomeOf(
|
76 |
+
[
|
77 |
+
A.ShiftScaleRotate(
|
78 |
+
shift_limit=0.2,
|
79 |
+
scale_limit=0.0,
|
80 |
+
rotate_limit=0,
|
81 |
+
border_mode=cv2.BORDER_CONSTANT,
|
82 |
+
p=1,
|
83 |
+
),
|
84 |
+
A.ShiftScaleRotate(
|
85 |
+
shift_limit=0.0,
|
86 |
+
scale_limit=0.2,
|
87 |
+
rotate_limit=0,
|
88 |
+
border_mode=cv2.BORDER_CONSTANT,
|
89 |
+
p=1,
|
90 |
+
),
|
91 |
+
A.ShiftScaleRotate(
|
92 |
+
shift_limit=0.0,
|
93 |
+
scale_limit=0.0,
|
94 |
+
rotate_limit=30,
|
95 |
+
border_mode=cv2.BORDER_CONSTANT,
|
96 |
+
p=1,
|
97 |
+
),
|
98 |
+
A.GaussianBlur(p=1),
|
99 |
+
A.GaussNoise(p=1),
|
100 |
+
A.RandomBrightnessContrast(
|
101 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
102 |
+
),
|
103 |
+
A.RandomBrightnessContrast(
|
104 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
105 |
+
),
|
106 |
+
],
|
107 |
+
n=3,
|
108 |
+
p=0.9,
|
109 |
+
replace=False,
|
110 |
+
),
|
111 |
+
]
|
112 |
+
)
|
113 |
+
|
114 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_MIL.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "MIL.net2d_basic_attn"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 1
|
23 |
+
cfg.attn_dropout = 0.0
|
24 |
+
cfg.attn_version = "v1"
|
25 |
+
cfg.normalization = "-1_1"
|
26 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
27 |
+
cfg.backbone_img_size = False
|
28 |
+
|
29 |
+
cfg.fold = 0
|
30 |
+
cfg.dataset = "boneage.female_channel_grid_patch"
|
31 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
32 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
33 |
+
cfg.inputs = "imgfile0"
|
34 |
+
cfg.targets = ["bone_age_years"]
|
35 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
36 |
+
cfg.patch_size = 224
|
37 |
+
cfg.patch_num_rows = 5
|
38 |
+
cfg.patch_num_cols = 3
|
39 |
+
cfg.num_workers = 16
|
40 |
+
cfg.pin_memory = True
|
41 |
+
cfg.persistent_workers = True
|
42 |
+
cfg.sampler = "IterationBasedSampler"
|
43 |
+
cfg.num_iterations_per_epoch = 1000
|
44 |
+
|
45 |
+
cfg.loss = "classification.L1Loss"
|
46 |
+
cfg.loss_params = {}
|
47 |
+
|
48 |
+
cfg.batch_size = 16
|
49 |
+
cfg.num_epochs = 10
|
50 |
+
cfg.optimizer = "AdamW"
|
51 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
52 |
+
|
53 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
54 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
55 |
+
cfg.scheduler_interval = "step"
|
56 |
+
|
57 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
58 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
59 |
+
cfg.val_metric = "mae_mean"
|
60 |
+
cfg.val_track = "min"
|
61 |
+
|
62 |
+
cfg.image_height = 560
|
63 |
+
cfg.image_width = cfg.image_height # not used
|
64 |
+
|
65 |
+
resize_transforms = [
|
66 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
67 |
+
]
|
68 |
+
|
69 |
+
cfg.train_transforms = A.Compose(
|
70 |
+
resize_transforms
|
71 |
+
+ [
|
72 |
+
A.VerticalFlip(p=0.5),
|
73 |
+
A.HorizontalFlip(p=0.5),
|
74 |
+
A.SomeOf(
|
75 |
+
[
|
76 |
+
A.ShiftScaleRotate(
|
77 |
+
shift_limit=0.2,
|
78 |
+
scale_limit=0.0,
|
79 |
+
rotate_limit=0,
|
80 |
+
border_mode=cv2.BORDER_CONSTANT,
|
81 |
+
p=1,
|
82 |
+
),
|
83 |
+
A.ShiftScaleRotate(
|
84 |
+
shift_limit=0.0,
|
85 |
+
scale_limit=0.2,
|
86 |
+
rotate_limit=0,
|
87 |
+
border_mode=cv2.BORDER_CONSTANT,
|
88 |
+
p=1,
|
89 |
+
),
|
90 |
+
A.ShiftScaleRotate(
|
91 |
+
shift_limit=0.0,
|
92 |
+
scale_limit=0.0,
|
93 |
+
rotate_limit=30,
|
94 |
+
border_mode=cv2.BORDER_CONSTANT,
|
95 |
+
p=1,
|
96 |
+
),
|
97 |
+
A.GaussianBlur(p=1),
|
98 |
+
A.GaussNoise(p=1),
|
99 |
+
A.RandomBrightnessContrast(
|
100 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
101 |
+
),
|
102 |
+
A.RandomBrightnessContrast(
|
103 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
104 |
+
),
|
105 |
+
],
|
106 |
+
n=3,
|
107 |
+
p=0.9,
|
108 |
+
replace=False,
|
109 |
+
),
|
110 |
+
]
|
111 |
+
)
|
112 |
+
|
113 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_MIL_lstm.py
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "MIL.net2d_attn"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 1
|
23 |
+
cfg.add_lstm = True
|
24 |
+
cfg.lstm_dropout = 0.0
|
25 |
+
cfg.lstm_num_layers = 1
|
26 |
+
cfg.attn_dropout = 0.0
|
27 |
+
cfg.attn_version = "v1"
|
28 |
+
cfg.normalization = "-1_1"
|
29 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
30 |
+
cfg.backbone_img_size = False
|
31 |
+
|
32 |
+
cfg.fold = 0
|
33 |
+
cfg.dataset = "boneage.female_channel_grid_patch"
|
34 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
35 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
36 |
+
cfg.inputs = "imgfile0"
|
37 |
+
cfg.targets = ["bone_age_years"]
|
38 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
39 |
+
cfg.patch_size = 224
|
40 |
+
cfg.patch_num_rows = 5
|
41 |
+
cfg.patch_num_cols = 3
|
42 |
+
cfg.num_workers = 16
|
43 |
+
cfg.pin_memory = True
|
44 |
+
cfg.persistent_workers = True
|
45 |
+
cfg.sampler = "IterationBasedSampler"
|
46 |
+
cfg.num_iterations_per_epoch = 1000
|
47 |
+
|
48 |
+
cfg.loss = "classification.L1Loss"
|
49 |
+
cfg.loss_params = {}
|
50 |
+
|
51 |
+
cfg.batch_size = 16
|
52 |
+
cfg.num_epochs = 10
|
53 |
+
cfg.optimizer = "AdamW"
|
54 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
55 |
+
|
56 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
57 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
58 |
+
cfg.scheduler_interval = "step"
|
59 |
+
|
60 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
61 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
62 |
+
cfg.val_metric = "mae_mean"
|
63 |
+
cfg.val_track = "min"
|
64 |
+
|
65 |
+
cfg.image_height = 560
|
66 |
+
cfg.image_width = cfg.image_height # not used
|
67 |
+
|
68 |
+
resize_transforms = [
|
69 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
70 |
+
]
|
71 |
+
|
72 |
+
cfg.train_transforms = A.Compose(
|
73 |
+
resize_transforms
|
74 |
+
+ [
|
75 |
+
A.VerticalFlip(p=0.5),
|
76 |
+
A.HorizontalFlip(p=0.5),
|
77 |
+
A.SomeOf(
|
78 |
+
[
|
79 |
+
A.ShiftScaleRotate(
|
80 |
+
shift_limit=0.2,
|
81 |
+
scale_limit=0.0,
|
82 |
+
rotate_limit=0,
|
83 |
+
border_mode=cv2.BORDER_CONSTANT,
|
84 |
+
p=1,
|
85 |
+
),
|
86 |
+
A.ShiftScaleRotate(
|
87 |
+
shift_limit=0.0,
|
88 |
+
scale_limit=0.2,
|
89 |
+
rotate_limit=0,
|
90 |
+
border_mode=cv2.BORDER_CONSTANT,
|
91 |
+
p=1,
|
92 |
+
),
|
93 |
+
A.ShiftScaleRotate(
|
94 |
+
shift_limit=0.0,
|
95 |
+
scale_limit=0.0,
|
96 |
+
rotate_limit=30,
|
97 |
+
border_mode=cv2.BORDER_CONSTANT,
|
98 |
+
p=1,
|
99 |
+
),
|
100 |
+
A.GaussianBlur(p=1),
|
101 |
+
A.GaussNoise(p=1),
|
102 |
+
A.RandomBrightnessContrast(
|
103 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
104 |
+
),
|
105 |
+
A.RandomBrightnessContrast(
|
106 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
107 |
+
),
|
108 |
+
],
|
109 |
+
n=3,
|
110 |
+
p=0.9,
|
111 |
+
replace=False,
|
112 |
+
),
|
113 |
+
]
|
114 |
+
)
|
115 |
+
|
116 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_MIL_transformer.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "MIL.net2d_attn"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = 1
|
23 |
+
cfg.reduce_feature_dim = 256
|
24 |
+
cfg.add_transformer = True
|
25 |
+
cfg.transformer_dropout = 0.0
|
26 |
+
cfg.transformer_num_layers = 1
|
27 |
+
cfg.attn_dropout = 0.0
|
28 |
+
cfg.attn_version = "v1"
|
29 |
+
cfg.normalization = "-1_1"
|
30 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
31 |
+
cfg.backbone_img_size = False
|
32 |
+
|
33 |
+
cfg.fold = 0
|
34 |
+
cfg.dataset = "boneage.female_channel_grid_patch"
|
35 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
36 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
37 |
+
cfg.inputs = "imgfile0"
|
38 |
+
cfg.targets = ["bone_age_years"]
|
39 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
40 |
+
cfg.patch_size = 224
|
41 |
+
cfg.patch_num_rows = 5
|
42 |
+
cfg.patch_num_cols = 3
|
43 |
+
cfg.num_workers = 16
|
44 |
+
cfg.pin_memory = True
|
45 |
+
cfg.persistent_workers = True
|
46 |
+
cfg.sampler = "IterationBasedSampler"
|
47 |
+
cfg.num_iterations_per_epoch = 1000
|
48 |
+
|
49 |
+
cfg.loss = "classification.L1Loss"
|
50 |
+
cfg.loss_params = {}
|
51 |
+
|
52 |
+
cfg.batch_size = 16
|
53 |
+
cfg.num_epochs = 10
|
54 |
+
cfg.optimizer = "AdamW"
|
55 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
56 |
+
|
57 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
58 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
59 |
+
cfg.scheduler_interval = "step"
|
60 |
+
|
61 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
62 |
+
cfg.metrics = ["classification.MAE", "classification.MSE"]
|
63 |
+
cfg.val_metric = "mae_mean"
|
64 |
+
cfg.val_track = "min"
|
65 |
+
|
66 |
+
cfg.image_height = 560
|
67 |
+
cfg.image_width = cfg.image_height # not used
|
68 |
+
|
69 |
+
resize_transforms = [
|
70 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
71 |
+
]
|
72 |
+
|
73 |
+
cfg.train_transforms = A.Compose(
|
74 |
+
resize_transforms
|
75 |
+
+ [
|
76 |
+
A.VerticalFlip(p=0.5),
|
77 |
+
A.HorizontalFlip(p=0.5),
|
78 |
+
A.SomeOf(
|
79 |
+
[
|
80 |
+
A.ShiftScaleRotate(
|
81 |
+
shift_limit=0.2,
|
82 |
+
scale_limit=0.0,
|
83 |
+
rotate_limit=0,
|
84 |
+
border_mode=cv2.BORDER_CONSTANT,
|
85 |
+
p=1,
|
86 |
+
),
|
87 |
+
A.ShiftScaleRotate(
|
88 |
+
shift_limit=0.0,
|
89 |
+
scale_limit=0.2,
|
90 |
+
rotate_limit=0,
|
91 |
+
border_mode=cv2.BORDER_CONSTANT,
|
92 |
+
p=1,
|
93 |
+
),
|
94 |
+
A.ShiftScaleRotate(
|
95 |
+
shift_limit=0.0,
|
96 |
+
scale_limit=0.0,
|
97 |
+
rotate_limit=30,
|
98 |
+
border_mode=cv2.BORDER_CONSTANT,
|
99 |
+
p=1,
|
100 |
+
),
|
101 |
+
A.GaussianBlur(p=1),
|
102 |
+
A.GaussNoise(p=1),
|
103 |
+
A.RandomBrightnessContrast(
|
104 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
105 |
+
),
|
106 |
+
A.RandomBrightnessContrast(
|
107 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
108 |
+
),
|
109 |
+
],
|
110 |
+
n=3,
|
111 |
+
p=0.9,
|
112 |
+
replace=False,
|
113 |
+
),
|
114 |
+
]
|
115 |
+
)
|
116 |
+
|
117 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_reg_cls.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 240]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.inputs = "imgfile0"
|
33 |
+
cfg.targets = ["bone_age"]
|
34 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
35 |
+
cfg.num_workers = 16
|
36 |
+
cfg.pin_memory = True
|
37 |
+
cfg.persistent_workers = True
|
38 |
+
cfg.sampler = "IterationBasedSampler"
|
39 |
+
cfg.num_iterations_per_epoch = 1000
|
40 |
+
|
41 |
+
cfg.loss = "classification.DoubleL1Loss"
|
42 |
+
cfg.loss_params = {"reg_weight": 1.0, "cls_weight": 0.4}
|
43 |
+
|
44 |
+
cfg.batch_size = 32
|
45 |
+
cfg.num_epochs = 10
|
46 |
+
cfg.optimizer = "AdamW"
|
47 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
48 |
+
|
49 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
50 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
51 |
+
cfg.scheduler_interval = "step"
|
52 |
+
|
53 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
54 |
+
cfg.metrics = ["classification.DoubleMAE"]
|
55 |
+
cfg.val_metric = "mae_reg"
|
56 |
+
cfg.val_track = "min"
|
57 |
+
|
58 |
+
cfg.image_height = 512
|
59 |
+
cfg.image_width = 512
|
60 |
+
|
61 |
+
resize_transforms = [
|
62 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
63 |
+
A.PadIfNeeded(
|
64 |
+
min_height=cfg.image_height,
|
65 |
+
min_width=cfg.image_width,
|
66 |
+
border_mode=cv2.BORDER_CONSTANT,
|
67 |
+
p=1,
|
68 |
+
),
|
69 |
+
]
|
70 |
+
|
71 |
+
cfg.train_transforms = A.Compose(
|
72 |
+
resize_transforms
|
73 |
+
+ [
|
74 |
+
A.VerticalFlip(p=0.5),
|
75 |
+
A.HorizontalFlip(p=0.5),
|
76 |
+
A.SomeOf(
|
77 |
+
[
|
78 |
+
A.ShiftScaleRotate(
|
79 |
+
shift_limit=0.2,
|
80 |
+
scale_limit=0.0,
|
81 |
+
rotate_limit=0,
|
82 |
+
border_mode=cv2.BORDER_CONSTANT,
|
83 |
+
p=1,
|
84 |
+
),
|
85 |
+
A.ShiftScaleRotate(
|
86 |
+
shift_limit=0.0,
|
87 |
+
scale_limit=0.2,
|
88 |
+
rotate_limit=0,
|
89 |
+
border_mode=cv2.BORDER_CONSTANT,
|
90 |
+
p=1,
|
91 |
+
),
|
92 |
+
A.ShiftScaleRotate(
|
93 |
+
shift_limit=0.0,
|
94 |
+
scale_limit=0.0,
|
95 |
+
rotate_limit=30,
|
96 |
+
border_mode=cv2.BORDER_CONSTANT,
|
97 |
+
p=1,
|
98 |
+
),
|
99 |
+
A.GaussianBlur(p=1),
|
100 |
+
A.GaussNoise(p=1),
|
101 |
+
A.RandomBrightnessContrast(
|
102 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
103 |
+
),
|
104 |
+
A.RandomBrightnessContrast(
|
105 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
106 |
+
),
|
107 |
+
],
|
108 |
+
n=3,
|
109 |
+
p=0.9,
|
110 |
+
replace=False,
|
111 |
+
),
|
112 |
+
]
|
113 |
+
)
|
114 |
+
|
115 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_reg_cls_clip_outliers_aug.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 240]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.inputs = "imgfile0"
|
33 |
+
cfg.targets = ["bone_age"]
|
34 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
35 |
+
cfg.num_workers = 16
|
36 |
+
cfg.clip_outlier_pixels_and_rescale = True
|
37 |
+
cfg.clip_as_data_aug = True
|
38 |
+
cfg.clip_proba = 0.5
|
39 |
+
cfg.clip_bounds = (1, 99)
|
40 |
+
cfg.pin_memory = True
|
41 |
+
cfg.persistent_workers = True
|
42 |
+
cfg.sampler = "IterationBasedSampler"
|
43 |
+
cfg.num_iterations_per_epoch = 1000
|
44 |
+
|
45 |
+
cfg.loss = "classification.DoubleL1Loss"
|
46 |
+
cfg.loss_params = {"reg_weight": 1.0, "cls_weight": 0.4}
|
47 |
+
|
48 |
+
cfg.batch_size = 32
|
49 |
+
cfg.num_epochs = 10
|
50 |
+
cfg.optimizer = "AdamW"
|
51 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
52 |
+
|
53 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
54 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
55 |
+
cfg.scheduler_interval = "step"
|
56 |
+
|
57 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
58 |
+
cfg.metrics = ["classification.DoubleMAE"]
|
59 |
+
cfg.val_metric = "mae_reg"
|
60 |
+
cfg.val_track = "min"
|
61 |
+
|
62 |
+
cfg.image_height = 512
|
63 |
+
cfg.image_width = 512
|
64 |
+
|
65 |
+
resize_transforms = [
|
66 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
67 |
+
A.PadIfNeeded(
|
68 |
+
min_height=cfg.image_height,
|
69 |
+
min_width=cfg.image_width,
|
70 |
+
border_mode=cv2.BORDER_CONSTANT,
|
71 |
+
p=1,
|
72 |
+
),
|
73 |
+
]
|
74 |
+
|
75 |
+
cfg.train_transforms = A.Compose(
|
76 |
+
resize_transforms
|
77 |
+
+ [
|
78 |
+
A.VerticalFlip(p=0.5),
|
79 |
+
A.HorizontalFlip(p=0.5),
|
80 |
+
A.SomeOf(
|
81 |
+
[
|
82 |
+
A.ShiftScaleRotate(
|
83 |
+
shift_limit=0.2,
|
84 |
+
scale_limit=0.0,
|
85 |
+
rotate_limit=0,
|
86 |
+
border_mode=cv2.BORDER_CONSTANT,
|
87 |
+
p=1,
|
88 |
+
),
|
89 |
+
A.ShiftScaleRotate(
|
90 |
+
shift_limit=0.0,
|
91 |
+
scale_limit=0.2,
|
92 |
+
rotate_limit=0,
|
93 |
+
border_mode=cv2.BORDER_CONSTANT,
|
94 |
+
p=1,
|
95 |
+
),
|
96 |
+
A.ShiftScaleRotate(
|
97 |
+
shift_limit=0.0,
|
98 |
+
scale_limit=0.0,
|
99 |
+
rotate_limit=30,
|
100 |
+
border_mode=cv2.BORDER_CONSTANT,
|
101 |
+
p=1,
|
102 |
+
),
|
103 |
+
A.GaussianBlur(p=1),
|
104 |
+
A.GaussNoise(p=1),
|
105 |
+
A.RandomBrightnessContrast(
|
106 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
107 |
+
),
|
108 |
+
A.RandomBrightnessContrast(
|
109 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
110 |
+
),
|
111 |
+
],
|
112 |
+
n=3,
|
113 |
+
p=0.9,
|
114 |
+
replace=False,
|
115 |
+
),
|
116 |
+
]
|
117 |
+
)
|
118 |
+
|
119 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_reg_cls_match_hist.py
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 240]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel_match_hist"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.ref_image_match_hist = "/mnt/stor/datasets/bone-age/reference_cropped_image_for_histogram_matching.png"
|
33 |
+
cfg.inputs = "imgfile0"
|
34 |
+
cfg.targets = ["bone_age"]
|
35 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
36 |
+
cfg.num_workers = 16
|
37 |
+
cfg.pin_memory = True
|
38 |
+
cfg.persistent_workers = True
|
39 |
+
cfg.sampler = "IterationBasedSampler"
|
40 |
+
cfg.num_iterations_per_epoch = 1000
|
41 |
+
|
42 |
+
cfg.loss = "classification.DoubleL1Loss"
|
43 |
+
cfg.loss_params = {"reg_weight": 1.0, "cls_weight": 0.4}
|
44 |
+
|
45 |
+
cfg.batch_size = 32
|
46 |
+
cfg.num_epochs = 10
|
47 |
+
cfg.optimizer = "AdamW"
|
48 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
49 |
+
|
50 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
51 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
52 |
+
cfg.scheduler_interval = "step"
|
53 |
+
|
54 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
55 |
+
cfg.metrics = ["classification.DoubleMAE"]
|
56 |
+
cfg.val_metric = "mae_reg"
|
57 |
+
cfg.val_track = "min"
|
58 |
+
|
59 |
+
cfg.image_height = 512
|
60 |
+
cfg.image_width = 512
|
61 |
+
|
62 |
+
resize_transforms = [
|
63 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
64 |
+
A.PadIfNeeded(
|
65 |
+
min_height=cfg.image_height,
|
66 |
+
min_width=cfg.image_width,
|
67 |
+
border_mode=cv2.BORDER_CONSTANT,
|
68 |
+
p=1,
|
69 |
+
),
|
70 |
+
]
|
71 |
+
|
72 |
+
cfg.train_transforms = A.Compose(
|
73 |
+
resize_transforms
|
74 |
+
+ [
|
75 |
+
A.VerticalFlip(p=0.5),
|
76 |
+
A.HorizontalFlip(p=0.5),
|
77 |
+
A.SomeOf(
|
78 |
+
[
|
79 |
+
A.ShiftScaleRotate(
|
80 |
+
shift_limit=0.2,
|
81 |
+
scale_limit=0.0,
|
82 |
+
rotate_limit=0,
|
83 |
+
border_mode=cv2.BORDER_CONSTANT,
|
84 |
+
p=1,
|
85 |
+
),
|
86 |
+
A.ShiftScaleRotate(
|
87 |
+
shift_limit=0.0,
|
88 |
+
scale_limit=0.2,
|
89 |
+
rotate_limit=0,
|
90 |
+
border_mode=cv2.BORDER_CONSTANT,
|
91 |
+
p=1,
|
92 |
+
),
|
93 |
+
A.ShiftScaleRotate(
|
94 |
+
shift_limit=0.0,
|
95 |
+
scale_limit=0.0,
|
96 |
+
rotate_limit=30,
|
97 |
+
border_mode=cv2.BORDER_CONSTANT,
|
98 |
+
p=1,
|
99 |
+
),
|
100 |
+
A.GaussianBlur(p=1),
|
101 |
+
A.GaussNoise(p=1),
|
102 |
+
A.RandomBrightnessContrast(
|
103 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
104 |
+
),
|
105 |
+
A.RandomBrightnessContrast(
|
106 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
107 |
+
),
|
108 |
+
],
|
109 |
+
n=3,
|
110 |
+
p=0.9,
|
111 |
+
replace=False,
|
112 |
+
),
|
113 |
+
]
|
114 |
+
)
|
115 |
+
|
116 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_with_cls.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 24]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel_with_cls"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.inputs = "imgfile0"
|
33 |
+
cfg.targets = ["bone_age_years", "bone_age_categorical"]
|
34 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
35 |
+
cfg.num_workers = 16
|
36 |
+
cfg.pin_memory = True
|
37 |
+
cfg.persistent_workers = True
|
38 |
+
cfg.sampler = "IterationBasedSampler"
|
39 |
+
cfg.num_iterations_per_epoch = 1000
|
40 |
+
|
41 |
+
cfg.loss = "classification.L1CELoss"
|
42 |
+
cfg.loss_params = {"l1_weight": 1.0, "ce_weight": 0.2}
|
43 |
+
|
44 |
+
cfg.batch_size = 32
|
45 |
+
cfg.num_epochs = 10
|
46 |
+
cfg.optimizer = "AdamW"
|
47 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
48 |
+
|
49 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
50 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
51 |
+
cfg.scheduler_interval = "step"
|
52 |
+
|
53 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
54 |
+
cfg.metrics = ["classification.MAE_Accuracy"]
|
55 |
+
cfg.val_metric = "mae_mean"
|
56 |
+
cfg.val_track = "min"
|
57 |
+
|
58 |
+
cfg.image_height = 512
|
59 |
+
cfg.image_width = 512
|
60 |
+
|
61 |
+
resize_transforms = [
|
62 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
63 |
+
A.PadIfNeeded(
|
64 |
+
min_height=cfg.image_height,
|
65 |
+
min_width=cfg.image_width,
|
66 |
+
border_mode=cv2.BORDER_CONSTANT,
|
67 |
+
p=1,
|
68 |
+
),
|
69 |
+
]
|
70 |
+
|
71 |
+
cfg.train_transforms = A.Compose(
|
72 |
+
resize_transforms
|
73 |
+
+ [
|
74 |
+
A.VerticalFlip(p=0.5),
|
75 |
+
A.HorizontalFlip(p=0.5),
|
76 |
+
A.SomeOf(
|
77 |
+
[
|
78 |
+
A.ShiftScaleRotate(
|
79 |
+
shift_limit=0.2,
|
80 |
+
scale_limit=0.0,
|
81 |
+
rotate_limit=0,
|
82 |
+
border_mode=cv2.BORDER_CONSTANT,
|
83 |
+
p=1,
|
84 |
+
),
|
85 |
+
A.ShiftScaleRotate(
|
86 |
+
shift_limit=0.0,
|
87 |
+
scale_limit=0.2,
|
88 |
+
rotate_limit=0,
|
89 |
+
border_mode=cv2.BORDER_CONSTANT,
|
90 |
+
p=1,
|
91 |
+
),
|
92 |
+
A.ShiftScaleRotate(
|
93 |
+
shift_limit=0.0,
|
94 |
+
scale_limit=0.0,
|
95 |
+
rotate_limit=30,
|
96 |
+
border_mode=cv2.BORDER_CONSTANT,
|
97 |
+
p=1,
|
98 |
+
),
|
99 |
+
A.GaussianBlur(p=1),
|
100 |
+
A.GaussNoise(p=1),
|
101 |
+
A.RandomBrightnessContrast(
|
102 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
103 |
+
),
|
104 |
+
A.RandomBrightnessContrast(
|
105 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
106 |
+
),
|
107 |
+
],
|
108 |
+
n=3,
|
109 |
+
p=0.9,
|
110 |
+
replace=False,
|
111 |
+
),
|
112 |
+
]
|
113 |
+
)
|
114 |
+
|
115 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 24]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel_with_cls"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.inputs = "imgfile0"
|
33 |
+
cfg.targets = ["bone_age_years", "bone_age_categorical"]
|
34 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
35 |
+
cfg.num_workers = 16
|
36 |
+
cfg.clip_outlier_pixels_and_rescale = True
|
37 |
+
cfg.clip_bounds = (1, 99)
|
38 |
+
cfg.pin_memory = True
|
39 |
+
cfg.persistent_workers = True
|
40 |
+
cfg.sampler = "IterationBasedSampler"
|
41 |
+
cfg.num_iterations_per_epoch = 1000
|
42 |
+
|
43 |
+
cfg.loss = "classification.L1CELoss"
|
44 |
+
cfg.loss_params = {"l1_weight": 1.0, "ce_weight": 0.2}
|
45 |
+
|
46 |
+
cfg.batch_size = 32
|
47 |
+
cfg.num_epochs = 10
|
48 |
+
cfg.optimizer = "AdamW"
|
49 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
50 |
+
|
51 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
52 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
53 |
+
cfg.scheduler_interval = "step"
|
54 |
+
|
55 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
56 |
+
cfg.metrics = ["classification.MAE_Accuracy"]
|
57 |
+
cfg.val_metric = "mae_mean"
|
58 |
+
cfg.val_track = "min"
|
59 |
+
|
60 |
+
cfg.image_height = 512
|
61 |
+
cfg.image_width = 512
|
62 |
+
|
63 |
+
resize_transforms = [
|
64 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
65 |
+
A.PadIfNeeded(
|
66 |
+
min_height=cfg.image_height,
|
67 |
+
min_width=cfg.image_width,
|
68 |
+
border_mode=cv2.BORDER_CONSTANT,
|
69 |
+
p=1,
|
70 |
+
),
|
71 |
+
]
|
72 |
+
|
73 |
+
cfg.train_transforms = A.Compose(
|
74 |
+
resize_transforms
|
75 |
+
+ [
|
76 |
+
A.VerticalFlip(p=0.5),
|
77 |
+
A.HorizontalFlip(p=0.5),
|
78 |
+
A.SomeOf(
|
79 |
+
[
|
80 |
+
A.ShiftScaleRotate(
|
81 |
+
shift_limit=0.2,
|
82 |
+
scale_limit=0.0,
|
83 |
+
rotate_limit=0,
|
84 |
+
border_mode=cv2.BORDER_CONSTANT,
|
85 |
+
p=1,
|
86 |
+
),
|
87 |
+
A.ShiftScaleRotate(
|
88 |
+
shift_limit=0.0,
|
89 |
+
scale_limit=0.2,
|
90 |
+
rotate_limit=0,
|
91 |
+
border_mode=cv2.BORDER_CONSTANT,
|
92 |
+
p=1,
|
93 |
+
),
|
94 |
+
A.ShiftScaleRotate(
|
95 |
+
shift_limit=0.0,
|
96 |
+
scale_limit=0.0,
|
97 |
+
rotate_limit=30,
|
98 |
+
border_mode=cv2.BORDER_CONSTANT,
|
99 |
+
p=1,
|
100 |
+
),
|
101 |
+
A.GaussianBlur(p=1),
|
102 |
+
A.GaussNoise(p=1),
|
103 |
+
A.RandomBrightnessContrast(
|
104 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
105 |
+
),
|
106 |
+
A.RandomBrightnessContrast(
|
107 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
108 |
+
),
|
109 |
+
],
|
110 |
+
n=3,
|
111 |
+
p=0.9,
|
112 |
+
replace=False,
|
113 |
+
),
|
114 |
+
]
|
115 |
+
)
|
116 |
+
|
117 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers_aug.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as A
|
2 |
+
import cv2
|
3 |
+
|
4 |
+
from skp.configs import Config
|
5 |
+
|
6 |
+
|
7 |
+
cfg = Config()
|
8 |
+
cfg.neptune_mode = "async"
|
9 |
+
|
10 |
+
cfg.save_dir = "/home/ian/projects/SKP/experiments/boneage/"
|
11 |
+
cfg.project = "gradientecho/SKP"
|
12 |
+
|
13 |
+
cfg.task = "classification"
|
14 |
+
|
15 |
+
cfg.model = "classification.net2d_multihead"
|
16 |
+
cfg.backbone = "tf_efficientnetv2_s"
|
17 |
+
cfg.pretrained = True
|
18 |
+
cfg.num_input_channels = 2
|
19 |
+
cfg.pool = "gem"
|
20 |
+
cfg.pool_params = {"p": 3}
|
21 |
+
cfg.dropout = 0.1
|
22 |
+
cfg.num_classes = [1, 24]
|
23 |
+
cfg.num_heads = 2
|
24 |
+
cfg.normalization = "-1_1"
|
25 |
+
cfg.normalization_params = {"min": 0, "max": 255}
|
26 |
+
cfg.backbone_img_size = False
|
27 |
+
|
28 |
+
cfg.fold = 0
|
29 |
+
cfg.dataset = "boneage.female_channel_with_cls"
|
30 |
+
cfg.data_dir = "/mnt/stor/datasets/bone-age/cropped_train_plus_valid/"
|
31 |
+
cfg.annotations_file = "/mnt/stor/datasets/bone-age/train_plus_valid_kfold.csv"
|
32 |
+
cfg.inputs = "imgfile0"
|
33 |
+
cfg.targets = ["bone_age_years", "bone_age_categorical"]
|
34 |
+
cfg.cv2_load_flag = cv2.IMREAD_GRAYSCALE
|
35 |
+
cfg.num_workers = 16
|
36 |
+
cfg.clip_outlier_pixels_and_rescale = True
|
37 |
+
cfg.clip_as_data_aug = True
|
38 |
+
cfg.clip_proba = 0.5
|
39 |
+
cfg.clip_bounds = (1, 99)
|
40 |
+
cfg.pin_memory = True
|
41 |
+
cfg.persistent_workers = True
|
42 |
+
cfg.sampler = "IterationBasedSampler"
|
43 |
+
cfg.num_iterations_per_epoch = 1000
|
44 |
+
|
45 |
+
cfg.loss = "classification.L1CELoss"
|
46 |
+
cfg.loss_params = {"l1_weight": 1.0, "ce_weight": 0.2}
|
47 |
+
|
48 |
+
cfg.batch_size = 32
|
49 |
+
cfg.num_epochs = 10
|
50 |
+
cfg.optimizer = "AdamW"
|
51 |
+
cfg.optimizer_params = {"lr": 3e-4}
|
52 |
+
|
53 |
+
cfg.scheduler = "LinearWarmupCosineAnnealingLR"
|
54 |
+
cfg.scheduler_params = {"pct_start": 0.1, "div_factor": 100, "final_div_factor": 1_000}
|
55 |
+
cfg.scheduler_interval = "step"
|
56 |
+
|
57 |
+
cfg.val_batch_size = cfg.batch_size * 2
|
58 |
+
cfg.metrics = ["classification.MAE_Accuracy"]
|
59 |
+
cfg.val_metric = "mae_mean"
|
60 |
+
cfg.val_track = "min"
|
61 |
+
|
62 |
+
cfg.image_height = 512
|
63 |
+
cfg.image_width = 512
|
64 |
+
|
65 |
+
resize_transforms = [
|
66 |
+
A.LongestMaxSize(max_size=cfg.image_height, p=1),
|
67 |
+
A.PadIfNeeded(
|
68 |
+
min_height=cfg.image_height,
|
69 |
+
min_width=cfg.image_width,
|
70 |
+
border_mode=cv2.BORDER_CONSTANT,
|
71 |
+
p=1,
|
72 |
+
),
|
73 |
+
]
|
74 |
+
|
75 |
+
cfg.train_transforms = A.Compose(
|
76 |
+
resize_transforms
|
77 |
+
+ [
|
78 |
+
A.VerticalFlip(p=0.5),
|
79 |
+
A.HorizontalFlip(p=0.5),
|
80 |
+
A.SomeOf(
|
81 |
+
[
|
82 |
+
A.ShiftScaleRotate(
|
83 |
+
shift_limit=0.2,
|
84 |
+
scale_limit=0.0,
|
85 |
+
rotate_limit=0,
|
86 |
+
border_mode=cv2.BORDER_CONSTANT,
|
87 |
+
p=1,
|
88 |
+
),
|
89 |
+
A.ShiftScaleRotate(
|
90 |
+
shift_limit=0.0,
|
91 |
+
scale_limit=0.2,
|
92 |
+
rotate_limit=0,
|
93 |
+
border_mode=cv2.BORDER_CONSTANT,
|
94 |
+
p=1,
|
95 |
+
),
|
96 |
+
A.ShiftScaleRotate(
|
97 |
+
shift_limit=0.0,
|
98 |
+
scale_limit=0.0,
|
99 |
+
rotate_limit=30,
|
100 |
+
border_mode=cv2.BORDER_CONSTANT,
|
101 |
+
p=1,
|
102 |
+
),
|
103 |
+
A.GaussianBlur(p=1),
|
104 |
+
A.GaussNoise(p=1),
|
105 |
+
A.RandomBrightnessContrast(
|
106 |
+
contrast_limit=0.3, brightness_limit=0.0, p=1
|
107 |
+
),
|
108 |
+
A.RandomBrightnessContrast(
|
109 |
+
contrast_limit=0.0, brightness_limit=0.3, p=1
|
110 |
+
),
|
111 |
+
],
|
112 |
+
n=3,
|
113 |
+
p=0.9,
|
114 |
+
replace=False,
|
115 |
+
),
|
116 |
+
]
|
117 |
+
)
|
118 |
+
|
119 |
+
cfg.val_transforms = A.Compose(resize_transforms)
|
skp/models/MIL/__pycache__/net2d_attn.cpython-312.pyc
ADDED
Binary file (15.2 kB). View file
|
|
skp/models/MIL/__pycache__/net2d_basic_attn.cpython-312.pyc
ADDED
Binary file (15 kB). View file
|
|
skp/models/MIL/net2d_attn.py
ADDED
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
2D model for multiple instance learning (MIL)
|
3 |
+
Performs attention over bag of features (i.e., attention-weighted mean of features)
|
4 |
+
Option to add LSTM or Transformer before attention aggregation
|
5 |
+
Uses timm backbones
|
6 |
+
"""
|
7 |
+
|
8 |
+
import re
|
9 |
+
import torch
|
10 |
+
import torch.nn as nn
|
11 |
+
|
12 |
+
from einops import rearrange
|
13 |
+
from timm import create_model
|
14 |
+
from typing import Dict, Optional, Tuple
|
15 |
+
|
16 |
+
from skp.configs.base import Config
|
17 |
+
from skp.models.modules import FeatureReduction
|
18 |
+
from skp.models.pooling import get_pool_layer
|
19 |
+
|
20 |
+
|
21 |
+
class Attention(nn.Module):
|
22 |
+
"""
|
23 |
+
Given a batch containing bags of features (B, N, D),
|
24 |
+
generate attention scores over the features in a bag, N,
|
25 |
+
and perform an attention-weighted mean of the features (B, D)
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self, embed_dim: int, dropout: float = 0.0, version: str = "v1"):
|
29 |
+
super().__init__()
|
30 |
+
version = version.lower()
|
31 |
+
if version == "v1":
|
32 |
+
self.mlp = nn.Sequential(
|
33 |
+
nn.Tanh(), nn.Dropout(dropout), nn.Linear(embed_dim, 1)
|
34 |
+
)
|
35 |
+
elif version == "v2":
|
36 |
+
self.mlp = nn.Sequential(
|
37 |
+
nn.Linear(embed_dim, embed_dim),
|
38 |
+
nn.Tanh(),
|
39 |
+
nn.Dropout(dropout),
|
40 |
+
nn.Linear(embed_dim, 1),
|
41 |
+
)
|
42 |
+
|
43 |
+
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor]:
|
44 |
+
a = self.mlp(x)
|
45 |
+
a = a.softmax(dim=1)
|
46 |
+
x = (x * a).sum(dim=1)
|
47 |
+
return x, a
|
48 |
+
|
49 |
+
|
50 |
+
class BiLSTM(nn.Module):
|
51 |
+
def __init__(self, embed_dim: int, dropout: float = 0.0, num_layers: int = 1):
|
52 |
+
super().__init__()
|
53 |
+
self.lstm = nn.LSTM(
|
54 |
+
input_size=embed_dim,
|
55 |
+
hidden_size=embed_dim // 2,
|
56 |
+
num_layers=num_layers,
|
57 |
+
bias=True,
|
58 |
+
batch_first=True,
|
59 |
+
dropout=dropout,
|
60 |
+
bidirectional=True,
|
61 |
+
)
|
62 |
+
|
63 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
64 |
+
x, _ = self.lstm(x)
|
65 |
+
return x
|
66 |
+
|
67 |
+
|
68 |
+
class Transformer(nn.Module):
|
69 |
+
def __init__(
|
70 |
+
self,
|
71 |
+
embed_dim: int,
|
72 |
+
dropout: float = 0.0,
|
73 |
+
num_layers: int = 1,
|
74 |
+
nhead: int = 16,
|
75 |
+
activation: str = "gelu",
|
76 |
+
):
|
77 |
+
super().__init__()
|
78 |
+
encoder_layer = nn.TransformerEncoderLayer(
|
79 |
+
d_model=embed_dim,
|
80 |
+
nhead=nhead,
|
81 |
+
dim_feedforward=embed_dim,
|
82 |
+
dropout=dropout,
|
83 |
+
activation=activation,
|
84 |
+
batch_first=True,
|
85 |
+
norm_first=False,
|
86 |
+
bias=True,
|
87 |
+
)
|
88 |
+
self.T = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
|
89 |
+
|
90 |
+
def forward(
|
91 |
+
self, x: torch.Tensor, mask: Optional[torch.Tensor] = None
|
92 |
+
) -> torch.Tensor:
|
93 |
+
return self.T(x, mask=mask)
|
94 |
+
|
95 |
+
|
96 |
+
class Net(nn.Module):
|
97 |
+
def __init__(self, cfg: Config):
|
98 |
+
super().__init__()
|
99 |
+
self.cfg = cfg
|
100 |
+
backbone_args = {
|
101 |
+
"pretrained": self.cfg.pretrained,
|
102 |
+
"num_classes": 0,
|
103 |
+
"global_pool": "",
|
104 |
+
"features_only": self.cfg.features_only,
|
105 |
+
"in_chans": self.cfg.num_input_channels,
|
106 |
+
}
|
107 |
+
if self.cfg.backbone_img_size:
|
108 |
+
# some models require specifying image size (e.g., coatnet)
|
109 |
+
if "efficientvit" in self.cfg.backbone:
|
110 |
+
backbone_args["img_size"] = self.cfg.image_height
|
111 |
+
else:
|
112 |
+
backbone_args["img_size"] = (
|
113 |
+
self.cfg.image_height,
|
114 |
+
self.cfg.image_width,
|
115 |
+
)
|
116 |
+
self.backbone = create_model(self.cfg.backbone, **backbone_args)
|
117 |
+
# get feature dim by passing sample through net
|
118 |
+
self.feature_dim = self.backbone(
|
119 |
+
torch.randn(
|
120 |
+
(
|
121 |
+
2,
|
122 |
+
self.cfg.num_input_channels,
|
123 |
+
self.cfg.image_height,
|
124 |
+
self.cfg.image_width,
|
125 |
+
)
|
126 |
+
)
|
127 |
+
).size(
|
128 |
+
-1 if "xcit" in self.cfg.backbone else 1
|
129 |
+
) # xcit models are channels-last
|
130 |
+
|
131 |
+
self.feature_dim = self.feature_dim * (2 if self.cfg.pool == "catavgmax" else 1)
|
132 |
+
self.pooling = get_pool_layer(self.cfg, dim=2)
|
133 |
+
|
134 |
+
if isinstance(self.cfg.reduce_feature_dim, int):
|
135 |
+
self.backbone = nn.Sequential(
|
136 |
+
self.backbone,
|
137 |
+
FeatureReduction(self.feature_dim, self.cfg.reduce_feature_dim),
|
138 |
+
)
|
139 |
+
self.feature_dim = self.cfg.reduce_feature_dim
|
140 |
+
|
141 |
+
if self.cfg.add_lstm:
|
142 |
+
self.pre_attn = BiLSTM(
|
143 |
+
embed_dim=self.feature_dim,
|
144 |
+
dropout=self.cfg.lstm_dropout or 0.0,
|
145 |
+
num_layers=self.cfg.lstm_num_layers or 1,
|
146 |
+
)
|
147 |
+
elif self.cfg.add_transformer:
|
148 |
+
self.pre_attn = Transformer(
|
149 |
+
embed_dim=self.feature_dim,
|
150 |
+
dropout=self.cfg.transformer_dropout or 0.0,
|
151 |
+
num_layers=self.cfg.transformer_num_layers or 1,
|
152 |
+
nhead=self.cfg.transformer_nhead or 16,
|
153 |
+
activation=self.cfg.transformer_act or "gelu",
|
154 |
+
)
|
155 |
+
else:
|
156 |
+
self.pre_attn = nn.Identity()
|
157 |
+
|
158 |
+
self.attn = Attention(
|
159 |
+
self.feature_dim,
|
160 |
+
dropout=self.cfg.attn_dropout,
|
161 |
+
version=self.cfg.attn_version or "v1",
|
162 |
+
)
|
163 |
+
self.dropout = nn.Dropout(p=self.cfg.dropout)
|
164 |
+
self.linear = nn.Linear(self.feature_dim, self.cfg.num_classes)
|
165 |
+
|
166 |
+
if self.cfg.load_pretrained_backbone:
|
167 |
+
print(
|
168 |
+
f"Loading pretrained backbone from {self.cfg.load_pretrained_backbone} ..."
|
169 |
+
)
|
170 |
+
weights = torch.load(
|
171 |
+
self.cfg.load_pretrained_backbone,
|
172 |
+
map_location=lambda storage, loc: storage,
|
173 |
+
)["state_dict"]
|
174 |
+
# Replace model prefix as this does not exist in Net
|
175 |
+
weights = {re.sub(r"^model.", "", k): v for k, v in weights.items()}
|
176 |
+
# Get backbone only
|
177 |
+
weights = {
|
178 |
+
re.sub(r"^backbone.", "", k): v
|
179 |
+
for k, v in weights.items()
|
180 |
+
if "backbone" in k
|
181 |
+
}
|
182 |
+
self.backbone.load_state_dict(weights)
|
183 |
+
|
184 |
+
self.criterion = None
|
185 |
+
|
186 |
+
self.backbone_frozen = False
|
187 |
+
if self.cfg.freeze_backbone:
|
188 |
+
self.freeze_backbone()
|
189 |
+
|
190 |
+
def normalize(self, x: torch.Tensor) -> torch.Tensor:
|
191 |
+
if self.cfg.normalization == "-1_1":
|
192 |
+
mini, maxi = (
|
193 |
+
self.cfg.normalization_params["min"],
|
194 |
+
self.cfg.normalization_params["max"],
|
195 |
+
)
|
196 |
+
x = x - mini
|
197 |
+
x = x / (maxi - mini)
|
198 |
+
x = x - 0.5
|
199 |
+
x = x * 2.0
|
200 |
+
elif self.cfg.normalization == "0_1":
|
201 |
+
mini, maxi = (
|
202 |
+
self.cfg.normalization_params["min"],
|
203 |
+
self.cfg.normalization_params["max"],
|
204 |
+
)
|
205 |
+
x = x - mini
|
206 |
+
x = x / (maxi - mini)
|
207 |
+
elif self.cfg.normalization == "mean_sd":
|
208 |
+
mean, sd = (
|
209 |
+
self.cfg.normalization_params["mean"],
|
210 |
+
self.cfg.normalization_params["sd"],
|
211 |
+
)
|
212 |
+
x = (x - mean) / sd
|
213 |
+
elif self.cfg.normalization == "per_channel_mean_sd":
|
214 |
+
mean, sd = (
|
215 |
+
self.cfg.normalization_params["mean"],
|
216 |
+
self.cfg.normalization_params["sd"],
|
217 |
+
)
|
218 |
+
assert len(mean) == len(sd) == x.size(1)
|
219 |
+
mean, sd = torch.tensor(mean).unsqueeze(0), torch.tensor(sd).unsqueeze(0)
|
220 |
+
for i in range(x.ndim - 2):
|
221 |
+
mean, sd = mean.unsqueeze(-1), sd.unsqueeze(-1)
|
222 |
+
x = (x - mean) / sd
|
223 |
+
elif self.cfg.normalization == "none":
|
224 |
+
x = x
|
225 |
+
return x
|
226 |
+
|
227 |
+
def forward(
|
228 |
+
self,
|
229 |
+
batch: Dict,
|
230 |
+
return_loss: bool = False,
|
231 |
+
return_features: bool = False,
|
232 |
+
return_attn_scores: bool = False,
|
233 |
+
) -> Dict[str, torch.Tensor]:
|
234 |
+
x = batch["x"]
|
235 |
+
y = batch.get("y", None)
|
236 |
+
|
237 |
+
if return_loss:
|
238 |
+
assert y is not None
|
239 |
+
|
240 |
+
b, n = x.shape[:2]
|
241 |
+
x = rearrange(x, "b n c h w -> (b n) c h w")
|
242 |
+
features = self.extract_features(x, normalize=True)
|
243 |
+
features = rearrange(features, "(b n) d -> b n d", b=b, n=n)
|
244 |
+
if isinstance(self.pre_attn, Transformer):
|
245 |
+
features = self.pre_attn(features, mask=batch.get("mask", None))
|
246 |
+
else:
|
247 |
+
features = self.pre_attn(features)
|
248 |
+
features, attn_scores = self.attn(features)
|
249 |
+
|
250 |
+
if self.cfg.multisample_dropout:
|
251 |
+
logits = torch.stack(
|
252 |
+
[self.linear(self.dropout(features)) for _ in range(5)]
|
253 |
+
).mean(0)
|
254 |
+
else:
|
255 |
+
logits = self.linear(self.dropout(features))
|
256 |
+
|
257 |
+
if self.cfg.model_activation_fn == "sigmoid":
|
258 |
+
logits = logits.sigmoid()
|
259 |
+
elif self.cfg.model_activation_fn == "softmax":
|
260 |
+
logits = logits.softmax(dim=1)
|
261 |
+
|
262 |
+
out = {"logits": logits}
|
263 |
+
if return_features:
|
264 |
+
out["features"] = features
|
265 |
+
if return_attn_scores:
|
266 |
+
out["attn_scores"] = attn_scores
|
267 |
+
if return_loss:
|
268 |
+
loss = self.criterion(out, batch)
|
269 |
+
if isinstance(loss, dict):
|
270 |
+
out.update(loss)
|
271 |
+
else:
|
272 |
+
out["loss"] = loss
|
273 |
+
|
274 |
+
return out
|
275 |
+
|
276 |
+
def extract_features(self, x: torch.Tensor, normalize: bool = True) -> torch.Tensor:
|
277 |
+
x = self.normalize(x) if normalize else x
|
278 |
+
return self.pooling(self.backbone(x))
|
279 |
+
|
280 |
+
def freeze_backbone(self) -> None:
|
281 |
+
for param in self.backbone.parameters():
|
282 |
+
param.requires_grad = False
|
283 |
+
self.backbone_frozen = True
|
284 |
+
|
285 |
+
def set_criterion(self, loss: nn.Module) -> None:
|
286 |
+
self.criterion = loss
|
skp/models/MIL/net2d_basic_attn.py
ADDED
@@ -0,0 +1,284 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
2D model for multiple instance learning (MIL)
|
3 |
+
Performs attention over bag of features (i.e., attention-weighted mean of features)
|
4 |
+
Uses timm backbones
|
5 |
+
"""
|
6 |
+
|
7 |
+
import re
|
8 |
+
import torch
|
9 |
+
import torch.nn as nn
|
10 |
+
|
11 |
+
from einops import rearrange
|
12 |
+
from timm import create_model
|
13 |
+
from typing import Dict, Optional, Tuple
|
14 |
+
|
15 |
+
from skp.configs.base import Config
|
16 |
+
from skp.models.modules import FeatureReduction
|
17 |
+
from skp.models.pooling import get_pool_layer
|
18 |
+
|
19 |
+
|
20 |
+
class Attention(nn.Module):
|
21 |
+
"""
|
22 |
+
Given a batch containing bags of features (B, N, D),
|
23 |
+
generate attention scores over the features in a bag, N,
|
24 |
+
and perform an attention-weighted mean of the features (B, D)
|
25 |
+
"""
|
26 |
+
|
27 |
+
def __init__(self, embed_dim: int, dropout: float = 0.0, version: str = "v1"):
|
28 |
+
super().__init__()
|
29 |
+
version = version.lower()
|
30 |
+
if version == "v1":
|
31 |
+
self.mlp = nn.Sequential(
|
32 |
+
nn.Tanh(), nn.Dropout(dropout), nn.Linear(embed_dim, 1)
|
33 |
+
)
|
34 |
+
elif version == "v2":
|
35 |
+
self.mlp = nn.Sequential(
|
36 |
+
nn.Linear(embed_dim, embed_dim),
|
37 |
+
nn.Tanh(),
|
38 |
+
nn.Dropout(dropout),
|
39 |
+
nn.Linear(embed_dim, 1),
|
40 |
+
)
|
41 |
+
|
42 |
+
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor]:
|
43 |
+
a = self.mlp(x)
|
44 |
+
a = a.softmax(dim=1)
|
45 |
+
x = (x * a).sum(dim=1)
|
46 |
+
return x, a
|
47 |
+
|
48 |
+
|
49 |
+
class BiLSTM(nn.Module):
|
50 |
+
def __init__(self, embed_dim: int, dropout: float = 0.0, num_layers: int = 1):
|
51 |
+
super().__init__()
|
52 |
+
self.lstm = nn.LSTM(
|
53 |
+
input_size=embed_dim,
|
54 |
+
hidden_size=embed_dim // 2,
|
55 |
+
num_layers=num_layers,
|
56 |
+
bias=True,
|
57 |
+
batch_first=True,
|
58 |
+
dropout=dropout,
|
59 |
+
bidirectional=True,
|
60 |
+
)
|
61 |
+
|
62 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
63 |
+
x, _ = self.lstm(x)
|
64 |
+
return x
|
65 |
+
|
66 |
+
|
67 |
+
class Transformer(nn.Module):
|
68 |
+
def __init__(
|
69 |
+
self,
|
70 |
+
embed_dim: int,
|
71 |
+
dropout: float = 0.0,
|
72 |
+
num_layers: int = 1,
|
73 |
+
nheads: int = 16,
|
74 |
+
activation: str = "gelu",
|
75 |
+
):
|
76 |
+
super().__init__()
|
77 |
+
encoder_layer = nn.TransformerEncoderLayer(
|
78 |
+
d_model=embed_dim,
|
79 |
+
dim_feedforward=embed_dim,
|
80 |
+
dropout=dropout,
|
81 |
+
activation=activation,
|
82 |
+
batch_first=True,
|
83 |
+
norm_first=False,
|
84 |
+
bias=True,
|
85 |
+
)
|
86 |
+
self.T = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
|
87 |
+
|
88 |
+
def forward(
|
89 |
+
self, x: torch.Tensor, mask: Optional[torch.Tensor] = None
|
90 |
+
) -> torch.Tensor:
|
91 |
+
return self.T(x, mask=mask)
|
92 |
+
|
93 |
+
|
94 |
+
class Net(nn.Module):
|
95 |
+
def __init__(self, cfg: Config):
|
96 |
+
super().__init__()
|
97 |
+
self.cfg = cfg
|
98 |
+
backbone_args = {
|
99 |
+
"pretrained": self.cfg.pretrained,
|
100 |
+
"num_classes": 0,
|
101 |
+
"global_pool": "",
|
102 |
+
"features_only": self.cfg.features_only,
|
103 |
+
"in_chans": self.cfg.num_input_channels,
|
104 |
+
}
|
105 |
+
if self.cfg.backbone_img_size:
|
106 |
+
# some models require specifying image size (e.g., coatnet)
|
107 |
+
if "efficientvit" in self.cfg.backbone:
|
108 |
+
backbone_args["img_size"] = self.cfg.image_height
|
109 |
+
else:
|
110 |
+
backbone_args["img_size"] = (
|
111 |
+
self.cfg.image_height,
|
112 |
+
self.cfg.image_width,
|
113 |
+
)
|
114 |
+
self.backbone = create_model(self.cfg.backbone, **backbone_args)
|
115 |
+
# get feature dim by passing sample through net
|
116 |
+
self.feature_dim = self.backbone(
|
117 |
+
torch.randn(
|
118 |
+
(
|
119 |
+
2,
|
120 |
+
self.cfg.num_input_channels,
|
121 |
+
self.cfg.image_height,
|
122 |
+
self.cfg.image_width,
|
123 |
+
)
|
124 |
+
)
|
125 |
+
).size(
|
126 |
+
-1 if "xcit" in self.cfg.backbone else 1
|
127 |
+
) # xcit models are channels-last
|
128 |
+
|
129 |
+
self.feature_dim = self.feature_dim * (2 if self.cfg.pool == "catavgmax" else 1)
|
130 |
+
self.pooling = get_pool_layer(self.cfg, dim=2)
|
131 |
+
|
132 |
+
if isinstance(self.cfg.reduce_feature_dim, int):
|
133 |
+
self.backbone = nn.Sequential(
|
134 |
+
self.backbone,
|
135 |
+
FeatureReduction(self.feature_dim, self.cfg.reduce_feature_dim),
|
136 |
+
)
|
137 |
+
self.feature_dim = self.cfg.reduce_feature_dim
|
138 |
+
|
139 |
+
if self.cfg.add_lstm:
|
140 |
+
self.pre_attn = BiLSTM(
|
141 |
+
embed_dim=self.feature_dim,
|
142 |
+
dropout=self.cfg.lstm_dropout or 0.0,
|
143 |
+
num_layers=self.cfg.lstm_num_layers or 1,
|
144 |
+
)
|
145 |
+
elif self.cfg.add_transformer:
|
146 |
+
self.pre_attn = Transformer(
|
147 |
+
embed_dim=self.feature_dim,
|
148 |
+
dropout=self.transformer_dropout or 0.0,
|
149 |
+
num_layers=self.transformer_num_layers or 1,
|
150 |
+
nheads=self.transformer_nheads or 16,
|
151 |
+
activation=self.transformer_act or "gelu",
|
152 |
+
)
|
153 |
+
else:
|
154 |
+
self.pre_attn = nn.Identity()
|
155 |
+
|
156 |
+
self.attn = Attention(
|
157 |
+
self.feature_dim,
|
158 |
+
dropout=self.cfg.attn_dropout,
|
159 |
+
version=self.cfg.attn_version or "v1",
|
160 |
+
)
|
161 |
+
self.dropout = nn.Dropout(p=self.cfg.dropout)
|
162 |
+
self.linear = nn.Linear(self.feature_dim, self.cfg.num_classes)
|
163 |
+
|
164 |
+
if self.cfg.load_pretrained_backbone:
|
165 |
+
print(
|
166 |
+
f"Loading pretrained backbone from {self.cfg.load_pretrained_backbone} ..."
|
167 |
+
)
|
168 |
+
weights = torch.load(
|
169 |
+
self.cfg.load_pretrained_backbone,
|
170 |
+
map_location=lambda storage, loc: storage,
|
171 |
+
)["state_dict"]
|
172 |
+
# Replace model prefix as this does not exist in Net
|
173 |
+
weights = {re.sub(r"^model.", "", k): v for k, v in weights.items()}
|
174 |
+
# Get backbone only
|
175 |
+
weights = {
|
176 |
+
re.sub(r"^backbone.", "", k): v
|
177 |
+
for k, v in weights.items()
|
178 |
+
if "backbone" in k
|
179 |
+
}
|
180 |
+
self.backbone.load_state_dict(weights)
|
181 |
+
|
182 |
+
self.criterion = None
|
183 |
+
|
184 |
+
self.backbone_frozen = False
|
185 |
+
if self.cfg.freeze_backbone:
|
186 |
+
self.freeze_backbone()
|
187 |
+
|
188 |
+
def normalize(self, x: torch.Tensor) -> torch.Tensor:
|
189 |
+
if self.cfg.normalization == "-1_1":
|
190 |
+
mini, maxi = (
|
191 |
+
self.cfg.normalization_params["min"],
|
192 |
+
self.cfg.normalization_params["max"],
|
193 |
+
)
|
194 |
+
x = x - mini
|
195 |
+
x = x / (maxi - mini)
|
196 |
+
x = x - 0.5
|
197 |
+
x = x * 2.0
|
198 |
+
elif self.cfg.normalization == "0_1":
|
199 |
+
mini, maxi = (
|
200 |
+
self.cfg.normalization_params["min"],
|
201 |
+
self.cfg.normalization_params["max"],
|
202 |
+
)
|
203 |
+
x = x - mini
|
204 |
+
x = x / (maxi - mini)
|
205 |
+
elif self.cfg.normalization == "mean_sd":
|
206 |
+
mean, sd = (
|
207 |
+
self.cfg.normalization_params["mean"],
|
208 |
+
self.cfg.normalization_params["sd"],
|
209 |
+
)
|
210 |
+
x = (x - mean) / sd
|
211 |
+
elif self.cfg.normalization == "per_channel_mean_sd":
|
212 |
+
mean, sd = (
|
213 |
+
self.cfg.normalization_params["mean"],
|
214 |
+
self.cfg.normalization_params["sd"],
|
215 |
+
)
|
216 |
+
assert len(mean) == len(sd) == x.size(1)
|
217 |
+
mean, sd = torch.tensor(mean).unsqueeze(0), torch.tensor(sd).unsqueeze(0)
|
218 |
+
for i in range(x.ndim - 2):
|
219 |
+
mean, sd = mean.unsqueeze(-1), sd.unsqueeze(-1)
|
220 |
+
x = (x - mean) / sd
|
221 |
+
elif self.cfg.normalization == "none":
|
222 |
+
x = x
|
223 |
+
return x
|
224 |
+
|
225 |
+
def forward(
|
226 |
+
self,
|
227 |
+
batch: Dict,
|
228 |
+
return_loss: bool = False,
|
229 |
+
return_features: bool = False,
|
230 |
+
return_attn_scores: bool = False,
|
231 |
+
) -> Dict[str, torch.Tensor]:
|
232 |
+
x = batch["x"]
|
233 |
+
y = batch.get("y", None)
|
234 |
+
|
235 |
+
if return_loss:
|
236 |
+
assert y is not None
|
237 |
+
|
238 |
+
b, n = x.shape[:2]
|
239 |
+
x = rearrange(x, "b n c h w -> (b n) c h w")
|
240 |
+
features = self.extract_features(x, normalize=True)
|
241 |
+
features = rearrange(features, "(b n) d -> b n d", b=b, n=n)
|
242 |
+
if isinstance(self.pre_attn, Transformer):
|
243 |
+
features = self.pre_attn(features, mask=batch.get("mask", None))
|
244 |
+
else:
|
245 |
+
features = self.pre_attn(features)
|
246 |
+
features, attn_scores = self.attn(features)
|
247 |
+
|
248 |
+
if self.cfg.multisample_dropout:
|
249 |
+
logits = torch.stack(
|
250 |
+
[self.linear(self.dropout(features)) for _ in range(5)]
|
251 |
+
).mean(0)
|
252 |
+
else:
|
253 |
+
logits = self.linear(self.dropout(features))
|
254 |
+
|
255 |
+
if self.cfg.model_activation_fn == "sigmoid":
|
256 |
+
logits = logits.sigmoid()
|
257 |
+
elif self.cfg.model_activation_fn == "softmax":
|
258 |
+
logits = logits.softmax(dim=1)
|
259 |
+
|
260 |
+
out = {"logits": logits}
|
261 |
+
if return_features:
|
262 |
+
out["features"] = features
|
263 |
+
if return_attn_scores:
|
264 |
+
out["attn_scores"] = attn_scores
|
265 |
+
if return_loss:
|
266 |
+
loss = self.criterion(out, batch)
|
267 |
+
if isinstance(loss, dict):
|
268 |
+
out.update(loss)
|
269 |
+
else:
|
270 |
+
out["loss"] = loss
|
271 |
+
|
272 |
+
return out
|
273 |
+
|
274 |
+
def extract_features(self, x: torch.Tensor, normalize: bool = True) -> torch.Tensor:
|
275 |
+
x = self.normalize(x) if normalize else x
|
276 |
+
return self.pooling(self.backbone(x))
|
277 |
+
|
278 |
+
def freeze_backbone(self) -> None:
|
279 |
+
for param in self.backbone.parameters():
|
280 |
+
param.requires_grad = False
|
281 |
+
self.backbone_frozen = True
|
282 |
+
|
283 |
+
def set_criterion(self, loss: nn.Module) -> None:
|
284 |
+
self.criterion = loss
|
skp/models/__pycache__/modules.cpython-312.pyc
ADDED
Binary file (1.78 kB). View file
|
|