From 310ad3701c74abb29e8cba78de25678580cc8b31 Mon Sep 17 00:00:00 2001 From: ChrisW09 <50968720+ChrisW09@users.noreply.github.com> Date: Wed, 21 Oct 2026 20:40:21 +0200 Subject: [PATCH] fix(distributions): keep zero targets exact in the Tweedie deviance evaluate_nll() clamped y to 1e-8 before y ** (2 - p), so every zero target added 2 * 1e-8 ** (2 - p) / ((1 - p)(2 - p)) to the deviance (about -3.5 per zero target for p = 1.9). Since 2 - p > 0, 0 ** (2 - p) is already 0; clamp at 0 only to keep negative values out. Fixes #525 --- deeptab/distributions/tweedie.py | 2 +- tests/distributions/test_distributions.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/deeptab/distributions/tweedie.py b/deeptab/distributions/tweedie.py index bd15dec0..efba250c 100644 --- a/deeptab/distributions/tweedie.py +++ b/deeptab/distributions/tweedie.py @@ -77,7 +77,7 @@ def evaluate_nll(self, y_true, y_pred): # Tweedie deviance: D(y, mu) = 2 * [y^(2-p)/((1-p)(2-p)) - y*mu^(1-p)/(1-p) + mu^(2-p)/(2-p)] p = self.p d = 2.0 * ( - torch.pow(y_true_tensor.clamp(min=1e-8), 2.0 - p) / ((1.0 - p) * (2.0 - p)) + torch.pow(y_true_tensor.clamp(min=0.0), 2.0 - p) / ((1.0 - p) * (2.0 - p)) - y_true_tensor * torch.pow(mu, 1.0 - p) / (1.0 - p) + torch.pow(mu, 2.0 - p) / (2.0 - p) ) diff --git a/tests/distributions/test_distributions.py b/tests/distributions/test_distributions.py index 22d3cc6f..1eb727f2 100644 --- a/tests/distributions/test_distributions.py +++ b/tests/distributions/test_distributions.py @@ -288,6 +288,20 @@ def test_evaluate_nll_keys(self): for key in ("NLL", "mse", "mae", "rmse", "tweedie_deviance"): assert key in metrics + @pytest.mark.parametrize("p", [1.5, 1.9]) + def test_evaluate_nll_deviance_matches_sklearn_with_zero_targets(self, p): + import numpy as np + from sklearn.metrics import mean_tweedie_deviance + + from deeptab.distributions import TweedieDistribution + + family = TweedieDistribution(p=p) + raw = self.torch.tensor([[-0.5], [0.3], [1.8], [-0.1]]) + targets = np.array([0.0, 0.0, 3.0, 1.0]) + means = family(raw)[:, 0].detach().numpy() + deviance = float(family.evaluate_nll(targets, raw.numpy())["tweedie_deviance"]) + assert deviance == pytest.approx(mean_tweedie_deviance(targets, means, power=p), rel=1e-5) + @pytest.mark.parametrize("p", [1.1, 1.5, 1.9]) def test_various_p_values(self, p): from deeptab.distributions import TweedieDistribution