radnn 0.0.8__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- radnn/__init__.py +5 -5
- radnn/benchmark/__init__.py +1 -0
- radnn/benchmark/latency.py +55 -0
- radnn/core.py +146 -2
- radnn/data/__init__.py +5 -10
- radnn/data/dataset_base.py +100 -260
- radnn/data/dataset_base_legacy.py +280 -0
- radnn/data/errors.py +32 -0
- radnn/data/sample_preprocessor.py +58 -0
- radnn/data/sample_set.py +203 -90
- radnn/data/sample_set_kind.py +126 -0
- radnn/data/sequence_dataset.py +25 -30
- radnn/data/structs/__init__.py +1 -0
- radnn/data/structs/tree.py +322 -0
- radnn/data_beta/__init__.py +12 -0
- radnn/{data → data_beta}/data_feed.py +1 -1
- radnn/data_beta/dataset_base.py +337 -0
- radnn/data_beta/sample_set.py +166 -0
- radnn/data_beta/sequence_dataset.py +134 -0
- radnn/data_beta/structures/__init__.py +2 -0
- radnn/data_beta/structures/dictionary.py +41 -0
- radnn/{data → data_beta}/tf_classification_data_feed.py +5 -2
- radnn/errors.py +10 -2
- radnn/experiment/__init__.py +2 -0
- radnn/experiment/identification.py +7 -0
- radnn/experiment/ml_experiment.py +7 -2
- radnn/experiment/ml_experiment_log.py +47 -0
- radnn/images/image_processor.py +4 -1
- radnn/learn/__init__.py +0 -7
- radnn/learn/keras/__init__.py +4 -0
- radnn/learn/{state → keras}/keras_best_state_saver.py +5 -1
- radnn/learn/{learning_algorithm.py → keras/keras_learning_algorithm.py} +5 -9
- radnn/learn/{keras_learning_rate_scheduler.py → keras/keras_learning_rate_scheduler.py} +4 -1
- radnn/learn/{keras_optimization_algorithm.py → keras/keras_optimization_combo.py} +7 -3
- radnn/learn/torch/__init__.py +3 -0
- radnn/learn/torch/ml_model_freezer.py +330 -0
- radnn/learn/torch/ml_trainer.py +461 -0
- radnn/learn/torch/staircase_lr_scheduler.py +21 -0
- radnn/ml_system.py +68 -52
- radnn/models/__init__.py +5 -0
- radnn/models/cnn/__init__.py +0 -0
- radnn/models/cnn/cnn_stem_setup.py +35 -0
- radnn/models/model_factory.py +85 -0
- radnn/models/model_hyperparams.py +128 -0
- radnn/models/model_info.py +91 -0
- radnn/plots/plot_learning_curve.py +19 -8
- radnn/system/__init__.py +1 -0
- radnn/system/files/__init__.py +1 -1
- radnn/system/files/csvfile.py +37 -5
- radnn/system/files/filelist.py +30 -0
- radnn/system/files/fileobject.py +11 -1
- radnn/system/files/imgfile.py +1 -1
- radnn/system/files/jsonfile.py +37 -9
- radnn/system/files/picklefile.py +3 -3
- radnn/system/files/textfile.py +39 -10
- radnn/system/files/zipfile.py +96 -0
- radnn/system/filestore.py +147 -47
- radnn/system/filesystem.py +3 -3
- radnn/test/__init__.py +1 -0
- radnn/test/tensor_hash.py +130 -0
- radnn/utils.py +16 -2
- radnn-0.1.0.dist-info/METADATA +30 -0
- radnn-0.1.0.dist-info/RECORD +99 -0
- {radnn-0.0.8.dist-info → radnn-0.1.0.dist-info}/WHEEL +1 -1
- {radnn-0.0.8.dist-info → radnn-0.1.0.dist-info/licenses}/LICENSE.txt +1 -1
- radnn/learn/state/__init__.py +0 -4
- radnn-0.0.8.dist-info/METADATA +0 -58
- radnn-0.0.8.dist-info/RECORD +0 -70
- /radnn/{data → data_beta}/dataset_folder.py +0 -0
- /radnn/{data → data_beta}/image_dataset.py +0 -0
- /radnn/{data → data_beta}/image_dataset_files.py +0 -0
- /radnn/{data → data_beta}/preprocess/__init__.py +0 -0
- /radnn/{data → data_beta}/preprocess/normalizer.py +0 -0
- /radnn/{data → data_beta}/preprocess/standardizer.py +0 -0
- /radnn/{data → data_beta}/subset_type.py +0 -0
- {radnn-0.0.8.dist-info → radnn-0.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
class MLModelFreezer(object):
|
|
2
|
+
def __init__(self, model, hyperparams):
|
|
3
|
+
self.model = model
|
|
4
|
+
self.hyperparams = hyperparams
|
|
5
|
+
self.model_name = self.hyperparams["Model.Name"]
|
|
6
|
+
self.variant = self.hyperparams.get("Training.FineTuning.Freeze", None)
|
|
7
|
+
|
|
8
|
+
if self.model_name == "MobileNet_V3_Large_Weights.IMAGENET1K_V1":
|
|
9
|
+
self.mobilenet_frozen()
|
|
10
|
+
elif self.model_name == "resnet50_Weights.IMAGENET1K_V1":
|
|
11
|
+
self.resnet50_frozen()
|
|
12
|
+
elif self.model_name == "efficientnet_b0_Weights.IMAGENET1K_V1":
|
|
13
|
+
self.efficientnet_frozen()
|
|
14
|
+
elif self.model_name == "convnext_tiny_Weights.IMAGENET1K_V1":
|
|
15
|
+
self.convnext_frozen()
|
|
16
|
+
elif self.model_name == "beitv2_large_patch16_224.in1k_ft_in22k_in1k":
|
|
17
|
+
self.beitv2_frozen_2()
|
|
18
|
+
|
|
19
|
+
def freeze_batch_norm(self):
|
|
20
|
+
oNamedParams = list(iter(self.model .named_parameters()))
|
|
21
|
+
for nIndex, oTuple in enumerate(oNamedParams):
|
|
22
|
+
name, p = oTuple
|
|
23
|
+
if ".bn" in name:
|
|
24
|
+
p.requires_grad = False
|
|
25
|
+
|
|
26
|
+
def freeze_layer_norm(self):
|
|
27
|
+
oNamedParams = list(iter(self.model .named_parameters()))
|
|
28
|
+
for nIndex, oTuple in enumerate(oNamedParams):
|
|
29
|
+
name, p = oTuple
|
|
30
|
+
if (".norm" in name) or (".gamma" in name) or (".beta" in name) :
|
|
31
|
+
p.requires_grad = False
|
|
32
|
+
|
|
33
|
+
def freeze_block(self, module, exclude_normalization=False):
|
|
34
|
+
oParams = list(iter(module.parameters()))
|
|
35
|
+
oNamedParams = list(iter(module.named_parameters()))
|
|
36
|
+
for nIndex, oTuple in enumerate(oNamedParams):
|
|
37
|
+
name, p = oTuple
|
|
38
|
+
p = oParams[nIndex]
|
|
39
|
+
bContinue = True
|
|
40
|
+
if exclude_normalization:
|
|
41
|
+
bContinue = not ".bn" in name
|
|
42
|
+
if bContinue:
|
|
43
|
+
p.requires_grad = False
|
|
44
|
+
|
|
45
|
+
def mobilenet_frozen(self):
|
|
46
|
+
"""
|
|
47
|
+
torchvision.mobilenet_v3_large has:
|
|
48
|
+
model.features: Sequential([...])
|
|
49
|
+
model.classifier: Sequential([...])
|
|
50
|
+
|
|
51
|
+
We'll freeze by coarse slices of `features`.
|
|
52
|
+
The exact indices can differ slightly across torchvision versions, but this is stable enough:
|
|
53
|
+
- features[0] is stem
|
|
54
|
+
- features[1:-1] are inverted residual blocks
|
|
55
|
+
- features[-1] is final 1x1 conv block before pooling/classifier
|
|
56
|
+
"""
|
|
57
|
+
if self.variant is None:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if not hasattr(self.model, "features"):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
feats = self.model.features
|
|
64
|
+
n = len(feats)
|
|
65
|
+
|
|
66
|
+
# Define slices (roughly early/mid/late)
|
|
67
|
+
# Stem
|
|
68
|
+
if 0 in self.variant:
|
|
69
|
+
self.freeze_block(feats[0])
|
|
70
|
+
|
|
71
|
+
# Early inverted residual blocks
|
|
72
|
+
if 1 in self.variant:
|
|
73
|
+
end = max(2, n // 4)
|
|
74
|
+
self.freeze_block(feats[1:end])
|
|
75
|
+
|
|
76
|
+
# Mid blocks
|
|
77
|
+
if 2 in self.variant:
|
|
78
|
+
start = max(1, n // 4)
|
|
79
|
+
end = max(start + 1, n // 2)
|
|
80
|
+
self.freeze_block(feats[start:end])
|
|
81
|
+
|
|
82
|
+
# Late blocks (excluding last conv)
|
|
83
|
+
if 3 in self.variant:
|
|
84
|
+
start = max(1, n // 2)
|
|
85
|
+
end = max(start + 1, n - 1)
|
|
86
|
+
self.freeze_block(feats[start:end])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def resnet50_frozen(self):
|
|
92
|
+
if self.variant is None:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
self.freeze_batch_norm()
|
|
96
|
+
|
|
97
|
+
if 0 in self.variant:
|
|
98
|
+
# Stem
|
|
99
|
+
self.freeze_block(self.model.step_conv)
|
|
100
|
+
self.freeze_block(self.model.bn1)
|
|
101
|
+
|
|
102
|
+
if 1 in self.variant:
|
|
103
|
+
# First block of multiple conv layers
|
|
104
|
+
self.freeze_block(self.model.block1)
|
|
105
|
+
|
|
106
|
+
if 2 in self.variant:
|
|
107
|
+
self.freeze_block(self.model.block2)
|
|
108
|
+
|
|
109
|
+
if 3 in self.variant:
|
|
110
|
+
self.freeze_block(self.model.block3)
|
|
111
|
+
|
|
112
|
+
if 4 in self.variant:
|
|
113
|
+
# Freezing all layers will train only the classification head
|
|
114
|
+
self.freeze_block(self.model.layer4)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def efficientnet_frozen(self):
|
|
118
|
+
"""
|
|
119
|
+
torchvision.efficientnet_b0 has:
|
|
120
|
+
model.features: Sequential of stem + stages + head conv
|
|
121
|
+
model.classifier
|
|
122
|
+
|
|
123
|
+
Commonly len(features) == 9 in torchvision:
|
|
124
|
+
0 stem
|
|
125
|
+
1..7 MBConv/FusedMBConv stages
|
|
126
|
+
8 head conv
|
|
127
|
+
We'll freeze in coarse groups.
|
|
128
|
+
"""
|
|
129
|
+
if self.variant is None:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if not hasattr(self.model, "features"):
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
feats = self.model.features
|
|
136
|
+
n = len(feats)
|
|
137
|
+
|
|
138
|
+
if n == 0:
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
# Stem
|
|
142
|
+
if 0 in self.variant:
|
|
143
|
+
self.freeze_block(feats[0])
|
|
144
|
+
|
|
145
|
+
# Early stages
|
|
146
|
+
if 1 in self.variant:
|
|
147
|
+
# typically features[1:3]
|
|
148
|
+
self.freeze_block(feats[1:min(3, n)])
|
|
149
|
+
|
|
150
|
+
# Mid stages
|
|
151
|
+
if 2 in self.variant:
|
|
152
|
+
# typically features[3:5]
|
|
153
|
+
self.freeze_block(feats[min(3, n):min(5, n)])
|
|
154
|
+
|
|
155
|
+
# Late stages
|
|
156
|
+
if 3 in self.variant:
|
|
157
|
+
# typically features[5:7]
|
|
158
|
+
self.freeze_block(feats[min(5, n):min(7, n)])
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def convnext_frozen(self):
|
|
163
|
+
"""
|
|
164
|
+
torchvision.convnext_tiny has:
|
|
165
|
+
model.features: Sequential of 8 modules:
|
|
166
|
+
0 stem
|
|
167
|
+
1 stage1
|
|
168
|
+
2 downsample1
|
|
169
|
+
3 stage2
|
|
170
|
+
4 downsample2
|
|
171
|
+
5 stage3
|
|
172
|
+
6 downsample3
|
|
173
|
+
7 stage4
|
|
174
|
+
model.classifier
|
|
175
|
+
We'll freeze stem/stages (and their downsample ops together).
|
|
176
|
+
"""
|
|
177
|
+
if self.variant is None:
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
if not hasattr(self.model, "features"):
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
feats = self.model.features
|
|
184
|
+
n = len(feats)
|
|
185
|
+
if n < 2:
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# Stem
|
|
189
|
+
if 0 in self.variant:
|
|
190
|
+
self.freeze_block(feats[0])
|
|
191
|
+
|
|
192
|
+
# Stage 1 (+ downsample after it if present)
|
|
193
|
+
if 1 in self.variant:
|
|
194
|
+
if n > 1: self.freeze_block(feats[1])
|
|
195
|
+
if n > 2: self.freeze_block(feats[2])
|
|
196
|
+
|
|
197
|
+
# Stage 2 (+ downsample)
|
|
198
|
+
if 2 in self.variant:
|
|
199
|
+
if n > 3: self.freeze_block(feats[3])
|
|
200
|
+
if n > 4: self.freeze_block(feats[4])
|
|
201
|
+
|
|
202
|
+
# Stage 3 (+ downsample)
|
|
203
|
+
if 3 in self.variant:
|
|
204
|
+
if n > 5: self.freeze_block(feats[5])
|
|
205
|
+
if n > 6: self.freeze_block(feats[6])
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def beitv2_frozen(self):
|
|
209
|
+
if self.variant is None:
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
freeze_patch_embed = 0 in self.variant
|
|
213
|
+
# ----------------
|
|
214
|
+
|
|
215
|
+
if freeze_patch_embed:
|
|
216
|
+
self.freeze_block(self.model.patch_embed)
|
|
217
|
+
|
|
218
|
+
blocks = self.model.blocks
|
|
219
|
+
for i, block in enumerate(blocks):
|
|
220
|
+
if i in self.variant:
|
|
221
|
+
self.freeze_block(block)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# -------------------------
|
|
225
|
+
# BEiT v2 (timm or HuggingFace)
|
|
226
|
+
# -------------------------
|
|
227
|
+
def beitv2_frozen_2(self):
|
|
228
|
+
"""
|
|
229
|
+
Supports:
|
|
230
|
+
- timm-style: model.patch_embed, model.blocks (list/ModuleList), model.norm
|
|
231
|
+
- HF-style: model.beit.embeddings, model.beit.encoder.layer, model.beit.layernorm
|
|
232
|
+
|
|
233
|
+
variant meaning (coarse, stage-like):
|
|
234
|
+
0: freeze patch embedding (+ early norms if present)
|
|
235
|
+
1: freeze first quarter of transformer blocks
|
|
236
|
+
2: freeze first half
|
|
237
|
+
3: freeze first 3/4
|
|
238
|
+
4: freeze all blocks (train head only)
|
|
239
|
+
|
|
240
|
+
If you prefer "freeze exact N blocks", you can pass an int in hyperparams instead of a list.
|
|
241
|
+
"""
|
|
242
|
+
if self.variant is None:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
# Allow variant to be an int: freeze first N blocks (and patch embed)
|
|
246
|
+
if isinstance(self.variant, int):
|
|
247
|
+
n_blocks_to_freeze = self.variant
|
|
248
|
+
self._beit_freeze_first_n_blocks(n_blocks_to_freeze, freeze_patch_embed=True)
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
# Otherwise assume list-like stages
|
|
252
|
+
vset = set(self.variant)
|
|
253
|
+
|
|
254
|
+
self.freeze_layer_norm()
|
|
255
|
+
|
|
256
|
+
# Try timm-style first
|
|
257
|
+
if hasattr(self.model, "blocks"):
|
|
258
|
+
blocks = self.model.blocks
|
|
259
|
+
num_blocks = len(blocks)
|
|
260
|
+
|
|
261
|
+
# 0: patch embedding / stem
|
|
262
|
+
if 0 in vset:
|
|
263
|
+
if hasattr(self.model, "patch_embed"):
|
|
264
|
+
self.freeze_block(self.model.patch_embed)
|
|
265
|
+
# some timm models have pos_embed / cls_token as params; those don't need freezing
|
|
266
|
+
|
|
267
|
+
# Helper: freeze first k blocks
|
|
268
|
+
def freeze_first_k(k):
|
|
269
|
+
for b in blocks[:max(0, min(k, num_blocks))]:
|
|
270
|
+
self.freeze_block(b)
|
|
271
|
+
|
|
272
|
+
# Stage mapping
|
|
273
|
+
if 1 in vset:
|
|
274
|
+
freeze_first_k(max(1, num_blocks // 4))
|
|
275
|
+
if 2 in vset:
|
|
276
|
+
freeze_first_k(max(1, num_blocks // 2))
|
|
277
|
+
if 3 in vset:
|
|
278
|
+
freeze_first_k(max(1, (3 * num_blocks) // 4))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# Fall back to HuggingFace-style
|
|
282
|
+
elif hasattr(self.model, "beit"):
|
|
283
|
+
beit = self.model.beit
|
|
284
|
+
|
|
285
|
+
if 0 in vset and hasattr(beit, "embeddings"):
|
|
286
|
+
self.freeze_block(beit.embeddings)
|
|
287
|
+
|
|
288
|
+
layers = None
|
|
289
|
+
if hasattr(beit, "encoder") and hasattr(beit.encoder, "layer"):
|
|
290
|
+
layers = beit.encoder.layer
|
|
291
|
+
|
|
292
|
+
if layers is not None:
|
|
293
|
+
num_blocks = len(layers)
|
|
294
|
+
|
|
295
|
+
def freeze_first_k(k):
|
|
296
|
+
for b in layers[:max(0, min(k, num_blocks))]:
|
|
297
|
+
self.freeze_block(b)
|
|
298
|
+
|
|
299
|
+
if 1 in vset:
|
|
300
|
+
freeze_first_k(max(1, num_blocks // 4))
|
|
301
|
+
if 2 in vset:
|
|
302
|
+
freeze_first_k(max(1, num_blocks // 2))
|
|
303
|
+
if 3 in vset:
|
|
304
|
+
freeze_first_k(max(1, (3 * num_blocks) // 4))
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _beit_freeze_first_n_blocks(self, n_blocks_to_freeze: int, freeze_patch_embed: bool = True):
|
|
309
|
+
# timm-style
|
|
310
|
+
if hasattr(self.model, "blocks"):
|
|
311
|
+
if freeze_patch_embed and hasattr(self.model, "patch_embed"):
|
|
312
|
+
self.freeze_block(self.model.patch_embed)
|
|
313
|
+
for b in self.model.blocks[:max(0, n_blocks_to_freeze)]:
|
|
314
|
+
self.freeze_block(b)
|
|
315
|
+
if hasattr(self.model, "norm") and n_blocks_to_freeze >= len(self.model.blocks):
|
|
316
|
+
self.freeze_block(self.model.norm)
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
# HF-style
|
|
320
|
+
if hasattr(self.model, "beit"):
|
|
321
|
+
beit = self.model.beit
|
|
322
|
+
if freeze_patch_embed and hasattr(beit, "embeddings"):
|
|
323
|
+
self.freeze_block(beit.embeddings)
|
|
324
|
+
if hasattr(beit, "encoder") and hasattr(beit.encoder, "layer"):
|
|
325
|
+
for b in beit.encoder.layer[:max(0, n_blocks_to_freeze)]:
|
|
326
|
+
self.freeze_block(b)
|
|
327
|
+
if hasattr(beit, "layernorm") and hasattr(beit, "encoder") and hasattr(beit.encoder, "layer"):
|
|
328
|
+
if n_blocks_to_freeze >= len(beit.encoder.layer):
|
|
329
|
+
self.freeze_block(beit.layernorm)
|
|
330
|
+
|