Grid & basis densities: Wasserstein-1 and Rank-N-Contrast¶
Tools for heads that predict a whole 1D distribution over a fixed support (binned classification, spline bases) rather than a parametric family, plus a representation loss that orders the feature space by the continuous target.
| # | Reference |
|---|---|
| 1 | Zha, Y.-J. et al. (2023). Rank-N-Contrast: Learning Continuous Representation Degrees for Improved Forecasting. NeurIPS 2023. |
| 2 | Ramsay, J. O. (1988). Monotone Regression Splines in Action. Statistical Science 3(4), 425–441. (M-splines) |
| 3 | Villani, C. (2009). Optimal Transport: Old and New. Springer. (Wasserstein distances) |
| 4 | de Boor, C. (2001). A Practical Guide to Splines. Springer. (B-spline basis, Cox–de Boor recursion) |
Related methods for other 1D distribution heads: → See Normalizing Flows, SLS (structured least-squares quantiles), and MDN.
BSplineDensityBasis (torchregress.utils)¶
A density on \([lo, hi]\) is written as
where \(B_{m,k}\) are order-\(k\) (degree \(k-1\)) B-splines on a clamped knot vector and \(M_m\) are their unit-integral (M-spline) rescalings. Standard B-splines satisfy \(\sum_m B_m = 1\), not \(\int B_m = 1\); the rescaling is what makes a softmax over coefficients produce a normalised, non-negative density with no extra pass.
| Method | Returns | Notes |
|---|---|---|
from_uniform(lo, hi, n_intervals, degree=3) |
basis | Equally spaced breakpoints |
from_quantiles(samples, n_interior, lo, hi) |
basis | Interior knots at empirical quantiles (balanced gradient flow) |
evaluate(x) |
(..., M) |
Cox–de Boor recursion; zero outside [lo, hi] |
density(coeffs, grid) |
(..., G) |
\(\sum_m c_m M_m(z_g)\) |
bin_integrals(edges) |
(K, M) |
\(T_{km} = \int_{e_k}^{e_{k+1}} M_m\); exact via piecewise Gauss–Legendre |
cdf(x) / cumulative_moment(x, r) |
(len(x), M) |
\(\int_{lo}^{x} u^r M_m(u)\,du\) |
absolute_deviation(x) |
(len(x), M) |
\(\mathbb{E}_{M_m}\lvert U - x\rvert\) — exact \(W_1\) to a point mass |
Bin masses are a fixed linear map of the coefficients, masses = coeffs @ T.T, so
tomographic/binned summaries of a predicted density need no sampling.
import torch
from torchregress.utils import BSplineDensityBasis
basis = BSplineDensityBasis.from_quantiles(train_targets, n_interior=12, lo=0.0, hi=3.0)
coeffs = torch.softmax(head(features), dim=-1) # [N, basis.n_basis]
nll = -(coeffs * basis.evaluate(y)).sum(-1).log() # exact density at the labels
w1 = (coeffs * basis.absolute_deviation(y)).sum(-1) # exact W1(p, delta_y) = E|U - y|
DiscreteWasserstein1Loss¶
For mass vectors \(p, q\) located at bin centres \(c_1<\dots<c_K\),
with \(F, G\) the cumulative sums. Unlike KL or cross-entropy, the penalty grows with
how far misplaced mass is from the target, which complements a local divergence when
training binned or basis heads. Targets may be [N, K] masses or [N] scalar values
(quantised to the containing bin; for continuous parameterisations prefer
BSplineDensityBasis.absolute_deviation).
from torchregress.losses import DiscreteWasserstein1Loss
loss = DiscreteWasserstein1Loss(bin_edges, from_logits=True)
value = loss(logits, target_masses) # or loss(logits, scalar_targets)
RankNContrastLoss¶
Rank-N-Contrast (Zha et al., NeurIPS 2023) trains features \(h_i\) so that similarity is ordered by label distance. For anchor \(i\) and any \(j \ne i\),
with \(s_{ij} = -\lVert h_i - h_j\rVert_2\) (default) or cosine similarity. Ties in label distance are included in \(S_{ij}\). The implementation sorts by label distance per anchor and uses a cumulative log-sum-exp, so it is \(O(N^2 \log N)\) rather than \(O(N^3)\).
from torchregress.losses import RankNContrastLoss
rnc = RankNContrastLoss(temperature=2.0)
value = rnc(features, labels) # features [N, D], labels [N] or [N, L]
mask removes samples from both the anchor set and every denominator; weights are
per anchor.
Scope
These are building blocks. The library does not claim any particular downstream accuracy from them; validate on your own held-out data.
When to use which¶
| Building block | Operates on | Best when | Not for |
|---|---|---|---|
BSplineDensityBasis |
Density output space (continuous variable) | You need a normalised, differentiable 1D density with exact evaluation, bin masses, CDF and moments from a softmax head | High-dimensional or multi-output densities (it is a 1D basis) |
DiscreteWasserstein1Loss |
Output space, binned masses | Penalising where misplaced mass lands, not just whether bins disagree (KL/CrossEntropy are blind to distance) | Continuous parameterisations — use BSplineDensityBasis.absolute_deviation instead |
RankNContrastLoss |
Representation space | Ordering features by target distance to improve downstream uncertainty/forecasting heads — pair it with a distribution head, not instead of one | Direct probabilistic prediction: RNC shapes features; it does not itself define a predictive distribution |
→ Combine them: train features with RankNContrastLoss, then a BSplineDensityBasis head with an NLL plus a small DiscreteWasserstein1Loss term.
Generic cumulative-CDF objective¶
CumulativeCDFLoss and cumulative_cdf_loss retain the
generic binned objective independently of an astronomy-specific recipe. For
ordered centers \(c_j\), bin probabilities \(p_j=\operatorname{softmax}(a)_j\),
and cumulative masses \(C_j=\sum_{k\le j}p_k\), the objective is
The mean-width weighting is a fixed-grid training objective; it is not the exact piecewise-linear CRPS on nonuniform bins. Use the distribution metrics for that score.
import torch
from torchregress.losses import CumulativeCDFLoss
loss = CumulativeCDFLoss(torch.linspace(-2., 2., 9))
logits = torch.zeros((3, 9), requires_grad=True)
value = loss(logits, torch.tensor([-.2, .3, 1.1]))
value.backward()
For continuous scoring, BinnedDensityLoss.log_prob divides each bin mass by
its width and returns negative infinity outside the finite support.
MixtureDensityLoss.log_prob instead evaluates the normalized continuous
mixture density. Binned classification cross-entropy alone has different units.