From 8314f866b58f9098ebfd10530662ff2acd216fa5 Mon Sep 17 00:00:00 2001 From: epens94 Date: Thu, 24 Oct 2024 16:22:13 +0200 Subject: [PATCH 1/4] bernstein class and config add --- .../radial_basis/bernstein.yaml | 4 + src/schnetpack/nn/radial.py | 118 +++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/schnetpack/configs/model/representation/radial_basis/bernstein.yaml diff --git a/src/schnetpack/configs/model/representation/radial_basis/bernstein.yaml b/src/schnetpack/configs/model/representation/radial_basis/bernstein.yaml new file mode 100644 index 000000000..fac40c51a --- /dev/null +++ b/src/schnetpack/configs/model/representation/radial_basis/bernstein.yaml @@ -0,0 +1,4 @@ +_target_: schnetpack.nn.radial.BernsteinRBF +n_rbf: 32 +cutoff: ${globals.cutoff} +init_alpha: 0.95 diff --git a/src/schnetpack/nn/radial.py b/src/schnetpack/nn/radial.py index 8c63c2a07..41354560b 100644 --- a/src/schnetpack/nn/radial.py +++ b/src/schnetpack/nn/radial.py @@ -3,7 +3,7 @@ import torch import torch.nn as nn -__all__ = ["gaussian_rbf", "GaussianRBF", "GaussianRBFCentered", "BesselRBF"] +__all__ = ["gaussian_rbf", "GaussianRBF", "GaussianRBFCentered", "BesselRBF","BernsteinRBF","PhysNetBasisRBF"] from torch import nn as nn @@ -108,3 +108,119 @@ def forward(self, inputs): norm = torch.where(inputs == 0, torch.tensor(1.0, device=inputs.device), inputs) y = sinax / norm[..., None] return y + + +class BernsteinRBF(torch.nn.Module): + + + r"""Bernstein radial basis functions. + + According to + B_{v,n}(x) = \binom{n}{v} x^v (1 - x)^{n - v} + with + B as the Bernstein polynomial of degree v + binom{k}{n} as the binomial coefficient n! / (k! * (n - k)!) + they become in logaritmic form log(n!) - log(k!) - log((n - k)!) + n as index running from 0 to degree k + + The logarithmic form of the k-th Bernstein polynominal of degree n is + + log(B_{k}_{n}) = logBinomCoeff + k * log(x) - (n-k) * log(1-x) + k_term is here k*log(x) + n_k_term is here (n-k)*log(1-x) + x is here the radial basis expansion : exp[-alpha*d] + + logBinomCoeff is a scalar + k_term is a vector + n_k_term is also a vector + + log to avoid numerical overflow errors, and ensure stability + """ + + def __init__( + self, n_rbf: int, cutoff:float, init_alpha:float = 0.95): + """ + Args: + n_rbf: total number of Bernstein functions, :math:`N_g`. + cutoff: center of last Bernstein function, :math:`\mu_{N_g}` + """ + + super(BernsteinRBF, self).__init__() + self.n_rbf = n_rbf + + # log binomal coefficient vector + b = self.calculate_log_binomial_coefficients(n_rbf) + n_idx = torch.arange(0, n_rbf) + n_k_idx = n_rbf - 1 - n_idx + + # register buffers and parameters + self.register_buffer("cutoff",torch.tensor(cutoff)) + self.register_buffer("b", b) + self.register_buffer("n", n_idx) + self.register_buffer("n_k", n_k_idx) + self.register_buffer("init_alpha",torch.tensor(init_alpha)) + + # log of factorial (n! or k! or n-k!) + def log_factorial(self,n): + # log of factorial degree n + return torch.sum(torch.log(torch.arange(1, n + 1))) + + # calculate log binominal coefficient + def log_binomial_coefficient(self,n, k): + # n_factorial - k_factorial - n_k_factorial + return self.log_factorial(n) - (self.log_factorial(k) + self.log_factorial(n - k)) + + # vector of log binominal coefficients + def calculate_log_binomial_coefficients(self,n_rbf): + # store the log binomial coefficients + # Loop through each value from 0 to n_rbf-1 + log_binomial_coeffs = [ + self.log_binomial_coefficient(n_rbf - 1, x) for x in range(n_rbf) + ] + return torch.tensor(log_binomial_coeffs) + + def forward(self, inputs): + exp_x = -self.init_alpha * inputs[...,None] + x = torch.exp(exp_x) + k_term = self.n * torch.where(self.n != 0, torch.log(x), torch.zeros_like(x)) + n_k_term = self.n_k * torch.where(self.n_k != 0, torch.log(1 - x), torch.zeros_like(x)) + y = torch.exp(self.b + k_term + n_k_term) + return y + + +class PhysNetBasisRBF(torch.nn.Module): + + """ + Expand distances in the basis used in PhysNet (see https://arxiv.org/abs/1902.08408) + + width (beta_k) = (2K^⁻1 * (1 - exp(-cutoff)))^-2) + center (mu_k) = equally spaced between exp(-cutoff) and 1 + + """ + + def __init__(self, n_rbf: int, cutoff:float, trainable:bool): + + """ + Args: + n_rbf: total number of basis functions. + cutoff: cutoff basis functions + """ + + super(PhysNetBasisRBF, self).__init__() + self.n_rbf = n_rbf + + # compute offset and width of Gaussian functions + widths = ((2 / self.n_rbf) * (1 - torch.exp(torch.Tensor([-cutoff])))) ** (-2) + r_0 = torch.exp(torch.Tensor([-cutoff])).item() + centers = torch.linspace(r_0,1,self.n_rbf) + + if trainable: + self.widths = torch.nn.Parameter(widths) + self.centers = torch.nn.Parameter(centers) + else: + self.register_buffer("widths", widths) + self.register_buffer("centers", centers) + + + def forward(self, inputs: torch.Tensor): + return torch.exp(-abs(self.widths) * (torch.exp(-inputs[...,None]) - self.centers) ** 2) \ No newline at end of file From db98bc75ae2d9b31b13e881d39f0860ecbeb00a7 Mon Sep 17 00:00:00 2001 From: epens94 Date: Thu, 24 Oct 2024 16:29:02 +0200 Subject: [PATCH 2/4] adaptive loss function and files --- pyproject.toml | 3 + src/schnetpack/train/adaptive_loss.py | 537 ++++++++++++++++++ .../partition_spline_for_robust_loss.npz | Bin 0 -> 197188 bytes 3 files changed, 540 insertions(+) create mode 100644 src/schnetpack/train/adaptive_loss.py create mode 100644 src/schnetpack/train/ressources/partition_spline_for_robust_loss.npz diff --git a/pyproject.toml b/pyproject.toml index 40ae9b72b..1a8ddf6ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,9 @@ script-files = [ [tool.setuptools.dynamic] version = {attr = "schnetpack.__version__"} +# Ensure package data such as resources are included +package-data = { "schnetpack.train" = ["ressources/partition_spline_for_robust_loss.npz"] } + [tool.setuptools.packages.find] where = ["src"] diff --git a/src/schnetpack/train/adaptive_loss.py b/src/schnetpack/train/adaptive_loss.py new file mode 100644 index 000000000..9df627086 --- /dev/null +++ b/src/schnetpack/train/adaptive_loss.py @@ -0,0 +1,537 @@ +import os +import torch +import numpy as np +from typing import Optional + + +__all__ = ["AdaptiveLossFunction"] + +def interpolate1d(x, values, tangents): + r"""Perform cubic hermite spline interpolation on a 1D spline. + + The x coordinates of the spline knots are at [0 : 1 : len(values)-1]. + Queries outside of the range of the spline are computed using linear + extrapolation. See https://en.wikipedia.org/wiki/Cubic_Hermite_spline + for details, where "x" corresponds to `x`, "p" corresponds to `values`, and + "m" corresponds to `tangents`. + + Args: + x: A tensor of any size of single or double precision floats containing the + set of values to be used for interpolation into the spline. + values: A vector of single or double precision floats containing the value + of each knot of the spline being interpolated into. Must be the same + length as `tangents` and the same type as `x`. + tangents: A vector of single or double precision floats containing the + tangent (derivative) of each knot of the spline being interpolated into. + Must be the same length as `values` and the same type as `x`. + + Returns: + The result of interpolating along the spline defined by `values`, and + `tangents`, using `x` as the query values. Will be the same length and type + as `x`. + """ + + assert torch.is_tensor(x) + assert torch.is_tensor(values) + assert torch.is_tensor(tangents) + float_dtype = x.dtype + assert values.dtype == float_dtype + assert tangents.dtype == float_dtype + assert len(values.shape) == 1 + assert len(tangents.shape) == 1 + assert values.shape[0] == tangents.shape[0] + + x_lo = torch.floor(torch.clamp(x, torch.as_tensor(0), + values.shape[0] - 2)).type(torch.int64) + x_hi = x_lo + 1 + + # Compute the relative distance between each `x` and the knot below it. + t = x - x_lo.type(float_dtype) + + # Compute the cubic hermite expansion of `t`. + t_sq = t**2 + t_cu = t * t_sq + h01 = -2. * t_cu + 3. * t_sq + h00 = 1. - h01 + h11 = t_cu - t_sq + h10 = h11 - t_sq + t + + # Linearly extrapolate above and below the extents of the spline for all + # values. + value_before = tangents[0] * t + values[0] + value_after = tangents[-1] * (t - 1.) + values[-1] + + # Cubically interpolate between the knots below and above each query point. + neighbor_values_lo = values[x_lo] + neighbor_values_hi = values[x_hi] + neighbor_tangents_lo = tangents[x_lo] + neighbor_tangents_hi = tangents[x_hi] + value_mid = ( + neighbor_values_lo * h00 + neighbor_values_hi * h01 + + neighbor_tangents_lo * h10 + neighbor_tangents_hi * h11) + + # Return the interpolated or extrapolated values for each query point, + # depending on whether or not the query lies within the span of the spline. + return torch.where(t < 0., value_before, + torch.where(t > 1., value_after, value_mid)) + + +def log_safe(x): + """The same as torch.log(x), but clamps the input to prevent NaNs.""" + return torch.log(torch.min(x, torch.tensor(33e37).to(x))) + + +def log1p_safe(x): + """The same as torch.log1p(x), but clamps the input to prevent NaNs.""" + return torch.log1p(torch.min(x, torch.tensor(33e37).to(x))) + + +def exp_safe(x): + """The same as torch.exp(x), but clamps the input to prevent NaNs.""" + return torch.exp(torch.min(x, torch.tensor(87.5).to(x))) + + +def expm1_safe(x): + """The same as tf.math.expm1(x), but clamps the input to prevent NaNs.""" + return torch.expm1(torch.min(x, torch.tensor(87.5).to(x))) + + +def inv_softplus(y): + """The inverse of tf.nn.softplus().""" + return torch.where(y > 87.5, y, torch.log(torch.expm1(y))) + + +def logit(y): + """The inverse of tf.nn.sigmoid().""" + return -torch.log(1. / y - 1.) + + +def affine_sigmoid(logits, lo=0, hi=1): + """Maps reals to (lo, hi), where 0 maps to (lo+hi)/2.""" + if not lo < hi: + raise ValueError('`lo` (%g) must be < `hi` (%g)' % (lo, hi)) + + alpha = torch.sigmoid(logits) * (hi - lo) + lo + return alpha + + +def inv_affine_sigmoid(probs, lo=0, hi=1): + """The inverse of affine_sigmoid(., lo, hi).""" + if not lo < hi: + raise ValueError('`lo` (%g) must be < `hi` (%g)' % (lo, hi)) + + logits = logit((probs - lo) / (hi - lo)) + return logits + + +def affine_softplus(x, lo=0, ref=1): + """Maps real numbers to (lo, infinity), where 0 maps to ref.""" + if not lo < ref: + raise ValueError('`lo` (%g) must be < `ref` (%g)' % (lo, ref)) + shift = inv_softplus(torch.tensor(1.)) + y = (ref - lo) * torch.nn.Softplus()(x + shift) + lo + return y + + +def inv_affine_softplus(y, lo=0, ref=1): + """The inverse of affine_softplus(., lo, ref).""" + if not lo < ref: + raise ValueError('`lo` (%g) must be < `ref` (%g)' % (lo, ref)) + shift = inv_softplus(torch.tensor(1.)) + x = inv_softplus((y - lo) / (ref - lo)) - shift + return x + + + +def lossfun(x, alpha, scale, approximate=False, epsilon=1e-6): + r"""Implements the general form of the loss. + + This implements the rho(x, \alpha, c) function described in "A General and + Adaptive Robust Loss Function", Jonathan T. Barron, + https://arxiv.org/abs/1701.03077. + + Args: + x: The residual for which the loss is being computed. x can have any shape, + and alpha and scale will be broadcasted to match x's shape if necessary. + Must be a tensor of floats. + alpha: The shape parameter of the loss (\alpha in the paper), where more + negative values produce a loss with more robust behavior (outliers "cost" + less), and more positive values produce a loss with less robust behavior + (outliers are penalized more heavily). Alpha can be any value in + [-infinity, infinity], but the gradient of the loss with respect to alpha + is 0 at -infinity, infinity, 0, and 2. Must be a tensor of floats with the + same precision as `x`. Varying alpha allows + for smooth interpolation between a number of discrete robust losses: + alpha=-Infinity: Welsch/Leclerc Loss. + alpha=-2: Geman-McClure loss. + alpha=0: Cauchy/Lortentzian loss. + alpha=1: Charbonnier/pseudo-Huber loss. + alpha=2: L2 loss. + scale: The scale parameter of the loss. When |x| < scale, the loss is an + L2-like quadratic bowl, and when |x| > scale the loss function takes on a + different shape according to alpha. Must be a tensor of single-precision + floats. + approximate: a bool, where if True, this function returns an approximate and + faster form of the loss, as described in the appendix of the paper. This + approximation holds well everywhere except as x and alpha approach zero. + epsilon: A float that determines how inaccurate the "approximate" version of + the loss will be. Larger values are less accurate but more numerically + stable. Must be great than single-precision machine epsilon. + + Returns: + The losses for each element of x, in the same shape and precision as x. + """ + + assert alpha.dtype == x.dtype + assert scale.dtype == x.dtype + assert (scale > 0).all() + if approximate: + # `epsilon` must be greater than single-precision machine epsilon. + assert epsilon > np.finfo(np.float32).eps + # Compute an approximate form of the loss which is faster, but innacurate + # when x and alpha are near zero. + b = torch.abs(alpha - 2) + epsilon + d = torch.where(alpha >= 0, alpha + epsilon, alpha - epsilon) + loss = (b / d) * (torch.pow((x / scale)**2 / b + 1., 0.5 * d) - 1.) + else: + # Compute the exact loss. + + # This will be used repeatedly. + squared_scaled_x = (x / scale)**2 + + # The loss when alpha == 2. + loss_two = 0.5 * squared_scaled_x + # The loss when alpha == 0. + loss_zero = log1p_safe(0.5 * squared_scaled_x) + # The loss when alpha == -infinity. + loss_neginf = -torch.expm1(-0.5 * squared_scaled_x) + # The loss when alpha == +infinity. + loss_posinf = expm1_safe(0.5 * squared_scaled_x) + + # The loss when not in one of the above special cases. + machine_epsilon = torch.tensor(np.finfo(np.float32).eps).to(x) + # Clamp |2-alpha| to be >= machine epsilon so that it's safe to divide by. + beta_safe = torch.max(machine_epsilon, torch.abs(alpha - 2.)) + # Clamp |alpha| to be >= machine epsilon so that it's safe to divide by. + alpha_safe = torch.where(alpha >= 0, torch.ones_like(alpha), + -torch.ones_like(alpha)) * torch.max( + machine_epsilon, torch.abs(alpha)) + loss_otherwise = (beta_safe / alpha_safe) * ( + torch.pow(squared_scaled_x / beta_safe + 1., 0.5 * alpha) - 1.) + + # Select which of the cases of the loss to return. + loss = torch.where( + alpha == -float('inf'), loss_neginf, + torch.where( + alpha == 0, loss_zero, + torch.where( + alpha == 2, loss_two, + torch.where(alpha == float('inf'), loss_posinf, + loss_otherwise)))) + + return loss + + +def partition_spline_curve(alpha): + """Applies a curve to alpha >= 0 to compress its range before interpolation. + + This is a weird hand-crafted function designed to take in alpha values and + curve them to occupy a short finite range that works well when using spline + interpolation to model the partition function Z(alpha). Because Z(alpha) + is only varied in [0, 4] and is especially interesting around alpha=2, this + curve is roughly linear in [0, 4] with a slope of ~1 at alpha=0 and alpha=4 + but a slope of ~10 at alpha=2. When alpha > 4 the curve becomes logarithmic. + Some (input, output) pairs for this function are: + [(0, 0), (1, ~1.2), (2, 4), (3, ~6.8), (4, 8), (8, ~8.8), (400000, ~12)] + This function is continuously differentiable. + + Args: + alpha: A numpy array or tensor (float32 or float64) with values >= 0. + + Returns: + An array/tensor of curved values >= 0 with the same type as `alpha`, to be + used as input x-coordinates for spline interpolation. + """ + alpha = torch.as_tensor(alpha) + x = torch.where(alpha < 4, (2.25 * alpha - 4.5) / + (torch.abs(alpha - 2) + 0.25) + alpha + 2, + 5. / 18. * log_safe(4 * alpha - 15) + 8) + return x + + +class Distribution(): + # This is only a class so that we can pre-load the partition function spline. + + def __init__(self): + # Load the values, tangents, and x-coordinate scaling of a spline that + # approximates the partition function. This was produced by running + # the script in fit_partition_spline.py + spline_file = (os.path.join(os.path.dirname(__file__), 'ressources/partition_spline_for_robust_loss.npz')) + with np.load(spline_file, allow_pickle=False) as f: + self._spline_x_scale = torch.tensor(f['x_scale']) + self._spline_values = torch.tensor(f['values']) + self._spline_tangents = torch.tensor(f['tangents']) + + def log_base_partition_function(self, alpha): + r"""Approximate the distribution's log-partition function with a 1D spline. + + Because the partition function (Z(\alpha) in the paper) of the distribution + is difficult to model analytically, we approximate it with a (transformed) + cubic hermite spline: Each alpha is pushed through a nonlinearity before + being used to interpolate into a spline, which allows us to use a relatively + small spline to accurately model the log partition function over the range + of all non-negative input values. + + Args: + alpha: A tensor or scalar of single or double precision floats containing + the set of alphas for which we would like an approximate log partition + function. Must be non-negative, as the partition function is undefined + when alpha < 0. + + Returns: + An approximation of log(Z(alpha)) accurate to within 1e-6 + """ + alpha = torch.as_tensor(alpha) + assert (alpha >= 0).all() + # Transform `alpha` to the form expected by the spline. + x = partition_spline_curve(alpha) + # Interpolate into the spline. + return interpolate1d(x * self._spline_x_scale.to(x), + self._spline_values.to(x), + self._spline_tangents.to(x)) + + def nllfun(self, x, alpha, scale): + r"""Implements the negative log-likelihood (NLL). + + Specifically, we implement -log(p(x | 0, \alpha, c) of Equation 16 in the + paper as nllfun(x, alpha, shape). + + Args: + x: The residual for which the NLL is being computed. x can have any shape, + and alpha and scale will be broadcasted to match x's shape if necessary. + Must be a tensor or numpy array of floats. + alpha: The shape parameter of the NLL (\alpha in the paper), where more + negative values cause outliers to "cost" more and inliers to "cost" + less. Alpha can be any non-negative value, but the gradient of the NLL + with respect to alpha has singularities at 0 and 2 so you may want to + limit usage to (0, 2) during gradient descent. Must be a tensor or numpy + array of floats. Varying alpha in that range allows for smooth + interpolation between a Cauchy distribution (alpha = 0) and a Normal + distribution (alpha = 2) similar to a Student's T distribution. + scale: The scale parameter of the loss. When |x| < scale, the NLL is like + that of a (possibly unnormalized) normal distribution, and when |x| > + scale the NLL takes on a different shape according to alpha. Must be a + tensor or numpy array of floats. + + Returns: + The NLLs for each element of x, in the same shape and precision as x. + """ + # `scale` and `alpha` must have the same type as `x`. + + assert (alpha >= 0).all() + assert (scale >= 0).all() + + float_dtype = x.dtype + assert alpha.dtype == float_dtype + assert scale.dtype == float_dtype + + loss = lossfun(x, alpha, scale, approximate=False) + log_partition = torch.log(scale) + self.log_base_partition_function(alpha) + nll = loss + log_partition + return nll + + def draw_samples(self, alpha, scale): + r"""Draw samples from the robust distribution. + + This function implements Algorithm 1 the paper. This code is written to + allow + for sampling from a set of different distributions, each parametrized by its + own alpha and scale values, as opposed to the more standard approach of + drawing N samples from the same distribution. This is done by repeatedly + performing N instances of rejection sampling for each of the N distributions + until at least one proposal for each of the N distributions has been + accepted. + All samples are drawn with a zero mean, to use a non-zero mean just add each + mean to each sample. + + Args: + alpha: A tensor/scalar or numpy array/scalar of floats where each element + is the shape parameter of that element's distribution. + scale: A tensor/scalar or numpy array/scalar of floats where each element + is the scale parameter of that element's distribution. Must be the same + shape as `alpha`. + + Returns: + A tensor with the same shape and precision as `alpha` and `scale` where + each element is a sample drawn from the distribution specified for that + element by `alpha` and `scale`. + """ + + assert (alpha >= 0).all() + assert (scale >= 0).all() + float_dtype = alpha.dtype + assert scale.dtype == float_dtype + + cauchy = torch.distributions.cauchy.Cauchy(0., np.sqrt(2.)) + uniform = torch.distributions.uniform.Uniform(0, 1) + samples = torch.zeros_like(alpha) + accepted = torch.zeros(alpha.shape).type(torch.bool) + while not accepted.type(torch.uint8).all(): + # Draw N samples from a Cauchy, our proposal distribution. + cauchy_sample = torch.reshape( + cauchy.sample((np.prod(alpha.shape),)), alpha.shape) + cauchy_sample = cauchy_sample.type(alpha.dtype) + + # Compute the likelihood of each sample under its target distribution. + nll = self.nllfun(cauchy_sample, + torch.as_tensor(alpha).to(cauchy_sample), + torch.tensor(1).to(cauchy_sample)) + + # Bound the NLL. We don't use the approximate loss as it may cause + # unpredictable behavior in the context of sampling. + nll_bound = lossfun( + cauchy_sample, + torch.tensor(0., dtype=cauchy_sample.dtype), + torch.tensor(1., dtype=cauchy_sample.dtype), + approximate=False) + self.log_base_partition_function(alpha) + + # Draw N samples from a uniform distribution, and use each uniform sample + # to decide whether or not to accept each proposal sample. + uniform_sample = torch.reshape( + uniform.sample((np.prod(alpha.shape),)), alpha.shape) + uniform_sample = uniform_sample.type(alpha.dtype) + accept = uniform_sample <= torch.exp(nll_bound - nll) + + # If a sample is accepted, replace its element in `samples` with the + # proposal sample, and set its bit in `accepted` to True. + samples = torch.where(accept, cauchy_sample, samples) + accepted = accepted | accept + + # Because our distribution is a location-scale family, we sample from + # p(x | 0, \alpha, 1) and then scale each sample by `scale`. + samples *= scale + return samples + +class AdaptiveLossFunction(torch.nn.Module): + """The adaptive loss function on a matrix. + + This class behaves differently from general.lossfun() and + distribution.nllfun(), which are "stateless", allow the caller to specify the + shape and scale of the loss, and allow for arbitrary sized inputs. This + class only allows for rank-2 inputs for the residual `x`, and expects that + `x` is of the form [batch_index, dimension_index]. This class then + constructs free parameters (torch Parameters) that define the alpha and scale + parameters for each dimension of `x`, such that all alphas are in + (`alpha_lo`, `alpha_hi`) and all scales are in (`scale_lo`, Infinity). + The assumption is that `x` is, say, a matrix where x[i,j] corresponds to a + pixel at location j for image i, with the idea being that all pixels at + location j should be modeled with the same shape and scale parameters across + all images in the batch. If the user wants to fix alpha or scale to be a + constant, + this can be done by setting alpha_lo=alpha_hi or scale_lo=scale_init + respectively. + """ + + def __init__(self, + num_dims: int, + dtype: torch.dtype = torch.float32, + alpha_lo: torch.Tensor = 0.001, + alpha_hi: torch.Tensor = 1.999, + alpha_init: Optional[torch.Tensor] = None, + scale_lo: torch.Tensor = 1e-5, + scale_init: torch.Tensor = 1.0): + """Sets up the loss function. + + Args: + num_dims: The number of dimensions of the input to come. + float_dtype: The floating point precision of the inputs to come. + device: The device to run on (cpu, cuda, etc). + alpha_lo: The lowest possible value for loss's alpha parameters, must be + >= 0 and a scalar. Should probably be in (0, 2). + alpha_hi: The highest possible value for loss's alpha parameters, must be + >= alpha_lo and a scalar. Should probably be in (0, 2). + alpha_init: The value that the loss's alpha parameters will be initialized + to, must be in (`alpha_lo`, `alpha_hi`), unless `alpha_lo` == `alpha_hi` + in which case this will be ignored. Defaults to (`alpha_lo` + + `alpha_hi`) / 2 + scale_lo: The lowest possible value for the loss's scale parameters. Must + be > 0 and a scalar. This value may have more of an effect than you + think, as the loss is unbounded as scale approaches zero (say, at a + delta function). + scale_init: The initial value used for the loss's scale parameters. This + also defines the zero-point of the latent representation of scales, so + SGD may cause optimization to gravitate towards producing scales near + this value. + """ + super(AdaptiveLossFunction, self).__init__() + + self.num_dims = num_dims + self.alpha_lo = torch.as_tensor(alpha_lo) + self.alpha_hi = torch.as_tensor(alpha_hi) + self.scale_lo = torch.as_tensor(scale_lo) + self.scale_init = torch.as_tensor(scale_init) + + self.distribution = Distribution() + + if alpha_lo == alpha_hi: + # If the range of alphas is a single item, then we just fix `alpha` to be + # a constant. + self.fixed_alpha = alpha_lo.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) + # Assuming alpha_lo is already a torch.Tensor + + self.alpha = lambda: self.fixed_alpha + else: + # Otherwise we construct a "latent" alpha variable and define `alpha` + # As an affine function of a sigmoid on that latent variable, initialized + # such that `alpha` starts off as `alpha_init`. + if alpha_init is None: + alpha_init = torch.as_tensor((alpha_lo + alpha_hi) / 2.) + latent_alpha_init = inv_affine_sigmoid(alpha_init, lo=alpha_lo, hi=alpha_hi) + + latent_alpha_init_1 = latent_alpha_init.clone().unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) + self.register_parameter('latent_alpha', torch.nn.Parameter(latent_alpha_init_1,requires_grad=True)) + + + self.alpha = lambda: affine_sigmoid(self.latent_alpha, lo=alpha_lo, hi=alpha_hi) + + if scale_lo == scale_init: + # If the difference between the minimum and initial scale is zero, then + # we just fix `scale` to be a constant. + self.fixed_scale = scale_init.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) + self.scale = lambda: self.fixed_scale + else: + # Otherwise we construct a "latent" scale variable and define `scale` + # As an affine function of a softplus on that latent variable. + + self.register_parameter('latent_scale',torch.nn.Parameter(torch.zeros((1, self.num_dims)),requires_grad=True)) + self.scale = lambda: affine_softplus(self.latent_scale, lo=scale_lo, ref=scale_init) + + + def lossfun(self, x, **kwargs): + """Computes the loss on a matrix. + + Args: + x: The residual for which the loss is being computed. Must be a rank-2 + tensor, where the innermost dimension is the batch index, and the + outermost dimension must be equal to self.num_dims. Must be a tensor or + numpy array of type self.float_dtype. + **kwargs: Arguments to be passed to the underlying distribution.nllfun(). + + Returns: + A tensor of the same type and shape as input `x`, containing the loss at + each element of `x`. These "losses" are actually negative log-likelihoods + (as produced by distribution.nllfun()) and so they are not actually + bounded from below by zero. You'll probably want to minimize their sum or + mean. + """ + + assert len(x.shape) == 2 + assert x.shape[1] == self.num_dims + return self.distribution.nllfun(x, self.alpha(), self.scale(), **kwargs) + + def forward(self,input,pred): + if pred.ndim == 1: + res = (input-pred)[:,None] + else: + res = input - pred + return torch.mean(self.lossfun(res)) \ No newline at end of file diff --git a/src/schnetpack/train/ressources/partition_spline_for_robust_loss.npz b/src/schnetpack/train/ressources/partition_spline_for_robust_loss.npz new file mode 100644 index 0000000000000000000000000000000000000000..e2813d0d8397d603eeed56c3b75e045ad5bd226b GIT binary patch literal 197188 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%0ATBWS_Mr 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�avT{bo9>BQ$zVit|cH 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 literal 0 HcmV?d00001 From b2c836709f6ec23b5cdc1e75f8caf4bd4fa562bf Mon Sep 17 00:00:00 2001 From: epens94 Date: Thu, 24 Oct 2024 21:05:22 +0200 Subject: [PATCH 3/4] fix formsatting --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1a8ddf6ff..a4ac8dbbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,10 +61,9 @@ script-files = [ version = {attr = "schnetpack.__version__"} # Ensure package data such as resources are included -package-data = { "schnetpack.train" = ["ressources/partition_spline_for_robust_loss.npz"] } [tool.setuptools.packages.find] where = ["src"] [tool.setuptools.package-data] -schnetpack = ["configs/**/*.yaml"] +schnetpack = ["configs/**/*.yaml","train/resources/*.npz"] From a5268e1a8bdd62eab68bf84ad06a9aea85a876f3 Mon Sep 17 00:00:00 2001 From: epens94 Date: Thu, 24 Oct 2024 21:17:03 +0200 Subject: [PATCH 4/4] fix formatting via black --- src/schnetpack/nn/radial.py | 61 +- src/schnetpack/train/adaptive_loss.py | 988 ++++++++++++++------------ 2 files changed, 549 insertions(+), 500 deletions(-) diff --git a/src/schnetpack/nn/radial.py b/src/schnetpack/nn/radial.py index 41354560b..d8312e804 100644 --- a/src/schnetpack/nn/radial.py +++ b/src/schnetpack/nn/radial.py @@ -3,7 +3,14 @@ import torch import torch.nn as nn -__all__ = ["gaussian_rbf", "GaussianRBF", "GaussianRBFCentered", "BesselRBF","BernsteinRBF","PhysNetBasisRBF"] +__all__ = [ + "gaussian_rbf", + "GaussianRBF", + "GaussianRBFCentered", + "BesselRBF", + "BernsteinRBF", + "PhysNetBasisRBF", +] from torch import nn as nn @@ -111,18 +118,16 @@ def forward(self, inputs): class BernsteinRBF(torch.nn.Module): - - r"""Bernstein radial basis functions. - - According to + + According to B_{v,n}(x) = \binom{n}{v} x^v (1 - x)^{n - v} - with + with B as the Bernstein polynomial of degree v binom{k}{n} as the binomial coefficient n! / (k! * (n - k)!) they become in logaritmic form log(n!) - log(k!) - log((n - k)!) n as index running from 0 to degree k - + The logarithmic form of the k-th Bernstein polynominal of degree n is log(B_{k}_{n}) = logBinomCoeff + k * log(x) - (n-k) * log(1-x) @@ -133,12 +138,11 @@ class BernsteinRBF(torch.nn.Module): logBinomCoeff is a scalar k_term is a vector n_k_term is also a vector - + log to avoid numerical overflow errors, and ensure stability """ - def __init__( - self, n_rbf: int, cutoff:float, init_alpha:float = 0.95): + def __init__(self, n_rbf: int, cutoff: float, init_alpha: float = 0.95): """ Args: n_rbf: total number of Bernstein functions, :math:`N_g`. @@ -154,52 +158,54 @@ def __init__( n_k_idx = n_rbf - 1 - n_idx # register buffers and parameters - self.register_buffer("cutoff",torch.tensor(cutoff)) + self.register_buffer("cutoff", torch.tensor(cutoff)) self.register_buffer("b", b) self.register_buffer("n", n_idx) self.register_buffer("n_k", n_k_idx) - self.register_buffer("init_alpha",torch.tensor(init_alpha)) + self.register_buffer("init_alpha", torch.tensor(init_alpha)) # log of factorial (n! or k! or n-k!) - def log_factorial(self,n): + def log_factorial(self, n): # log of factorial degree n return torch.sum(torch.log(torch.arange(1, n + 1))) # calculate log binominal coefficient - def log_binomial_coefficient(self,n, k): + def log_binomial_coefficient(self, n, k): # n_factorial - k_factorial - n_k_factorial - return self.log_factorial(n) - (self.log_factorial(k) + self.log_factorial(n - k)) + return self.log_factorial(n) - ( + self.log_factorial(k) + self.log_factorial(n - k) + ) # vector of log binominal coefficients - def calculate_log_binomial_coefficients(self,n_rbf): + def calculate_log_binomial_coefficients(self, n_rbf): # store the log binomial coefficients # Loop through each value from 0 to n_rbf-1 log_binomial_coeffs = [ self.log_binomial_coefficient(n_rbf - 1, x) for x in range(n_rbf) ] - return torch.tensor(log_binomial_coeffs) + return torch.tensor(log_binomial_coeffs) def forward(self, inputs): - exp_x = -self.init_alpha * inputs[...,None] + exp_x = -self.init_alpha * inputs[..., None] x = torch.exp(exp_x) k_term = self.n * torch.where(self.n != 0, torch.log(x), torch.zeros_like(x)) - n_k_term = self.n_k * torch.where(self.n_k != 0, torch.log(1 - x), torch.zeros_like(x)) + n_k_term = self.n_k * torch.where( + self.n_k != 0, torch.log(1 - x), torch.zeros_like(x) + ) y = torch.exp(self.b + k_term + n_k_term) return y - -class PhysNetBasisRBF(torch.nn.Module): +class PhysNetBasisRBF(torch.nn.Module): """ Expand distances in the basis used in PhysNet (see https://arxiv.org/abs/1902.08408) width (beta_k) = (2K^⁻1 * (1 - exp(-cutoff)))^-2) center (mu_k) = equally spaced between exp(-cutoff) and 1 - - """ - def __init__(self, n_rbf: int, cutoff:float, trainable:bool): + """ + def __init__(self, n_rbf: int, cutoff: float, trainable: bool): """ Args: n_rbf: total number of basis functions. @@ -212,7 +218,7 @@ def __init__(self, n_rbf: int, cutoff:float, trainable:bool): # compute offset and width of Gaussian functions widths = ((2 / self.n_rbf) * (1 - torch.exp(torch.Tensor([-cutoff])))) ** (-2) r_0 = torch.exp(torch.Tensor([-cutoff])).item() - centers = torch.linspace(r_0,1,self.n_rbf) + centers = torch.linspace(r_0, 1, self.n_rbf) if trainable: self.widths = torch.nn.Parameter(widths) @@ -221,6 +227,7 @@ def __init__(self, n_rbf: int, cutoff:float, trainable:bool): self.register_buffer("widths", widths) self.register_buffer("centers", centers) - def forward(self, inputs: torch.Tensor): - return torch.exp(-abs(self.widths) * (torch.exp(-inputs[...,None]) - self.centers) ** 2) \ No newline at end of file + return torch.exp( + -abs(self.widths) * (torch.exp(-inputs[..., None]) - self.centers) ** 2 + ) diff --git a/src/schnetpack/train/adaptive_loss.py b/src/schnetpack/train/adaptive_loss.py index 9df627086..10adc5e23 100644 --- a/src/schnetpack/train/adaptive_loss.py +++ b/src/schnetpack/train/adaptive_loss.py @@ -6,532 +6,574 @@ __all__ = ["AdaptiveLossFunction"] + def interpolate1d(x, values, tangents): - r"""Perform cubic hermite spline interpolation on a 1D spline. - - The x coordinates of the spline knots are at [0 : 1 : len(values)-1]. - Queries outside of the range of the spline are computed using linear - extrapolation. See https://en.wikipedia.org/wiki/Cubic_Hermite_spline - for details, where "x" corresponds to `x`, "p" corresponds to `values`, and - "m" corresponds to `tangents`. - - Args: - x: A tensor of any size of single or double precision floats containing the - set of values to be used for interpolation into the spline. - values: A vector of single or double precision floats containing the value - of each knot of the spline being interpolated into. Must be the same - length as `tangents` and the same type as `x`. - tangents: A vector of single or double precision floats containing the - tangent (derivative) of each knot of the spline being interpolated into. - Must be the same length as `values` and the same type as `x`. - - Returns: - The result of interpolating along the spline defined by `values`, and - `tangents`, using `x` as the query values. Will be the same length and type - as `x`. - """ - - assert torch.is_tensor(x) - assert torch.is_tensor(values) - assert torch.is_tensor(tangents) - float_dtype = x.dtype - assert values.dtype == float_dtype - assert tangents.dtype == float_dtype - assert len(values.shape) == 1 - assert len(tangents.shape) == 1 - assert values.shape[0] == tangents.shape[0] - - x_lo = torch.floor(torch.clamp(x, torch.as_tensor(0), - values.shape[0] - 2)).type(torch.int64) - x_hi = x_lo + 1 - - # Compute the relative distance between each `x` and the knot below it. - t = x - x_lo.type(float_dtype) - - # Compute the cubic hermite expansion of `t`. - t_sq = t**2 - t_cu = t * t_sq - h01 = -2. * t_cu + 3. * t_sq - h00 = 1. - h01 - h11 = t_cu - t_sq - h10 = h11 - t_sq + t - - # Linearly extrapolate above and below the extents of the spline for all - # values. - value_before = tangents[0] * t + values[0] - value_after = tangents[-1] * (t - 1.) + values[-1] - - # Cubically interpolate between the knots below and above each query point. - neighbor_values_lo = values[x_lo] - neighbor_values_hi = values[x_hi] - neighbor_tangents_lo = tangents[x_lo] - neighbor_tangents_hi = tangents[x_hi] - value_mid = ( - neighbor_values_lo * h00 + neighbor_values_hi * h01 + - neighbor_tangents_lo * h10 + neighbor_tangents_hi * h11) - - # Return the interpolated or extrapolated values for each query point, - # depending on whether or not the query lies within the span of the spline. - return torch.where(t < 0., value_before, - torch.where(t > 1., value_after, value_mid)) + r"""Perform cubic hermite spline interpolation on a 1D spline. + + The x coordinates of the spline knots are at [0 : 1 : len(values)-1]. + Queries outside of the range of the spline are computed using linear + extrapolation. See https://en.wikipedia.org/wiki/Cubic_Hermite_spline + for details, where "x" corresponds to `x`, "p" corresponds to `values`, and + "m" corresponds to `tangents`. + + Args: + x: A tensor of any size of single or double precision floats containing the + set of values to be used for interpolation into the spline. + values: A vector of single or double precision floats containing the value + of each knot of the spline being interpolated into. Must be the same + length as `tangents` and the same type as `x`. + tangents: A vector of single or double precision floats containing the + tangent (derivative) of each knot of the spline being interpolated into. + Must be the same length as `values` and the same type as `x`. + + Returns: + The result of interpolating along the spline defined by `values`, and + `tangents`, using `x` as the query values. Will be the same length and type + as `x`. + """ + + assert torch.is_tensor(x) + assert torch.is_tensor(values) + assert torch.is_tensor(tangents) + float_dtype = x.dtype + assert values.dtype == float_dtype + assert tangents.dtype == float_dtype + assert len(values.shape) == 1 + assert len(tangents.shape) == 1 + assert values.shape[0] == tangents.shape[0] + + x_lo = torch.floor(torch.clamp(x, torch.as_tensor(0), values.shape[0] - 2)).type( + torch.int64 + ) + x_hi = x_lo + 1 + + # Compute the relative distance between each `x` and the knot below it. + t = x - x_lo.type(float_dtype) + + # Compute the cubic hermite expansion of `t`. + t_sq = t**2 + t_cu = t * t_sq + h01 = -2.0 * t_cu + 3.0 * t_sq + h00 = 1.0 - h01 + h11 = t_cu - t_sq + h10 = h11 - t_sq + t + + # Linearly extrapolate above and below the extents of the spline for all + # values. + value_before = tangents[0] * t + values[0] + value_after = tangents[-1] * (t - 1.0) + values[-1] + + # Cubically interpolate between the knots below and above each query point. + neighbor_values_lo = values[x_lo] + neighbor_values_hi = values[x_hi] + neighbor_tangents_lo = tangents[x_lo] + neighbor_tangents_hi = tangents[x_hi] + value_mid = ( + neighbor_values_lo * h00 + + neighbor_values_hi * h01 + + neighbor_tangents_lo * h10 + + neighbor_tangents_hi * h11 + ) + + # Return the interpolated or extrapolated values for each query point, + # depending on whether or not the query lies within the span of the spline. + return torch.where( + t < 0.0, value_before, torch.where(t > 1.0, value_after, value_mid) + ) def log_safe(x): - """The same as torch.log(x), but clamps the input to prevent NaNs.""" - return torch.log(torch.min(x, torch.tensor(33e37).to(x))) + """The same as torch.log(x), but clamps the input to prevent NaNs.""" + return torch.log(torch.min(x, torch.tensor(33e37).to(x))) def log1p_safe(x): - """The same as torch.log1p(x), but clamps the input to prevent NaNs.""" - return torch.log1p(torch.min(x, torch.tensor(33e37).to(x))) + """The same as torch.log1p(x), but clamps the input to prevent NaNs.""" + return torch.log1p(torch.min(x, torch.tensor(33e37).to(x))) def exp_safe(x): - """The same as torch.exp(x), but clamps the input to prevent NaNs.""" - return torch.exp(torch.min(x, torch.tensor(87.5).to(x))) + """The same as torch.exp(x), but clamps the input to prevent NaNs.""" + return torch.exp(torch.min(x, torch.tensor(87.5).to(x))) def expm1_safe(x): - """The same as tf.math.expm1(x), but clamps the input to prevent NaNs.""" - return torch.expm1(torch.min(x, torch.tensor(87.5).to(x))) + """The same as tf.math.expm1(x), but clamps the input to prevent NaNs.""" + return torch.expm1(torch.min(x, torch.tensor(87.5).to(x))) def inv_softplus(y): - """The inverse of tf.nn.softplus().""" - return torch.where(y > 87.5, y, torch.log(torch.expm1(y))) + """The inverse of tf.nn.softplus().""" + return torch.where(y > 87.5, y, torch.log(torch.expm1(y))) def logit(y): - """The inverse of tf.nn.sigmoid().""" - return -torch.log(1. / y - 1.) + """The inverse of tf.nn.sigmoid().""" + return -torch.log(1.0 / y - 1.0) def affine_sigmoid(logits, lo=0, hi=1): - """Maps reals to (lo, hi), where 0 maps to (lo+hi)/2.""" - if not lo < hi: - raise ValueError('`lo` (%g) must be < `hi` (%g)' % (lo, hi)) + """Maps reals to (lo, hi), where 0 maps to (lo+hi)/2.""" + if not lo < hi: + raise ValueError("`lo` (%g) must be < `hi` (%g)" % (lo, hi)) - alpha = torch.sigmoid(logits) * (hi - lo) + lo - return alpha + alpha = torch.sigmoid(logits) * (hi - lo) + lo + return alpha def inv_affine_sigmoid(probs, lo=0, hi=1): - """The inverse of affine_sigmoid(., lo, hi).""" - if not lo < hi: - raise ValueError('`lo` (%g) must be < `hi` (%g)' % (lo, hi)) + """The inverse of affine_sigmoid(., lo, hi).""" + if not lo < hi: + raise ValueError("`lo` (%g) must be < `hi` (%g)" % (lo, hi)) - logits = logit((probs - lo) / (hi - lo)) - return logits + logits = logit((probs - lo) / (hi - lo)) + return logits def affine_softplus(x, lo=0, ref=1): - """Maps real numbers to (lo, infinity), where 0 maps to ref.""" - if not lo < ref: - raise ValueError('`lo` (%g) must be < `ref` (%g)' % (lo, ref)) - shift = inv_softplus(torch.tensor(1.)) - y = (ref - lo) * torch.nn.Softplus()(x + shift) + lo - return y + """Maps real numbers to (lo, infinity), where 0 maps to ref.""" + if not lo < ref: + raise ValueError("`lo` (%g) must be < `ref` (%g)" % (lo, ref)) + shift = inv_softplus(torch.tensor(1.0)) + y = (ref - lo) * torch.nn.Softplus()(x + shift) + lo + return y def inv_affine_softplus(y, lo=0, ref=1): - """The inverse of affine_softplus(., lo, ref).""" - if not lo < ref: - raise ValueError('`lo` (%g) must be < `ref` (%g)' % (lo, ref)) - shift = inv_softplus(torch.tensor(1.)) - x = inv_softplus((y - lo) / (ref - lo)) - shift - return x - + """The inverse of affine_softplus(., lo, ref).""" + if not lo < ref: + raise ValueError("`lo` (%g) must be < `ref` (%g)" % (lo, ref)) + shift = inv_softplus(torch.tensor(1.0)) + x = inv_softplus((y - lo) / (ref - lo)) - shift + return x def lossfun(x, alpha, scale, approximate=False, epsilon=1e-6): - r"""Implements the general form of the loss. - - This implements the rho(x, \alpha, c) function described in "A General and - Adaptive Robust Loss Function", Jonathan T. Barron, - https://arxiv.org/abs/1701.03077. - - Args: - x: The residual for which the loss is being computed. x can have any shape, - and alpha and scale will be broadcasted to match x's shape if necessary. - Must be a tensor of floats. - alpha: The shape parameter of the loss (\alpha in the paper), where more - negative values produce a loss with more robust behavior (outliers "cost" - less), and more positive values produce a loss with less robust behavior - (outliers are penalized more heavily). Alpha can be any value in - [-infinity, infinity], but the gradient of the loss with respect to alpha - is 0 at -infinity, infinity, 0, and 2. Must be a tensor of floats with the - same precision as `x`. Varying alpha allows - for smooth interpolation between a number of discrete robust losses: - alpha=-Infinity: Welsch/Leclerc Loss. - alpha=-2: Geman-McClure loss. - alpha=0: Cauchy/Lortentzian loss. - alpha=1: Charbonnier/pseudo-Huber loss. - alpha=2: L2 loss. - scale: The scale parameter of the loss. When |x| < scale, the loss is an - L2-like quadratic bowl, and when |x| > scale the loss function takes on a - different shape according to alpha. Must be a tensor of single-precision - floats. - approximate: a bool, where if True, this function returns an approximate and - faster form of the loss, as described in the appendix of the paper. This - approximation holds well everywhere except as x and alpha approach zero. - epsilon: A float that determines how inaccurate the "approximate" version of - the loss will be. Larger values are less accurate but more numerically - stable. Must be great than single-precision machine epsilon. - - Returns: - The losses for each element of x, in the same shape and precision as x. - """ - - assert alpha.dtype == x.dtype - assert scale.dtype == x.dtype - assert (scale > 0).all() - if approximate: - # `epsilon` must be greater than single-precision machine epsilon. - assert epsilon > np.finfo(np.float32).eps - # Compute an approximate form of the loss which is faster, but innacurate - # when x and alpha are near zero. - b = torch.abs(alpha - 2) + epsilon - d = torch.where(alpha >= 0, alpha + epsilon, alpha - epsilon) - loss = (b / d) * (torch.pow((x / scale)**2 / b + 1., 0.5 * d) - 1.) - else: - # Compute the exact loss. - - # This will be used repeatedly. - squared_scaled_x = (x / scale)**2 - - # The loss when alpha == 2. - loss_two = 0.5 * squared_scaled_x - # The loss when alpha == 0. - loss_zero = log1p_safe(0.5 * squared_scaled_x) - # The loss when alpha == -infinity. - loss_neginf = -torch.expm1(-0.5 * squared_scaled_x) - # The loss when alpha == +infinity. - loss_posinf = expm1_safe(0.5 * squared_scaled_x) - - # The loss when not in one of the above special cases. - machine_epsilon = torch.tensor(np.finfo(np.float32).eps).to(x) - # Clamp |2-alpha| to be >= machine epsilon so that it's safe to divide by. - beta_safe = torch.max(machine_epsilon, torch.abs(alpha - 2.)) - # Clamp |alpha| to be >= machine epsilon so that it's safe to divide by. - alpha_safe = torch.where(alpha >= 0, torch.ones_like(alpha), - -torch.ones_like(alpha)) * torch.max( - machine_epsilon, torch.abs(alpha)) - loss_otherwise = (beta_safe / alpha_safe) * ( - torch.pow(squared_scaled_x / beta_safe + 1., 0.5 * alpha) - 1.) - - # Select which of the cases of the loss to return. - loss = torch.where( - alpha == -float('inf'), loss_neginf, - torch.where( - alpha == 0, loss_zero, - torch.where( - alpha == 2, loss_two, - torch.where(alpha == float('inf'), loss_posinf, - loss_otherwise)))) + r"""Implements the general form of the loss. - return loss - - -def partition_spline_curve(alpha): - """Applies a curve to alpha >= 0 to compress its range before interpolation. - - This is a weird hand-crafted function designed to take in alpha values and - curve them to occupy a short finite range that works well when using spline - interpolation to model the partition function Z(alpha). Because Z(alpha) - is only varied in [0, 4] and is especially interesting around alpha=2, this - curve is roughly linear in [0, 4] with a slope of ~1 at alpha=0 and alpha=4 - but a slope of ~10 at alpha=2. When alpha > 4 the curve becomes logarithmic. - Some (input, output) pairs for this function are: - [(0, 0), (1, ~1.2), (2, 4), (3, ~6.8), (4, 8), (8, ~8.8), (400000, ~12)] - This function is continuously differentiable. - - Args: - alpha: A numpy array or tensor (float32 or float64) with values >= 0. - - Returns: - An array/tensor of curved values >= 0 with the same type as `alpha`, to be - used as input x-coordinates for spline interpolation. - """ - alpha = torch.as_tensor(alpha) - x = torch.where(alpha < 4, (2.25 * alpha - 4.5) / - (torch.abs(alpha - 2) + 0.25) + alpha + 2, - 5. / 18. * log_safe(4 * alpha - 15) + 8) - return x - - -class Distribution(): - # This is only a class so that we can pre-load the partition function spline. - - def __init__(self): - # Load the values, tangents, and x-coordinate scaling of a spline that - # approximates the partition function. This was produced by running - # the script in fit_partition_spline.py - spline_file = (os.path.join(os.path.dirname(__file__), 'ressources/partition_spline_for_robust_loss.npz')) - with np.load(spline_file, allow_pickle=False) as f: - self._spline_x_scale = torch.tensor(f['x_scale']) - self._spline_values = torch.tensor(f['values']) - self._spline_tangents = torch.tensor(f['tangents']) - - def log_base_partition_function(self, alpha): - r"""Approximate the distribution's log-partition function with a 1D spline. - - Because the partition function (Z(\alpha) in the paper) of the distribution - is difficult to model analytically, we approximate it with a (transformed) - cubic hermite spline: Each alpha is pushed through a nonlinearity before - being used to interpolate into a spline, which allows us to use a relatively - small spline to accurately model the log partition function over the range - of all non-negative input values. + This implements the rho(x, \alpha, c) function described in "A General and + Adaptive Robust Loss Function", Jonathan T. Barron, + https://arxiv.org/abs/1701.03077. Args: - alpha: A tensor or scalar of single or double precision floats containing - the set of alphas for which we would like an approximate log partition - function. Must be non-negative, as the partition function is undefined - when alpha < 0. + x: The residual for which the loss is being computed. x can have any shape, + and alpha and scale will be broadcasted to match x's shape if necessary. + Must be a tensor of floats. + alpha: The shape parameter of the loss (\alpha in the paper), where more + negative values produce a loss with more robust behavior (outliers "cost" + less), and more positive values produce a loss with less robust behavior + (outliers are penalized more heavily). Alpha can be any value in + [-infinity, infinity], but the gradient of the loss with respect to alpha + is 0 at -infinity, infinity, 0, and 2. Must be a tensor of floats with the + same precision as `x`. Varying alpha allows + for smooth interpolation between a number of discrete robust losses: + alpha=-Infinity: Welsch/Leclerc Loss. + alpha=-2: Geman-McClure loss. + alpha=0: Cauchy/Lortentzian loss. + alpha=1: Charbonnier/pseudo-Huber loss. + alpha=2: L2 loss. + scale: The scale parameter of the loss. When |x| < scale, the loss is an + L2-like quadratic bowl, and when |x| > scale the loss function takes on a + different shape according to alpha. Must be a tensor of single-precision + floats. + approximate: a bool, where if True, this function returns an approximate and + faster form of the loss, as described in the appendix of the paper. This + approximation holds well everywhere except as x and alpha approach zero. + epsilon: A float that determines how inaccurate the "approximate" version of + the loss will be. Larger values are less accurate but more numerically + stable. Must be great than single-precision machine epsilon. Returns: - An approximation of log(Z(alpha)) accurate to within 1e-6 + The losses for each element of x, in the same shape and precision as x. """ - alpha = torch.as_tensor(alpha) - assert (alpha >= 0).all() - # Transform `alpha` to the form expected by the spline. - x = partition_spline_curve(alpha) - # Interpolate into the spline. - return interpolate1d(x * self._spline_x_scale.to(x), - self._spline_values.to(x), - self._spline_tangents.to(x)) - - def nllfun(self, x, alpha, scale): - r"""Implements the negative log-likelihood (NLL). - Specifically, we implement -log(p(x | 0, \alpha, c) of Equation 16 in the - paper as nllfun(x, alpha, shape). - - Args: - x: The residual for which the NLL is being computed. x can have any shape, - and alpha and scale will be broadcasted to match x's shape if necessary. - Must be a tensor or numpy array of floats. - alpha: The shape parameter of the NLL (\alpha in the paper), where more - negative values cause outliers to "cost" more and inliers to "cost" - less. Alpha can be any non-negative value, but the gradient of the NLL - with respect to alpha has singularities at 0 and 2 so you may want to - limit usage to (0, 2) during gradient descent. Must be a tensor or numpy - array of floats. Varying alpha in that range allows for smooth - interpolation between a Cauchy distribution (alpha = 0) and a Normal - distribution (alpha = 2) similar to a Student's T distribution. - scale: The scale parameter of the loss. When |x| < scale, the NLL is like - that of a (possibly unnormalized) normal distribution, and when |x| > - scale the NLL takes on a different shape according to alpha. Must be a - tensor or numpy array of floats. + assert alpha.dtype == x.dtype + assert scale.dtype == x.dtype + assert (scale > 0).all() + if approximate: + # `epsilon` must be greater than single-precision machine epsilon. + assert epsilon > np.finfo(np.float32).eps + # Compute an approximate form of the loss which is faster, but innacurate + # when x and alpha are near zero. + b = torch.abs(alpha - 2) + epsilon + d = torch.where(alpha >= 0, alpha + epsilon, alpha - epsilon) + loss = (b / d) * (torch.pow((x / scale) ** 2 / b + 1.0, 0.5 * d) - 1.0) + else: + # Compute the exact loss. + + # This will be used repeatedly. + squared_scaled_x = (x / scale) ** 2 + + # The loss when alpha == 2. + loss_two = 0.5 * squared_scaled_x + # The loss when alpha == 0. + loss_zero = log1p_safe(0.5 * squared_scaled_x) + # The loss when alpha == -infinity. + loss_neginf = -torch.expm1(-0.5 * squared_scaled_x) + # The loss when alpha == +infinity. + loss_posinf = expm1_safe(0.5 * squared_scaled_x) + + # The loss when not in one of the above special cases. + machine_epsilon = torch.tensor(np.finfo(np.float32).eps).to(x) + # Clamp |2-alpha| to be >= machine epsilon so that it's safe to divide by. + beta_safe = torch.max(machine_epsilon, torch.abs(alpha - 2.0)) + # Clamp |alpha| to be >= machine epsilon so that it's safe to divide by. + alpha_safe = torch.where( + alpha >= 0, torch.ones_like(alpha), -torch.ones_like(alpha) + ) * torch.max(machine_epsilon, torch.abs(alpha)) + loss_otherwise = (beta_safe / alpha_safe) * ( + torch.pow(squared_scaled_x / beta_safe + 1.0, 0.5 * alpha) - 1.0 + ) + + # Select which of the cases of the loss to return. + loss = torch.where( + alpha == -float("inf"), + loss_neginf, + torch.where( + alpha == 0, + loss_zero, + torch.where( + alpha == 2, + loss_two, + torch.where(alpha == float("inf"), loss_posinf, loss_otherwise), + ), + ), + ) - Returns: - The NLLs for each element of x, in the same shape and precision as x. - """ - # `scale` and `alpha` must have the same type as `x`. + return loss - assert (alpha >= 0).all() - assert (scale >= 0).all() - float_dtype = x.dtype - assert alpha.dtype == float_dtype - assert scale.dtype == float_dtype - - loss = lossfun(x, alpha, scale, approximate=False) - log_partition = torch.log(scale) + self.log_base_partition_function(alpha) - nll = loss + log_partition - return nll - - def draw_samples(self, alpha, scale): - r"""Draw samples from the robust distribution. - - This function implements Algorithm 1 the paper. This code is written to - allow - for sampling from a set of different distributions, each parametrized by its - own alpha and scale values, as opposed to the more standard approach of - drawing N samples from the same distribution. This is done by repeatedly - performing N instances of rejection sampling for each of the N distributions - until at least one proposal for each of the N distributions has been - accepted. - All samples are drawn with a zero mean, to use a non-zero mean just add each - mean to each sample. +def partition_spline_curve(alpha): + """Applies a curve to alpha >= 0 to compress its range before interpolation. + + This is a weird hand-crafted function designed to take in alpha values and + curve them to occupy a short finite range that works well when using spline + interpolation to model the partition function Z(alpha). Because Z(alpha) + is only varied in [0, 4] and is especially interesting around alpha=2, this + curve is roughly linear in [0, 4] with a slope of ~1 at alpha=0 and alpha=4 + but a slope of ~10 at alpha=2. When alpha > 4 the curve becomes logarithmic. + Some (input, output) pairs for this function are: + [(0, 0), (1, ~1.2), (2, 4), (3, ~6.8), (4, 8), (8, ~8.8), (400000, ~12)] + This function is continuously differentiable. Args: - alpha: A tensor/scalar or numpy array/scalar of floats where each element - is the shape parameter of that element's distribution. - scale: A tensor/scalar or numpy array/scalar of floats where each element - is the scale parameter of that element's distribution. Must be the same - shape as `alpha`. + alpha: A numpy array or tensor (float32 or float64) with values >= 0. Returns: - A tensor with the same shape and precision as `alpha` and `scale` where - each element is a sample drawn from the distribution specified for that - element by `alpha` and `scale`. + An array/tensor of curved values >= 0 with the same type as `alpha`, to be + used as input x-coordinates for spline interpolation. """ + alpha = torch.as_tensor(alpha) + x = torch.where( + alpha < 4, + (2.25 * alpha - 4.5) / (torch.abs(alpha - 2) + 0.25) + alpha + 2, + 5.0 / 18.0 * log_safe(4 * alpha - 15) + 8, + ) + return x + + +class Distribution: + # This is only a class so that we can pre-load the partition function spline. + + def __init__(self): + # Load the values, tangents, and x-coordinate scaling of a spline that + # approximates the partition function. This was produced by running + # the script in fit_partition_spline.py + spline_file = os.path.join( + os.path.dirname(__file__), "ressources/partition_spline_for_robust_loss.npz" + ) + with np.load(spline_file, allow_pickle=False) as f: + self._spline_x_scale = torch.tensor(f["x_scale"]) + self._spline_values = torch.tensor(f["values"]) + self._spline_tangents = torch.tensor(f["tangents"]) + + def log_base_partition_function(self, alpha): + r"""Approximate the distribution's log-partition function with a 1D spline. + + Because the partition function (Z(\alpha) in the paper) of the distribution + is difficult to model analytically, we approximate it with a (transformed) + cubic hermite spline: Each alpha is pushed through a nonlinearity before + being used to interpolate into a spline, which allows us to use a relatively + small spline to accurately model the log partition function over the range + of all non-negative input values. + + Args: + alpha: A tensor or scalar of single or double precision floats containing + the set of alphas for which we would like an approximate log partition + function. Must be non-negative, as the partition function is undefined + when alpha < 0. + + Returns: + An approximation of log(Z(alpha)) accurate to within 1e-6 + """ + alpha = torch.as_tensor(alpha) + assert (alpha >= 0).all() + # Transform `alpha` to the form expected by the spline. + x = partition_spline_curve(alpha) + # Interpolate into the spline. + return interpolate1d( + x * self._spline_x_scale.to(x), + self._spline_values.to(x), + self._spline_tangents.to(x), + ) + + def nllfun(self, x, alpha, scale): + r"""Implements the negative log-likelihood (NLL). + + Specifically, we implement -log(p(x | 0, \alpha, c) of Equation 16 in the + paper as nllfun(x, alpha, shape). + + Args: + x: The residual for which the NLL is being computed. x can have any shape, + and alpha and scale will be broadcasted to match x's shape if necessary. + Must be a tensor or numpy array of floats. + alpha: The shape parameter of the NLL (\alpha in the paper), where more + negative values cause outliers to "cost" more and inliers to "cost" + less. Alpha can be any non-negative value, but the gradient of the NLL + with respect to alpha has singularities at 0 and 2 so you may want to + limit usage to (0, 2) during gradient descent. Must be a tensor or numpy + array of floats. Varying alpha in that range allows for smooth + interpolation between a Cauchy distribution (alpha = 0) and a Normal + distribution (alpha = 2) similar to a Student's T distribution. + scale: The scale parameter of the loss. When |x| < scale, the NLL is like + that of a (possibly unnormalized) normal distribution, and when |x| > + scale the NLL takes on a different shape according to alpha. Must be a + tensor or numpy array of floats. + + Returns: + The NLLs for each element of x, in the same shape and precision as x. + """ + # `scale` and `alpha` must have the same type as `x`. + + assert (alpha >= 0).all() + assert (scale >= 0).all() + + float_dtype = x.dtype + assert alpha.dtype == float_dtype + assert scale.dtype == float_dtype + + loss = lossfun(x, alpha, scale, approximate=False) + log_partition = torch.log(scale) + self.log_base_partition_function(alpha) + nll = loss + log_partition + return nll + + def draw_samples(self, alpha, scale): + r"""Draw samples from the robust distribution. + + This function implements Algorithm 1 the paper. This code is written to + allow + for sampling from a set of different distributions, each parametrized by its + own alpha and scale values, as opposed to the more standard approach of + drawing N samples from the same distribution. This is done by repeatedly + performing N instances of rejection sampling for each of the N distributions + until at least one proposal for each of the N distributions has been + accepted. + All samples are drawn with a zero mean, to use a non-zero mean just add each + mean to each sample. + + Args: + alpha: A tensor/scalar or numpy array/scalar of floats where each element + is the shape parameter of that element's distribution. + scale: A tensor/scalar or numpy array/scalar of floats where each element + is the scale parameter of that element's distribution. Must be the same + shape as `alpha`. + + Returns: + A tensor with the same shape and precision as `alpha` and `scale` where + each element is a sample drawn from the distribution specified for that + element by `alpha` and `scale`. + """ + + assert (alpha >= 0).all() + assert (scale >= 0).all() + float_dtype = alpha.dtype + assert scale.dtype == float_dtype + + cauchy = torch.distributions.cauchy.Cauchy(0.0, np.sqrt(2.0)) + uniform = torch.distributions.uniform.Uniform(0, 1) + samples = torch.zeros_like(alpha) + accepted = torch.zeros(alpha.shape).type(torch.bool) + while not accepted.type(torch.uint8).all(): + # Draw N samples from a Cauchy, our proposal distribution. + cauchy_sample = torch.reshape( + cauchy.sample((np.prod(alpha.shape),)), alpha.shape + ) + cauchy_sample = cauchy_sample.type(alpha.dtype) + + # Compute the likelihood of each sample under its target distribution. + nll = self.nllfun( + cauchy_sample, + torch.as_tensor(alpha).to(cauchy_sample), + torch.tensor(1).to(cauchy_sample), + ) + + # Bound the NLL. We don't use the approximate loss as it may cause + # unpredictable behavior in the context of sampling. + nll_bound = lossfun( + cauchy_sample, + torch.tensor(0.0, dtype=cauchy_sample.dtype), + torch.tensor(1.0, dtype=cauchy_sample.dtype), + approximate=False, + ) + self.log_base_partition_function(alpha) + + # Draw N samples from a uniform distribution, and use each uniform sample + # to decide whether or not to accept each proposal sample. + uniform_sample = torch.reshape( + uniform.sample((np.prod(alpha.shape),)), alpha.shape + ) + uniform_sample = uniform_sample.type(alpha.dtype) + accept = uniform_sample <= torch.exp(nll_bound - nll) + + # If a sample is accepted, replace its element in `samples` with the + # proposal sample, and set its bit in `accepted` to True. + samples = torch.where(accept, cauchy_sample, samples) + accepted = accepted | accept + + # Because our distribution is a location-scale family, we sample from + # p(x | 0, \alpha, 1) and then scale each sample by `scale`. + samples *= scale + return samples - assert (alpha >= 0).all() - assert (scale >= 0).all() - float_dtype = alpha.dtype - assert scale.dtype == float_dtype - - cauchy = torch.distributions.cauchy.Cauchy(0., np.sqrt(2.)) - uniform = torch.distributions.uniform.Uniform(0, 1) - samples = torch.zeros_like(alpha) - accepted = torch.zeros(alpha.shape).type(torch.bool) - while not accepted.type(torch.uint8).all(): - # Draw N samples from a Cauchy, our proposal distribution. - cauchy_sample = torch.reshape( - cauchy.sample((np.prod(alpha.shape),)), alpha.shape) - cauchy_sample = cauchy_sample.type(alpha.dtype) - - # Compute the likelihood of each sample under its target distribution. - nll = self.nllfun(cauchy_sample, - torch.as_tensor(alpha).to(cauchy_sample), - torch.tensor(1).to(cauchy_sample)) - - # Bound the NLL. We don't use the approximate loss as it may cause - # unpredictable behavior in the context of sampling. - nll_bound = lossfun( - cauchy_sample, - torch.tensor(0., dtype=cauchy_sample.dtype), - torch.tensor(1., dtype=cauchy_sample.dtype), - approximate=False) + self.log_base_partition_function(alpha) - - # Draw N samples from a uniform distribution, and use each uniform sample - # to decide whether or not to accept each proposal sample. - uniform_sample = torch.reshape( - uniform.sample((np.prod(alpha.shape),)), alpha.shape) - uniform_sample = uniform_sample.type(alpha.dtype) - accept = uniform_sample <= torch.exp(nll_bound - nll) - - # If a sample is accepted, replace its element in `samples` with the - # proposal sample, and set its bit in `accepted` to True. - samples = torch.where(accept, cauchy_sample, samples) - accepted = accepted | accept - - # Because our distribution is a location-scale family, we sample from - # p(x | 0, \alpha, 1) and then scale each sample by `scale`. - samples *= scale - return samples class AdaptiveLossFunction(torch.nn.Module): - """The adaptive loss function on a matrix. - - This class behaves differently from general.lossfun() and - distribution.nllfun(), which are "stateless", allow the caller to specify the - shape and scale of the loss, and allow for arbitrary sized inputs. This - class only allows for rank-2 inputs for the residual `x`, and expects that - `x` is of the form [batch_index, dimension_index]. This class then - constructs free parameters (torch Parameters) that define the alpha and scale - parameters for each dimension of `x`, such that all alphas are in - (`alpha_lo`, `alpha_hi`) and all scales are in (`scale_lo`, Infinity). - The assumption is that `x` is, say, a matrix where x[i,j] corresponds to a - pixel at location j for image i, with the idea being that all pixels at - location j should be modeled with the same shape and scale parameters across - all images in the batch. If the user wants to fix alpha or scale to be a - constant, - this can be done by setting alpha_lo=alpha_hi or scale_lo=scale_init - respectively. - """ - - def __init__(self, - num_dims: int, - dtype: torch.dtype = torch.float32, - alpha_lo: torch.Tensor = 0.001, - alpha_hi: torch.Tensor = 1.999, - alpha_init: Optional[torch.Tensor] = None, - scale_lo: torch.Tensor = 1e-5, - scale_init: torch.Tensor = 1.0): - """Sets up the loss function. - - Args: - num_dims: The number of dimensions of the input to come. - float_dtype: The floating point precision of the inputs to come. - device: The device to run on (cpu, cuda, etc). - alpha_lo: The lowest possible value for loss's alpha parameters, must be - >= 0 and a scalar. Should probably be in (0, 2). - alpha_hi: The highest possible value for loss's alpha parameters, must be - >= alpha_lo and a scalar. Should probably be in (0, 2). - alpha_init: The value that the loss's alpha parameters will be initialized - to, must be in (`alpha_lo`, `alpha_hi`), unless `alpha_lo` == `alpha_hi` - in which case this will be ignored. Defaults to (`alpha_lo` + - `alpha_hi`) / 2 - scale_lo: The lowest possible value for the loss's scale parameters. Must - be > 0 and a scalar. This value may have more of an effect than you - think, as the loss is unbounded as scale approaches zero (say, at a - delta function). - scale_init: The initial value used for the loss's scale parameters. This - also defines the zero-point of the latent representation of scales, so - SGD may cause optimization to gravitate towards producing scales near - this value. - """ - super(AdaptiveLossFunction, self).__init__() - - self.num_dims = num_dims - self.alpha_lo = torch.as_tensor(alpha_lo) - self.alpha_hi = torch.as_tensor(alpha_hi) - self.scale_lo = torch.as_tensor(scale_lo) - self.scale_init = torch.as_tensor(scale_init) - - self.distribution = Distribution() - - if alpha_lo == alpha_hi: - # If the range of alphas is a single item, then we just fix `alpha` to be - # a constant. - self.fixed_alpha = alpha_lo.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) - # Assuming alpha_lo is already a torch.Tensor - - self.alpha = lambda: self.fixed_alpha - else: - # Otherwise we construct a "latent" alpha variable and define `alpha` - # As an affine function of a sigmoid on that latent variable, initialized - # such that `alpha` starts off as `alpha_init`. - if alpha_init is None: - alpha_init = torch.as_tensor((alpha_lo + alpha_hi) / 2.) - latent_alpha_init = inv_affine_sigmoid(alpha_init, lo=alpha_lo, hi=alpha_hi) - - latent_alpha_init_1 = latent_alpha_init.clone().unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) - self.register_parameter('latent_alpha', torch.nn.Parameter(latent_alpha_init_1,requires_grad=True)) - - - self.alpha = lambda: affine_sigmoid(self.latent_alpha, lo=alpha_lo, hi=alpha_hi) - - if scale_lo == scale_init: - # If the difference between the minimum and initial scale is zero, then - # we just fix `scale` to be a constant. - self.fixed_scale = scale_init.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) - self.scale = lambda: self.fixed_scale - else: - # Otherwise we construct a "latent" scale variable and define `scale` - # As an affine function of a softplus on that latent variable. - - self.register_parameter('latent_scale',torch.nn.Parameter(torch.zeros((1, self.num_dims)),requires_grad=True)) - self.scale = lambda: affine_softplus(self.latent_scale, lo=scale_lo, ref=scale_init) - - - def lossfun(self, x, **kwargs): - """Computes the loss on a matrix. - - Args: - x: The residual for which the loss is being computed. Must be a rank-2 - tensor, where the innermost dimension is the batch index, and the - outermost dimension must be equal to self.num_dims. Must be a tensor or - numpy array of type self.float_dtype. - **kwargs: Arguments to be passed to the underlying distribution.nllfun(). - - Returns: - A tensor of the same type and shape as input `x`, containing the loss at - each element of `x`. These "losses" are actually negative log-likelihoods - (as produced by distribution.nllfun()) and so they are not actually - bounded from below by zero. You'll probably want to minimize their sum or - mean. + """The adaptive loss function on a matrix. + + This class behaves differently from general.lossfun() and + distribution.nllfun(), which are "stateless", allow the caller to specify the + shape and scale of the loss, and allow for arbitrary sized inputs. This + class only allows for rank-2 inputs for the residual `x`, and expects that + `x` is of the form [batch_index, dimension_index]. This class then + constructs free parameters (torch Parameters) that define the alpha and scale + parameters for each dimension of `x`, such that all alphas are in + (`alpha_lo`, `alpha_hi`) and all scales are in (`scale_lo`, Infinity). + The assumption is that `x` is, say, a matrix where x[i,j] corresponds to a + pixel at location j for image i, with the idea being that all pixels at + location j should be modeled with the same shape and scale parameters across + all images in the batch. If the user wants to fix alpha or scale to be a + constant, + this can be done by setting alpha_lo=alpha_hi or scale_lo=scale_init + respectively. """ - assert len(x.shape) == 2 - assert x.shape[1] == self.num_dims - return self.distribution.nllfun(x, self.alpha(), self.scale(), **kwargs) - - def forward(self,input,pred): - if pred.ndim == 1: - res = (input-pred)[:,None] - else: - res = input - pred - return torch.mean(self.lossfun(res)) \ No newline at end of file + def __init__( + self, + num_dims: int, + dtype: torch.dtype = torch.float32, + alpha_lo: torch.Tensor = 0.001, + alpha_hi: torch.Tensor = 1.999, + alpha_init: Optional[torch.Tensor] = None, + scale_lo: torch.Tensor = 1e-5, + scale_init: torch.Tensor = 1.0, + ): + """Sets up the loss function. + + Args: + num_dims: The number of dimensions of the input to come. + float_dtype: The floating point precision of the inputs to come. + device: The device to run on (cpu, cuda, etc). + alpha_lo: The lowest possible value for loss's alpha parameters, must be + >= 0 and a scalar. Should probably be in (0, 2). + alpha_hi: The highest possible value for loss's alpha parameters, must be + >= alpha_lo and a scalar. Should probably be in (0, 2). + alpha_init: The value that the loss's alpha parameters will be initialized + to, must be in (`alpha_lo`, `alpha_hi`), unless `alpha_lo` == `alpha_hi` + in which case this will be ignored. Defaults to (`alpha_lo` + + `alpha_hi`) / 2 + scale_lo: The lowest possible value for the loss's scale parameters. Must + be > 0 and a scalar. This value may have more of an effect than you + think, as the loss is unbounded as scale approaches zero (say, at a + delta function). + scale_init: The initial value used for the loss's scale parameters. This + also defines the zero-point of the latent representation of scales, so + SGD may cause optimization to gravitate towards producing scales near + this value. + """ + super(AdaptiveLossFunction, self).__init__() + + self.num_dims = num_dims + self.alpha_lo = torch.as_tensor(alpha_lo) + self.alpha_hi = torch.as_tensor(alpha_hi) + self.scale_lo = torch.as_tensor(scale_lo) + self.scale_init = torch.as_tensor(scale_init) + + self.distribution = Distribution() + + if alpha_lo == alpha_hi: + # If the range of alphas is a single item, then we just fix `alpha` to be + # a constant. + self.fixed_alpha = ( + alpha_lo.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) + ) + # Assuming alpha_lo is already a torch.Tensor + + self.alpha = lambda: self.fixed_alpha + else: + # Otherwise we construct a "latent" alpha variable and define `alpha` + # As an affine function of a sigmoid on that latent variable, initialized + # such that `alpha` starts off as `alpha_init`. + if alpha_init is None: + alpha_init = torch.as_tensor((alpha_lo + alpha_hi) / 2.0) + latent_alpha_init = inv_affine_sigmoid(alpha_init, lo=alpha_lo, hi=alpha_hi) + + latent_alpha_init_1 = ( + latent_alpha_init.clone() + .unsqueeze(0) + .unsqueeze(0) + .repeat(1, self.num_dims) + ) + self.register_parameter( + "latent_alpha", + torch.nn.Parameter(latent_alpha_init_1, requires_grad=True), + ) + + self.alpha = lambda: affine_sigmoid( + self.latent_alpha, lo=alpha_lo, hi=alpha_hi + ) + + if scale_lo == scale_init: + # If the difference between the minimum and initial scale is zero, then + # we just fix `scale` to be a constant. + self.fixed_scale = ( + scale_init.unsqueeze(0).unsqueeze(0).repeat(1, self.num_dims) + ) + self.scale = lambda: self.fixed_scale + else: + # Otherwise we construct a "latent" scale variable and define `scale` + # As an affine function of a softplus on that latent variable. + + self.register_parameter( + "latent_scale", + torch.nn.Parameter(torch.zeros((1, self.num_dims)), requires_grad=True), + ) + self.scale = lambda: affine_softplus( + self.latent_scale, lo=scale_lo, ref=scale_init + ) + + def lossfun(self, x, **kwargs): + """Computes the loss on a matrix. + + Args: + x: The residual for which the loss is being computed. Must be a rank-2 + tensor, where the innermost dimension is the batch index, and the + outermost dimension must be equal to self.num_dims. Must be a tensor or + numpy array of type self.float_dtype. + **kwargs: Arguments to be passed to the underlying distribution.nllfun(). + + Returns: + A tensor of the same type and shape as input `x`, containing the loss at + each element of `x`. These "losses" are actually negative log-likelihoods + (as produced by distribution.nllfun()) and so they are not actually + bounded from below by zero. You'll probably want to minimize their sum or + mean. + """ + + assert len(x.shape) == 2 + assert x.shape[1] == self.num_dims + return self.distribution.nllfun(x, self.alpha(), self.scale(), **kwargs) + + def forward(self, input, pred): + if pred.ndim == 1: + res = (input - pred)[:, None] + else: + res = input - pred + return torch.mean(self.lossfun(res))