ianpan commited on
Commit
455e8ef
·
1 Parent(s): 73aa433

update models, output, examples

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +0 -13
  2. app.py +142 -80
  3. crop.pt +1 -0
  4. examples/10043.png +0 -0
  5. examples/2639.png +0 -0
  6. examples/8888.png +0 -0
  7. fold0.ckpt +0 -3
  8. fold1.ckpt +0 -3
  9. fold2.ckpt +0 -3
  10. greulich_and_pyle_ages.json +65 -0
  11. net0.pt +1 -0
  12. net1.pt +1 -0
  13. net2.pt +1 -0
  14. ref_img.png +0 -0
  15. requirements.txt +6 -5
  16. skp/__pycache__/utils.cpython-312.pyc +0 -0
  17. skp/configs/__init__.py +21 -0
  18. skp/configs/__pycache__/__init__.cpython-312.pyc +0 -0
  19. skp/configs/__pycache__/base.cpython-312.pyc +0 -0
  20. skp/configs/base.py +21 -0
  21. skp/configs/boneage/__pycache__/cfg_baseline.cpython-312.pyc +0 -0
  22. skp/configs/boneage/__pycache__/cfg_crop.cpython-312.pyc +0 -0
  23. skp/configs/boneage/__pycache__/cfg_crop_simple_resize.cpython-312.pyc +0 -0
  24. skp/configs/boneage/__pycache__/cfg_female_channel.cpython-312.pyc +0 -0
  25. skp/configs/boneage/__pycache__/cfg_female_channel_MIL.cpython-312.pyc +0 -0
  26. skp/configs/boneage/__pycache__/cfg_female_channel_MIL_lstm.cpython-312.pyc +0 -0
  27. skp/configs/boneage/__pycache__/cfg_female_channel_MIL_transformer.cpython-312.pyc +0 -0
  28. skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls.cpython-312.pyc +0 -0
  29. skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_clip_outliers_aug.cpython-312.pyc +0 -0
  30. skp/configs/boneage/__pycache__/cfg_female_channel_reg_cls_match_hist.cpython-312.pyc +0 -0
  31. skp/configs/boneage/__pycache__/cfg_female_channel_with_cls.cpython-312.pyc +0 -0
  32. skp/configs/boneage/__pycache__/cfg_female_channel_with_cls_clip_outliers.cpython-312.pyc +0 -0
  33. skp/configs/boneage/cfg_baseline.py +117 -0
  34. skp/configs/boneage/cfg_crop.py +123 -0
  35. skp/configs/boneage/cfg_crop_simple_resize.py +117 -0
  36. skp/configs/boneage/cfg_female_channel.py +114 -0
  37. skp/configs/boneage/cfg_female_channel_MIL.py +113 -0
  38. skp/configs/boneage/cfg_female_channel_MIL_lstm.py +116 -0
  39. skp/configs/boneage/cfg_female_channel_MIL_transformer.py +117 -0
  40. skp/configs/boneage/cfg_female_channel_reg_cls.py +115 -0
  41. skp/configs/boneage/cfg_female_channel_reg_cls_clip_outliers_aug.py +119 -0
  42. skp/configs/boneage/cfg_female_channel_reg_cls_match_hist.py +116 -0
  43. skp/configs/boneage/cfg_female_channel_with_cls.py +115 -0
  44. skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers.py +117 -0
  45. skp/configs/boneage/cfg_female_channel_with_cls_clip_outliers_aug.py +119 -0
  46. skp/models/MIL/__pycache__/net2d_attn.cpython-312.pyc +0 -0
  47. skp/models/MIL/__pycache__/net2d_basic_attn.cpython-312.pyc +0 -0
  48. skp/models/MIL/net2d_attn.py +286 -0
  49. skp/models/MIL/net2d_basic_attn.py +284 -0
  50. 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 timm
3
- import torch
 
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
- class Ensemble(nn.Module):
47
-
48
- def __init__(self, model_list):
49
  super().__init__()
50
- self.model_list = nn.ModuleList(model_list)
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 predict_bone_age(Radiograph, Sex):
63
- img = torch.from_numpy(Radiograph)
64
- img = img.unsqueeze(0).unsqueeze(0)
65
- img = img / img.max()
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
- if months == 12:
 
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 = f"{years} years, {months} months" if months != 1 else f"{years} years, 1 month"
89
- return f"Estimated Bone Age: {str_output}"
 
 
 
 
 
 
 
 
 
 
 
 
90
 
 
 
91
 
92
- image = gr.Image(shape=(512, 512), image_mode="L")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  sex = gr.Radio(["Male", "Female"], type="index")
94
- label = gr.Label(show_label=True, label="Result")
 
95
 
96
- demo = gr.Interface(
97
- fn=predict_bone_age,
98
- inputs=[image, sex],
99
- outputs=label,
100
- )
101
 
 
 
 
 
 
102
 
103
- if __name__ == "__main__":
104
- demo.launch()
 
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
- gradio==3.45.0
2
- numpy
3
- omegaconf
 
 
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