Loss Reference¤
Training Neural Operators effectively requires evaluating the error in function space rather than just on discrete grid points. Standard ML losses (like basic MSE) are discretization-dependent. To ensure our models are resolution-invariant and respect the underlying continuous functions, neojax provides discretization-invariant, norm-based losses.
Lp-Losses¤
The standard approach for training Neural Operators is using continuous \(L^p\) norms.
LpLoss: Computes the standard \(L^p\) norm of the error over the spatial domain.RelativeLpLoss: Computes the \(L^p\) error normalized by the \(L^p\) norm of the true target function. This is standard practice in operator learning because physical data often ranges across different magnitudes, and a relative loss ensures stable gradients across samples.
neojax.losses.lp_losses.LpLoss
¤
General \(L^{p}\)-loss.
Computes $$ \Vert y - \hat{y}\Vert_{L^p} = \Bigg(\int_{\Omega} \Big| y - \hat{y} \Big|^p dx \Bigg)^{\frac{1}{p}} $$ where \(y\) is the ground truth and \(\hat{y}\) is the model prediction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p
|
float | int
|
Power of the norm. |
required |
weight
|
float
|
(Learnable) weight. Loss is computed as |
1.0
|
learnable_weight
|
bool
|
Whether |
False
|
Internal Attributes
These fields store the internal state of the loss.
- weight (
Float[Array, ""]): Learnable loss weight. Filter during training to prevent updates. - p (
float | int): Power of the norm. - learnable_weight (
bool): Flag indicating whetherweightis learnable.
neojax.losses.lp_losses.RelativeLpLoss
¤
General relative \(L^{p}\)-loss.
Computes $$ \frac{\Vert y - \hat{y}\Vert_{L^p}}{\Vert y \Vert_{L^p}} $$ where \(y\) is the ground truth and \(\hat{y}\) is the model prediction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p
|
float | int
|
Power of the norm. |
required |
weight
|
float
|
(Learnable) weight. Loss is computed as |
1.0
|
learnable_weight
|
bool
|
Whether |
False
|
Internal Attributes
These fields store the internal state of the loss.
- weight (
Float[Array, ""]): Learnable loss weight. Filter during training to prevent updates. - p (
float | int): Power of the norm. - learnable_weight (
bool): Flag indicating whetherweightis learnable.
Sobolev Losses¤
While \(L^p\) losses only measure the error in the predicted function values, Sobolev losses (\(H^1\), \(W^{k,p}\)) also penalize errors in the derivatives of the functions. By aligning the gradients of the prediction with the true gradients, the network is forced to learn the underlying physical differential operators more accurately.
Implementation Details and Efficient Usage¤
Computing derivatives in Automatic Differentiation (AD) for Neural Operator training requires careful consideration of input and output dimensions. Choosing the wrong method or differentiation mode can determine whether training is highly efficient or fails due to Out-of-Memory (OOM) issues or excessive runtimes.
Throughout this section, we assume a model mapping inputs to outputs: $$ f: \mathbb{R}^n \rightarrow \mathbb{R}^m $$ where \(n\) is the input dimension (e.g., number of input channels times spatial grid size) and \(m\) is the output dimension.
neojax supports three primary methods for computing the derivatives, chosen via the method argument:
1. Exact Mode (method="exact")¤
This method computes the exact Jacobian of the model \(f(x)\) with respect to \(x\) using standard first-order AD.
- Inner AD Mode (diff_mode):
- Forward-mode (fwd): Computes derivatives column-by-column, scaling with the input dimension \(n\).
- Reverse-mode (bwd): Computes derivatives row-by-row, scaling with the output dimension \(m\).
- Auto (auto): neojax automatically uses forward-mode AD if \(n \le m\), and backward-mode AD if \(n > m\).
- Compounding Outer-Pass Effect:
During model training, the outer optimizer loop takes the gradient of the scalar loss with respect to the model parameters \(\theta\) (using a backward pass, e.g. jax.grad).
- If the inner mode is Forward (fwd), JAX performs Reverse-over-Forward differentiation, which is highly memory-efficient because it does not store a nested backward tape.
- If the inner mode is Reverse (bwd), JAX performs Reverse-over-Reverse differentiation, which requires storing nested Wengert tapes for both passes, drastically increasing the memory footprint.
Tip: Even when \(n \approx m\), diff_mode="fwd" is often superior due to its memory efficiency.
2. Interpolated Mode (method="interpolate")¤
For higher-order derivatives (\(k \ge 2\)), computing the exact Jacobian recursively scales exponentially. Instead, neojax uses JAX's Taylor-mode AD (jax.experimental.jet), which propagates truncated Taylor polynomials.
- The interpolated method evaluates the Taylor expansion of \(f(x)\) in all \(n\) canonical basis directions.
- This computes the exact higher-order derivatives, but requires a jax.vmap loop over \(n\) directions.
- It is highly efficient for moderate input dimensions (\(n\)), but becomes expensive as the spatial resolution or input channel count increases.
3. Stochastic Mode (method="stochastic")¤
When the input dimension \(n\) is very large (e.g., high-resolution grids), computing the Taylor polynomial in all \(n\) directions is intractable.
- The stochastic method uses the Stochastic Taylor Derivative Estimator (STDE).
- Instead of computing derivatives along every canonical basis, it samples a small number of random direction vectors (Rademacher or Normal distribution) and evaluates the Taylor expansion only in those random directions.
- The computational cost is independent of the input dimension \(n\) and scales only with the number of random samples (n_random_samples), which defaults to 10.
Auto Routing (method="auto")¤
If you set method="auto", neojax automatically routes to the most suitable strategy:
- \(k = 1\): Uses "exact".
- \(k \ge 2\): Uses "interpolate" if all spatial grid dimensions of the input \(x\) are \(\le 10\), otherwise defaults to "stochastic" to prevent OOM and runtime issues.
neojax.losses.sobolev_losses.SobolevLoss
¤
General (Sobolev) \(W^{k,p}\)-loss.
Computes the loss in the \(W^{k,p}\) Sobolev space with the associated norm: $$ \begin{aligned} \Vert (y - \hat{y}) \Vert_{W^{k,p}(\Omega)} &= \begin{cases} \left(\sum_{\vert \alpha \vert \le k} \Vert D^\alpha (y - \hat{y}) \Vert_{L^p(\Omega)}^p\right)^{1/p}, & \text{if } p < \infty, \\ \max_{\vert \alpha \vert \le k} \Vert D^\alpha (y - \hat{y}) \Vert_{L^\infty(\Omega)}, & \text{if } p = \infty. \end{cases} \end{aligned} $$ where \(D^{\alpha}(y - \hat{y})\) are all combinations of partial derivatives up to the \(k\)-th order and \(y\) is the ground truth and \(\hat{y}\) is the model prediction.
Default is the \(W^{1,2}\)-loss (known as \(H^1\)-loss).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
k
|
int
|
Order of partial derivatives. Default is 1. |
1
|
p
|
float
|
Power of the \(L^{p}\)-norm. Default is 2.0. |
2.0
|
method
|
Literal['exact', 'interpolate', 'stochastic', 'auto']
|
Method for computing higher order derivatives.
See documentation for details. Default is |
'auto'
|
diff_mode
|
Literal['fwd', 'bwd', 'auto']
|
Inner AD mode. See documentation for details.
Default is |
'auto'
|
weight
|
float
|
(Learnable) weight. Loss is computed as |
1.0
|
learnable_weight
|
bool
|
Whether |
False
|
n_random_samples
|
int
|
Number of random vectors to use in
|
10
|
random_type
|
Literal['rademacher', 'normal']
|
Type of distribution to sample from
in |
'normal'
|
Internal Attributes
These fields store the internal state of the loss.
- k (
int): Order of partial derivatives. - p (
float): Power of the \(L^{p}\)-norm. - method (
Literal["exact", "interpolate", "stochastic", "auto"]): Method for computing higher order derivatives. See documentation for details. - diff_mode (
Literal["fwd", "bwd", "auto"]): Inner AD mode. See documentation for details. - weight (
Float[Array, ""]): Learnable loss weight. Filter during training to prevent updates. - learnable_weight (
bool): Flag indicating whetherweightis learnable. - n_random_samples (
int): Number of random vectors to sample. - random_type (
Literal["rademacher", "normal"]): Type of distribution to sample from in"stochastic"method. Default is standard Normal distribution.
Info
Computing higher order derivates is expensive. Given a function $f: \mathbb{R}^m \rightarrow \mathbb{R}^d $, computing the derivatives up to order k, the XLA graph scales with \(\mathcal{O}(m \cdot d^k)\).
exact_sobolev(model: Module, *, x: Float[Array, 'in_c ...'], target: Float[Array, 'c ...']) -> Float[Array, '']
¤
Computes the Sobolev loss exactly for a single sample.
Uses either eqx.filter_jacrev or eqx.filter_jacfwd
to compute the exact derivative w.r.t. x,
depending on diff_mode.
If diff_mode is auto, uses forward-mode AD if
all output dimensions are larger or the same as
all input dimensions, else backward-mode AD.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
The model being trained. |
required |
x
|
Float[Array, 'in_c ...']
|
Model input array shaped (in_c, d1, ..., dN). |
required |
target
|
Float[Array, 'c ...']
|
Ground truth array shaped (c, d1, ..., dN). |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar Sobolev loss. |
interpolated_sobolev(model: Module, *, x: Float[Array, 'in_c ...'], target: Float[Array, 'c ...']) -> Float[Array, '']
¤
Compute Sobolev loss for a single sample by propagating a higher order Taylor polynomial.
Uses jax.experimental.jet, which forwards a higher order
Taylor polynomial. This is more efficient than the exact method
for higher order derivatives.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
The model being trained. |
required |
x
|
Float[Array, 'in_c ...']
|
Model input array shaped (in_c, d1, ..., dN). |
required |
target
|
Float[Array, 'c ...']
|
Ground truth array shaped (c, d1, ..., dN). |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar Sobolev loss. |
stochastic_sobolev(model: Module, *, x: Float[Array, 'in_c ...'], target: Float[Array, 'c ...'], key: PRNGKeyArray) -> Float[Array, '']
¤
Computes the stochastic Sobolev loss.
Uses the STDE approach, making jet more
efficient by sampling random vectors instead
of using all basis tensors.
Cite
Stochastic Taylor Derivative Estimator: Efficient amortization for arbitrary differential operators
@article{shi2024stochastic,
title={Stochastic taylor derivative estimator:
Efficient amortization for arbitrary differential operators},
author={Shi, Zekun and Hu, Zheyuan and Lin, Min and Kawaguchi, Kenji},
journal={Advances in Neural Information Processing Systems},
volume={37},
pages={122316--122353},
year={2024}
}
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
The model being trained. |
required |
x
|
Float[Array, 'in_c ...']
|
Model input array shaped (c, d1, ..., dN). |
required |
target
|
Float[Array, 'c ...']
|
Ground truth array shaped (c, d1, ..., dN). |
required |
key
|
PRNGKeyArray
|
Random key. |
required |
Returns:
| Type | Description |
|---|---|
Float[Array, '']
|
Scalar Sobolev loss. |
Composing Losses¤
Real-world physics problems often require minimizing multiple objectives simultaneously (e.g., combining a data loss with a physics-informed regularization term). ComposedLoss allows you to pipe multiple loss functions together, computing the weighted sum of its constituent losses.
neojax.losses.composed_loss.ComposedLoss
¤
Wraps loss compositions.
Composes different losses by passing them to the classes init method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
losses
|
BaseLoss
|
Arbitrary number of loss instances to compose. |
required |
weight
|
float
|
Optional weighting for ComposedLoss. Default is 1.0, i.e., no weighting. |
1.0
|
Internal Attributes
These fields store the internal state of the loss.
- weight (
Float[Array, ""]): Learnable loss weight. Filter during training to prevent updates. - losses (
tuple[BaseLoss, ...]): Composed loss functions. The composition is the sum.
Info
This loss may be called without a model by using keywords:
loss_fn(pred=y_hat, target=y). The model parameter is kept
for clean compatibility with JAX transformations and
if composing model-dependent losses (e.g. Sobolev loss).
Examples:
import jax.numpy as jnp
from neojax.losses import ComposedLoss, LpLoss, RelativeLpLoss
loss_1 = LpLoss(p=2)
loss_2 = RelativeLpLoss(p=2)
composed_loss = ComposedLoss(loss_1, loss_2)
x = jnp.ones((1, 10))
y = jnp.zeros((1, 10))
loss_val = composed_loss(pred=x, target=y)
Learnable Loss Weights¤
When combining multiple losses, balancing their static weights can be notoriously difficult. neojax solves this by supporting dynamically learnable loss weights.
If you enable learnable weights in a ComposedLoss, custom BaseLoss, or any other loss instance, you must ensure JAX differentiates with respect to them. neojax provides the is_learnable_loss_weight utility to create the correct filter specification for equinox.
import equinox as eqx
from neojax.losses.utils import is_learnable_loss_weight
# 1. Create a filter spec that isolates the network parameters AND learnable loss weights
filter_spec = is_learnable_loss_weight(model_and_losses)
# 2. Pass the spec to filter_grad
grads = eqx.filter_grad(loss_fn, filter=filter_spec)(model_and_losses, data, targets)
neojax.losses.utils.is_learnable_loss_weight(model: PyTree) -> PyTree
¤
Creates a filter spec to correctly handle learnable weights.
Produces a boolean PyTree that can be used directly with
eqx.partition, eqx.filter_grad, or eqx.filter_jit.
It returns False for most fields, but True for:
1. All inexact arrays (trainable parameters) in the model.
2. Loss weight attrs only if their learnable_weight is True.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
PyTree
|
The model or list of losses to generate a filter for. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
A boolean PyTree of the same structure as |
Example
import equinox as eqx
from neojax.losses.utils import is_learnable_loss_weight
# Directly use as the filter for grad
filter_spec = is_learnable_loss_weight(model)
grads = eqx.filter_grad(loss_fn, filter=filter_spec)(model, ...)
Custom Losses¤
When implementing custom losses, inherit from BaseLoss.
Each custom loss should implement the __call__ method, taking the predicted array and the target array as inputs and returning a scalar loss.