Skip to content

Commit c4f5aac

Browse files
Muhammad-Rebaalphoeenniixxfnhirwafkiraly
authored
[BUG] Safe fallback for retrieving index from QuantileLoss.to_prediction when 0.5 is unavailable (#2256)
References : Fixes #2255 ## Description If a user creates an instance of `QuantileLoss` defining bounding target quantiles (e.g., `[0.1, 0.9]`) but excluding the direct median probability `0.5`, performing a `.to_prediction()` pass crashes parsing the output. ## Changes Made - Introduced a conditional flow handling. It first checks `if 0.5 in self.quantiles`, returning exact. If absent, it establishes the minimum offset matching `0.5`, safely reverting to the closest approximated center. Modified `pytorch_forecasting/metrics/quantile.py`. CC : @phoeenniixx , @PranavBhatP , @fkiraly --------- Co-authored-by: Aryan Saini <116151399+phoeenniixx@users.noreply.github.com> Co-authored-by: Felix Hirwa Nshuti <hirwanshutiflx@gmail.com> Co-authored-by: Franz Király <fkiraly@gcos.ai>
1 parent 4d8d97c commit c4f5aac

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

pytorch_forecasting/metrics/quantile.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ def to_prediction(self, y_pred: torch.Tensor) -> torch.Tensor:
5656
point prediction
5757
"""
5858
if y_pred.ndim == 3:
59-
idx = self.quantiles.index(0.5)
59+
if 0.5 in self.quantiles:
60+
idx = self.quantiles.index(0.5)
61+
else:
62+
idx = min(
63+
range(len(self.quantiles)),
64+
key=lambda i: abs(self.quantiles[i] - 0.5),
65+
)
6066
y_pred = y_pred[..., idx]
6167
return y_pred
6268

tests/test_metrics.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
MultivariateNormalDistributionLoss,
2020
NegativeBinomialDistributionLoss,
2121
NormalDistributionLoss,
22+
QuantileLoss,
2223
)
2324
from pytorch_forecasting.metrics.base_metrics import (
2425
AggregationMetric,
@@ -570,3 +571,22 @@ def test_MASE():
570571

571572
assert scaling.shape == (batch_size,)
572573
assert (scaling > 0).all(), "Scaling should be positive"
574+
575+
576+
def test_QuantileLoss_to_prediction_fallback():
577+
"""Test to_prediction selects median when present, nearest quantile otherwise."""
578+
579+
loss_with_median = QuantileLoss(quantiles=[0.1, 0.5, 0.9])
580+
y_pred_3d = torch.tensor([[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]])
581+
result = loss_with_median.to_prediction(y_pred_3d)
582+
expected = torch.tensor([[2.0, 5.0]])
583+
assert torch.equal(result, expected)
584+
585+
loss_no_median = QuantileLoss(quantiles=[0.1, 0.4, 0.9])
586+
result_fallback = loss_no_median.to_prediction(y_pred_3d)
587+
expected_fallback = torch.tensor([[2.0, 5.0]])
588+
assert torch.equal(result_fallback, expected_fallback)
589+
590+
y_pred_2d = torch.tensor([[10.0, 20.0]])
591+
result_2d = loss_no_median.to_prediction(y_pred_2d)
592+
assert torch.equal(result_2d, y_pred_2d)

0 commit comments

Comments
 (0)