Repository navigation
[ENH] Adding Support of Distribution Loss in v2 Models #2420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
dac3f96
4cb6fdf
641e6c1
8caf327
3983b4d
fcd6d85
95886f8
c0fbb2f
333cb6c
2682958
7c1b947
fdb69dd
0c8c615
6bcaae4
2aa4ad0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why are we changing the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be a cleaner and more robust way to handles the shapes? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,8 @@ | |
| import pytest | ||
| import torch | ||
|
|
||
| from pytorch_forecasting.metrics import MAE | ||
| from pytorch_forecasting.data.encoders import EncoderNormalizer | ||
| from pytorch_forecasting.metrics import MAE, NormalDistributionLoss | ||
| from pytorch_forecasting.models.base._base_model_v2 import BaseModel | ||
|
|
||
|
|
||
|
|
@@ -122,3 +123,46 @@ def test_optimizer_instance(): | |
| model.optimizer = opt | ||
| cfg = model.configure_optimizers() | ||
| assert cfg["optimizer"] is opt | ||
|
|
||
|
|
||
| def test_transform_output_dict_target_scale(): | ||
| """Dict target_scale applies affine denormalization correctly.""" | ||
| model = _make_model() | ||
| raw = torch.randn(4, 12, 1) | ||
| center = torch.tensor([10.0, 20.0, 30.0, 40.0]) | ||
| scale = torch.tensor([2.0, 3.0, 4.0, 5.0]) | ||
| target_scale = {"center": center, "scale": scale} | ||
|
|
||
| result = model.transform_output(raw, target_scale) | ||
|
|
||
| assert result.shape == raw.shape | ||
| assert torch.allclose(result[0], raw[0] * 2.0 + 10.0) | ||
| assert torch.allclose(result[3], raw[3] * 5.0 + 40.0) | ||
|
|
||
|
|
||
| def test_transform_output_plain_tensor_target_scale(): | ||
|
Comment on lines
+128
to
+143
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how are these two tests useful? Why not direclty use the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two tests only validate the affine denormalization path (pred * scale + center), which is the straightforward case.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My doubt is -
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 2 tests are checking specifically making sure that Quantile Loss and Point Forecasts (like MAE) get un-scaled correctly, since I made changes in the |
||
| """Plain tensor target_scale correctly applies affine denormalization.""" | ||
| model = _make_model() | ||
| raw = torch.randn(4, 12, 1) | ||
| target_scale = torch.tensor([[10.0, 2.0], [20.0, 3.0], [30.0, 4.0], [40.0, 5.0]]) | ||
|
|
||
| result = model.transform_output(raw, target_scale) | ||
|
|
||
| assert result.shape == raw.shape | ||
| assert torch.allclose(result[0], raw[0] * 2.0 + 10.0) | ||
| assert torch.allclose(result[2], raw[2] * 4.0 + 30.0) | ||
|
|
||
|
|
||
| def test_transform_output_distribution_loss(): | ||
| """DistributionLoss path rescales parameters via the loss function.""" | ||
|
|
||
| model = _make_model(loss=NormalDistributionLoss()) | ||
| model.target_normalizer = EncoderNormalizer() | ||
|
|
||
| raw = torch.randn(2, 4, 2) | ||
| target_scale = torch.tensor([[5.0, 2.0], [3.0, 1.5]]) | ||
|
|
||
| result = model.transform_output(raw, target_scale) | ||
|
|
||
| assert result.shape == (2, 4, 4) | ||
| assert not torch.equal(result[..., 2:], raw) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we pass a whole object in metadata? is this actually necessary? I think metadata should be lightweight and only should have the "metadata" and not objects unless it is actually 100% necessary