-
Notifications
You must be signed in to change notification settings - Fork 885
[ENH] Units_v2 Model added
#2165
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 34 commits
930f855
0f4faf4
43dd89d
2d46f32
e9cd22f
c8c81a9
61e4d29
ee65d40
7e13f3b
8900902
5701122
4152064
9efb9df
e17fb97
78b7bd2
a7c0172
08f46b0
49c27f8
349c726
9f192ed
24f57c9
858a146
bc89293
18afcbb
4327f59
1fa26b3
8e7919f
7eb7fce
0dc7a8d
26e8290
013bd15
96c229e
36f2e6a
dccfe8c
3c3b662
e069a29
874e477
0de97fc
038d8c4
30b0aab
aca98b1
81fc45a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| from pytorch_forecasting.layers._blocks._residual_block_dsipts import ResidualBlock | ||
| from pytorch_forecasting.layers._blocks._transformer_block import _TransformerBlock | ||
|
|
||
| __all__ = ["ResidualBlock"] | ||
| __all__ = ["ResidualBlock", "_TransformerBlock"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| """ | ||
| Pre-norm Transformer Encoder Block for PTF. | ||
| """ | ||
|
|
||
| import torch | ||
| import torch.nn as nn | ||
|
|
||
|
|
||
| class _TransformerBlock(nn.Module): | ||
| """ | ||
| Pre-norm transformer encoder block (MHSA + FFN). | ||
|
|
||
| Parameters | ||
| ---------- | ||
| d_model : int | ||
| Model dimension. | ||
| n_heads : int | ||
| Number of attention heads. | ||
| d_ff : int | ||
| Feed-forward hidden dimension. | ||
| dropout : float | ||
| Dropout probability. | ||
| """ | ||
|
|
||
| def __init__(self, d_model: int, n_heads: int, d_ff: int, dropout: float = 0.1): | ||
| super().__init__() | ||
| self.norm1 = nn.LayerNorm(d_model) | ||
| self.norm2 = nn.LayerNorm(d_model) | ||
| self.attn = nn.MultiheadAttention( | ||
| embed_dim=d_model, num_heads=n_heads, dropout=dropout, batch_first=True | ||
| ) | ||
| self.ff = nn.Sequential( | ||
| nn.Linear(d_model, d_ff), | ||
| nn.GELU(), | ||
| nn.Dropout(dropout), | ||
| nn.Linear(d_ff, d_model), | ||
| nn.Dropout(dropout), | ||
| ) | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| normed = self.norm1(x) | ||
| attn_out, _ = self.attn(normed, normed, normed) | ||
| x = x + attn_out | ||
| x = x + self.ff(self.norm2(x)) | ||
| return x |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| """ | ||
| Patch Embedding Layer for PTF. | ||
| """ | ||
|
|
||
| import torch | ||
| import torch.nn as nn | ||
|
|
||
|
|
||
| class _PatchEmbedding(nn.Module): | ||
| """ | ||
| Project strided patches of a multivariate time series into d_model space. | ||
|
|
||
| Uses channel-independent patching: each channel's patches are projected | ||
| separately with a shared Linear(patch_len, d_model), then averaged across | ||
| channels to match the UniTS paper's channel-independent approach. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| patch_len : int | ||
| Length of each patch window in time steps. | ||
| stride : int | ||
| Stride between consecutive patches. | ||
| d_model : int | ||
| Output embedding dimension. | ||
| dropout : float | ||
| Dropout probability. | ||
| """ | ||
|
|
||
| def __init__(self, patch_len: int, stride: int, d_model: int, dropout: float = 0.1): | ||
| super().__init__() | ||
| self.patch_len = patch_len | ||
| self.stride = stride | ||
| self.projection = nn.Linear(patch_len, d_model) | ||
| self.drop = nn.Dropout(dropout) | ||
|
|
||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| """ | ||
| Parameters | ||
| ---------- | ||
| x : torch.Tensor | ||
| Shape (batch, seq_len, n_channels). | ||
|
|
||
| Returns | ||
| ------- | ||
| torch.Tensor | ||
| Shape (batch, num_patches, d_model). | ||
| """ | ||
| patches = x.unfold(dimension=1, size=self.patch_len, step=self.stride) | ||
| B, num_patches, C, P = patches.shape | ||
| patches = patches.permute(0, 2, 1, 3).contiguous().view(B * C, num_patches, P) | ||
| emb = self.drop(self.projection(patches)) | ||
| emb = emb.view(B, C, num_patches, self.projection.out_features) | ||
|
|
||
| # Channel independence: average across channels as per UniTS logic | ||
| return emb.mean(dim=1) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| """ | ||
| UniTS: Unified Time Series Model for time series forecasting. | ||
| """ | ||
|
|
||
| from pytorch_forecasting.models.units._units_pkg_v2 import UniTS_pkg_v2 | ||
| from pytorch_forecasting.models.units._units_v2 import UniTS | ||
|
|
||
| __all__ = ["UniTS", "UniTS_pkg_v2"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| """ | ||
| Packages container for UniTS model. | ||
| """ | ||
|
|
||
| from pytorch_forecasting.base._base_pkg import Base_pkg | ||
|
|
||
|
|
||
| class UniTS_pkg_v2(Base_pkg): | ||
| """ | ||
| UniTS: Unified Time Series Model. | ||
| Reference: https://arxiv.org/abs/2403.00131 | ||
| Github: https://github.com/mims-harvard/UniTS | ||
| """ | ||
|
|
||
| _tags = { | ||
| "info:name": "UniTS", | ||
| "info:compute": 4, | ||
| "authors": ["Muhammad-Rebaal", "gasvn", "sohamukute"], | ||
| "capability:exogenous": True, | ||
| "capability:multivariate": True, | ||
| "info:pred_type": ["point", "quantile", "distribution"], | ||
| "info:y_type": ["numeric"], | ||
| "capability:pred_int": True, | ||
| "capability:flexible_history_length": True, | ||
| "capability:cold_start": False, | ||
| } | ||
|
|
||
| @classmethod | ||
| def get_cls(cls): | ||
| from pytorch_forecasting.models.units._units_v2 import UniTS | ||
|
|
||
| return UniTS | ||
|
|
||
| @classmethod | ||
| def get_datamodule_cls(cls): | ||
| from pytorch_forecasting.data.data_module import ( | ||
| EncoderDecoderTimeSeriesDataModule, | ||
| ) | ||
|
|
||
| return EncoderDecoderTimeSeriesDataModule | ||
|
|
||
| @classmethod | ||
| def get_base_test_params(cls): | ||
|
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. As I mentioned earlier,
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. looping over all the losses is not a good idea rn for v2
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. Aren't these tags
Can you suggest me the preferred ones ? so I'd add those
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.
I have reservations about this :)
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. Hmm, right |
||
| """Return testing parameter settings for the trainer. | ||
|
|
||
| Returns | ||
| ------- | ||
| params : dict or list of dict, default = {} | ||
| Parameters to create testing instances of the class. | ||
| Each dict are parameters to construct an "interesting" test instance, i.e., | ||
| ``MyClass(**params)`` or ``MyClass(**params[i])`` creates a valid test | ||
| instance. ``create_test_instance`` uses the first (or only) dictionary in | ||
| ``params``. | ||
| """ | ||
| from pytorch_forecasting.metrics import NormalDistributionLoss, QuantileLoss | ||
|
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. I am not sure if
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. No I didn't find any implementation as well that's why I asked you above. That's why in my last commit I remove the code implementation as well but forget to remove this dead import sorry for confusion
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. Is there any open issue regarding the support of |
||
|
|
||
| params = [ | ||
| {}, | ||
| { | ||
|
phoeenniixx marked this conversation as resolved.
|
||
| "patch_len": 8, | ||
| "stride": 4, | ||
|
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. please add some loss functions as well here - Is this model only compatible with point prediction losses, or can it also handle quantile and distribution losses?
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. In the paper the model it is mentioned only about point prediction losses but we can extend it so I extend it to both quantile and distribution losses. |
||
| }, | ||
| { | ||
| "d_model": 32, | ||
| "n_heads": 4, | ||
| "patch_len": 8, | ||
| "stride": 4, | ||
| }, | ||
| { | ||
| "patch_len": 8, | ||
| "stride": 4, | ||
| "datamodule_cfg": { | ||
| "max_encoder_length": 16, | ||
| "max_prediction_length": 4, | ||
| }, | ||
| }, | ||
| { | ||
| "patch_len": 8, | ||
| "stride": 4, | ||
| "loss": QuantileLoss(quantiles=[0.1, 0.5, 0.9]), | ||
| }, | ||
| { | ||
| "patch_len": 8, | ||
| "stride": 4, | ||
| "loss": NormalDistributionLoss(), | ||
| }, | ||
| ] | ||
|
|
||
| base_dm_cfg = {"max_encoder_length": 16, "max_prediction_length": 4} | ||
|
|
||
| for param in params: | ||
| merged = base_dm_cfg.copy() | ||
| merged.update(param.get("datamodule_cfg", {})) | ||
| param["datamodule_cfg"] = merged | ||
|
|
||
| return params | ||
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.
Why are we using this method instead of
get_test_train_params?I feel like an AI hallucination? It was correct earlier