netloader 3.11.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.
- netloader/__init__.py +61 -0
- netloader/architectures/__init__.py +48 -0
- netloader/architectures/base.py +1350 -0
- netloader/architectures/encoder_decoder.py +630 -0
- netloader/architectures/flows.py +501 -0
- netloader/architectures/utils.py +179 -0
- netloader/data.py +1092 -0
- netloader/layers/__init__.py +88 -0
- netloader/layers/base.py +367 -0
- netloader/layers/blocks.py +145 -0
- netloader/layers/convolutional.py +1008 -0
- netloader/layers/flows.py +123 -0
- netloader/layers/linear.py +445 -0
- netloader/layers/misc.py +879 -0
- netloader/layers/multi_layer.py +470 -0
- netloader/layers/pooling.py +298 -0
- netloader/layers/recurrent.py +163 -0
- netloader/layers/utils.py +168 -0
- netloader/loss_funcs.py +201 -0
- netloader/models/__init__.py +22 -0
- netloader/models/convnext.py +582 -0
- netloader/models/misc.py +209 -0
- netloader/network.py +895 -0
- netloader/networks/__init__.py +48 -0
- netloader/schedulers.py +409 -0
- netloader/transforms.py +847 -0
- netloader/utils/__init__.py +46 -0
- netloader/utils/configs.py +241 -0
- netloader/utils/transforms.py +22 -0
- netloader/utils/types.py +130 -0
- netloader/utils/utils.py +677 -0
- netloader-3.11.0.dist-info/METADATA +59 -0
- netloader-3.11.0.dist-info/RECORD +36 -0
- netloader-3.11.0.dist-info/WHEEL +5 -0
- netloader-3.11.0.dist-info/licenses/LICENSE.txt +21 -0
- netloader-3.11.0.dist-info/top_level.txt +1 -0
netloader/layers/misc.py
ADDED
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Miscellaneous network layers
|
|
3
|
+
"""
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from warnings import warn
|
|
6
|
+
from typing import Any, Self, cast, overload
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
import numpy as np
|
|
10
|
+
from torch import nn, Tensor
|
|
11
|
+
|
|
12
|
+
from netloader.utils import Shapes
|
|
13
|
+
from netloader.data import DataList
|
|
14
|
+
from netloader.layers import multi_layer
|
|
15
|
+
from netloader.utils.types import TensorListLike
|
|
16
|
+
from netloader.layers.base import BaseLayer, BaseSingleLayer
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BatchNorm(BaseSingleLayer):
|
|
20
|
+
"""
|
|
21
|
+
Batch Normalisation layer
|
|
22
|
+
|
|
23
|
+
Attributes
|
|
24
|
+
----------
|
|
25
|
+
group : int
|
|
26
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
27
|
+
Networks
|
|
28
|
+
description : str
|
|
29
|
+
Description of the layer
|
|
30
|
+
layers : Sequential
|
|
31
|
+
Layers to loop through in the forward pass
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self, shapes: Shapes, **kwargs: Any) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
shapes : Shapes
|
|
38
|
+
Shape of the outputs from each layer
|
|
39
|
+
|
|
40
|
+
**kwargs
|
|
41
|
+
Leftover parameters to pass to base layer for checking
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(**kwargs)
|
|
44
|
+
self._check_shape((1, 4), shapes[-1])
|
|
45
|
+
self.layers.add_module(
|
|
46
|
+
'BatchNorm',
|
|
47
|
+
[
|
|
48
|
+
nn.BatchNorm1d,
|
|
49
|
+
nn.BatchNorm2d,
|
|
50
|
+
nn.BatchNorm3d,
|
|
51
|
+
][max(0, len(shapes[-1]) - 2)](shapes[-1][0]),
|
|
52
|
+
)
|
|
53
|
+
shapes.append(shapes[-1].copy())
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Checkpoint(BaseLayer):
|
|
57
|
+
"""
|
|
58
|
+
Constructs a layer to create a checkpoint for using the output from the previous layer in
|
|
59
|
+
future layers.
|
|
60
|
+
|
|
61
|
+
Attributes
|
|
62
|
+
----------
|
|
63
|
+
group : int
|
|
64
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
65
|
+
Networks
|
|
66
|
+
description : str
|
|
67
|
+
Description of the layer
|
|
68
|
+
"""
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
shapes: Shapes,
|
|
72
|
+
check_shapes: Shapes,
|
|
73
|
+
**kwargs: Any) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
shapes : Shapes
|
|
78
|
+
Shape of the outputs from each layer
|
|
79
|
+
check_shapes : Shapes
|
|
80
|
+
Shape of the outputs from each checkpoint
|
|
81
|
+
**kwargs
|
|
82
|
+
Leftover parameters to pass to base layer for checking
|
|
83
|
+
"""
|
|
84
|
+
super().__init__(**kwargs)
|
|
85
|
+
self._check_num: int = len(check_shapes)
|
|
86
|
+
shapes.append(deepcopy(shapes.get(-1, True)))
|
|
87
|
+
check_shapes.append(deepcopy(shapes.get(-1, True)))
|
|
88
|
+
|
|
89
|
+
def forward(
|
|
90
|
+
self,
|
|
91
|
+
x: TensorListLike,
|
|
92
|
+
checkpoints: list[TensorListLike],
|
|
93
|
+
*_: Any,
|
|
94
|
+
**__: Any) -> TensorListLike:
|
|
95
|
+
"""
|
|
96
|
+
Forward pass of the checkpoint layer.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
x : TensorListLike
|
|
101
|
+
Input with tensors of shape (N,...) and type float, where N is the batch size
|
|
102
|
+
checkpoints : list[TensorListLike]
|
|
103
|
+
List of checkpoint values with tensors of shape (N,...) and type float
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
TensorListLike
|
|
108
|
+
Output with tensors of shape (N,...) and type float
|
|
109
|
+
"""
|
|
110
|
+
checkpoints.append(x.clone())
|
|
111
|
+
return x
|
|
112
|
+
|
|
113
|
+
def extra_repr(self) -> str:
|
|
114
|
+
"""
|
|
115
|
+
Displays layer parameters when printing the network
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
str
|
|
120
|
+
Layer parameters
|
|
121
|
+
"""
|
|
122
|
+
return f'checkpoint_num={self._check_num}'
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Dropout(BaseSingleLayer):
|
|
126
|
+
"""
|
|
127
|
+
Constructs a dropout layer to randomly zero out elements of the input tensor.
|
|
128
|
+
|
|
129
|
+
Attributes
|
|
130
|
+
----------
|
|
131
|
+
group : int
|
|
132
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
133
|
+
Networks
|
|
134
|
+
description : str
|
|
135
|
+
Description of the layer
|
|
136
|
+
layers : Sequential
|
|
137
|
+
Layers to loop through in the forward pass
|
|
138
|
+
"""
|
|
139
|
+
def __init__(
|
|
140
|
+
self,
|
|
141
|
+
prob: float,
|
|
142
|
+
*,
|
|
143
|
+
shapes: Shapes | None = None,
|
|
144
|
+
**kwargs: Any) -> None:
|
|
145
|
+
"""
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
prob : float
|
|
149
|
+
Probability of dropout
|
|
150
|
+
shapes : Shapes | None, Optional
|
|
151
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
152
|
+
necessary
|
|
153
|
+
**kwargs
|
|
154
|
+
Leftover parameters to pass to base layer for checking
|
|
155
|
+
"""
|
|
156
|
+
super().__init__(**kwargs)
|
|
157
|
+
self.layers.add_module('Dropout', nn.Dropout(prob))
|
|
158
|
+
|
|
159
|
+
# If not used as a layer in a network
|
|
160
|
+
if not shapes:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
shapes.append(shapes[-1].copy())
|
|
164
|
+
|
|
165
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
166
|
+
return super().__getstate__() | {'prob': self.layers.Dropout.p} # type: ignore[union-attr]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class DropPath(BaseLayer):
|
|
170
|
+
"""
|
|
171
|
+
Constructs a drop path layer to drop samples in a batch.
|
|
172
|
+
|
|
173
|
+
See `FractalNet: Ultra-Deep Neural Networks without Residuals
|
|
174
|
+
<https://arxiv.org/abs/1605.07648>`_ by Larsson et al. (2016) for details.
|
|
175
|
+
|
|
176
|
+
Custom version of implementation by `timm
|
|
177
|
+
<https://github.com/pprp/timm/blob/master/timm/layers/drop.py#L157>`_.
|
|
178
|
+
|
|
179
|
+
Attributes
|
|
180
|
+
----------
|
|
181
|
+
group : int
|
|
182
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
183
|
+
Networks
|
|
184
|
+
description : str
|
|
185
|
+
Description of the layer
|
|
186
|
+
"""
|
|
187
|
+
def __init__(
|
|
188
|
+
self,
|
|
189
|
+
prob: float,
|
|
190
|
+
*,
|
|
191
|
+
shapes: Shapes | None = None,
|
|
192
|
+
**kwargs: Any) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
prob : float
|
|
197
|
+
Probability of dropout
|
|
198
|
+
shapes : Shapes | None, Optional
|
|
199
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
200
|
+
necessary
|
|
201
|
+
**kwargs
|
|
202
|
+
Leftover parameters to pass to base layer for checking
|
|
203
|
+
"""
|
|
204
|
+
super().__init__(**kwargs)
|
|
205
|
+
self._keep_prob: float = 1 - prob
|
|
206
|
+
|
|
207
|
+
# If not used as a layer in a network
|
|
208
|
+
if not shapes:
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
shapes.append(shapes[-1].copy())
|
|
212
|
+
|
|
213
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
214
|
+
return super().__getstate__() | {'prob': 1 - self._keep_prob}
|
|
215
|
+
|
|
216
|
+
def forward(self, x: Tensor, *_: Any, **__: Any) -> Tensor:
|
|
217
|
+
"""
|
|
218
|
+
Forward pass of the drop path layer.
|
|
219
|
+
|
|
220
|
+
Parameters
|
|
221
|
+
----------
|
|
222
|
+
x : Tensor
|
|
223
|
+
Input tensor of shape (N,...) and type float, where N is the batch size
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
Tensor
|
|
228
|
+
Output tensor of shape (N,...) and type float
|
|
229
|
+
"""
|
|
230
|
+
if not self.training or self._keep_prob == 1:
|
|
231
|
+
return x
|
|
232
|
+
|
|
233
|
+
mask: Tensor = x.new_empty(x.shape[0:1] + (1,) * (x.ndim - 1)).bernoulli_(self._keep_prob)
|
|
234
|
+
|
|
235
|
+
if self._keep_prob:
|
|
236
|
+
mask.div_(self._keep_prob)
|
|
237
|
+
|
|
238
|
+
return x * mask
|
|
239
|
+
|
|
240
|
+
def extra_repr(self) -> str:
|
|
241
|
+
"""
|
|
242
|
+
Displays layer parameters when printing the network
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
str
|
|
247
|
+
Layer parameters
|
|
248
|
+
"""
|
|
249
|
+
return f'prob={1 - self._keep_prob}'
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class Index(BaseLayer):
|
|
253
|
+
"""
|
|
254
|
+
Constructs a layer to slice the last dimension from the output from the previous layer.
|
|
255
|
+
|
|
256
|
+
Attributes
|
|
257
|
+
----------
|
|
258
|
+
group : int
|
|
259
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
260
|
+
Networks
|
|
261
|
+
description : str
|
|
262
|
+
Description of the layer
|
|
263
|
+
"""
|
|
264
|
+
@overload
|
|
265
|
+
def __init__(
|
|
266
|
+
self,
|
|
267
|
+
*,
|
|
268
|
+
index: int | list[int | None] | tuple[int | None, ...] | slice,
|
|
269
|
+
map_: bool = ...,
|
|
270
|
+
shapes: Shapes | None = ...,
|
|
271
|
+
**kwargs: Any) -> None: ...
|
|
272
|
+
|
|
273
|
+
@overload
|
|
274
|
+
def __init__(
|
|
275
|
+
self,
|
|
276
|
+
number: int,
|
|
277
|
+
*,
|
|
278
|
+
map_: bool = ...,
|
|
279
|
+
greater: bool = ...,
|
|
280
|
+
shapes: Shapes | None = ...,
|
|
281
|
+
**kwargs: Any) -> None: ...
|
|
282
|
+
|
|
283
|
+
def __init__(
|
|
284
|
+
self,
|
|
285
|
+
*args: int,
|
|
286
|
+
map_: bool = True,
|
|
287
|
+
index: int | list[int | None] | tuple[int | None, ...] | slice | None = None,
|
|
288
|
+
shapes: Shapes | None = None,
|
|
289
|
+
**kwargs: Any) -> None:
|
|
290
|
+
r"""
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
index : int | list[int | None] | tuple[int | None, ...] | slice
|
|
294
|
+
Index or slice to apply to the last dimension of the input tensor(s) or if map\_ is
|
|
295
|
+
False, the DataList itself
|
|
296
|
+
map\_ : bool, Optional
|
|
297
|
+
If slicing should be applied to each tensor in a DataList, or if the DataList should be
|
|
298
|
+
sliced itself, only used if input is a DataList, default = True
|
|
299
|
+
shapes : Shapes | None, Optional
|
|
300
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
301
|
+
necessary
|
|
302
|
+
**kwargs
|
|
303
|
+
Leftover parameters to pass to base layer for checking
|
|
304
|
+
"""
|
|
305
|
+
super().__init__(**kwargs)
|
|
306
|
+
number: int
|
|
307
|
+
shape: list[int]
|
|
308
|
+
in_shape: list[int] | list[list[int]]
|
|
309
|
+
self._map: bool = map_
|
|
310
|
+
self._idx: int | slice
|
|
311
|
+
|
|
312
|
+
if args or 'number' in kwargs:
|
|
313
|
+
warn(
|
|
314
|
+
'number argument and greater keyword argument are deprecated, please use '
|
|
315
|
+
'index instead',
|
|
316
|
+
DeprecationWarning,
|
|
317
|
+
stacklevel=2,
|
|
318
|
+
)
|
|
319
|
+
number = kwargs.pop('number', args[0])
|
|
320
|
+
self._idx = slice(number, None) if kwargs.pop('greater', True) else slice(number)
|
|
321
|
+
elif index is None and 'idx' in kwargs:
|
|
322
|
+
warn(
|
|
323
|
+
'idx argument is deprecated, please use index instead',
|
|
324
|
+
DeprecationWarning,
|
|
325
|
+
stacklevel=2,
|
|
326
|
+
)
|
|
327
|
+
self._idx = kwargs['idx']
|
|
328
|
+
else:
|
|
329
|
+
assert index is not None
|
|
330
|
+
self._idx = slice(*index) if isinstance(index, (tuple, list)) else index
|
|
331
|
+
|
|
332
|
+
# If not used as a layer in a network
|
|
333
|
+
if not shapes:
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
in_shape = deepcopy(shapes.get(-1, True))
|
|
337
|
+
|
|
338
|
+
# Length of slice
|
|
339
|
+
if isinstance(in_shape[0], int):
|
|
340
|
+
in_shape = self._shape_index(cast(list[int], in_shape))
|
|
341
|
+
elif isinstance(in_shape[0], list) and self._map:
|
|
342
|
+
in_shape = [self._shape_index(shape) for shape in cast(list[list[int]], in_shape)]
|
|
343
|
+
else:
|
|
344
|
+
in_shape = cast(list[list[int]], in_shape)[self._idx]
|
|
345
|
+
|
|
346
|
+
shapes.append(in_shape)
|
|
347
|
+
|
|
348
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
349
|
+
return super().__getstate__() | {'map_': self._map, 'index': self._idx}
|
|
350
|
+
|
|
351
|
+
def _shape_index(self, shape: list[int]) -> list[int]:
|
|
352
|
+
"""
|
|
353
|
+
Returns the output shape after indexing.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
shape : list[int]
|
|
358
|
+
Input shape
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
list[int]
|
|
363
|
+
Output shape
|
|
364
|
+
"""
|
|
365
|
+
out_shape: list[int] = shape.copy()
|
|
366
|
+
idx: tuple[int, int, int]
|
|
367
|
+
|
|
368
|
+
if isinstance(self._idx, int):
|
|
369
|
+
out_shape[-1] = 1
|
|
370
|
+
else:
|
|
371
|
+
idx = self._idx.indices(shape[-1])
|
|
372
|
+
out_shape[-1] = (idx[1] - idx[0] + (idx[2] - 1)) // idx[2]
|
|
373
|
+
return out_shape
|
|
374
|
+
|
|
375
|
+
def forward(self, x: TensorListLike, *_: Any, **__: Any) -> TensorListLike:
|
|
376
|
+
"""
|
|
377
|
+
Forward pass of the indexing layer.
|
|
378
|
+
|
|
379
|
+
Parameters
|
|
380
|
+
----------
|
|
381
|
+
x : Tensor
|
|
382
|
+
Input tensor of shape (N,...) and type float, where N is the batch size
|
|
383
|
+
|
|
384
|
+
Returns
|
|
385
|
+
-------
|
|
386
|
+
Tensor
|
|
387
|
+
Output tensor of shape (N,...) and type float
|
|
388
|
+
"""
|
|
389
|
+
if isinstance(x, DataList) and not self._map:
|
|
390
|
+
return x.get(self._idx, list_=True) if isinstance(self._idx, int) else (
|
|
391
|
+
DataList(x.get(self._idx, list_=True)))
|
|
392
|
+
return x[..., self._idx] if isinstance(x, Tensor) else x[self._idx]
|
|
393
|
+
|
|
394
|
+
def extra_repr(self) -> str:
|
|
395
|
+
"""
|
|
396
|
+
Displays layer parameters when printing the network.
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
str
|
|
401
|
+
Layer parameters
|
|
402
|
+
"""
|
|
403
|
+
return f'map={bool(self._map)}, idx={self._idx}'
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class LayerNorm(BaseLayer):
|
|
407
|
+
"""
|
|
408
|
+
Constructs a layer normalisation layer that is the same as PyTorch's LayerNorm, except
|
|
409
|
+
normalisation priority is the first dimension after batch dimension to follow ConvNeXt
|
|
410
|
+
implementation.
|
|
411
|
+
|
|
412
|
+
Attributes
|
|
413
|
+
----------
|
|
414
|
+
group : int
|
|
415
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
416
|
+
Networks
|
|
417
|
+
description : str
|
|
418
|
+
Description of the layer
|
|
419
|
+
"""
|
|
420
|
+
def __init__(
|
|
421
|
+
self,
|
|
422
|
+
*,
|
|
423
|
+
dims: int | None = None,
|
|
424
|
+
shape: list[int] | None = None,
|
|
425
|
+
shapes: Shapes | None = None,
|
|
426
|
+
**kwargs: Any) -> None:
|
|
427
|
+
"""
|
|
428
|
+
Parameters
|
|
429
|
+
----------
|
|
430
|
+
dims : int | None, Optional
|
|
431
|
+
Number of dimensions to normalise starting with the first dimension, ignoring batch
|
|
432
|
+
dimension, requires shapes argument, won't be used if shape is provided
|
|
433
|
+
shape : list[int] | None, Optional
|
|
434
|
+
Input shape or shape of the first dimension to normalise, will be used if provided, else
|
|
435
|
+
dims will be used
|
|
436
|
+
shapes : Shapes | None, Optional
|
|
437
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
438
|
+
necessary
|
|
439
|
+
**kwargs
|
|
440
|
+
Leftover parameters to pass to base layer for checking
|
|
441
|
+
"""
|
|
442
|
+
super().__init__(**kwargs)
|
|
443
|
+
self._layer: nn.LayerNorm
|
|
444
|
+
|
|
445
|
+
if not shape and (dims and shapes):
|
|
446
|
+
shape = shapes[-1][:dims]
|
|
447
|
+
elif not shape:
|
|
448
|
+
raise ValueError(f'Either shape ({shape}) or dims ({dims}) and shapes must be provided')
|
|
449
|
+
|
|
450
|
+
self._layer = nn.LayerNorm(shape[::-1])
|
|
451
|
+
|
|
452
|
+
# If not used as a layer in a network
|
|
453
|
+
if not shapes:
|
|
454
|
+
return
|
|
455
|
+
|
|
456
|
+
shapes.append(shapes[-1].copy())
|
|
457
|
+
|
|
458
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
459
|
+
return super().__getstate__() | {'shape': self._layer.normalized_shape}
|
|
460
|
+
|
|
461
|
+
def forward(self, x: Tensor, *_: Any, **__: Any) -> Tensor:
|
|
462
|
+
"""
|
|
463
|
+
Forward pass of the layer normalisation layer.
|
|
464
|
+
|
|
465
|
+
Parameters
|
|
466
|
+
----------
|
|
467
|
+
x : Tensor
|
|
468
|
+
Input tensor of shape (N,...) and type float, where N is the batch size
|
|
469
|
+
|
|
470
|
+
Returns
|
|
471
|
+
-------
|
|
472
|
+
Tensor
|
|
473
|
+
Output Tensor of shape (N,...) and type float
|
|
474
|
+
"""
|
|
475
|
+
permutation: list[int] = list(range(x.ndim - 1, 0, -1))
|
|
476
|
+
return self._layer(x.permute(0, *permutation)).permute(0, *permutation)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
class Pad(BaseLayer):
|
|
480
|
+
"""
|
|
481
|
+
Constructs a padding layer to pad the output from the previous layer.
|
|
482
|
+
|
|
483
|
+
Attributes
|
|
484
|
+
----------
|
|
485
|
+
group : int
|
|
486
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
487
|
+
Networks
|
|
488
|
+
description : str
|
|
489
|
+
Description of the layer
|
|
490
|
+
|
|
491
|
+
Methods
|
|
492
|
+
-------
|
|
493
|
+
forward(x) -> Tensor
|
|
494
|
+
Forward pass of the padding layer
|
|
495
|
+
extra_repr() -> str
|
|
496
|
+
Displays layer parameters when printing the network
|
|
497
|
+
"""
|
|
498
|
+
@overload
|
|
499
|
+
def __init__(
|
|
500
|
+
self,
|
|
501
|
+
pad: list[int] | list[tuple[int, int]] | tuple[int, ...],
|
|
502
|
+
*,
|
|
503
|
+
shapes: Shapes | None = ...,
|
|
504
|
+
pad_kwargs: dict[str, Any] | None = ...,
|
|
505
|
+
**kwargs: Any) -> None: ...
|
|
506
|
+
|
|
507
|
+
@overload
|
|
508
|
+
def __init__(
|
|
509
|
+
self,
|
|
510
|
+
pad: int,
|
|
511
|
+
*,
|
|
512
|
+
dims: int = ...,
|
|
513
|
+
shapes: Shapes = ...,
|
|
514
|
+
pad_kwargs: dict[str, Any] | None = ...,
|
|
515
|
+
**kwargs: Any) -> None: ...
|
|
516
|
+
|
|
517
|
+
def __init__(
|
|
518
|
+
self,
|
|
519
|
+
pad: int | list[int] | list[tuple[int, int]] | tuple[int, ...],
|
|
520
|
+
*,
|
|
521
|
+
dims: int = -1,
|
|
522
|
+
shapes: Shapes | None = None,
|
|
523
|
+
pad_kwargs: dict[str, Any] | None = None,
|
|
524
|
+
**kwargs: Any) -> None:
|
|
525
|
+
"""
|
|
526
|
+
Parameters
|
|
527
|
+
----------
|
|
528
|
+
pad : int | list[int] | list[tuple[int, int]] | tuple[int, ...]
|
|
529
|
+
Amount of padding to apply to each dimension of the input tensor, ignoring batch
|
|
530
|
+
dimension and from right to left, if int is provided, same padding will be applied to
|
|
531
|
+
left and right of all dimensions according to dims, if list of integers is provided,
|
|
532
|
+
each integer will be applied to left and right of the corresponding dimension, if list
|
|
533
|
+
of tuple of integers is provided, then asymmetric (left, right) padding will be applied,
|
|
534
|
+
if tuple of integers is provided, each pair of integers will be applied to left and
|
|
535
|
+
right of the corresponding dimension
|
|
536
|
+
dims : int, Optional
|
|
537
|
+
Number of dimensions to pad starting with the last dimension, ignoring batch dimension,
|
|
538
|
+
if shapes is not provided, dims must be greater than 0, if negative, excludes the first
|
|
539
|
+
abs(dims) dimensions, if 0 pads all dimensions, won't be used if padding is a list of
|
|
540
|
+
integers or list of tuple of integers, default = -1
|
|
541
|
+
shapes : Shapes | None, Optional
|
|
542
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
543
|
+
necessary
|
|
544
|
+
pad_kwargs : dict[str, Any] | None, Optional
|
|
545
|
+
Additional keyword arguments to pass to the padding function
|
|
546
|
+
**kwargs
|
|
547
|
+
Leftover parameters to pass to base layer for checking
|
|
548
|
+
"""
|
|
549
|
+
super().__init__(**kwargs)
|
|
550
|
+
self._pad: tuple[int, ...]
|
|
551
|
+
self._kwargs: dict[str, Any] = pad_kwargs or {}
|
|
552
|
+
pad_: list[int]
|
|
553
|
+
sub_shape: list[int]
|
|
554
|
+
shape: list[int] | list[list[int]]
|
|
555
|
+
|
|
556
|
+
if isinstance(pad, int) and dims <= 0 and shapes is None:
|
|
557
|
+
raise ValueError(f'Dims ({dims}) must be greater than 0 if pad ({pad}) is an integer '
|
|
558
|
+
f'and shapes is not provided')
|
|
559
|
+
|
|
560
|
+
if shapes is None and dims <= 0:
|
|
561
|
+
dims = 1
|
|
562
|
+
|
|
563
|
+
if isinstance(pad, tuple) and len(pad) % 2 != 0:
|
|
564
|
+
raise ValueError(f'If pad is a tuple, it must contain an even number of integers '
|
|
565
|
+
f'({len(pad)})')
|
|
566
|
+
|
|
567
|
+
if dims < 0 and shapes:
|
|
568
|
+
dims = len(shapes[-1]) + dims
|
|
569
|
+
elif dims == 0 and shapes:
|
|
570
|
+
len(shapes[-1])
|
|
571
|
+
|
|
572
|
+
if isinstance(pad, int):
|
|
573
|
+
self._pad = tuple([pad] * 2 * dims)
|
|
574
|
+
elif isinstance(pad, list) and isinstance(pad[0], int):
|
|
575
|
+
self._pad = tuple(np.repeat(pad, 2).tolist())
|
|
576
|
+
elif isinstance(pad, list):
|
|
577
|
+
self._pad = tuple(np.array(pad).flatten().tolist())
|
|
578
|
+
else:
|
|
579
|
+
self._pad = pad
|
|
580
|
+
|
|
581
|
+
if shapes is None:
|
|
582
|
+
return
|
|
583
|
+
|
|
584
|
+
shape = shapes.get(-1, True)
|
|
585
|
+
pad_ = ([0] * (len(shape) - len(self._pad) // 2) +
|
|
586
|
+
[sum(self._pad[i - 2:i]) for i in range(len(self._pad), 0, -2)])
|
|
587
|
+
|
|
588
|
+
if isinstance(shape[0], list):
|
|
589
|
+
shapes.append([
|
|
590
|
+
self._pad_shape(cast(list[int], sub_shape), pad_) for sub_shape in shape
|
|
591
|
+
])
|
|
592
|
+
else:
|
|
593
|
+
shapes.append(self._pad_shape(cast(list[int], shape), pad_))
|
|
594
|
+
|
|
595
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
596
|
+
return super().__getstate__() | {
|
|
597
|
+
'pad': self._pad,
|
|
598
|
+
'pad_kwargs': self._kwargs,
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
@staticmethod
|
|
602
|
+
def _pad_shape(shape: list[int], dim_pads: list[int]) -> list[int]:
|
|
603
|
+
"""
|
|
604
|
+
Returns the output shape after padding.
|
|
605
|
+
|
|
606
|
+
Parameters
|
|
607
|
+
----------
|
|
608
|
+
shape : list[int]
|
|
609
|
+
Input shape
|
|
610
|
+
dim_pads : list[int]
|
|
611
|
+
Amount of padding applied to each dimension
|
|
612
|
+
|
|
613
|
+
Returns
|
|
614
|
+
-------
|
|
615
|
+
list[int]
|
|
616
|
+
Output shape
|
|
617
|
+
"""
|
|
618
|
+
dim: int
|
|
619
|
+
dim_pad: int
|
|
620
|
+
return [dim + dim_pad for dim, dim_pad in zip(shape, dim_pads)]
|
|
621
|
+
|
|
622
|
+
def extra_repr(self) -> str:
|
|
623
|
+
"""
|
|
624
|
+
Displays layer parameters when printing the network.
|
|
625
|
+
|
|
626
|
+
Returns
|
|
627
|
+
-------
|
|
628
|
+
str
|
|
629
|
+
Layer parameters
|
|
630
|
+
"""
|
|
631
|
+
return f'pad={list(zip(self._pad[::2], self._pad[1::2]))}, pad_kwargs={self._kwargs}'
|
|
632
|
+
|
|
633
|
+
def forward(self, x: TensorListLike, *_: Any, **__: Any) -> TensorListLike:
|
|
634
|
+
"""
|
|
635
|
+
Forward pass of the padding layer.
|
|
636
|
+
|
|
637
|
+
Parameters
|
|
638
|
+
----------
|
|
639
|
+
x : TensorListLike
|
|
640
|
+
Input with tensor(s) of shape (N,...) and type float, where N is the batch size
|
|
641
|
+
|
|
642
|
+
Returns
|
|
643
|
+
-------
|
|
644
|
+
TensorListLike
|
|
645
|
+
Output with tensor(s) of shape (N,...) and type float
|
|
646
|
+
"""
|
|
647
|
+
if isinstance(x, Tensor):
|
|
648
|
+
return nn.functional.pad(x, self._pad, **self._kwargs)
|
|
649
|
+
return x.apply(nn.functional.pad, self._pad, **self._kwargs)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class Reshape(BaseLayer):
|
|
653
|
+
"""
|
|
654
|
+
Constructs a reshaping layer to change the data dimensions.
|
|
655
|
+
|
|
656
|
+
Attributes
|
|
657
|
+
----------
|
|
658
|
+
group : int
|
|
659
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
660
|
+
Networks
|
|
661
|
+
description : str
|
|
662
|
+
Description of the layer
|
|
663
|
+
"""
|
|
664
|
+
def __init__(
|
|
665
|
+
self,
|
|
666
|
+
shape: list[int],
|
|
667
|
+
*,
|
|
668
|
+
factor: bool = False,
|
|
669
|
+
layer: int | None = None,
|
|
670
|
+
net_out: list[int] | None = None,
|
|
671
|
+
shapes: Shapes | None = None,
|
|
672
|
+
**kwargs: Any) -> None:
|
|
673
|
+
"""
|
|
674
|
+
Parameters
|
|
675
|
+
----------
|
|
676
|
+
shape : list[int]
|
|
677
|
+
Desired shape of the output tensor, ignoring first dimension
|
|
678
|
+
factor : bool, Optional
|
|
679
|
+
If reshape should be relative to the network output shape, or if layer is provided,
|
|
680
|
+
which layer to be relative to, requires tracking layer outputs, default = False
|
|
681
|
+
layer : int | None, Optional
|
|
682
|
+
If factor is True, which layer for factor to be relative to, if None, network output
|
|
683
|
+
will be used
|
|
684
|
+
net_out : list[int] | None, Optional
|
|
685
|
+
Shape of the network's output, required only if factor is True
|
|
686
|
+
shapes : Shapes | None, Optional
|
|
687
|
+
Shape of the outputs from each layer, only required if tracking layer outputs is
|
|
688
|
+
necessary
|
|
689
|
+
**kwargs
|
|
690
|
+
Leftover parameters to pass to base layer for checking
|
|
691
|
+
"""
|
|
692
|
+
super().__init__(**kwargs)
|
|
693
|
+
self._shape: list[int] = shape
|
|
694
|
+
prod: int
|
|
695
|
+
elm: int
|
|
696
|
+
target: list[int]
|
|
697
|
+
|
|
698
|
+
# If not used as a layer in a network
|
|
699
|
+
if shapes is None:
|
|
700
|
+
return
|
|
701
|
+
|
|
702
|
+
if factor and shapes is not None and net_out is not None:
|
|
703
|
+
target = shapes[layer] if layer is not None else net_out
|
|
704
|
+
self._shape = [
|
|
705
|
+
int(elm * length) if elm != -1 else -1 for elm, length in zip(self._shape, target)
|
|
706
|
+
]
|
|
707
|
+
if factor and net_out is None:
|
|
708
|
+
raise ValueError('factor requires net_out to not be None')
|
|
709
|
+
|
|
710
|
+
# If -1 in output shape, calculate the dimension length from the input dimensions
|
|
711
|
+
if -1 not in self._shape:
|
|
712
|
+
shapes.append(self._shape)
|
|
713
|
+
elif self._shape.count(-1) == 1:
|
|
714
|
+
shape = self._shape.copy()
|
|
715
|
+
prod = np.prod(np.array(shape)[np.array(shape) != -1])
|
|
716
|
+
shape[shape.index(-1)] = int(np.prod(shapes[-1]) // prod)
|
|
717
|
+
shapes.append(shape)
|
|
718
|
+
else:
|
|
719
|
+
raise ValueError(f'Cannot infer output shape as -1 occurs more than once in '
|
|
720
|
+
f'{self._shape}')
|
|
721
|
+
|
|
722
|
+
# If input tensor cannot be reshaped into output shape
|
|
723
|
+
self._check_reshape(shapes[-2], shapes[-1])
|
|
724
|
+
|
|
725
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
726
|
+
return super().__getstate__() | {'shape': self._shape}
|
|
727
|
+
|
|
728
|
+
@staticmethod
|
|
729
|
+
def _check_reshape(in_shape: list[int], out_shape: list[int]) -> None:
|
|
730
|
+
"""
|
|
731
|
+
Checks if the input tensor can be reshaped into the output tensor.
|
|
732
|
+
|
|
733
|
+
Parameters
|
|
734
|
+
----------
|
|
735
|
+
in_shape : list[int]
|
|
736
|
+
Input shape
|
|
737
|
+
out_shape : list[int]
|
|
738
|
+
Output shape
|
|
739
|
+
"""
|
|
740
|
+
if np.prod(out_shape) != np.prod(in_shape):
|
|
741
|
+
raise ValueError(f'Input size does not match output size for input shape {in_shape} '
|
|
742
|
+
f'& output shape {out_shape}')
|
|
743
|
+
|
|
744
|
+
def forward(self, x: Tensor, *_: Any, **__: Any) -> Tensor:
|
|
745
|
+
"""
|
|
746
|
+
Forward pass of reshaping tensors.
|
|
747
|
+
|
|
748
|
+
Parameters
|
|
749
|
+
----------
|
|
750
|
+
x : Tensor
|
|
751
|
+
Input tensor of shape (N,...) and type float, where N is the batch size
|
|
752
|
+
|
|
753
|
+
Returns
|
|
754
|
+
-------
|
|
755
|
+
Tensor
|
|
756
|
+
Output tensor of shape (N,...) and type float
|
|
757
|
+
"""
|
|
758
|
+
return x.contiguous().view(x.size(0), *self._shape)
|
|
759
|
+
|
|
760
|
+
def extra_repr(self) -> str:
|
|
761
|
+
"""
|
|
762
|
+
Displays layer parameters when printing the network
|
|
763
|
+
|
|
764
|
+
Returns
|
|
765
|
+
-------
|
|
766
|
+
str
|
|
767
|
+
Layer parameters
|
|
768
|
+
"""
|
|
769
|
+
return f'output={self._shape}'
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
class Scale(BaseLayer):
|
|
773
|
+
"""
|
|
774
|
+
Constructs a scaling layer that scales the output by a learnable tensor.
|
|
775
|
+
|
|
776
|
+
Attributes
|
|
777
|
+
----------
|
|
778
|
+
group : int
|
|
779
|
+
Layer group, if 0 it will always be used, else it will only be used if its group matches the
|
|
780
|
+
Networks
|
|
781
|
+
description : str
|
|
782
|
+
Description of the layer
|
|
783
|
+
"""
|
|
784
|
+
def __init__(
|
|
785
|
+
self,
|
|
786
|
+
dims: int,
|
|
787
|
+
scale: float,
|
|
788
|
+
shapes: Shapes,
|
|
789
|
+
*,
|
|
790
|
+
first: bool = True,
|
|
791
|
+
**kwargs: Any) -> None:
|
|
792
|
+
"""
|
|
793
|
+
Parameters
|
|
794
|
+
----------
|
|
795
|
+
dims : int
|
|
796
|
+
Number of dimensions to have individual scales for
|
|
797
|
+
scale : float
|
|
798
|
+
Initial scale factor
|
|
799
|
+
shapes : Shapes
|
|
800
|
+
Shape of the outputs from each layer
|
|
801
|
+
first : bool, Optional
|
|
802
|
+
If dims should count from the first dimension after the batch dimension, or from the
|
|
803
|
+
final dimension backwards, default = True
|
|
804
|
+
**kwargs
|
|
805
|
+
Leftover parameters to pass to base layer for checking
|
|
806
|
+
"""
|
|
807
|
+
super().__init__(**kwargs)
|
|
808
|
+
self._first: bool = first
|
|
809
|
+
self._scale: Tensor
|
|
810
|
+
shape: list[int]
|
|
811
|
+
|
|
812
|
+
if self._first:
|
|
813
|
+
shape = shapes[-1][:dims]
|
|
814
|
+
else:
|
|
815
|
+
shape = shapes[-1][-dims:]
|
|
816
|
+
|
|
817
|
+
self._scale = nn.Parameter(scale * torch.ones(*shape), requires_grad=True).to(self._device)
|
|
818
|
+
|
|
819
|
+
shapes.append(shapes[-1].copy())
|
|
820
|
+
|
|
821
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
822
|
+
return super().__getstate__() | {
|
|
823
|
+
'first': self._first,
|
|
824
|
+
'dims': self._scale.ndim,
|
|
825
|
+
'scale': self._scale.detach().flatten().cpu().tolist()[0],
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
def extra_repr(self) -> str:
|
|
829
|
+
return (f'first={self._first}, dims={self._scale.ndim}, '
|
|
830
|
+
f'scale={self._scale.detach().flatten().cpu().tolist()[0]}')
|
|
831
|
+
|
|
832
|
+
def forward(self, x: Tensor, *_: Any, **__: Any) -> Tensor:
|
|
833
|
+
"""
|
|
834
|
+
Forward pass of the scale layer.
|
|
835
|
+
|
|
836
|
+
Parameters
|
|
837
|
+
----------
|
|
838
|
+
x : Tensor
|
|
839
|
+
Input tensor of shape (N,...) and type float, where N is the batch size
|
|
840
|
+
|
|
841
|
+
Returns
|
|
842
|
+
-------
|
|
843
|
+
Tensor
|
|
844
|
+
Output tensor of shape (N,...) and type float
|
|
845
|
+
"""
|
|
846
|
+
if self._first:
|
|
847
|
+
permutation = np.arange(x.ndim - 1, 0, -1)
|
|
848
|
+
return (self._scale * x.permute(0, *permutation)).permute(0, *permutation)
|
|
849
|
+
return self._scale * x
|
|
850
|
+
|
|
851
|
+
def to(self, *args: Any, **kwargs: Any) -> Self:
|
|
852
|
+
super().to(*args, **kwargs)
|
|
853
|
+
self._scale = self._scale.to(*args, **kwargs)
|
|
854
|
+
return self
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
__all__ = [
|
|
858
|
+
'BatchNorm',
|
|
859
|
+
'Checkpoint',
|
|
860
|
+
'Dropout',
|
|
861
|
+
'DropPath',
|
|
862
|
+
'Index',
|
|
863
|
+
'LayerNorm',
|
|
864
|
+
'Pad',
|
|
865
|
+
'Reshape',
|
|
866
|
+
'Scale',
|
|
867
|
+
]
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def __getattr__(name: str) -> Any:
|
|
871
|
+
if hasattr(multi_layer, name):
|
|
872
|
+
warn(
|
|
873
|
+
f'Accessing {name} from layers.misc is deprecated, please use layers or '
|
|
874
|
+
f'layers.multi_layer instead',
|
|
875
|
+
DeprecationWarning,
|
|
876
|
+
stacklevel=2,
|
|
877
|
+
)
|
|
878
|
+
return getattr(multi_layer, name)
|
|
879
|
+
raise AttributeError(f"module {__name__} has no attribute {name}")
|