Source code for bayesflow.adapters.transforms.standardize
import keras.ops as ops
from bayesflow.utils.serialization import serializable, serialize
from bayesflow.types import Tensor
from .elementwise_transform import ElementwiseTransform
[docs]
@serializable("bayesflow.adapters")
class Standardize(ElementwiseTransform):
"""
Transform that when applied standardizes data using typical z-score standardization with
fixed means and std, i.e. for some unstandardized data x the standardized version z would be
>>> z = (x - mean(x)) / std(x)
Important: Ensure dynamic standardization (employed by BayesFlow estimators) has been
turned off when using this transform.
Parameters
----------
mean : int or float
Specifies the mean (location) of the transform.
std : int or float
Specifies the standard deviation (scale) of the transform.
Examples
--------
>>> adapter = bf.Adapter().standardize(include="beta", mean=5, std=10)
"""
def __init__(
self,
mean: int | float | Tensor,
std: int | float | Tensor,
):
super().__init__()
self.mean = mean
self.std = std
[docs]
def get_config(self) -> dict:
config = {
"mean": self.mean,
"std": self.std,
}
return serialize(config)
def _mean_std(self, data: Tensor) -> tuple[Tensor, Tensor]:
mean = ops.convert_to_tensor(self.mean, dtype=ops.dtype(data))
std = ops.convert_to_tensor(self.std, dtype=ops.dtype(data))
return mean, std
[docs]
def forward(self, data: Tensor, **kwargs) -> Tensor:
mean, std = self._mean_std(data)
return ops.divide(ops.subtract(data, mean), std)
[docs]
def inverse(self, data: Tensor, **kwargs) -> Tensor:
mean, std = self._mean_std(data)
return ops.add(ops.multiply(data, std), mean)
[docs]
def log_det_jac(self, data: Tensor, inverse: bool = False, **kwargs) -> Tensor:
_, std = self._mean_std(data)
# Unlike forward/inverse, the Jacobian reduction must account for
# every transformed element, so expand the broadcast here.
std = ops.broadcast_to(std, ops.shape(data))
ldj = -ops.log(ops.abs(std))
if inverse:
ldj = -ldj
return self._sum_except_batch(ldj)