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.
@@ -0,0 +1,470 @@
1
+ """
2
+ Network layers that use the outputs from multiple layers.
3
+ """
4
+ from copy import deepcopy
5
+ from typing import Any, cast
6
+
7
+ import torch
8
+ import numpy as np
9
+ from torch import Tensor
10
+ from numpy import ndarray
11
+
12
+ from netloader.utils.types import TensorListLike
13
+ from netloader.utils import Shapes, compare_versions
14
+ from netloader.layers.base import BaseMultiLayer, BaseLayer
15
+
16
+
17
+ class Concatenate(BaseMultiLayer):
18
+ """
19
+ Constructs a concatenation layer to combine the outputs from two layers.
20
+
21
+ Attributes
22
+ ----------
23
+ group : int
24
+ Layer group, if 0 it will always be used, else it will only be used if its group matches the
25
+ Networks
26
+ description : str
27
+ Description of the layer
28
+ """
29
+ def __init__(
30
+ self,
31
+ net_check: bool,
32
+ layer: int | list[int],
33
+ shapes: Shapes,
34
+ check_shapes: Shapes,
35
+ *,
36
+ checkpoint: bool = False,
37
+ dim: int = 0,
38
+ **kwargs: Any) -> None:
39
+ """
40
+ Parameters
41
+ ----------
42
+ net_check : bool
43
+ If layer index should be relative to checkpoint layers
44
+ layer : int | list[int]
45
+ Layer index(es) to concatenate the previous layer output with
46
+ shapes : Shapes
47
+ Shape of the outputs from each layer
48
+ check_shapes : Shapes
49
+ Shape of the outputs from each checkpoint
50
+ checkpoint : bool, Optional
51
+ If layer index should be relative to checkpoint layers or network layers, if checkpoints
52
+ in net is True, layer will always be relative to checkpoints, default = False
53
+ dim : int, Optional
54
+ Dimension to concatenate to, default = 0
55
+ **kwargs
56
+ Leftover parameters to pass to base layer for checking
57
+ """
58
+ super().__init__(net_check, layer, shapes, check_shapes, checkpoint=checkpoint, **kwargs)
59
+ self._dim: int = dim
60
+ target: list[int]
61
+ shape: list[int] = self._shapes[0]
62
+
63
+ # If tensors can be concatenated along the specified dimension
64
+ for target in self._shapes[1:]:
65
+ if not self._check_concatenation(shape, target):
66
+ raise ValueError(f'Input shape ({shape}) does not match the target '
67
+ f'shape ({target}) from output shapes {self._shapes[1:]} in '
68
+ f"{'checkpoints' if self._checkpoint else 'layers'} "
69
+ f"{self._layer} for concatenation over dimension {self._dim}")
70
+
71
+ shape[self._dim] += target[self._dim]
72
+
73
+ shapes.append(shape)
74
+
75
+ def __getstate__(self) -> dict[str, Any]:
76
+ return super().__getstate__() | {'dim': self._dim}
77
+
78
+ def _check_concatenation(self, shape1: list[int], shape2: list[int]) -> bool:
79
+ """
80
+ Checks if input shape and target shape are compatible for concatenation.
81
+
82
+ Parameters
83
+ ----------
84
+ shape1 : list[int]
85
+ First shape
86
+ shape2 : list[int]
87
+ Second shape
88
+
89
+ Returns
90
+ -------
91
+ bool
92
+ Whether the shapes are compatible for concatenation
93
+ """
94
+ if ((shape2[:self._dim] + (shape2[self._dim + 1:] if self._dim != -1 else []) !=
95
+ shape1[:self._dim] + (shape1[self._dim + 1:] if self._dim != -1 else [])) or
96
+ (len(shape2) != len(shape1))):
97
+ return False
98
+ return True
99
+
100
+ def forward(
101
+ self,
102
+ x: TensorListLike,
103
+ outputs: list[TensorListLike],
104
+ checkpoints: list[TensorListLike],
105
+ *_: Any,
106
+ **__: Any) -> Tensor:
107
+ """
108
+ Forward pass of the concatenation layer.
109
+
110
+ Parameters
111
+ ----------
112
+ x : Tensor
113
+ Input tensor of shape (N,...) and type float, where N is the batch size
114
+ outputs : list[TensorListLike]
115
+ Output from each layer with tensors of shape (N,...) and type float
116
+ checkpoints : list[TensorListLike]
117
+ Output from each checkpoint with tensors of shape (N,...) and type float
118
+
119
+ Returns
120
+ -------
121
+ Tensor
122
+ Output tensor of shape (N,...) and type float
123
+ """
124
+ return torch.cat(
125
+ list(super().forward(x, outputs=outputs, checkpoints=checkpoints)),
126
+ dim=self._dim + 1 if self._dim >= 0 else self._dim,
127
+ )
128
+
129
+ def extra_repr(self) -> str:
130
+ """
131
+ Displays layer parameters when printing the network.
132
+
133
+ Returns
134
+ -------
135
+ str
136
+ Layer parameters
137
+ """
138
+ return f'{super().extra_repr()}, dim={self._dim}'
139
+
140
+
141
+ class Pack(BaseMultiLayer):
142
+ """
143
+ Constructs a packing layer to combine the output from the previous layer with the output from
144
+ a specified layer into a DataList.
145
+
146
+ Attributes
147
+ ----------
148
+ group : int
149
+ Layer group, if 0 it will always be used, else it will only be used if its group matches the
150
+ Networks
151
+ description : str
152
+ Description of the layer
153
+ """
154
+ def __init__(
155
+ self,
156
+ net_check: bool,
157
+ shapes: Shapes,
158
+ check_shapes: Shapes,
159
+ *,
160
+ checkpoint: bool = False,
161
+ layer: int | list[int] | None = None,
162
+ **kwargs: Any) -> None:
163
+ """
164
+ Parameters
165
+ ----------
166
+ net_check : bool
167
+ If layer index should be relative to checkpoint layers
168
+ shapes : Shapes
169
+ Shape of the outputs from each layer
170
+ check_shapes : Shapes
171
+ Shape of the outputs from each checkpoint
172
+ checkpoint : bool, Optional
173
+ If layer index should be relative to checkpoint layers, default = False
174
+ layer : int | None, Optional
175
+ Layer index to pack with the previous layer, if layer is None, then only the last layer
176
+ will be packed
177
+ **kwargs
178
+ Leftover parameters to pass to base layer for checking
179
+ """
180
+ super().__init__(
181
+ net_check,
182
+ layer or 0,
183
+ shapes,
184
+ check_shapes,
185
+ checkpoint=checkpoint,
186
+ **kwargs,
187
+ )
188
+
189
+ if layer is None:
190
+ self._layer = None
191
+ self._shapes = self._shape_flatten(deepcopy(shapes.get(-1, True)))
192
+
193
+ shapes.append(self._shapes)
194
+
195
+
196
+ class Shortcut(BaseMultiLayer):
197
+ """
198
+ Constructs a shortcut layer to add the outputs from two layers.
199
+
200
+ Attributes
201
+ ----------
202
+ group : int
203
+ Layer group, if 0 it will always be used, else it will only be used if its group matches the
204
+ Networks
205
+ description : str
206
+ Description of the layer
207
+ """
208
+ def __init__(
209
+ self,
210
+ net_check: bool,
211
+ layer: int | list[int],
212
+ shapes: Shapes,
213
+ check_shapes: Shapes,
214
+ *,
215
+ checkpoint: bool = False,
216
+ **kwargs: Any) -> None:
217
+ """
218
+ Parameters
219
+ ----------
220
+ net_check : bool
221
+ If layer index should be relative to checkpoint layers
222
+ layer : int | list[int]
223
+ Layer index(es) to add the previous layer output with
224
+ shapes : Shapes
225
+ Shape of the outputs from each layer
226
+ check_shapes : Shapes
227
+ Shape of the outputs from each checkpoint
228
+ checkpoint : bool, Optional
229
+ If layer index should be relative to checkpoint layers or network layers, if checkpoints
230
+ in net is True, layer will always be relative to checkpoints, default = False
231
+ **kwargs
232
+ Leftover parameters to pass to base layer for checking
233
+ """
234
+ super().__init__(net_check, layer, shapes, check_shapes, checkpoint=checkpoint, **kwargs)
235
+ idxs: ndarray
236
+ mask: ndarray
237
+ shapes_: ndarray = np.array(self._shapes)
238
+ shape: ndarray = shapes_[0]
239
+
240
+ mask = (shapes_[0] != 1) & (shapes_[1:] != 1)
241
+ self._check_addition(shapes_[0], shapes_[1:], mask)
242
+
243
+ # If input has any dimensions of length one, output will take the target dimension
244
+ if 1 in shape:
245
+ idxs = np.where(shape == 1)[0]
246
+ shape[idxs] = np.max(shapes_[:, idxs], axis=0)
247
+
248
+ shapes.append(shape.tolist())
249
+
250
+ def _check_addition(self, shape: ndarray, target: ndarray, mask: ndarray) -> None:
251
+ """
252
+ Checks if input shape and target shape are compatible for addition.
253
+
254
+ Parameters
255
+ ----------
256
+ shape : ndarray
257
+ Input shape
258
+ mask : ndarray
259
+ Mask for dimensions with size of one in both input and target
260
+ """
261
+ if np.ndim(target) == 2 and all(
262
+ np.array_equal(shape[mask[i]], target[i, mask[i]]) for i in range(len(target))):
263
+ return
264
+ if np.ndim(target) == 1 and np.array_equal(shape[mask], target[mask]):
265
+ return
266
+ raise ValueError(f'Input shape {shape} is not compatible with target shape {target} in '
267
+ f"{'checkpoint' if self._checkpoint else 'layer'} "
268
+ f'{self._layer} for addition')
269
+
270
+ def forward(
271
+ self,
272
+ x: Tensor,
273
+ outputs: list[TensorListLike],
274
+ checkpoints: list[TensorListLike],
275
+ *_: Any,
276
+ **__: Any) -> Tensor:
277
+ """
278
+ Forward pass of the shortcut layer
279
+
280
+ Parameters
281
+ ----------
282
+ x : Tensor
283
+ Input tensor of shape (N,...) and type float, where N is the batch size
284
+ outputs : list[TensorListLike]
285
+ Output from each layer with tensors of shape (N,...) and type float
286
+ checkpoints : list[TensorListLike]
287
+ Output from each checkpoint with tensors of shape (N,...) and type float
288
+
289
+ Returns
290
+ -------
291
+ Tensor
292
+ Output tensor of shape (N,...) and type float
293
+ """
294
+ return torch.add(*super().forward(
295
+ x,
296
+ outputs=outputs,
297
+ checkpoints=checkpoints,
298
+ ).get_data())
299
+
300
+
301
+ class Skip(BaseMultiLayer):
302
+ """
303
+ Bypasses previous layers by retrieving the output from the defined layer.
304
+
305
+ Attributes
306
+ ----------
307
+ group : int
308
+ Layer group, if 0 it will always be used, else it will only be used if its group matches the
309
+ Networks
310
+ description : str
311
+ Description of the layer
312
+ """
313
+ def __init__(
314
+ self,
315
+ net_check: bool,
316
+ layer: int,
317
+ shapes: Shapes,
318
+ check_shapes: Shapes,
319
+ *,
320
+ checkpoint: bool = False,
321
+ **kwargs: Any) -> None:
322
+ """
323
+ Parameters
324
+ ----------
325
+ net_check : bool
326
+ If layer index should be relative to checkpoint layers
327
+ layer : int
328
+ Layer index to get the output from
329
+ shapes : Shapes
330
+ Shape of the outputs from each layer
331
+ check_shapes : Shapes
332
+ Shape of the outputs from each checkpoint
333
+ checkpoint : bool, Optional
334
+ If layer index should be relative to checkpoint layers or network layers, if checkpoints
335
+ in net is True, layer will always be relative to checkpoints, default = False
336
+ **kwargs
337
+ Leftover parameters to pass to base layer for checking
338
+ """
339
+ assert isinstance(layer, int)
340
+ super().__init__(net_check, layer, shapes, check_shapes, checkpoint=checkpoint, **kwargs)
341
+ shapes.append(cast(list[int], self._target))
342
+
343
+ def forward(
344
+ self,
345
+ *_: Any,
346
+ outputs: list[TensorListLike],
347
+ checkpoints: list[TensorListLike],
348
+ **__: Any) -> TensorListLike:
349
+ """
350
+ Forward pass of the skip layer.
351
+
352
+ Parameters
353
+ ----------
354
+ outputs : list[TensorListLike]
355
+ Output from each layer with tensors of shape (N,...) and type float
356
+ checkpoints : list[TensorListLike]
357
+ Output from each checkpoint with tensors of shape (N,...) and type float
358
+
359
+ Returns
360
+ -------
361
+ TensorListLike
362
+ Output with tensors of shape (N,...) and type float
363
+ """
364
+ return (checkpoints if self._checkpoint else outputs)[self._layer]
365
+
366
+
367
+ class Unpack(BaseLayer):
368
+ """
369
+ Enables a list of Tensors as input into the network, then selects which Tensor in the list to
370
+ output.
371
+ """
372
+ def __init__(
373
+ self,
374
+ net_check: bool,
375
+ index: int,
376
+ shapes: Shapes,
377
+ check_shapes: Shapes,
378
+ *,
379
+ checkpoint: bool = False,
380
+ layer: int | None = None,
381
+ **kwargs: Any) -> None:
382
+ """
383
+ Parameters
384
+ ----------
385
+ net_check : bool
386
+ If layer index should be relative to checkpoint layers
387
+ index : int
388
+ Index of input Tensor list
389
+ shapes : Shapes
390
+ Shape of the outputs from each layer
391
+ check_shapes : Shapes
392
+ Shape of the outputs from each checkpoint
393
+ checkpoint : bool, Optional
394
+ If layer index should be relative to checkpoint layers or network layers, if checkpoints
395
+ in net is True, layer will always be relative to checkpoints, default = False
396
+ layer : int | None, Optional
397
+ Layer index to get the output from if checkpoint is True, default = None
398
+ **kwargs
399
+ Leftover parameters to pass to base layer for checking
400
+ """
401
+ super().__init__(**kwargs)
402
+ self._checkpoint: bool = checkpoint or net_check
403
+ self._layer: int | None = layer \
404
+ if layer or compare_versions(self._ver, '3.11.0') else 0
405
+ self._idx: int = index
406
+
407
+ if self._layer is None:
408
+ shapes.append(shapes[(-1, self._idx)].copy())
409
+ else:
410
+ shapes.append(
411
+ (check_shapes if self._checkpoint else shapes)[(self._layer, self._idx)].copy(),
412
+ )
413
+
414
+ def __getstate__(self) -> dict[str, Any]:
415
+ return super().__getstate__() | {
416
+ 'checkpoint': self._checkpoint,
417
+ 'layer': self._layer,
418
+ 'index': self._idx,
419
+ }
420
+
421
+ def forward(
422
+ self,
423
+ x: TensorListLike,
424
+ outputs: list[TensorListLike],
425
+ checkpoints: list[TensorListLike],
426
+ **__: Any) -> Tensor:
427
+ """
428
+ Forward pass of the unpack layer.
429
+
430
+ Parameters
431
+ ----------
432
+ outputs : list[TensorListLike]
433
+ Output from each layer with tensors of shape (N,...) and type float, where N is the
434
+ batch size
435
+ checkpoints : list[TensorListLike]
436
+ Output from each checkpoint with tensors of shape (N,...) and type float, where N is the
437
+ batch size
438
+
439
+ Returns
440
+ -------
441
+ Tensor
442
+ Output tensor with shape (N,...) and type float
443
+ """
444
+ if self._layer is None:
445
+ self._check_num_outputs(
446
+ x,
447
+ single=False,
448
+ )
449
+ return cast(Tensor, x.get(self._idx, True))
450
+
451
+ inputs: TensorListLike = (checkpoints if self._checkpoint else outputs)[self._layer]
452
+ self._check_num_outputs(
453
+ inputs,
454
+ single=False,
455
+ )
456
+ return cast(Tensor, inputs.get(self._idx, True))
457
+
458
+ def extra_repr(self) -> str:
459
+ """
460
+ Displays layer parameters when printing the network
461
+
462
+ Returns
463
+ -------
464
+ str
465
+ Layer parameters
466
+ """
467
+ return f'index={self._idx}, checkpoint={bool(self._checkpoint)}, layer={self._layer}'
468
+
469
+
470
+ __all__ = ['Concatenate', 'Pack', 'Shortcut', 'Skip', 'Unpack']