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/transforms.py
ADDED
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Transformations that can be inverted
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from warnings import warn
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Callable, Any, Generic, TypeVar, cast, overload
|
|
9
|
+
|
|
10
|
+
import torch
|
|
11
|
+
import numpy as np
|
|
12
|
+
from torch import Tensor
|
|
13
|
+
from numpy import ndarray
|
|
14
|
+
|
|
15
|
+
from netloader.data import Data
|
|
16
|
+
from netloader.utils.types import ArrayLike, ArrayT, ArrayCT
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
InArrayCT = TypeVar('InArrayCT', ndarray, Tensor)
|
|
20
|
+
OutArrayCT = TypeVar('OutArrayCT', ndarray, Tensor)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BaseTransform(ABC, Generic[InArrayCT, OutArrayCT]):
|
|
24
|
+
"""
|
|
25
|
+
Base transformation class that other types of transforms build from.
|
|
26
|
+
"""
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
# Adds all transform classes to list of safe PyTorch classes when loading saved architectures
|
|
29
|
+
torch.serialization.add_safe_globals([self.__class__])
|
|
30
|
+
|
|
31
|
+
@overload
|
|
32
|
+
def __call__(self, x: InArrayCT, *, back: bool = ...) -> OutArrayCT: ...
|
|
33
|
+
|
|
34
|
+
@overload
|
|
35
|
+
def __call__(self, x: Data[InArrayCT], *, back: bool = ...) -> Data[OutArrayCT]: ...
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def __call__(
|
|
39
|
+
self,
|
|
40
|
+
x: InArrayCT,
|
|
41
|
+
*,
|
|
42
|
+
back: bool = ...,
|
|
43
|
+
uncertainty: InArrayCT) -> tuple[OutArrayCT, OutArrayCT]: ...
|
|
44
|
+
|
|
45
|
+
def __call__(
|
|
46
|
+
self,
|
|
47
|
+
x: InArrayCT | Data[InArrayCT],
|
|
48
|
+
*,
|
|
49
|
+
back: bool = False,
|
|
50
|
+
uncertainty: InArrayCT | None = None) -> OutArrayCT | Data[OutArrayCT] | tuple[OutArrayCT, OutArrayCT]:
|
|
51
|
+
"""
|
|
52
|
+
Calling function returns the forward, backwards or uncertainty propagation of the
|
|
53
|
+
transformation
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
x : ArrayT | Data[ArrayCT]
|
|
58
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
59
|
+
back : bool, Optional
|
|
60
|
+
If the inverse transformation should be applied, default = False
|
|
61
|
+
uncertainty : ArrayT, Optional
|
|
62
|
+
Corresponding uncertainties for the input data for uncertainty propagation of shape
|
|
63
|
+
(N,...)
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
ArrayT | Data[ArrayCT] | tuple[ArrayT, ArrayT]
|
|
68
|
+
Transformed array or tensor of shape (N,...) and propagated uncertainties of shape
|
|
69
|
+
(N,...) if provided
|
|
70
|
+
"""
|
|
71
|
+
if isinstance(x, Data):
|
|
72
|
+
return Data(*self._call(x.data, back=back, uncertainty=x.uncertainty))
|
|
73
|
+
return self._call(x, back=back, uncertainty=uncertainty)
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
"""
|
|
77
|
+
Representation of the transformation
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
str
|
|
82
|
+
Representation string
|
|
83
|
+
"""
|
|
84
|
+
return f'{self.__class__.__name__}({self.extra_repr()})'
|
|
85
|
+
|
|
86
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
87
|
+
"""
|
|
88
|
+
Returns a dictionary containing the state of the transformation for pickling
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
dict[str, Any]
|
|
93
|
+
Dictionary containing the state of the transformation
|
|
94
|
+
"""
|
|
95
|
+
return {}
|
|
96
|
+
|
|
97
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
98
|
+
"""
|
|
99
|
+
Sets the state of the transformation for pickling
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
state : dict[str, Any]
|
|
104
|
+
Dictionary containing the state of the transformation
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def _call(
|
|
108
|
+
self,
|
|
109
|
+
x: InArrayCT,
|
|
110
|
+
back: bool = False,
|
|
111
|
+
uncertainty: InArrayCT | None = None) -> OutArrayCT | tuple[OutArrayCT, OutArrayCT]:
|
|
112
|
+
"""
|
|
113
|
+
Internal calling function to handle the different types of calls
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
x : ArrayT
|
|
118
|
+
Input array or tensor of shape (N,...) and type float, where N is the number of elements
|
|
119
|
+
back : bool, Optional
|
|
120
|
+
If the inverse transformation should be applied, default = False
|
|
121
|
+
uncertainty : ArrayT | None, Optional
|
|
122
|
+
Corresponding uncertainties for the input data for uncertainty propagation of shape
|
|
123
|
+
(N,...) and type float
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
ArrayT | tuple[ArrayT]
|
|
128
|
+
Transformed array or tensor and propagated uncertainties if provided of shape
|
|
129
|
+
(N,...) and type float
|
|
130
|
+
"""
|
|
131
|
+
if back and uncertainty is not None:
|
|
132
|
+
return self.backward_grad(x, uncertainty)
|
|
133
|
+
if back:
|
|
134
|
+
return self.backward(x)
|
|
135
|
+
if uncertainty is not None:
|
|
136
|
+
return self.forward_grad(x, uncertainty)
|
|
137
|
+
return self.forward(x)
|
|
138
|
+
|
|
139
|
+
@abstractmethod
|
|
140
|
+
def forward(self, x: InArrayCT) -> OutArrayCT:
|
|
141
|
+
"""
|
|
142
|
+
Forward pass of the transformation
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
x : ArrayT
|
|
147
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
ArrayT
|
|
152
|
+
Transformed array or tensor of shape (N,...)
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
@abstractmethod
|
|
156
|
+
def backward(self, x: InArrayCT) -> OutArrayCT:
|
|
157
|
+
"""
|
|
158
|
+
Backwards pass to invert the transformation
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
x : ArrayT
|
|
163
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
ArrayT
|
|
168
|
+
Untransformed array or tensor of shape (N,...)
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def forward_grad(self, x: InArrayCT, uncertainty: InArrayCT) -> tuple[OutArrayCT, OutArrayCT]:
|
|
173
|
+
"""
|
|
174
|
+
Forward pass of the transformation and uncertainty propagation
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
x : ArrayT
|
|
179
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
180
|
+
uncertainty : ArrayT
|
|
181
|
+
Uncertainty of the input array or tensor of shape (N,...)
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
tuple[ArrayT, ArrayT]
|
|
186
|
+
Transformed array or tensor of shape (N,...) and transformed uncertainty of shape
|
|
187
|
+
(N,...)
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def backward_grad(self, x: InArrayCT, uncertainty: InArrayCT) -> tuple[OutArrayCT, OutArrayCT]:
|
|
192
|
+
"""
|
|
193
|
+
Backwards pass to invert the transformation and uncertainty propagation
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
x : ArrayT
|
|
198
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
199
|
+
uncertainty : ArrayT
|
|
200
|
+
Uncertainty of the input array or tensor of shape (N,...)
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
tuple[ArrayT, ArrayT]
|
|
205
|
+
Untransformed array or tensor of shape (N,...) and untransformed uncertainty of shape
|
|
206
|
+
(N,...)
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def extra_repr(self) -> str:
|
|
210
|
+
"""
|
|
211
|
+
Additional representation of the transformation
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
str
|
|
216
|
+
Transform specific representation
|
|
217
|
+
"""
|
|
218
|
+
return ''
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class BaseTypeTransform(BaseTransform[ArrayCT, ArrayCT]):
|
|
222
|
+
"""
|
|
223
|
+
Base transformation class that other types of transforms build from, with the same input and
|
|
224
|
+
output type.
|
|
225
|
+
"""
|
|
226
|
+
def forward(self, x: ArrayCT) -> ArrayCT:
|
|
227
|
+
"""
|
|
228
|
+
Forward pass of the transformation
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
x : ArrayT
|
|
233
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
ArrayT
|
|
238
|
+
Transformed array or tensor of shape (N,...)
|
|
239
|
+
"""
|
|
240
|
+
return x
|
|
241
|
+
|
|
242
|
+
def backward(self, x: ArrayCT) -> ArrayCT:
|
|
243
|
+
"""
|
|
244
|
+
Backwards pass to invert the transformation
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
x : ArrayT
|
|
249
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
ArrayT
|
|
254
|
+
Untransformed array or tensor of shape (N,...)
|
|
255
|
+
"""
|
|
256
|
+
return x
|
|
257
|
+
|
|
258
|
+
def forward_grad(self, x: ArrayCT, uncertainty: ArrayCT) -> tuple[ArrayCT, ArrayCT]:
|
|
259
|
+
"""
|
|
260
|
+
Forward pass of the transformation and uncertainty propagation
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
x : ArrayT
|
|
265
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
266
|
+
uncertainty : ArrayT
|
|
267
|
+
Uncertainty of the input array or tensor of shape (N,...)
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
tuple[ArrayT, ArrayT]
|
|
272
|
+
Transformed array or tensor of shape (N,...) and transformed uncertainty of shape
|
|
273
|
+
(N,...)
|
|
274
|
+
"""
|
|
275
|
+
return self(x), uncertainty
|
|
276
|
+
|
|
277
|
+
def backward_grad(self, x: ArrayCT, uncertainty: ArrayCT) -> tuple[ArrayCT, ArrayCT]:
|
|
278
|
+
"""
|
|
279
|
+
Backwards pass to invert the transformation and uncertainty propagation
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
x : ArrayT
|
|
284
|
+
Input array or tensor of shape (N,...), where N is the number of elements
|
|
285
|
+
uncertainty : ArrayT
|
|
286
|
+
Uncertainty of the input array or tensor of shape (N,...)
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
tuple[ArrayT, ArrayT]
|
|
291
|
+
Untransformed array or tensor of shape (N,...) and untransformed uncertainty of shape
|
|
292
|
+
(N,...)
|
|
293
|
+
"""
|
|
294
|
+
return self(x, back=True), uncertainty
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class Index(BaseTypeTransform):
|
|
298
|
+
"""
|
|
299
|
+
Slices the input along a given dimension assuming the input meets the required shape.
|
|
300
|
+
"""
|
|
301
|
+
def __init__(
|
|
302
|
+
self,
|
|
303
|
+
*,
|
|
304
|
+
dim: int = -1,
|
|
305
|
+
in_shape: tuple[int, ...] | None = None,
|
|
306
|
+
slice_: slice = slice(None)) -> None:
|
|
307
|
+
"""
|
|
308
|
+
Parameters
|
|
309
|
+
----------
|
|
310
|
+
dim : int, Optional
|
|
311
|
+
Dimension to slice over, default = -1
|
|
312
|
+
in_shape : tuple[int, ...] | None, Optional
|
|
313
|
+
Target shape ignoring batch size so that the slice only occurs if the input has the
|
|
314
|
+
same shape to prevent repeated slicing, if any dimension has a shape of -1, then the
|
|
315
|
+
size of the dimension will be ignored
|
|
316
|
+
slice_ : slice, Optional
|
|
317
|
+
Slicing object, default = slice(None)
|
|
318
|
+
"""
|
|
319
|
+
super().__init__()
|
|
320
|
+
self._shape: tuple[int, ...] = tuple(in_shape or ())
|
|
321
|
+
self._slice: list[slice] = [slice(None)] * (len(self._shape) or 1)
|
|
322
|
+
self._slice[dim] = slice_
|
|
323
|
+
|
|
324
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
325
|
+
return {'in_shape': self._shape, 'slice': [(s.start, s.stop, s.step) for s in self._slice]}
|
|
326
|
+
|
|
327
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
328
|
+
self._shape = state['in_shape']
|
|
329
|
+
|
|
330
|
+
if isinstance(state['slice'][0], slice):
|
|
331
|
+
warn(
|
|
332
|
+
f'{self.__class__.__name__} transform is saved in old non-weights safe '
|
|
333
|
+
'format and is deprecated, please resave the transform in the new format using '
|
|
334
|
+
'BaseArchitecture.save()',
|
|
335
|
+
DeprecationWarning,
|
|
336
|
+
stacklevel=2,
|
|
337
|
+
)
|
|
338
|
+
self._slice = state['slice']
|
|
339
|
+
else:
|
|
340
|
+
self._slice = [slice(*s) for s in state['slice']]
|
|
341
|
+
|
|
342
|
+
def forward(self, x: ArrayT) -> ArrayT:
|
|
343
|
+
idxs: ndarray = np.array(np.array(self._shape) != -1)
|
|
344
|
+
|
|
345
|
+
if np.array(np.array(x.shape[1:])[idxs] != np.array(self._shape)[idxs]).any():
|
|
346
|
+
return super().forward(x)
|
|
347
|
+
return cast(ArrayT, x[:, *self._slice])
|
|
348
|
+
|
|
349
|
+
def forward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
350
|
+
return self(x), self(uncertainty)
|
|
351
|
+
|
|
352
|
+
def extra_repr(self) -> str:
|
|
353
|
+
return f'in_shape: {self._shape}, slice: {self._slice}'
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class Log(BaseTypeTransform):
|
|
357
|
+
"""
|
|
358
|
+
Logarithm transform.
|
|
359
|
+
"""
|
|
360
|
+
def __init__(self, *, base: float = 10, idxs: list[int] | None = None) -> None:
|
|
361
|
+
"""
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
base : float, Optional
|
|
365
|
+
Base of the logarithm, default = 10
|
|
366
|
+
idxs : list[int] | None, Optional
|
|
367
|
+
Indices to slice the last dimension to perform the log on
|
|
368
|
+
"""
|
|
369
|
+
super().__init__()
|
|
370
|
+
self._base: float = base
|
|
371
|
+
self._idxs: list[int] | None = idxs
|
|
372
|
+
|
|
373
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
374
|
+
return {'base': self._base, 'idxs': self._idxs}
|
|
375
|
+
|
|
376
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
377
|
+
try:
|
|
378
|
+
self._base = state['base']
|
|
379
|
+
self._idxs = state['idxs']
|
|
380
|
+
except KeyError:
|
|
381
|
+
self._base = state['_base']
|
|
382
|
+
self._idxs = state['_idxs']
|
|
383
|
+
warn(f'{self.__class__.__name__} transform is saved in an old format and is '
|
|
384
|
+
f'deprecated, please resave the transform', DeprecationWarning, stacklevel=2)
|
|
385
|
+
|
|
386
|
+
def forward(self, x: ArrayT) -> ArrayT:
|
|
387
|
+
module: ModuleType = torch if isinstance(x, Tensor) else np
|
|
388
|
+
logs: dict[float, Callable] = {module.e: module.log, 10: module.log10, 2: module.log2}
|
|
389
|
+
x = x.clone() if isinstance(x, Tensor) else x.copy()
|
|
390
|
+
|
|
391
|
+
if self._base in logs and self._idxs is not None:
|
|
392
|
+
x[..., self._idxs] = logs[self._base](x[..., self._idxs])
|
|
393
|
+
elif self._base in logs:
|
|
394
|
+
x = logs[self._base](x)
|
|
395
|
+
elif self._idxs is not None:
|
|
396
|
+
x[..., self._idxs] = module.log(x[..., self._idxs]) / np.log(self._base)
|
|
397
|
+
else:
|
|
398
|
+
x = module.log(x) / np.log(self._base)
|
|
399
|
+
return x
|
|
400
|
+
|
|
401
|
+
def backward(self, x: ArrayT) -> ArrayT:
|
|
402
|
+
x = x.clone() if isinstance(x, Tensor) else x.copy()
|
|
403
|
+
|
|
404
|
+
if self._idxs is not None:
|
|
405
|
+
x[..., self._idxs] = self._base ** x[..., self._idxs] # type: ignore
|
|
406
|
+
else:
|
|
407
|
+
x = cast(ArrayT, self._base ** x)
|
|
408
|
+
return x
|
|
409
|
+
|
|
410
|
+
def forward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
411
|
+
module: ModuleType = torch if isinstance(x, Tensor) else np
|
|
412
|
+
uncertainty = uncertainty.clone() if isinstance(uncertainty, Tensor) else uncertainty.copy()
|
|
413
|
+
|
|
414
|
+
if self._idxs is not None:
|
|
415
|
+
uncertainty[..., self._idxs] /= x[..., self._idxs] * np.log(self._base)
|
|
416
|
+
else:
|
|
417
|
+
uncertainty /= x * np.log(self._base)
|
|
418
|
+
return self(x), module.abs(uncertainty)
|
|
419
|
+
|
|
420
|
+
def backward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
421
|
+
module: ModuleType = torch if isinstance(x, Tensor) else np
|
|
422
|
+
uncertainty = uncertainty.clone() if isinstance(uncertainty, Tensor) else uncertainty.copy()
|
|
423
|
+
x = self(x, back=True)
|
|
424
|
+
|
|
425
|
+
if self._idxs is not None:
|
|
426
|
+
uncertainty[..., self._idxs] *= x[..., self._idxs]
|
|
427
|
+
else:
|
|
428
|
+
uncertainty *= x
|
|
429
|
+
|
|
430
|
+
if self._base != module.e and self._idxs is not None:
|
|
431
|
+
uncertainty[..., self._idxs] *= np.log(self._base)
|
|
432
|
+
elif self._base != module.e:
|
|
433
|
+
uncertainty *= np.log(self._base)
|
|
434
|
+
return x, module.abs(uncertainty)
|
|
435
|
+
|
|
436
|
+
def extra_repr(self) -> str:
|
|
437
|
+
return f'base: {self._base}, idxs: {self._idxs}'
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class MinClamp(BaseTypeTransform):
|
|
441
|
+
"""
|
|
442
|
+
Clamps the minimum value to be the smallest positive value.
|
|
443
|
+
"""
|
|
444
|
+
def __init__(self, *, dim: int | None = None, idxs: list[int] | None = None) -> None:
|
|
445
|
+
"""
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
dim : int | None, Optional
|
|
449
|
+
Dimension to take the minimum value over
|
|
450
|
+
idxs : list[int] | None, Optional
|
|
451
|
+
Indices to slice the last dimension to perform the min clamp on
|
|
452
|
+
"""
|
|
453
|
+
super().__init__()
|
|
454
|
+
self._dim: int | None = dim
|
|
455
|
+
self._idxs: list[int] | None = idxs
|
|
456
|
+
|
|
457
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
458
|
+
return {'dim': self._dim, 'idxs': self._idxs}
|
|
459
|
+
|
|
460
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
461
|
+
try:
|
|
462
|
+
self._dim = state['dim']
|
|
463
|
+
self._idxs = state['idxs']
|
|
464
|
+
except KeyError:
|
|
465
|
+
self._dim = state['_dim']
|
|
466
|
+
self._idxs = state['_idxs']
|
|
467
|
+
warn(f'{self.__class__.__name__} transform is saved in an old format and is '
|
|
468
|
+
f'deprecated, please resave the transform', DeprecationWarning, stacklevel=2)
|
|
469
|
+
|
|
470
|
+
def forward(self, x: ArrayT) -> ArrayT:
|
|
471
|
+
kwargs: dict[str, Any]
|
|
472
|
+
module: ModuleType = torch if isinstance(x, Tensor) else np
|
|
473
|
+
x_clamp: ArrayT
|
|
474
|
+
min_count: ArrayT
|
|
475
|
+
|
|
476
|
+
if isinstance(x, Tensor):
|
|
477
|
+
kwargs = {'dim': self._dim, 'keepdim': True}
|
|
478
|
+
else:
|
|
479
|
+
kwargs = {'axis': self._dim, 'keepdims': True}
|
|
480
|
+
|
|
481
|
+
if self._idxs is None:
|
|
482
|
+
min_count = module.amin(module.where(x > 0, x, module.max(x)), **kwargs)
|
|
483
|
+
x = module.maximum(x, min_count)
|
|
484
|
+
else:
|
|
485
|
+
x_clamp = cast(ArrayT, x[..., self._idxs])
|
|
486
|
+
min_count = module.amin(module.where(
|
|
487
|
+
x_clamp > 0,
|
|
488
|
+
x_clamp,
|
|
489
|
+
module.max(x_clamp),
|
|
490
|
+
), **kwargs)
|
|
491
|
+
x[..., self._idxs] = module.maximum(x_clamp, min_count)
|
|
492
|
+
return x
|
|
493
|
+
|
|
494
|
+
def extra_repr(self) -> str:
|
|
495
|
+
return f'dim: {self._dim}, idxs: {self._idxs}'
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
class MultiTransform(BaseTransform):
|
|
499
|
+
"""
|
|
500
|
+
Applies multiple transformations.
|
|
501
|
+
|
|
502
|
+
Attributes
|
|
503
|
+
----------
|
|
504
|
+
transforms : list[BaseTransform]
|
|
505
|
+
List of transformations
|
|
506
|
+
"""
|
|
507
|
+
def __init__(self, *args: BaseTransform) -> None:
|
|
508
|
+
"""
|
|
509
|
+
Parameters
|
|
510
|
+
----------
|
|
511
|
+
*args : BaseTransform
|
|
512
|
+
Transformations
|
|
513
|
+
"""
|
|
514
|
+
super().__init__()
|
|
515
|
+
self.transforms: list[BaseTransform]
|
|
516
|
+
|
|
517
|
+
if isinstance(args[0], list):
|
|
518
|
+
warn(
|
|
519
|
+
'List of transforms is deprecated, pass transforms as arguments directly',
|
|
520
|
+
DeprecationWarning,
|
|
521
|
+
stacklevel=2,
|
|
522
|
+
)
|
|
523
|
+
self.transforms = args[0]
|
|
524
|
+
else:
|
|
525
|
+
self.transforms = list(args)
|
|
526
|
+
|
|
527
|
+
@overload
|
|
528
|
+
def __getitem__(self, item: int) -> BaseTransform: ...
|
|
529
|
+
|
|
530
|
+
@overload
|
|
531
|
+
def __getitem__(self, item: slice) -> MultiTransform: ...
|
|
532
|
+
|
|
533
|
+
def __getitem__(self, item: int | slice) -> BaseTransform:
|
|
534
|
+
"""
|
|
535
|
+
Gets a transform or a slice of transforms from the list of transforms.
|
|
536
|
+
|
|
537
|
+
Parameters
|
|
538
|
+
----------
|
|
539
|
+
item : int | slice
|
|
540
|
+
Index or slice of the transform(s) to get
|
|
541
|
+
|
|
542
|
+
Returns
|
|
543
|
+
-------
|
|
544
|
+
BaseTransform
|
|
545
|
+
Transform or MultiTransform containing the slice of transforms
|
|
546
|
+
"""
|
|
547
|
+
if isinstance(item, int):
|
|
548
|
+
return self.transforms[item]
|
|
549
|
+
return MultiTransform(*self.transforms[item])
|
|
550
|
+
|
|
551
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
552
|
+
return {'transforms': [(
|
|
553
|
+
transform.__class__.__name__,
|
|
554
|
+
transform.__getstate__(),
|
|
555
|
+
) for transform in self.transforms]}
|
|
556
|
+
|
|
557
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
558
|
+
self.transforms = []
|
|
559
|
+
|
|
560
|
+
if isinstance(state['transforms'][0], BaseTransform):
|
|
561
|
+
warn(
|
|
562
|
+
f'{self.__class__.__name__} transform is saved in old non-weights safe '
|
|
563
|
+
'format and is deprecated, please resave the transform in the new format using '
|
|
564
|
+
'BaseArchitecture.save()',
|
|
565
|
+
DeprecationWarning,
|
|
566
|
+
stacklevel=2,
|
|
567
|
+
)
|
|
568
|
+
self.transforms = state['transforms']
|
|
569
|
+
else:
|
|
570
|
+
for name, transform in state['transforms']:
|
|
571
|
+
self.transforms.append(globals()[name]())
|
|
572
|
+
self.transforms[-1].__setstate__(transform)
|
|
573
|
+
|
|
574
|
+
def forward(self, x: ArrayLike) -> ArrayLike:
|
|
575
|
+
transform: BaseTransform
|
|
576
|
+
|
|
577
|
+
for transform in self.transforms:
|
|
578
|
+
x = transform(x)
|
|
579
|
+
return x
|
|
580
|
+
|
|
581
|
+
def backward(self, x: ArrayLike) -> ArrayLike:
|
|
582
|
+
transform: BaseTransform
|
|
583
|
+
|
|
584
|
+
for transform in self.transforms[::-1]:
|
|
585
|
+
x = transform(x, back=True)
|
|
586
|
+
return x
|
|
587
|
+
|
|
588
|
+
def forward_grad(self, x: ArrayLike, uncertainty: ArrayLike) -> tuple[ArrayLike, ArrayLike]:
|
|
589
|
+
transform: BaseTransform
|
|
590
|
+
|
|
591
|
+
for transform in self.transforms:
|
|
592
|
+
x, uncertainty = transform(x, uncertainty=uncertainty)
|
|
593
|
+
return x, uncertainty
|
|
594
|
+
|
|
595
|
+
def backward_grad(self, x: ArrayLike, uncertainty: ArrayLike) -> tuple[ArrayLike, ArrayLike]:
|
|
596
|
+
transform: BaseTransform
|
|
597
|
+
|
|
598
|
+
for transform in self.transforms[::-1]:
|
|
599
|
+
x, uncertainty = transform(x, back=True, uncertainty=uncertainty)
|
|
600
|
+
return x, uncertainty
|
|
601
|
+
|
|
602
|
+
def append(self, transform: BaseTransform) -> None:
|
|
603
|
+
"""
|
|
604
|
+
Appends a transform to the list of transforms
|
|
605
|
+
|
|
606
|
+
Parameters
|
|
607
|
+
----------
|
|
608
|
+
transform : BaseTransform
|
|
609
|
+
Transform to append to the list of transforms
|
|
610
|
+
"""
|
|
611
|
+
self.transforms.append(transform)
|
|
612
|
+
|
|
613
|
+
def extend(self, *args: BaseTransform) -> None:
|
|
614
|
+
"""
|
|
615
|
+
Extends the list of transforms with another list of transforms
|
|
616
|
+
|
|
617
|
+
Parameters
|
|
618
|
+
----------
|
|
619
|
+
*args : BaseTransform
|
|
620
|
+
Transformations to extend MultiTransform transforms list
|
|
621
|
+
"""
|
|
622
|
+
self.transforms.extend([*args])
|
|
623
|
+
|
|
624
|
+
def extra_repr(self) -> str:
|
|
625
|
+
transform_repr: str
|
|
626
|
+
extra_repr: str = ''
|
|
627
|
+
|
|
628
|
+
for i, transform in enumerate(self.transforms):
|
|
629
|
+
transform_repr = repr(transform).replace('\n', '\n\t')
|
|
630
|
+
extra_repr += f"\n\t({i}): {transform_repr},"
|
|
631
|
+
|
|
632
|
+
return f'{extra_repr}\n'
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class Normalise(BaseTypeTransform):
|
|
636
|
+
"""
|
|
637
|
+
Normalises the data to zero mean and unit variance, or between 0 and 1.
|
|
638
|
+
|
|
639
|
+
Attributes
|
|
640
|
+
----------
|
|
641
|
+
offset : ndarray
|
|
642
|
+
Offset to subtract from the data
|
|
643
|
+
scale : ndarray
|
|
644
|
+
Scale to divide the data by
|
|
645
|
+
"""
|
|
646
|
+
@overload
|
|
647
|
+
def __init__(self, *, offset: ndarray, scale: ndarray) -> None:
|
|
648
|
+
...
|
|
649
|
+
|
|
650
|
+
@overload
|
|
651
|
+
def __init__(
|
|
652
|
+
self,
|
|
653
|
+
*,
|
|
654
|
+
data: ArrayLike,
|
|
655
|
+
mean: bool = ...,
|
|
656
|
+
dim: int | tuple[int, ...] | None = ...) -> None:
|
|
657
|
+
...
|
|
658
|
+
|
|
659
|
+
def __init__(
|
|
660
|
+
self,
|
|
661
|
+
*,
|
|
662
|
+
mean: bool = True,
|
|
663
|
+
dim: int | tuple[int, ...] | None = None,
|
|
664
|
+
offset: ndarray | None = None,
|
|
665
|
+
scale: ndarray | None = None,
|
|
666
|
+
data: ArrayLike | None = None) -> None:
|
|
667
|
+
"""
|
|
668
|
+
Parameters
|
|
669
|
+
----------
|
|
670
|
+
mean : bool, Optional
|
|
671
|
+
If data should be normalised to zero mean and unit variance, or between 0 and 1,
|
|
672
|
+
default = True
|
|
673
|
+
dim : int | tuple[int, ...] | None, Optional
|
|
674
|
+
Dimensions to normalise over, if None, all dimensions will be normalised over
|
|
675
|
+
offset : ndarray | None, Optional
|
|
676
|
+
Offset to subtract from the data if data argument is None
|
|
677
|
+
scale : ndarray | None, Optional
|
|
678
|
+
Scale to divide the data if data argument is None
|
|
679
|
+
data : ArrayLike | None, Optional
|
|
680
|
+
Data to normalise with shape (N,...), where N is the number of elements
|
|
681
|
+
"""
|
|
682
|
+
super().__init__()
|
|
683
|
+
self.offset: ndarray
|
|
684
|
+
self.scale: ndarray
|
|
685
|
+
|
|
686
|
+
if isinstance(data, Tensor):
|
|
687
|
+
data = data.cpu().numpy()
|
|
688
|
+
elif data is None:
|
|
689
|
+
self.offset = np.array([0]) if offset is None else offset
|
|
690
|
+
self.scale = np.array([1]) if scale is None else scale
|
|
691
|
+
return
|
|
692
|
+
|
|
693
|
+
if mean:
|
|
694
|
+
self.offset = np.mean(data, axis=dim, keepdims=True)
|
|
695
|
+
self.scale = np.std(data, axis=dim, keepdims=True)
|
|
696
|
+
else:
|
|
697
|
+
self.offset = np.amin(data, axis=dim, keepdims=True)
|
|
698
|
+
self.scale = np.amax(data, axis=dim, keepdims=True) - self.offset
|
|
699
|
+
|
|
700
|
+
self.scale = np.where(self.scale == 0, 1, self.scale)
|
|
701
|
+
|
|
702
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
703
|
+
return {'offset': self.offset.tolist(), 'scale': self.scale.tolist()}
|
|
704
|
+
|
|
705
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
706
|
+
self.offset = np.array(state['offset'])
|
|
707
|
+
self.scale = np.array(state['scale'])
|
|
708
|
+
|
|
709
|
+
def forward(self, x: ArrayT) -> ArrayT:
|
|
710
|
+
if isinstance(x, Tensor):
|
|
711
|
+
return cast(ArrayT, (x - x.new_tensor(self.offset)) / x.new_tensor(self.scale))
|
|
712
|
+
return cast(ArrayT, (x - self.offset) / self.scale)
|
|
713
|
+
|
|
714
|
+
def backward(self, x: ArrayT) -> ArrayT:
|
|
715
|
+
if isinstance(x, Tensor):
|
|
716
|
+
return cast(ArrayT, x * x.new_tensor(self.scale) + x.new_tensor(self.offset))
|
|
717
|
+
return cast(ArrayT, x * self.scale + self.offset)
|
|
718
|
+
|
|
719
|
+
def forward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
720
|
+
return self(x), cast(ArrayT, uncertainty / (
|
|
721
|
+
uncertainty.new_tensor(self.scale) if isinstance(uncertainty, Tensor) else
|
|
722
|
+
self.scale
|
|
723
|
+
))
|
|
724
|
+
|
|
725
|
+
def backward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
726
|
+
return self(x, back=True), cast(ArrayT, uncertainty * (
|
|
727
|
+
uncertainty.new_tensor(self.scale) if isinstance(uncertainty, Tensor) else
|
|
728
|
+
self.scale
|
|
729
|
+
))
|
|
730
|
+
|
|
731
|
+
def extra_repr(self) -> str:
|
|
732
|
+
if 1 < self.offset.size <= 10:
|
|
733
|
+
return (f"\n\toffset: {np.vectorize(lambda x: f'{x:.2g}')(self.offset)},"
|
|
734
|
+
f"\n\tscale: {np.vectorize(lambda x: f'{x:.2g}')(self.scale)},\n")
|
|
735
|
+
|
|
736
|
+
if self.offset.size > 1:
|
|
737
|
+
return f'offset shape: {self.offset.shape}, scale shape: {self.scale.shape}'
|
|
738
|
+
|
|
739
|
+
return f'offset: {self.offset.item():.2g}, scale: {self.scale.item():.2g}'
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
class NumpyTensor(BaseTransform):
|
|
743
|
+
"""
|
|
744
|
+
Converts Numpy arrays to PyTorch tensors.
|
|
745
|
+
|
|
746
|
+
Attributes
|
|
747
|
+
----------
|
|
748
|
+
dtype : torch.dtype
|
|
749
|
+
Data type of the tensor
|
|
750
|
+
"""
|
|
751
|
+
def __init__(self, *, dtype: torch.dtype = torch.float32) -> None:
|
|
752
|
+
"""
|
|
753
|
+
Parameters
|
|
754
|
+
----------
|
|
755
|
+
dtype : dtype, Optional
|
|
756
|
+
Data type of the tensor, default = torch.float32
|
|
757
|
+
"""
|
|
758
|
+
super().__init__()
|
|
759
|
+
self.dtype: torch.dtype = dtype
|
|
760
|
+
|
|
761
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
762
|
+
return {'dtype': self.dtype}
|
|
763
|
+
|
|
764
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
765
|
+
try:
|
|
766
|
+
self.dtype = state['dtype']
|
|
767
|
+
except KeyError:
|
|
768
|
+
self.dtype = torch.float32
|
|
769
|
+
|
|
770
|
+
def forward(self, x: ArrayLike) -> Tensor:
|
|
771
|
+
if isinstance(x, ndarray):
|
|
772
|
+
return torch.from_numpy(x).type(self.dtype)
|
|
773
|
+
return x
|
|
774
|
+
|
|
775
|
+
def backward(self, x: ArrayLike) -> ndarray:
|
|
776
|
+
if isinstance(x, Tensor):
|
|
777
|
+
return x.detach().cpu().numpy()
|
|
778
|
+
return x
|
|
779
|
+
|
|
780
|
+
def forward_grad(self, x: ArrayLike, uncertainty: ArrayLike) -> tuple[Tensor, Tensor]:
|
|
781
|
+
return self(x), self(uncertainty)
|
|
782
|
+
|
|
783
|
+
def backward_grad(self, x: ArrayLike, uncertainty: ArrayLike) -> tuple[ndarray, ndarray]:
|
|
784
|
+
return self.backward(x), self.backward(uncertainty)
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
class Reshape(BaseTypeTransform):
|
|
788
|
+
"""
|
|
789
|
+
Reshapes the data.
|
|
790
|
+
"""
|
|
791
|
+
def __init__(
|
|
792
|
+
self,
|
|
793
|
+
*,
|
|
794
|
+
in_shape: list[int] | None = None,
|
|
795
|
+
out_shape: list[int] | None = None) -> None:
|
|
796
|
+
"""
|
|
797
|
+
Parameters
|
|
798
|
+
----------
|
|
799
|
+
in_shape : list[int] | None, Optional
|
|
800
|
+
Original shape of the data
|
|
801
|
+
out_shape : list[int] | None, Optional
|
|
802
|
+
Output shape of the data
|
|
803
|
+
"""
|
|
804
|
+
super().__init__()
|
|
805
|
+
self._in_shape: list[int] | None = in_shape
|
|
806
|
+
self._out_shape: list[int] | None = out_shape
|
|
807
|
+
|
|
808
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
809
|
+
return {'in_shape': self._in_shape, 'out_shape': self._out_shape}
|
|
810
|
+
|
|
811
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
812
|
+
self._in_shape = state['in_shape']
|
|
813
|
+
self._out_shape = state['out_shape']
|
|
814
|
+
|
|
815
|
+
def forward(self, x: ArrayT) -> ArrayT:
|
|
816
|
+
if self._out_shape is None:
|
|
817
|
+
return super().forward(x)
|
|
818
|
+
return getattr(x, 'view' if isinstance(x, Tensor) else 'reshape')(len(x), *self._out_shape)
|
|
819
|
+
|
|
820
|
+
def backward(self, x: ArrayT) -> ArrayT:
|
|
821
|
+
if self._in_shape is None:
|
|
822
|
+
return super().backward(x)
|
|
823
|
+
return getattr(x, 'view' if isinstance(x, Tensor) else 'reshape')(len(x), *self._in_shape)
|
|
824
|
+
|
|
825
|
+
def forward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
826
|
+
return self(x), self(uncertainty)
|
|
827
|
+
|
|
828
|
+
def backward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
|
|
829
|
+
return self(x, back=True), self(uncertainty, back=True)
|
|
830
|
+
|
|
831
|
+
def extra_repr(self) -> str:
|
|
832
|
+
return f'in_shape: {self._in_shape}, out_shape: {self._out_shape}'
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
__all__ = [
|
|
836
|
+
'BaseTransform',
|
|
837
|
+
'BaseTypeTransform',
|
|
838
|
+
'Index',
|
|
839
|
+
'Log',
|
|
840
|
+
'MinClamp',
|
|
841
|
+
'MultiTransform',
|
|
842
|
+
'Normalise',
|
|
843
|
+
'NumpyTensor',
|
|
844
|
+
'Reshape',
|
|
845
|
+
'InArrayCT',
|
|
846
|
+
'OutArrayCT',
|
|
847
|
+
]
|