Transforms#
PyTorch Network Loader provides the BaseTransform class along with some common transforms
for PyTorch safe weights and data preprocessing.
Transforms are applied to the data before it is passed to the network for training or inference.
Transforms can be saved in architectures so normalisation is consistent between forward passes & during prediction, the
predictions get untransformed.
Transforms are Numpy/PyTorch agnostic, so either can be passed through.
Custom Transforms#
NetLoader provides the BaseTransform class for data preprocessing, inverse
transformations, and uncertainty propagation.
The BaseTransform class has the following methods:
- abstractmethod BaseTransform.forward(x)[source]
Forward pass of the transformation
- abstractmethod BaseTransform.backward(x)[source]
Backwards pass to invert the transformation
- abstractmethod BaseTransform.forward_grad(x, uncertainty)[source]
Forward pass of the transformation and uncertainty propagation
- abstractmethod BaseTransform.backward_grad(x, uncertainty)[source]
Backwards pass to invert the transformation and uncertainty propagation
Along with the magic method:
- BaseTransform.__call__(x, *, back=False, uncertainty=None)[source]
Calling function returns the forward, backwards or uncertainty propagation of the transformation
- Overloads:
- __call__(self, x: InArrayCT, *, back: bool = ...) OutArrayCT
- __call__(self, x: Data[InArrayCT], *, back: bool = ...) Data[OutArrayCT]
- __call__(self, x: InArrayCT, *, back: bool = ..., uncertainty: InArrayCT) tuple[OutArrayCT, OutArrayCT]
- Parameters:
- Returns:
All transform operations can be performed via the __call__ method with the optional arguments back and
uncertainty.
BaseTransform also accepts Data objects as input to transform
the data and uncertainties.
To transform data, first initialise the transform object, then call the object with the data (and optional
uncertainties) to be transformed.
import torch
from torch import Tensor
from netloader.data import Data
from netloader import transforms
values: Tensor
values_2: Tensor
trans_values: Tensor
uncertainties: Tensor
uncertainties_2: Tensor
trans_uncertainties: Tensor
data: Data
data_2: Data
trans_data: Data
transform: transforms.BaseTransform
values = torch.tensor([1.0, 2.0, 3.0])
uncertainties = torch.tensor([0.1, 0.2, 0.3])
data = Data(values, uncertainty=uncertainties)
transform = transforms.Log()
# Transform data
trans_values, trans_uncertainties = transform(values, uncertainty=uncertainties)
trans_data = transform(data)
# Untransform data
values_2, uncertainties_2 = transform(trans_values, back=True, uncertainty=trans_uncertainties)
data_2 = transform(trans_data, back=True)
assert torch.isclose(values, values_2).all()
assert torch.isclose(uncertainties, uncertainties_2).all()
assert torch.isclose(data.data, data_2.data).all()
assert torch.isclose(data.uncertainty, data_2.uncertainty).all()
Custom Transforms from BaseTransform#
The BaseTransform class provides the base functionality for all transforms.
When creating a custom transform from BaseTransform, if the transform’s forwards or
backwards operations are different from an identity transform, then the forward and/or backward methods should
be defined.
If the transform’s forwards or backwards uncertainty propagation is different from an identity transform, then the
forward_grad and/or backward_grad methods should also be defined.
All custom transforms must also implement the __getstate__ and __setstate__ methods if extra attributes are
required.
Example implementing a logarithmic transform from BaseTransform:
from typing import Any
from types import ModuleType
import torch
import numpy as np
from netloader.utils.types import ArrayT
from netloader.transforms import BaseTransform
class Log(BaseTransform):
"""
Logarithmic transform.
"""
def __init__(self, *, base: float = 10) -> None:
"""
Parameters
----------
base : float, Optional
Base of the logarithm, default = 10
"""
super().__init__()
self._base: float = base
def __getstate__(self) -> dict[str, Any]:
return super().__getstate__() | {'base': self._base}
def __setstate__(self, state: dict[str, Any]) -> None:
super().__setstate__(state)
self._base = state['base']
def forward(self, x: ArrayT) -> ArrayT:
module: ModuleType = torch if isinstance(x, Tensor) else np
return module.log(x) / np.log(self._base)
def backward(self, x: ArrayT) -> ArrayT:
return self._base ** x
def forward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
module: ModuleType = torch if isinstance(x, Tensor) else np
return self(x), uncertainty / module.abs(x * np.log(self._base))
def backward_grad(self, x: ArrayT, uncertainty: ArrayT) -> tuple[ArrayT, ArrayT]:
module: ModuleType = torch if isinstance(x, Tensor) else np
x = self(x, back=True)
return x, uncertainty * module.abs(x * np.log(self._base))
Built-in Transforms#
All transforms currently supported by NetLoader are listed below with a brief description of their functionality and
their initialisation arguments, excluding general initialisation arguments defined in
BaseTransform.
For more information on each transform, see API documentation.
BaseTransform : Base transformation class that other types of transforms build from.
BaseTypeTransform : Base transformation class that other types of transforms build from, with the same input and
Index : Slices the input along a given dimension assuming the input meets the required shape.
dim:int=-1– Dimension to slice overin_shape:tuple[int,EllipsisType] |NoneType=None– Target shape ignoring batch size so that the slice only occurs if the input has the same shape to prevent repeated slicing, if any dimension has a shape of -1, then the size of the dimension will be ignoredslice_:slice=slice(None, None, None)– Slicing object
Log : Logarithm transform.
base:float=10– Base of the logarithmidxs:list[int] |NoneType=None– Indices to slice the last dimension to perform the log on
MinClamp : Clamps the minimum value to be the smallest positive value.
dim:int|NoneType=None– Dimension to take the minimum value overidxs:list[int] |NoneType=None– Indices to slice the last dimension to perform the min clamp on
MultiTransform : Applies multiple transformations.
*args:BaseTransform– Transformations
Normalise : Normalises the data to zero mean and unit variance, or between 0 and 1.
mean:bool=True– If data should be normalised to zero mean and unit variance, or between 0 and 1, default = Truedim:int|tuple[int,EllipsisType] |NoneType=None– Dimensions to normalise over, if None, all dimensions will be normalised overoffset:ndarray|NoneType=None– Offset to subtract from the data if data argument is Nonescale:ndarray|NoneType=None– Scale to divide the data if data argument is Nonedata:ndarray|Tensor|NoneType=None– Data to normalise with shape (N,…), where N is the number of elements
NumpyTensor : Converts Numpy arrays to PyTorch tensors.
dtype:dtype=torch.float32– Data type of the tensor
Reshape : Reshapes the data.