Regression Metrics
The regression metrics are the family of metrics for continuous targets. Every one of them is a mean over elementwise residuals between the model output and the target:
value = resolve( mean( residual(out - target) ) )
How it works
The family keeps a single accumulator per head (the summed residual and the count of elements folded into it). Both are exactly additive, so batches fold in by addition and shards merge the same way, and the epoch value does not depend on how the evaluation set happened to divide.
Residuals are taken columnwise across the whole packed row and then scattered into per-head totals, so heads of any width cost one pass rather than one pass each.
A target that is NaN is left out of both the summed residual and the count. A head whose targets were all NaN over the whole epoch has no value that epoch, so its running average holds its last value.
The resolve step exists because not every metric reports the mean itself. A
transform that does not distribute over addition
cannot be folded into the accumulator without changing the answer, so it is
applied once, after the full mean is known.
Variants
MAE: mean of
|out - target|.MSE: mean of
(out - target)^2.RMSE: the square root of the mean of
(out - target)^2, back in target units.
All three are best at 0, which the family reports as the optimum.
Registering a new regression metric
A regression metric is a subclass of RegressionMetric that declares a name
and version and supplies the residual it accumulates. The base implements the
whole monoid (initial, update_state, combine and finalize) plus
the optimum, so a subclass implements only:
residual(self, diff) -> TensorThe elementwise error to accumulate, given
out - target.diffis a fresh tensor and may be rewritten in place.resolve(self, mean) -> TensorOptional. Map the per-head mean residual to the reported value. Defaults to the mean itself.
repr(self) -> strA short string label for the metric.
A metric that takes no options inherits the family’s empty Config. One that
does declares its own config model and overrides validate_config alongside its
binding of the config type.
# plugin.py
from typing import ClassVar
from torch import Tensor
from icegraph.engine.services.metrics.metric import MetricFactory
from icegraph.engine.services.metrics.metric.variants.regression.plugin import RegressionMetric
from icegraph.engine.services.metrics.metric.variants.regression.config import Config
class MaxError(RegressionMetric[Config]):
name: ClassVar[str] = "max-error"
version: ClassVar[int] = 1
def repr(self) -> str:
return "max_error"
def residual(self, diff: Tensor) -> Tensor:
... # elementwise error to accumulate
MetricFactory.register(MaxError)