patchfm 3.1.1__tar.gz → 3.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchfm-3.1.1/src/patchfm.egg-info → patchfm-3.3.0}/PKG-INFO +42 -27
- {patchfm-3.1.1 → patchfm-3.3.0}/README.md +42 -27
- {patchfm-3.1.1 → patchfm-3.3.0}/pyproject.toml +1 -1
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm/configs/model_config.py +5 -5
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm/inference/forecaster.py +35 -3
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm/inference/modules.py +60 -11
- {patchfm-3.1.1 → patchfm-3.3.0/src/patchfm.egg-info}/PKG-INFO +42 -27
- {patchfm-3.1.1 → patchfm-3.3.0}/LICENSE +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/setup.cfg +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm/__init__.py +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm.egg-info/SOURCES.txt +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm.egg-info/dependency_links.txt +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm.egg-info/requires.txt +0 -0
- {patchfm-3.1.1 → patchfm-3.3.0}/src/patchfm.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchfm
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: a Foundation Model for Univariate Time Series Forecasting
|
|
5
5
|
Author-email: Samy-Melwan Vilhes <samy-melwan.vilhes@insa-rouen.fr>
|
|
6
6
|
License: MIT License
|
|
@@ -40,32 +40,35 @@ Requires-Dist: numpy>=1.26.0
|
|
|
40
40
|
Requires-Dist: safetensors==0.5.3
|
|
41
41
|
Dynamic: license-file
|
|
42
42
|
|
|
43
|
-
# A
|
|
43
|
+
# A Foundation Model for Univariate Time Series Forecasting
|
|
44
44
|
|
|
45
45
|
A concise, reproducible recipe for training a transformer-based, patch-to-patch forecasting model for univariate time series. The approach mirrors Large Language Model (LLM) practices (next-token → next-patch) while remaining lightweight compared to a classic LLM and practical.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
The model card is available on `assets/modelcard.pdf`.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Our model (with leakage) is deployed on the [TS-Arena benchmark](https://ts-arena.live) and achieves competitive performance against state-of-the-art methods under the name `LITIS/PatchFM-Large`.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
## Results on the FEV benchmark:
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
**Our model (without leakage) remains competitive, though it does not match the top-performing state-of-the-art models.**
|
|
54
|
+
|
|
55
|
+
<div style="overflow-x: auto;">
|
|
56
|
+
|
|
57
|
+
| Metric | Chronos-2 | TiRex-2 | Toto-2.0-2.5B | Toto-2.0-1B | Toto-2.0-313m | TimesFM-2.5 | TiRex | Toto-2.0-22m | TS-ICL | **PatchFM** | FlowState | TabPFN-TS-3 | citras-fm | Toto-1.0 | Toto-2.0-4m | Moirai-2.0 | Chronos-Bolt | TFT | Sundial-Base | PatchTST | CatBoost | LightGBM | AutoTheta | Seasonal Naive | Naive | Drift |
|
|
58
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
59
|
+
| Win Rate | 0.81 | 0.78 | 0.77 | 0.77 | 0.75 | 0.71 | 0.70 | 0.67 | 0.61 | **0.61** | 0.60 | 0.60 | 0.58 | 0.57 | 0.55 | 0.53 | 0.49 | 0.37 | 0.32 | 0.31 | 0.22 | 0.21 | 0.20 | 0.13 | 0.09 | 0.07 |
|
|
60
|
+
| Skill Score | 0.47 | 0.45 | 0.44 | 0.44 | 0.44 | 0.47 | 0.43 | 0.43 | 0.43 | **0.41** | 0.42 | 0.43 | 0.41 | 0.41 | 0.41 | 0.40 | 0.39 | 0.32 | 0.34 | 0.30 | 0.23 | 0.21 | 0.05 | 0.00 | -0.45 | -0.46 |
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Acknowledgements
|
|
66
|
+
This work builds on ideas, model designs, and code from the following projects:
|
|
67
|
+
- [TiRex](https://github.com/NX-AI/tirex)
|
|
68
|
+
- [Toto](https://github.com/datadog/toto)
|
|
69
|
+
- [TimesFM](https://github.com/google-research/timesfm)
|
|
70
|
+
- [Chronos](https://github.com/amazon-science/chronos-forecasting)
|
|
71
|
+
We are grateful to their authors.
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
## Highlights
|
|
@@ -74,14 +77,24 @@ Our model is deployed on the [TS-Arena benchmark](https://ts-arena.live) and ach
|
|
|
74
77
|
- Causal masking self-attention with RoPE (relative positions)
|
|
75
78
|
- Causal RevIN with $\sinh^{-1}$ transform for normalization
|
|
76
79
|
- SwiGLU feed-forward networks
|
|
80
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
77
81
|
- Autoregressive multi-quantile decoding [MOIRAI2.0](https://arxiv.org/pdf/2511.11698)
|
|
78
82
|
- KV-cache for efficient long-horizon inference
|
|
79
83
|
- flips equivariance during inference (optional) [Reverso](https://arxiv.org/pdf/2602.17634v1)
|
|
84
|
+
- [Muon](https://kellerjordan.github.io/posts/muon/) optimizer for 2D weights and AdamW for the rest, as in Toto 2.0 ([implementation of Muon used](https://arxiv.org/abs/2502.16982))
|
|
80
85
|
|
|
81
86
|
## Quick Start
|
|
82
87
|
|
|
83
88
|
### from source code
|
|
84
89
|
|
|
90
|
+
> **⚠️ Important:** Two pretrained versions of PatchFM are available:
|
|
91
|
+
|
|
92
|
+
>
|
|
93
|
+
|
|
94
|
+
> - **`PatchFM-Leakage`**: trained on the GIFT-Eval datasets. This version may provide higher benchmark performance but includes training data that overlaps with the commonly used evaluation datasets.
|
|
95
|
+
|
|
96
|
+
> - **`PatchFM`** (**recommended for fair evaluation**): trained without any commonly used benchmark (except BOOM), preventing potential data leakage and ensuring a more reliable assessment of generalization performance.
|
|
97
|
+
|
|
85
98
|
1. Clone the repository and install dependencies
|
|
86
99
|
```bash
|
|
87
100
|
git clone https://github.com/vilhess/PatchFM
|
|
@@ -97,7 +110,7 @@ from configs import PatchFMConfig
|
|
|
97
110
|
from model import Forecaster
|
|
98
111
|
|
|
99
112
|
# --- Instantiate model ---
|
|
100
|
-
config = PatchFMConfig(load_from_hub=True)
|
|
113
|
+
config = PatchFMConfig(load_from_hub=True, full_leakage=False) # set full_leakage=True to load the version trained with data leakage
|
|
101
114
|
model = Forecaster(config)
|
|
102
115
|
|
|
103
116
|
# --- Inference ---
|
|
@@ -119,6 +132,7 @@ import torch
|
|
|
119
132
|
|
|
120
133
|
from patchfm import Forecaster, PatchFMConfig
|
|
121
134
|
|
|
135
|
+
config = PatchFMConfig(full_leakage=False)
|
|
122
136
|
# same as above
|
|
123
137
|
pred_median, pred_quantiles = model(seq, forecast_horizon=forecast_horizon, quantiles=[0.1, 0.5, 0.9], flip_equivariance=True) # (batch, time), (batch, time, quantiles)
|
|
124
138
|
```
|
|
@@ -152,6 +166,7 @@ Aggregate over positions, patch elements, and quantiles.
|
|
|
152
166
|
- Input MLP: $\mathbb{R}^{P_{len}} \to \mathbb{R}^{dim}$ residual 2-layer MLP (ReLU)
|
|
153
167
|
- Multi-Head Attention: causal mask, RoPE; queries/keys/values per head
|
|
154
168
|
- FFN: SwiGLU (SiLU-gated), pre-norm + residual
|
|
169
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
155
170
|
- Output heads: |Q| linear maps $\mathbb{R}^{dim} \to \mathbb{R}^{P_{len}}$ (one per quantile)
|
|
156
171
|
|
|
157
172
|
### Model Details
|
|
@@ -234,10 +249,12 @@ With this strategy, the median prediction (0.5 quantile) does **not necessarily*
|
|
|
234
249
|
This discrepancy arises because the *quantile collapse* step aggregates predictions across all possible quantile paths. As a result, the median is computed from the combined multi-path distribution rather than from a single deterministic trajectory, which can lead to different estimates compared to the single-path (median-only) autoregressive approach.
|
|
235
250
|
|
|
236
251
|
## Datasets
|
|
237
|
-
- GIFT-
|
|
252
|
+
- GIFT-pretraining dataset [GIFT]: aligned with the GIFT-Eval dataset but without data leakage issue with the benchmark. The dataset contains approximately 71 univariate and 17 multivariate time series datasets from various
|
|
238
253
|
domains and various frequencies. After preprocessing, this yields approximately 600K univariate series.
|
|
239
254
|
- Chronos synthetic datasets [Chronos]: two large synthetic datasets generated with Chronos, one with TSMixup and one with KernelSynth. Each contains approximately 10 million univariate series and 1 million respectively, each signal of length 1024.
|
|
240
255
|
- Artificial: ~1M synthetic series (sinusoidal, linear, polynomial, logarithmic) plus mixtures via TSMixup [Chronos]; Gaussian Process samples via KernelSynth (mixtures of RBF/periodic/linear kernels with swept hyperparameters).
|
|
256
|
+
- BOOM: Approximately 5 million time series of length 1024 sampled from the BOOM dataset [BOOM]. It is important to note that the BOOM dataset, which is also used as an evaluation benchmark, is included in the training data of both PatchFM variants (with and without benchmark leakage) to increase data diversity. Consequently, neither version can be fairly evaluated on the BOOM benchmark. Upon request, we can provide an alternative checkpoint trained without any BOOM data, enabling a fair evaluation on this benchmark.
|
|
257
|
+
- TSMixup [Chronos]: mixing synthetic datasets and GIFT-pretraining datasets to further increase diversity.
|
|
241
258
|
|
|
242
259
|
## Repository Layout
|
|
243
260
|
|
|
@@ -254,14 +271,12 @@ domains and various frequencies. After preprocessing, this yields approximately
|
|
|
254
271
|
|
|
255
272
|
- `dataset/` — data loading and preprocessing
|
|
256
273
|
- `artificial.py` — synthetic dataset : artificial signals + TSMixup + KernelSynth
|
|
274
|
+
- `synthetic.py` — other synthetic signals (Claude for the generation)
|
|
257
275
|
- `gift.py` — GIFT-Eval pretraining dataset loading and preprocessing
|
|
258
276
|
- `get_data.py` — utility to fetch and preprocess datasets
|
|
259
277
|
- `chronosdata.py` — loading of the synthetic datasets generated with Chronos (TSMixup and KernelSynth) with download functions integrated
|
|
278
|
+
- `boom.py` — loading of the BOOM dataset with download functions integrated
|
|
260
279
|
|
|
261
280
|
- `configs/` — model and training configurations
|
|
262
281
|
- `notebooks/inference` — how to load a trained model and generate forecasts
|
|
263
282
|
- `training.py` — training script using PyTorch Lightning
|
|
264
|
-
|
|
265
|
-
## Acknowledgements
|
|
266
|
-
We thank the authors of the following repositories for inspiration and code snippets:
|
|
267
|
-
- [TiRex](https://github.com/NX-AI/tirex)
|
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
# A
|
|
1
|
+
# A Foundation Model for Univariate Time Series Forecasting
|
|
2
2
|
|
|
3
3
|
A concise, reproducible recipe for training a transformer-based, patch-to-patch forecasting model for univariate time series. The approach mirrors Large Language Model (LLM) practices (next-token → next-patch) while remaining lightweight compared to a classic LLM and practical.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The model card is available on `assets/modelcard.pdf`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Our model (with leakage) is deployed on the [TS-Arena benchmark](https://ts-arena.live) and achieves competitive performance against state-of-the-art methods under the name `LITIS/PatchFM-Large`.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Results on the FEV benchmark:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
**Our model (without leakage) remains competitive, though it does not match the top-performing state-of-the-art models.**
|
|
12
|
+
|
|
13
|
+
<div style="overflow-x: auto;">
|
|
14
|
+
|
|
15
|
+
| Metric | Chronos-2 | TiRex-2 | Toto-2.0-2.5B | Toto-2.0-1B | Toto-2.0-313m | TimesFM-2.5 | TiRex | Toto-2.0-22m | TS-ICL | **PatchFM** | FlowState | TabPFN-TS-3 | citras-fm | Toto-1.0 | Toto-2.0-4m | Moirai-2.0 | Chronos-Bolt | TFT | Sundial-Base | PatchTST | CatBoost | LightGBM | AutoTheta | Seasonal Naive | Naive | Drift |
|
|
16
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
17
|
+
| Win Rate | 0.81 | 0.78 | 0.77 | 0.77 | 0.75 | 0.71 | 0.70 | 0.67 | 0.61 | **0.61** | 0.60 | 0.60 | 0.58 | 0.57 | 0.55 | 0.53 | 0.49 | 0.37 | 0.32 | 0.31 | 0.22 | 0.21 | 0.20 | 0.13 | 0.09 | 0.07 |
|
|
18
|
+
| Skill Score | 0.47 | 0.45 | 0.44 | 0.44 | 0.44 | 0.47 | 0.43 | 0.43 | 0.43 | **0.41** | 0.42 | 0.43 | 0.41 | 0.41 | 0.41 | 0.40 | 0.39 | 0.32 | 0.34 | 0.30 | 0.23 | 0.21 | 0.05 | 0.00 | -0.45 | -0.46 |
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Acknowledgements
|
|
24
|
+
This work builds on ideas, model designs, and code from the following projects:
|
|
25
|
+
- [TiRex](https://github.com/NX-AI/tirex)
|
|
26
|
+
- [Toto](https://github.com/datadog/toto)
|
|
27
|
+
- [TimesFM](https://github.com/google-research/timesfm)
|
|
28
|
+
- [Chronos](https://github.com/amazon-science/chronos-forecasting)
|
|
29
|
+
We are grateful to their authors.
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
## Highlights
|
|
@@ -32,14 +35,24 @@ Our model is deployed on the [TS-Arena benchmark](https://ts-arena.live) and ach
|
|
|
32
35
|
- Causal masking self-attention with RoPE (relative positions)
|
|
33
36
|
- Causal RevIN with $\sinh^{-1}$ transform for normalization
|
|
34
37
|
- SwiGLU feed-forward networks
|
|
38
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
35
39
|
- Autoregressive multi-quantile decoding [MOIRAI2.0](https://arxiv.org/pdf/2511.11698)
|
|
36
40
|
- KV-cache for efficient long-horizon inference
|
|
37
41
|
- flips equivariance during inference (optional) [Reverso](https://arxiv.org/pdf/2602.17634v1)
|
|
42
|
+
- [Muon](https://kellerjordan.github.io/posts/muon/) optimizer for 2D weights and AdamW for the rest, as in Toto 2.0 ([implementation of Muon used](https://arxiv.org/abs/2502.16982))
|
|
38
43
|
|
|
39
44
|
## Quick Start
|
|
40
45
|
|
|
41
46
|
### from source code
|
|
42
47
|
|
|
48
|
+
> **⚠️ Important:** Two pretrained versions of PatchFM are available:
|
|
49
|
+
|
|
50
|
+
>
|
|
51
|
+
|
|
52
|
+
> - **`PatchFM-Leakage`**: trained on the GIFT-Eval datasets. This version may provide higher benchmark performance but includes training data that overlaps with the commonly used evaluation datasets.
|
|
53
|
+
|
|
54
|
+
> - **`PatchFM`** (**recommended for fair evaluation**): trained without any commonly used benchmark (except BOOM), preventing potential data leakage and ensuring a more reliable assessment of generalization performance.
|
|
55
|
+
|
|
43
56
|
1. Clone the repository and install dependencies
|
|
44
57
|
```bash
|
|
45
58
|
git clone https://github.com/vilhess/PatchFM
|
|
@@ -55,7 +68,7 @@ from configs import PatchFMConfig
|
|
|
55
68
|
from model import Forecaster
|
|
56
69
|
|
|
57
70
|
# --- Instantiate model ---
|
|
58
|
-
config = PatchFMConfig(load_from_hub=True)
|
|
71
|
+
config = PatchFMConfig(load_from_hub=True, full_leakage=False) # set full_leakage=True to load the version trained with data leakage
|
|
59
72
|
model = Forecaster(config)
|
|
60
73
|
|
|
61
74
|
# --- Inference ---
|
|
@@ -77,6 +90,7 @@ import torch
|
|
|
77
90
|
|
|
78
91
|
from patchfm import Forecaster, PatchFMConfig
|
|
79
92
|
|
|
93
|
+
config = PatchFMConfig(full_leakage=False)
|
|
80
94
|
# same as above
|
|
81
95
|
pred_median, pred_quantiles = model(seq, forecast_horizon=forecast_horizon, quantiles=[0.1, 0.5, 0.9], flip_equivariance=True) # (batch, time), (batch, time, quantiles)
|
|
82
96
|
```
|
|
@@ -110,6 +124,7 @@ Aggregate over positions, patch elements, and quantiles.
|
|
|
110
124
|
- Input MLP: $\mathbb{R}^{P_{len}} \to \mathbb{R}^{dim}$ residual 2-layer MLP (ReLU)
|
|
111
125
|
- Multi-Head Attention: causal mask, RoPE; queries/keys/values per head
|
|
112
126
|
- FFN: SwiGLU (SiLU-gated), pre-norm + residual
|
|
127
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
113
128
|
- Output heads: |Q| linear maps $\mathbb{R}^{dim} \to \mathbb{R}^{P_{len}}$ (one per quantile)
|
|
114
129
|
|
|
115
130
|
### Model Details
|
|
@@ -192,10 +207,12 @@ With this strategy, the median prediction (0.5 quantile) does **not necessarily*
|
|
|
192
207
|
This discrepancy arises because the *quantile collapse* step aggregates predictions across all possible quantile paths. As a result, the median is computed from the combined multi-path distribution rather than from a single deterministic trajectory, which can lead to different estimates compared to the single-path (median-only) autoregressive approach.
|
|
193
208
|
|
|
194
209
|
## Datasets
|
|
195
|
-
- GIFT-
|
|
210
|
+
- GIFT-pretraining dataset [GIFT]: aligned with the GIFT-Eval dataset but without data leakage issue with the benchmark. The dataset contains approximately 71 univariate and 17 multivariate time series datasets from various
|
|
196
211
|
domains and various frequencies. After preprocessing, this yields approximately 600K univariate series.
|
|
197
212
|
- Chronos synthetic datasets [Chronos]: two large synthetic datasets generated with Chronos, one with TSMixup and one with KernelSynth. Each contains approximately 10 million univariate series and 1 million respectively, each signal of length 1024.
|
|
198
213
|
- Artificial: ~1M synthetic series (sinusoidal, linear, polynomial, logarithmic) plus mixtures via TSMixup [Chronos]; Gaussian Process samples via KernelSynth (mixtures of RBF/periodic/linear kernels with swept hyperparameters).
|
|
214
|
+
- BOOM: Approximately 5 million time series of length 1024 sampled from the BOOM dataset [BOOM]. It is important to note that the BOOM dataset, which is also used as an evaluation benchmark, is included in the training data of both PatchFM variants (with and without benchmark leakage) to increase data diversity. Consequently, neither version can be fairly evaluated on the BOOM benchmark. Upon request, we can provide an alternative checkpoint trained without any BOOM data, enabling a fair evaluation on this benchmark.
|
|
215
|
+
- TSMixup [Chronos]: mixing synthetic datasets and GIFT-pretraining datasets to further increase diversity.
|
|
199
216
|
|
|
200
217
|
## Repository Layout
|
|
201
218
|
|
|
@@ -212,14 +229,12 @@ domains and various frequencies. After preprocessing, this yields approximately
|
|
|
212
229
|
|
|
213
230
|
- `dataset/` — data loading and preprocessing
|
|
214
231
|
- `artificial.py` — synthetic dataset : artificial signals + TSMixup + KernelSynth
|
|
232
|
+
- `synthetic.py` — other synthetic signals (Claude for the generation)
|
|
215
233
|
- `gift.py` — GIFT-Eval pretraining dataset loading and preprocessing
|
|
216
234
|
- `get_data.py` — utility to fetch and preprocess datasets
|
|
217
235
|
- `chronosdata.py` — loading of the synthetic datasets generated with Chronos (TSMixup and KernelSynth) with download functions integrated
|
|
236
|
+
- `boom.py` — loading of the BOOM dataset with download functions integrated
|
|
218
237
|
|
|
219
238
|
- `configs/` — model and training configurations
|
|
220
239
|
- `notebooks/inference` — how to load a trained model and generate forecasts
|
|
221
|
-
- `training.py` — training script using PyTorch Lightning
|
|
222
|
-
|
|
223
|
-
## Acknowledgements
|
|
224
|
-
We thank the authors of the following repositories for inspiration and code snippets:
|
|
225
|
-
- [TiRex](https://github.com/NX-AI/tirex)
|
|
240
|
+
- `training.py` — training script using PyTorch Lightning
|
|
@@ -5,13 +5,13 @@ from dataclasses import asdict, dataclass, field
|
|
|
5
5
|
class PatchFMConfig:
|
|
6
6
|
max_seq_len: int = 1024
|
|
7
7
|
patch_len: int = 32
|
|
8
|
-
d_model: int =
|
|
9
|
-
n_heads: int =
|
|
10
|
-
n_layers_encoder: int =
|
|
8
|
+
d_model: int = 1024
|
|
9
|
+
n_heads: int = 16
|
|
10
|
+
n_layers_encoder: int = 20
|
|
11
11
|
quantiles: list[float] = field(default_factory=lambda: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
|
|
12
12
|
|
|
13
|
-
compile: bool =
|
|
14
|
-
full_leakage: bool =
|
|
13
|
+
compile: bool = False
|
|
14
|
+
full_leakage: bool = False
|
|
15
15
|
|
|
16
16
|
def __getitem__(self, key):
|
|
17
17
|
return getattr(self, key)
|
|
@@ -4,7 +4,8 @@ import torch.nn as nn
|
|
|
4
4
|
from einops import rearrange
|
|
5
5
|
|
|
6
6
|
from patchfm.inference.modules import (CausalRevIN, PatchFM, ResidualBlock,
|
|
7
|
-
SeqTypeConverter, TransformerEncoder
|
|
7
|
+
SeqTypeConverter, TransformerEncoder,
|
|
8
|
+
quantile_widths, monotone_quantiles)
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# --- Forecaster Model ---
|
|
@@ -22,6 +23,9 @@ class Forecaster(nn.Module):
|
|
|
22
23
|
self.n_quantiles = len(self.quantiles)
|
|
23
24
|
self.max_patches = self.max_seq_len // self.patch_len
|
|
24
25
|
|
|
26
|
+
widths, self.median_idx = quantile_widths(self.quantiles)
|
|
27
|
+
self.register_buffer("quantile_widths", widths, persistent=False)
|
|
28
|
+
|
|
25
29
|
if config["full_leakage"]:
|
|
26
30
|
print("Loading full leakage model from HuggingFace Hub...")
|
|
27
31
|
model_name = "vilhess/PatchFM-Leakage"
|
|
@@ -39,14 +43,16 @@ class Forecaster(nn.Module):
|
|
|
39
43
|
self.converter = SeqTypeConverter()
|
|
40
44
|
|
|
41
45
|
if config["compile"]:
|
|
42
|
-
self = torch.compile(self)
|
|
46
|
+
self.proj_embedding = torch.compile(self.proj_embedding)
|
|
47
|
+
self.transformer_encoder = torch.compile(self.transformer_encoder)
|
|
48
|
+
self.proj_output = torch.compile(self.proj_output)
|
|
43
49
|
|
|
44
50
|
def _init_components(self):
|
|
45
51
|
"""Initialize modules from scratch."""
|
|
46
52
|
self.revin = CausalRevIN()
|
|
47
53
|
self.proj_embedding = ResidualBlock(
|
|
48
54
|
in_dim=self.patch_len,
|
|
49
|
-
hid_dim=
|
|
55
|
+
hid_dim=self.d_model,
|
|
50
56
|
out_dim=self.d_model
|
|
51
57
|
)
|
|
52
58
|
self.transformer_encoder = TransformerEncoder(
|
|
@@ -101,6 +107,19 @@ class Forecaster(nn.Module):
|
|
|
101
107
|
x = x[:, -1:, :] # Keep only the last patch for autoregressive forecasting
|
|
102
108
|
|
|
103
109
|
forecasting = self.proj_output(x)
|
|
110
|
+
forecasting = rearrange(
|
|
111
|
+
forecasting,
|
|
112
|
+
"b 1 (pl q) -> b 1 pl q",
|
|
113
|
+
pl=self.patch_len,
|
|
114
|
+
q=self.n_quantiles,
|
|
115
|
+
)
|
|
116
|
+
forecasting = monotone_quantiles(
|
|
117
|
+
forecasting, self.quantile_widths, self.median_idx, width_factor=2.0
|
|
118
|
+
)
|
|
119
|
+
forecasting = rearrange(
|
|
120
|
+
forecasting,
|
|
121
|
+
"b 1 pl q -> b 1 (pl q)",
|
|
122
|
+
)
|
|
104
123
|
forecasting = self.revin(forecasting, mode="denorm")
|
|
105
124
|
|
|
106
125
|
# Reshape to (bs, patch_num, patch_len, n_quantiles)
|
|
@@ -120,6 +139,19 @@ class Forecaster(nn.Module):
|
|
|
120
139
|
x = self.transformer_encoder(x)
|
|
121
140
|
x = x[:, -1:, :] # Keep only the last patch for autoregressive forecasting
|
|
122
141
|
forecasting = self.proj_output(x)
|
|
142
|
+
forecasting = rearrange(
|
|
143
|
+
forecasting,
|
|
144
|
+
"b 1 (pl q) -> b 1 pl q",
|
|
145
|
+
pl=self.patch_len,
|
|
146
|
+
q=self.n_quantiles,
|
|
147
|
+
)
|
|
148
|
+
forecasting = monotone_quantiles(
|
|
149
|
+
forecasting, self.quantile_widths, self.median_idx, width_factor=2.0
|
|
150
|
+
)
|
|
151
|
+
forecasting = rearrange(
|
|
152
|
+
forecasting,
|
|
153
|
+
"b 1 pl q -> b 1 (pl q)",
|
|
154
|
+
)
|
|
123
155
|
forecasting = self.revin(forecasting, mode="denorm")
|
|
124
156
|
|
|
125
157
|
# Reshape to (bs*n_quantiles, patch_num, patch_len, n_quantiles)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import torch
|
|
5
5
|
import torch.nn as nn
|
|
6
|
+
import math
|
|
6
7
|
from einops import rearrange
|
|
7
8
|
from huggingface_hub import PyTorchModelHubMixin
|
|
8
9
|
from rotary_embedding_torch import RotaryEmbedding
|
|
@@ -86,9 +87,8 @@ def nanstd(o, dim, keepdim=False):
|
|
|
86
87
|
return std
|
|
87
88
|
|
|
88
89
|
class CausalRevIN(nn.Module):
|
|
89
|
-
def __init__(self
|
|
90
|
+
def __init__(self):
|
|
90
91
|
super().__init__()
|
|
91
|
-
self.eps = eps
|
|
92
92
|
self.cached_mean = None
|
|
93
93
|
self.cached_std = None
|
|
94
94
|
|
|
@@ -158,7 +158,7 @@ class CausalRevIN(nn.Module):
|
|
|
158
158
|
self.cached_cumsum_x2 = cumsum_x2[:, -1:, :]
|
|
159
159
|
|
|
160
160
|
var = (cumsum_x2 - 2 * mean * cumsum_x + counts * mean**2) / counts
|
|
161
|
-
std = torch.sqrt(var
|
|
161
|
+
std = torch.sqrt(var.clamp_min(0)).clamp_min(1e-3)
|
|
162
162
|
|
|
163
163
|
return mean, std
|
|
164
164
|
|
|
@@ -184,14 +184,30 @@ class ResidualBlock(nn.Module):
|
|
|
184
184
|
out = out+res
|
|
185
185
|
return out
|
|
186
186
|
|
|
187
|
+
class PerDimScale(nn.Module):
|
|
188
|
+
def __init__(self, num_dims: int):
|
|
189
|
+
super().__init__()
|
|
190
|
+
self.num_dims = num_dims
|
|
191
|
+
self.per_dim_scale = nn.Parameter(torch.zeros(num_dims))
|
|
192
|
+
self._RECIPROCAL_OF_SOFTPLUS_0 = 1.442695041
|
|
193
|
+
|
|
194
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
195
|
+
return (
|
|
196
|
+
x
|
|
197
|
+
* self._RECIPROCAL_OF_SOFTPLUS_0
|
|
198
|
+
/ math.sqrt(self.num_dims)
|
|
199
|
+
* torch.nn.functional.softplus(self.per_dim_scale)
|
|
200
|
+
)
|
|
201
|
+
|
|
187
202
|
class MultiHeadAttention(nn.Module):
|
|
188
203
|
def __init__(self, d_model, n_heads, last=False):
|
|
189
204
|
super().__init__()
|
|
190
205
|
assert d_model%n_heads==0, f"d_model ({d_model}) must be divisible by n_heads ({n_heads})"
|
|
191
206
|
|
|
192
|
-
self.WQ = nn.Linear(d_model, d_model)
|
|
193
|
-
self.WK = nn.Linear(d_model, d_model)
|
|
194
|
-
self.WV = nn.Linear(d_model, d_model)
|
|
207
|
+
self.WQ = nn.Linear(d_model, d_model, bias=False)
|
|
208
|
+
self.WK = nn.Linear(d_model, d_model, bias=False)
|
|
209
|
+
self.WV = nn.Linear(d_model, d_model, bias=False)
|
|
210
|
+
self.gate = nn.Linear(d_model, d_model, bias=False)
|
|
195
211
|
|
|
196
212
|
self.out_proj = nn.Linear(d_model, d_model)
|
|
197
213
|
|
|
@@ -200,6 +216,10 @@ class MultiHeadAttention(nn.Module):
|
|
|
200
216
|
|
|
201
217
|
self.rope = RotaryEmbedding(dim=self.head_dim//2)
|
|
202
218
|
|
|
219
|
+
self.q_norm = nn.RMSNorm(self.head_dim)
|
|
220
|
+
self.k_norm = nn.RMSNorm(self.head_dim)
|
|
221
|
+
self.per_dim_scale = PerDimScale(self.head_dim)
|
|
222
|
+
|
|
203
223
|
self.k_cache = None
|
|
204
224
|
self.v_cache = None
|
|
205
225
|
|
|
@@ -218,6 +238,8 @@ class MultiHeadAttention(nn.Module):
|
|
|
218
238
|
is_causal = False
|
|
219
239
|
offset += (context - 1)
|
|
220
240
|
|
|
241
|
+
gated = self.gate(q)
|
|
242
|
+
|
|
221
243
|
q = self.WQ(q).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
222
244
|
k = self.WK(k).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
223
245
|
v = self.WV(v).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
@@ -237,9 +259,14 @@ class MultiHeadAttention(nn.Module):
|
|
|
237
259
|
q = self.rope.rotate_queries_or_keys(q, offset=offset)
|
|
238
260
|
k = self.rope.rotate_queries_or_keys(k)
|
|
239
261
|
|
|
240
|
-
|
|
262
|
+
q = self.q_norm(q)
|
|
263
|
+
k = self.k_norm(k)
|
|
264
|
+
q = self.per_dim_scale(q)
|
|
265
|
+
|
|
266
|
+
values = nn.functional.scaled_dot_product_attention(q, k, v, is_causal=is_causal, scale=1.0)
|
|
241
267
|
|
|
242
268
|
values = values.transpose(1, 2).reshape(bs, -1, dim)
|
|
269
|
+
values = gated.sigmoid() * values
|
|
243
270
|
values = self.out_proj(values)
|
|
244
271
|
return values
|
|
245
272
|
|
|
@@ -269,9 +296,9 @@ class FeedForward(nn.Module):
|
|
|
269
296
|
class TransformerEncoderLayer(nn.Module):
|
|
270
297
|
def __init__(self, d_model, n_heads, last=False):
|
|
271
298
|
super().__init__()
|
|
272
|
-
self.ln1 = nn.
|
|
299
|
+
self.ln1 = nn.RMSNorm(d_model)
|
|
273
300
|
self.attn = MultiHeadAttention(d_model=d_model, n_heads=n_heads, last=last)
|
|
274
|
-
self.ln2 = nn.
|
|
301
|
+
self.ln2 = nn.RMSNorm(d_model)
|
|
275
302
|
self.ff = FeedForward(d_model=d_model)
|
|
276
303
|
|
|
277
304
|
def forward(self, x):
|
|
@@ -290,12 +317,34 @@ class TransformerEncoder(nn.Module):
|
|
|
290
317
|
]
|
|
291
318
|
)
|
|
292
319
|
self.layers.append(TransformerEncoderLayer(d_model=d_model, n_heads=n_heads, last=True))
|
|
293
|
-
self.norm = nn.
|
|
320
|
+
self.norm = nn.RMSNorm(d_model)
|
|
294
321
|
|
|
295
322
|
def forward(self, x):
|
|
296
323
|
for layer in self.layers:
|
|
297
324
|
x = layer(x)
|
|
298
325
|
return self.norm(x)
|
|
326
|
+
|
|
327
|
+
def quantile_widths(quantiles):
|
|
328
|
+
m = quantiles.index(0.5)
|
|
329
|
+
widths = [0.0] * len(quantiles)
|
|
330
|
+
for i in range(m + 1, len(quantiles)):
|
|
331
|
+
widths[i] = quantiles[i] - quantiles[i - 1]
|
|
332
|
+
for i in range(m):
|
|
333
|
+
widths[i] = quantiles[i + 1] - quantiles[i]
|
|
334
|
+
return torch.tensor(widths), m
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def monotone_quantiles(raw, widths, m, width_factor=2.0):
|
|
338
|
+
inc = width_factor * widths.to(raw.dtype) * torch.nn.functional.softplus(raw)
|
|
339
|
+
median = raw[..., m : m + 1]
|
|
340
|
+
up = torch.cumsum(inc[..., m + 1 :], dim=-1)
|
|
341
|
+
down = torch.flip(
|
|
342
|
+
torch.cumsum(torch.flip(inc[..., :m], dims=[-1]), dim=-1), dims=[-1]
|
|
343
|
+
)
|
|
344
|
+
big = torch.finfo(raw.dtype).max
|
|
345
|
+
return torch.cat([median - down, median, median + up], dim=-1).clamp(
|
|
346
|
+
min=-big, max=big
|
|
347
|
+
)
|
|
299
348
|
|
|
300
349
|
class PatchFM(nn.Module, PyTorchModelHubMixin):
|
|
301
350
|
def __init__(self, config):
|
|
@@ -313,7 +362,7 @@ class PatchFM(nn.Module, PyTorchModelHubMixin):
|
|
|
313
362
|
self.revin = CausalRevIN()
|
|
314
363
|
self.proj_embedding = ResidualBlock(
|
|
315
364
|
in_dim=self.patch_len,
|
|
316
|
-
hid_dim=
|
|
365
|
+
hid_dim=self.d_model,
|
|
317
366
|
out_dim=self.d_model
|
|
318
367
|
)
|
|
319
368
|
self.transformer_encoder = TransformerEncoder(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: patchfm
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: a Foundation Model for Univariate Time Series Forecasting
|
|
5
5
|
Author-email: Samy-Melwan Vilhes <samy-melwan.vilhes@insa-rouen.fr>
|
|
6
6
|
License: MIT License
|
|
@@ -40,32 +40,35 @@ Requires-Dist: numpy>=1.26.0
|
|
|
40
40
|
Requires-Dist: safetensors==0.5.3
|
|
41
41
|
Dynamic: license-file
|
|
42
42
|
|
|
43
|
-
# A
|
|
43
|
+
# A Foundation Model for Univariate Time Series Forecasting
|
|
44
44
|
|
|
45
45
|
A concise, reproducible recipe for training a transformer-based, patch-to-patch forecasting model for univariate time series. The approach mirrors Large Language Model (LLM) practices (next-token → next-patch) while remaining lightweight compared to a classic LLM and practical.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
The model card is available on `assets/modelcard.pdf`.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Our model (with leakage) is deployed on the [TS-Arena benchmark](https://ts-arena.live) and achieves competitive performance against state-of-the-art methods under the name `LITIS/PatchFM-Large`.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
## Results on the FEV benchmark:
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
**Our model (without leakage) remains competitive, though it does not match the top-performing state-of-the-art models.**
|
|
54
|
+
|
|
55
|
+
<div style="overflow-x: auto;">
|
|
56
|
+
|
|
57
|
+
| Metric | Chronos-2 | TiRex-2 | Toto-2.0-2.5B | Toto-2.0-1B | Toto-2.0-313m | TimesFM-2.5 | TiRex | Toto-2.0-22m | TS-ICL | **PatchFM** | FlowState | TabPFN-TS-3 | citras-fm | Toto-1.0 | Toto-2.0-4m | Moirai-2.0 | Chronos-Bolt | TFT | Sundial-Base | PatchTST | CatBoost | LightGBM | AutoTheta | Seasonal Naive | Naive | Drift |
|
|
58
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
59
|
+
| Win Rate | 0.81 | 0.78 | 0.77 | 0.77 | 0.75 | 0.71 | 0.70 | 0.67 | 0.61 | **0.61** | 0.60 | 0.60 | 0.58 | 0.57 | 0.55 | 0.53 | 0.49 | 0.37 | 0.32 | 0.31 | 0.22 | 0.21 | 0.20 | 0.13 | 0.09 | 0.07 |
|
|
60
|
+
| Skill Score | 0.47 | 0.45 | 0.44 | 0.44 | 0.44 | 0.47 | 0.43 | 0.43 | 0.43 | **0.41** | 0.42 | 0.43 | 0.41 | 0.41 | 0.41 | 0.40 | 0.39 | 0.32 | 0.34 | 0.30 | 0.23 | 0.21 | 0.05 | 0.00 | -0.45 | -0.46 |
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Acknowledgements
|
|
66
|
+
This work builds on ideas, model designs, and code from the following projects:
|
|
67
|
+
- [TiRex](https://github.com/NX-AI/tirex)
|
|
68
|
+
- [Toto](https://github.com/datadog/toto)
|
|
69
|
+
- [TimesFM](https://github.com/google-research/timesfm)
|
|
70
|
+
- [Chronos](https://github.com/amazon-science/chronos-forecasting)
|
|
71
|
+
We are grateful to their authors.
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
## Highlights
|
|
@@ -74,14 +77,24 @@ Our model is deployed on the [TS-Arena benchmark](https://ts-arena.live) and ach
|
|
|
74
77
|
- Causal masking self-attention with RoPE (relative positions)
|
|
75
78
|
- Causal RevIN with $\sinh^{-1}$ transform for normalization
|
|
76
79
|
- SwiGLU feed-forward networks
|
|
80
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
77
81
|
- Autoregressive multi-quantile decoding [MOIRAI2.0](https://arxiv.org/pdf/2511.11698)
|
|
78
82
|
- KV-cache for efficient long-horizon inference
|
|
79
83
|
- flips equivariance during inference (optional) [Reverso](https://arxiv.org/pdf/2602.17634v1)
|
|
84
|
+
- [Muon](https://kellerjordan.github.io/posts/muon/) optimizer for 2D weights and AdamW for the rest, as in Toto 2.0 ([implementation of Muon used](https://arxiv.org/abs/2502.16982))
|
|
80
85
|
|
|
81
86
|
## Quick Start
|
|
82
87
|
|
|
83
88
|
### from source code
|
|
84
89
|
|
|
90
|
+
> **⚠️ Important:** Two pretrained versions of PatchFM are available:
|
|
91
|
+
|
|
92
|
+
>
|
|
93
|
+
|
|
94
|
+
> - **`PatchFM-Leakage`**: trained on the GIFT-Eval datasets. This version may provide higher benchmark performance but includes training data that overlaps with the commonly used evaluation datasets.
|
|
95
|
+
|
|
96
|
+
> - **`PatchFM`** (**recommended for fair evaluation**): trained without any commonly used benchmark (except BOOM), preventing potential data leakage and ensuring a more reliable assessment of generalization performance.
|
|
97
|
+
|
|
85
98
|
1. Clone the repository and install dependencies
|
|
86
99
|
```bash
|
|
87
100
|
git clone https://github.com/vilhess/PatchFM
|
|
@@ -97,7 +110,7 @@ from configs import PatchFMConfig
|
|
|
97
110
|
from model import Forecaster
|
|
98
111
|
|
|
99
112
|
# --- Instantiate model ---
|
|
100
|
-
config = PatchFMConfig(load_from_hub=True)
|
|
113
|
+
config = PatchFMConfig(load_from_hub=True, full_leakage=False) # set full_leakage=True to load the version trained with data leakage
|
|
101
114
|
model = Forecaster(config)
|
|
102
115
|
|
|
103
116
|
# --- Inference ---
|
|
@@ -119,6 +132,7 @@ import torch
|
|
|
119
132
|
|
|
120
133
|
from patchfm import Forecaster, PatchFMConfig
|
|
121
134
|
|
|
135
|
+
config = PatchFMConfig(full_leakage=False)
|
|
122
136
|
# same as above
|
|
123
137
|
pred_median, pred_quantiles = model(seq, forecast_horizon=forecast_horizon, quantiles=[0.1, 0.5, 0.9], flip_equivariance=True) # (batch, time), (batch, time, quantiles)
|
|
124
138
|
```
|
|
@@ -152,6 +166,7 @@ Aggregate over positions, patch elements, and quantiles.
|
|
|
152
166
|
- Input MLP: $\mathbb{R}^{P_{len}} \to \mathbb{R}^{dim}$ residual 2-layer MLP (ReLU)
|
|
153
167
|
- Multi-Head Attention: causal mask, RoPE; queries/keys/values per head
|
|
154
168
|
- FFN: SwiGLU (SiLU-gated), pre-norm + residual
|
|
169
|
+
- QK-Norm with per-dimension adaptive scaling on queries, following [TimesFM-3](https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/)
|
|
155
170
|
- Output heads: |Q| linear maps $\mathbb{R}^{dim} \to \mathbb{R}^{P_{len}}$ (one per quantile)
|
|
156
171
|
|
|
157
172
|
### Model Details
|
|
@@ -234,10 +249,12 @@ With this strategy, the median prediction (0.5 quantile) does **not necessarily*
|
|
|
234
249
|
This discrepancy arises because the *quantile collapse* step aggregates predictions across all possible quantile paths. As a result, the median is computed from the combined multi-path distribution rather than from a single deterministic trajectory, which can lead to different estimates compared to the single-path (median-only) autoregressive approach.
|
|
235
250
|
|
|
236
251
|
## Datasets
|
|
237
|
-
- GIFT-
|
|
252
|
+
- GIFT-pretraining dataset [GIFT]: aligned with the GIFT-Eval dataset but without data leakage issue with the benchmark. The dataset contains approximately 71 univariate and 17 multivariate time series datasets from various
|
|
238
253
|
domains and various frequencies. After preprocessing, this yields approximately 600K univariate series.
|
|
239
254
|
- Chronos synthetic datasets [Chronos]: two large synthetic datasets generated with Chronos, one with TSMixup and one with KernelSynth. Each contains approximately 10 million univariate series and 1 million respectively, each signal of length 1024.
|
|
240
255
|
- Artificial: ~1M synthetic series (sinusoidal, linear, polynomial, logarithmic) plus mixtures via TSMixup [Chronos]; Gaussian Process samples via KernelSynth (mixtures of RBF/periodic/linear kernels with swept hyperparameters).
|
|
256
|
+
- BOOM: Approximately 5 million time series of length 1024 sampled from the BOOM dataset [BOOM]. It is important to note that the BOOM dataset, which is also used as an evaluation benchmark, is included in the training data of both PatchFM variants (with and without benchmark leakage) to increase data diversity. Consequently, neither version can be fairly evaluated on the BOOM benchmark. Upon request, we can provide an alternative checkpoint trained without any BOOM data, enabling a fair evaluation on this benchmark.
|
|
257
|
+
- TSMixup [Chronos]: mixing synthetic datasets and GIFT-pretraining datasets to further increase diversity.
|
|
241
258
|
|
|
242
259
|
## Repository Layout
|
|
243
260
|
|
|
@@ -254,14 +271,12 @@ domains and various frequencies. After preprocessing, this yields approximately
|
|
|
254
271
|
|
|
255
272
|
- `dataset/` — data loading and preprocessing
|
|
256
273
|
- `artificial.py` — synthetic dataset : artificial signals + TSMixup + KernelSynth
|
|
274
|
+
- `synthetic.py` — other synthetic signals (Claude for the generation)
|
|
257
275
|
- `gift.py` — GIFT-Eval pretraining dataset loading and preprocessing
|
|
258
276
|
- `get_data.py` — utility to fetch and preprocess datasets
|
|
259
277
|
- `chronosdata.py` — loading of the synthetic datasets generated with Chronos (TSMixup and KernelSynth) with download functions integrated
|
|
278
|
+
- `boom.py` — loading of the BOOM dataset with download functions integrated
|
|
260
279
|
|
|
261
280
|
- `configs/` — model and training configurations
|
|
262
281
|
- `notebooks/inference` — how to load a trained model and generate forecasts
|
|
263
282
|
- `training.py` — training script using PyTorch Lightning
|
|
264
|
-
|
|
265
|
-
## Acknowledgements
|
|
266
|
-
We thank the authors of the following repositories for inspiration and code snippets:
|
|
267
|
-
- [TiRex](https://github.com/NX-AI/tirex)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|