Skip to content
SR-Forge

Wrappers

Metrics that wrap another metric and change how it is evaluated.

UncertaintyLoss

Bases: Metric

Heteroscedastic-NLL data term for any pointwise base metric.

Multiplies the base's per-element error by exp(-log_var) (the predicted precision, since precision = 1/variance = exp(-log_var)). The result is the data term of the heteroscedastic NLL:

::

UncertaintyLoss(..., log_var) = prefactor · base.pointwise(...) · exp(-log_var)

Its inputs are the base's, plus log_var (and y_mask, like every pointwise metric): (x, y, log_var, y_mask) around L1 or MSE, (x, log_var, y_mask) around a no-reference metric, the base's own names around a custom one. Bind them on the UncertaintyLoss; the base's own io is not consulted, since only its pointwise runs.

When is it a likelihood? base · exp(-log_var) + log_var is a true negative log-likelihood when the base is a residual between a prediction and a target: squared error gives the Gaussian, absolute error the Laplacian (family sets the matching constant). Around any other per-pixel term — a no-reference score, a weighted or perceptual error — it is learned loss attenuation: the model learns where to discount the term, paying log_var for it. A common and legitimate use, just not a likelihood.

Note: the parameter is named log_var because that's semantically what the network predicts — the log of the variance. log_var = log(σ²), so exp(-log_var) = 1/σ².

The +log_var regularizer (the log-determinant of the Gaussian / Laplacian density) is NOT included — pair this with a :class:Regularizer (penalty="identity") on the same log_var field in the same combiner. Splitting the data term and the regularizer makes shared-uncertainty multi-task setups composable without double-counting the regularizer when several base metrics share one log_var.

Parameters:

Name Type Description Default
base_metric Metric

Any :class:Metric with a pointwise, whatever its inputs. Wrapping a base without pointwise — or one that already has an input called log_var, such as another UncertaintyLoss — raises :class:TypeError at construction.

required
family str

Selects the NLL prefactor.

  • "laplace" (default) — prefactor 1.0. Pair with L1 / Charbonnier base for the Laplacian NLL.
  • "gauss" — prefactor 0.5. Pair with MSE base for the Gaussian NLL (factor comes from 1/(2σ²)).

The prefactor is a multiplicative constant; it doesn't change the optimum, only the numerical loss value. Pick "gauss" when you want loss values that match a real log-likelihood (e.g. for comparison across papers).

'laplace'
logvar_clamp tuple

(min, max) bounds applied to log_var before exp(-log_var). Without this, an outlier prediction can push exp(-log_var) to 0 or infinity, producing vanishing / exploding gradients. Default (-14.0, 14.0), which keeps exp(-log_var) ∈ [≈1.2e-6, ≈1.2e6] — well within float32 safe range. Pass None to disable clamping.

(-14.0, 14.0)
weight float

Scalar weight passed to the base Metric constructor.

1.0
**kwargs Any

Forwarded to the base Metric constructor (name, reduction, etc.).

{}

Example (YAML)::

loss:
  _target: srforge.metrics.MetricCombiner
  params:
    losses:
      - _target: srforge.metrics.wrappers.UncertaintyLoss
        params:
          base_metric: {_target: srforge.metrics.regression.MSE}
          family: gauss
        io: {inputs: {x: sr, y: hr, log_var: head_log_var}}
      - _target: srforge.metrics.regularization.Regularizer
        params: {penalty: identity}
        io: {inputs: {x: head_log_var}}

pointwise(*args, **kwargs) -> torch.Tensor

Per-pixel data term: prefactor · base.pointwise(...) · exp(-log_var).

Takes the inputs :meth:_pointwise_parameters lists — the base's plus log_var — positionally or by name.

log_var is clamped to :attr:logvar_clamp first (if enabled) to prevent exp(-log_var) from over/underflowing.

The +log_var regularizer is NOT included — pair this with a :class:Regularizer (penalty="identity") on the same log_var field.

CorrectedLoss

Bases: Metric

Shift- and photometrically-corrected wrapper around any base :class:Metric.

Applies one base metric at every shift of the prediction within a border-pixel window and keeps each pixel's best-aligned shift, using :func:torch.Tensor.unfold for the shift extraction — no Python-level for-loop, no intermediate torch.stack copy.

Per sample:

  1. Slice the central SR patch (border-pixel margin on each spatial side).
  2. Extract all (2*border+1)² shifted HR patches via a single view-based unfold.
  3. Optional photometric correction: b = mean(hr_patch - sr_patch) is added to sr_patch so per-shift intensity offset is normalised out.
  4. Build a synthetic :class:~srforge.data.Entry with canonical field names (:data:_PRED_FIELD, :data:_TARGET_FIELD, :data:_MASK_FIELD) and call the base metric via its standard __call__/forward pipeline. The base's own IO binding routes those fields to its parameters — no manual parameter-name resolution needed in this wrapper.
  5. Pick per-pixel min (if base.best_min) or max over shifts — i.e. each pixel gets credited for its best-aligned shift.

Two IO bindings, two roles:

  • Outer io (on :class:CorrectedLoss itself) maps the caller's :class:Entry fields to x / y / y_mask — same as any other Metric.
  • Inner io (on base) maps the base's parameters to the canonical field names listed below. For bases with canonical (x, y, y_mask) parameter names, the default identity map suffices and no inner io is needed. For bases with custom parameter names, declare an inner io block routing them to the canonical field names.

Example with a base that uses (sr, hr, mask) naming::

loss:
  _target: srforge.metrics.wrappers.CorrectedLoss
  params:
    base_metric:
      _target: my_module.MyLoss          # params: (sr, hr, mask)
      io:
        inputs:                          # ← inner IO routes
          sr: x                          #    base params to
          hr: y                          #    canonical inner
          mask: y_mask                   #    field names
    border: 3
  io:
    inputs: {x: pred_field, y: hr_field, y_mask: valid_mask}

Parameters:

Name Type Description Default
base_metric Metric

The base :class:Metric to apply per shift.

required
border int

How many pixels on each spatial side define the shift range. (2*border + 1)² total shifts. Default 3 ⇒ 49 shifts (PROBA-V challenge convention).

3
do_correction bool

Apply the additive photometric bias before the base-metric call. Default True.

True