metafilm 0.1.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.
- metafilm-0.1.0/.gitignore +37 -0
- metafilm-0.1.0/CHANGELOG.md +22 -0
- metafilm-0.1.0/CITATION.cff +55 -0
- metafilm-0.1.0/LICENSE +21 -0
- metafilm-0.1.0/PKG-INFO +235 -0
- metafilm-0.1.0/README.md +194 -0
- metafilm-0.1.0/data/README.md +26 -0
- metafilm-0.1.0/examples/quickstart.py +58 -0
- metafilm-0.1.0/pyproject.toml +75 -0
- metafilm-0.1.0/results/README.md +19 -0
- metafilm-0.1.0/src/metafilm/__init__.py +29 -0
- metafilm-0.1.0/src/metafilm/forecaster.py +1013 -0
- metafilm-0.1.0/src/metafilm/py.typed +0 -0
- metafilm-0.1.0/tests/conftest.py +46 -0
- metafilm-0.1.0/tests/test_forecaster.py +147 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
|
|
5
|
+
# Editor / backups
|
|
6
|
+
*.bak
|
|
7
|
+
|
|
8
|
+
# Large regenerable prediction binaries (rebuild via experiments/bench_*.py)
|
|
9
|
+
*_preds.npz
|
|
10
|
+
|
|
11
|
+
# Run logs
|
|
12
|
+
*.log
|
|
13
|
+
results/**/*.log
|
|
14
|
+
|
|
15
|
+
# LaTeX build artifacts
|
|
16
|
+
*.aux
|
|
17
|
+
*.bbl
|
|
18
|
+
*.blg
|
|
19
|
+
*.out
|
|
20
|
+
*.synctex.gz
|
|
21
|
+
*.fls
|
|
22
|
+
*.fdb_latexmk
|
|
23
|
+
*.toc
|
|
24
|
+
|
|
25
|
+
# Downloaded dataset archives (keep the extracted .tsf, not the zip)
|
|
26
|
+
data/**/*.zip
|
|
27
|
+
|
|
28
|
+
# Large raw M5 data (re-download via datasetsforecast.M5)
|
|
29
|
+
data/m5_dl/
|
|
30
|
+
data/m5/
|
|
31
|
+
|
|
32
|
+
# Packaging and test artefacts
|
|
33
|
+
dist/
|
|
34
|
+
build/
|
|
35
|
+
*.egg-info/
|
|
36
|
+
.pytest_cache/
|
|
37
|
+
.venv/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-10-29)
|
|
4
|
+
|
|
5
|
+
First public release, accompanying the CENTERIS 2026 paper
|
|
6
|
+
"MetaFiLM: Metadata-Conditioned FiLM Modulation for Cold-Start Demand
|
|
7
|
+
Forecasting".
|
|
8
|
+
|
|
9
|
+
- `metafilm` package: `MetaFiLMConfig`, `MetaFiLMForecaster` (fit, predict,
|
|
10
|
+
save, load) and the `MetaFiLMModel` network, with the ablation switches used
|
|
11
|
+
in the paper (`film_mode`, `film_init`, `use_level`, `use_history`,
|
|
12
|
+
`use_residual`, `loss`).
|
|
13
|
+
- Early stopping restores the weights of the best validation epoch by default
|
|
14
|
+
(`restore_best_weights=True`). The paper's experiments kept the final-epoch
|
|
15
|
+
weights; the experiment scripts set `restore_best_weights=False` so every
|
|
16
|
+
published number reproduces exactly.
|
|
17
|
+
- `MetaFiLMForecaster.load` works with PyTorch 2.6 and later.
|
|
18
|
+
- Works with pandas 2 and pandas 3 (including the pandas 3 string dtype);
|
|
19
|
+
metadata values unseen at fit time still map to the reserved "unknown" code.
|
|
20
|
+
- Test suite (including the parameter counts reported in the paper),
|
|
21
|
+
documentation, and the experiment scripts and results for the public
|
|
22
|
+
benchmarks (M5 weekly and Monash CarParts).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use MetaFiLM, please cite the paper below."
|
|
3
|
+
title: "MetaFiLM: metadata-conditioned FiLM modulation for cold-start demand forecasting"
|
|
4
|
+
type: software
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
date-released: 2026-10-29
|
|
7
|
+
license: MIT
|
|
8
|
+
repository-code: "https://github.com/FH-Prevail/MetaFilm"
|
|
9
|
+
url: "https://pypi.org/project/metafilm/"
|
|
10
|
+
keywords:
|
|
11
|
+
- demand forecasting
|
|
12
|
+
- cold start
|
|
13
|
+
- feature-wise linear modulation
|
|
14
|
+
- metadata
|
|
15
|
+
- time series
|
|
16
|
+
authors:
|
|
17
|
+
- family-names: Mirshahi
|
|
18
|
+
given-names: Sina
|
|
19
|
+
email: sina.mirshahi@fh-steyr.at
|
|
20
|
+
affiliation: "University of Applied Sciences Upper Austria"
|
|
21
|
+
- family-names: Falatouri
|
|
22
|
+
given-names: Taha
|
|
23
|
+
affiliation: "University of Applied Sciences Upper Austria"
|
|
24
|
+
- family-names: Brandtner
|
|
25
|
+
given-names: Patrick
|
|
26
|
+
affiliation: "University of Applied Sciences Upper Austria"
|
|
27
|
+
- family-names: Nasseri
|
|
28
|
+
given-names: Mehran
|
|
29
|
+
affiliation: "University of Applied Sciences Upper Austria"
|
|
30
|
+
- family-names: Kominkova Oplatkova
|
|
31
|
+
given-names: Zuzana
|
|
32
|
+
affiliation: "Tomas Bata University in Zlin"
|
|
33
|
+
preferred-citation:
|
|
34
|
+
type: conference-paper
|
|
35
|
+
title: "MetaFiLM: Metadata-Conditioned FiLM Modulation for Cold-Start Demand Forecasting"
|
|
36
|
+
authors:
|
|
37
|
+
- family-names: Mirshahi
|
|
38
|
+
given-names: Sina
|
|
39
|
+
- family-names: Falatouri
|
|
40
|
+
given-names: Taha
|
|
41
|
+
- family-names: Brandtner
|
|
42
|
+
given-names: Patrick
|
|
43
|
+
- family-names: Nasseri
|
|
44
|
+
given-names: Mehran
|
|
45
|
+
- family-names: Kominkova Oplatkova
|
|
46
|
+
given-names: Zuzana
|
|
47
|
+
collection-title: "CENTERIS 2026: International Conference on ENTERprise Information Systems"
|
|
48
|
+
conference:
|
|
49
|
+
name: "CENTERIS 2026"
|
|
50
|
+
city: Seville
|
|
51
|
+
country: ES
|
|
52
|
+
date-start: 2026-11-11
|
|
53
|
+
date-end: 2026-11-13
|
|
54
|
+
year: 2026
|
|
55
|
+
notes: "To appear in Procedia Computer Science."
|
metafilm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sina Mirshahi, Taha Falatouri, Patrick Brandtner, Mehran Nasseri and Zuzana Kominkova Oplatkova
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
metafilm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metafilm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Metadata-conditioned FiLM modulation for cold-start demand forecasting
|
|
5
|
+
Project-URL: Homepage, https://github.com/FH-Prevail/MetaFilm
|
|
6
|
+
Project-URL: Repository, https://github.com/FH-Prevail/MetaFilm
|
|
7
|
+
Project-URL: Issues, https://github.com/FH-Prevail/MetaFilm/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/FH-Prevail/MetaFilm/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Taha Falatouri, Patrick Brandtner, Mehran Nasseri, Zuzana Kominkova Oplatkova
|
|
10
|
+
Author-email: Sina Mirshahi <sina.mirshahi@fh-steyr.at>
|
|
11
|
+
Maintainer-email: Sina Mirshahi <sina.mirshahi@fh-steyr.at>
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: FiLM,cold start,deep learning,demand forecasting,feature-wise linear modulation,metadata,pytorch,retail,supply chain,time series
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Requires-Dist: numpy>=1.23
|
|
30
|
+
Requires-Dist: pandas>=1.5
|
|
31
|
+
Requires-Dist: scikit-learn>=1.1
|
|
32
|
+
Requires-Dist: torch>=2.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
36
|
+
Requires-Dist: twine>=6.1; extra == 'dev'
|
|
37
|
+
Provides-Extra: experiments
|
|
38
|
+
Requires-Dist: pyarrow>=10; extra == 'experiments'
|
|
39
|
+
Requires-Dist: xgboost>=1.7; extra == 'experiments'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# MetaFiLM
|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/metafilm/)
|
|
45
|
+
[](https://pypi.org/project/metafilm/)
|
|
46
|
+
[](https://github.com/FH-Prevail/MetaFilm/actions/workflows/tests.yml)
|
|
47
|
+
[](https://github.com/FH-Prevail/MetaFilm/blob/main/LICENSE)
|
|
48
|
+
|
|
49
|
+
**Metadata-conditioned FiLM modulation for cold-start demand forecasting.**
|
|
50
|
+
|
|
51
|
+
MetaFiLM forecasts demand for products, stores and item-location pairs that a
|
|
52
|
+
model has never seen, by conditioning a compact neural forecaster on the
|
|
53
|
+
metadata that is known at launch: category, department, store, location. It is
|
|
54
|
+
the reference implementation of the paper *MetaFiLM: Metadata-Conditioned FiLM
|
|
55
|
+
Modulation for Cold-Start Demand Forecasting* (CENTERIS 2026).
|
|
56
|
+
|
|
57
|
+
- **Two metadata pathways.** A level head sets the demand scale; a bounded
|
|
58
|
+
FiLM block modulates how the forecaster reads the calendar and history.
|
|
59
|
+
- **Built for cold start.** Forecasts new series from a short history, or from
|
|
60
|
+
metadata alone. Unseen category values map to a reserved "unknown" code.
|
|
61
|
+
- **Small and fast.** 78,722 parameters in the paper's company configuration;
|
|
62
|
+
trains and serves on a CPU, about 50 microseconds per series in batches on
|
|
63
|
+
one thread.
|
|
64
|
+
- **Familiar API.** `fit`, `predict`, `save` and `load` on a long-format
|
|
65
|
+
pandas frame.
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install metafilm
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Requires Python 3.10 or newer and PyTorch 2.0 or newer. If you need a specific
|
|
74
|
+
PyTorch build (CPU-only or a particular CUDA version), install it first as
|
|
75
|
+
described on [pytorch.org](https://pytorch.org/get-started/locally/).
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
The input is a long-format frame with one row per series and period: an id, a
|
|
80
|
+
date, the target and the metadata columns (constant within a series).
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import pandas as pd
|
|
84
|
+
from metafilm import MetaFiLMConfig, MetaFiLMForecaster
|
|
85
|
+
|
|
86
|
+
config = MetaFiLMConfig(
|
|
87
|
+
n_steps_lag=8, # history window
|
|
88
|
+
forecast_horizon=4, # steps forecast at once
|
|
89
|
+
context_features=["category", "store"],
|
|
90
|
+
item_id_col="item", date_col="week", target_col="sales",
|
|
91
|
+
freq="W-SUN", # weekly; use "MS" for monthly data
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
model = MetaFiLMForecaster(config).fit(train_df)
|
|
95
|
+
|
|
96
|
+
forecasts = model.predict(new_items_df) # {item: array of 4 forecasts}
|
|
97
|
+
model.save("metafilm.pt")
|
|
98
|
+
model = MetaFiLMForecaster.load("metafilm.pt")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A series with at least `n_steps_lag` rows is forecast from its latest history;
|
|
102
|
+
a shorter one from its metadata and the calendar. For items with **no sales
|
|
103
|
+
history at all**, train with `use_history=False` so the model learns to forecast
|
|
104
|
+
from metadata alone. [`examples/quickstart.py`](https://github.com/FH-Prevail/MetaFilm/blob/main/examples/quickstart.py)
|
|
105
|
+
runs both cases end to end on synthetic data:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
New items, 8 weeks of history (MAE 1.12)
|
|
109
|
+
item50 forecast [30.9 28.7 26.3 25.4] actual [34.4 27.5 27.9 25.4]
|
|
110
|
+
item52 forecast [9.5 7.3 4.9 4. ] actual [8.8 7.1 5.5 6.8]
|
|
111
|
+
New items, metadata only (MAE 1.42)
|
|
112
|
+
item50 forecast [32.5 31.1 27.1 26.7] actual [34.4 27.5 27.9 25.4]
|
|
113
|
+
item52 forecast [11. 9.6 5.6 5.2] actual [8.8 7.1 5.5 6.8]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## How it works
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
For a series with metadata `c`, recent history `y` and future calendar
|
|
121
|
+
features `phi_k` (k = 1 ... H):
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
level l(c) = MLP(c) base demand level
|
|
125
|
+
history h_enc = LSTM_2(y, calendar)[final state] zero if no history
|
|
126
|
+
decoder d_k = LSTM_1(phi_k), zero initial state
|
|
127
|
+
fusion g_k = d_k + 0.3 * h_enc
|
|
128
|
+
FiLM z(c) = ReLU(W_1 c)
|
|
129
|
+
gamma = 1 + 0.1 * tanh(W_g z(c)), beta = 0.1 * tanh(W_b z(c))
|
|
130
|
+
h_k = gamma * g_k + beta + 0.5 * g_k
|
|
131
|
+
output y_hat_k = w . h_k + b + l(c)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
FiLM (Feature-wise Linear Modulation) comes from computer vision, where a
|
|
135
|
+
question about an image scales and shifts the image network's features
|
|
136
|
+
([Perez et al., AAAI 2018](https://doi.org/10.1609/aaai.v32i1.11671)). MetaFiLM
|
|
137
|
+
applies the same mechanism with product metadata in place of the question. The
|
|
138
|
+
modulation is bounded to stay close to the identity, so conditioning starts as
|
|
139
|
+
almost no change and grows only where training supports it.
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
`MetaFiLMConfig` fields, with defaults:
|
|
144
|
+
|
|
145
|
+
| Field | Default | Meaning |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `n_steps_lag` | 12 | history window fed to the encoder |
|
|
148
|
+
| `forecast_horizon` | 12 | steps forecast at once |
|
|
149
|
+
| `context_features` | `[]` | metadata columns (categorical or numeric) |
|
|
150
|
+
| `temporal_features` | `[]` | optional extra per-step inputs for the encoder |
|
|
151
|
+
| `item_id_col`, `date_col`, `target_col` | `compoundkey`, `orderweek_dt`, `quantity` | column names |
|
|
152
|
+
| `freq` | `"W-SUN"` | pandas frequency of the series (`"MS"` for monthly) |
|
|
153
|
+
| `hidden_dim`, `num_lstm_layers`, `dropout` | 128, 2, 0.3 | network size |
|
|
154
|
+
| `loss` | `"mse"` | training loss: `"mse"`, `"l1"` (absolute error) or `"huber"` |
|
|
155
|
+
| `film_mode` | `"bounded"` | `"bounded"`, `"unbounded"`, `"concat"` or `"off"` |
|
|
156
|
+
| `use_level`, `use_history`, `use_residual` | `True` | switch individual pathways off |
|
|
157
|
+
| `batch_size`, `learning_rate`, `weight_decay` | 32, 0.001, 0.01 | optimiser settings |
|
|
158
|
+
| `num_epochs`, `patience` | 100, 10 | training length and early stopping |
|
|
159
|
+
| `restore_best_weights` | `True` | keep the best-validation epoch after early stopping |
|
|
160
|
+
| `random_seed`, `device` | 42, CUDA if available | reproducibility and placement |
|
|
161
|
+
|
|
162
|
+
**Match the loss to how you are measured.** If forecasts are judged by mean
|
|
163
|
+
absolute error, train with `loss="l1"` or `loss="huber"`. In the paper this
|
|
164
|
+
lowered cold-start MAE by up to about 5% and moved accuracy more reliably than
|
|
165
|
+
any architectural change; `"huber"` is the more balanced choice for
|
|
166
|
+
intermittent demand, because `"l1"` tends to under-forecast.
|
|
167
|
+
|
|
168
|
+
## Results from the paper
|
|
169
|
+
|
|
170
|
+
Cold-start MAE on series held out from training, mean ± standard deviation over
|
|
171
|
+
five seeds (lower is better):
|
|
172
|
+
|
|
173
|
+
| Method | M5 weekly (Walmart) | Company partner | Monash CarParts |
|
|
174
|
+
|---|---|---|---|
|
|
175
|
+
| Zero forecast | 61.33 ± 6.13 | 124.75 ± 9.11 | 0.364 ± 0.019 |
|
|
176
|
+
| Persistence | 20.69 ± 2.21 | 95.95 ± 6.68 | 0.512 ± 0.019 |
|
|
177
|
+
| Ridge | 19.19 ± 1.85 | 87.18 ± 5.07 | 0.439 ± 0.025 |
|
|
178
|
+
| XGBoost (global) | 20.69 ± 2.34 | 80.97 ± 5.97 | 0.387 ± 0.020 |
|
|
179
|
+
| **MetaFiLM** | 19.64 ± 2.20 | **79.71 ± 4.37** | **0.377 ± 0.021** |
|
|
180
|
+
|
|
181
|
+
MetaFiLM has the lowest error of all learned models on the company and CarParts
|
|
182
|
+
panels, and is on par with the strongest baseline on M5 and the company panel
|
|
183
|
+
(paired tests over five seeds: p = .172 against Ridge, p = .328 against
|
|
184
|
+
XGBoost). On CarParts, where 81% of the test values are zero, the constant zero
|
|
185
|
+
forecast is hard to beat under squared-error training; trained with the Huber
|
|
186
|
+
loss, MetaFiLM reaches 0.359.
|
|
187
|
+
|
|
188
|
+
The benchmarks: M5 weekly (1,000 Walmart item-store series, 150 held out,
|
|
189
|
+
horizon 4), an industrial company-partner panel (3,000 item-location pairs,
|
|
190
|
+
monthly, horizon 6; proprietary and not distributed) and Monash CarParts
|
|
191
|
+
(2,674 intermittent monthly series, horizon 6, no categorical metadata, used
|
|
192
|
+
as a control).
|
|
193
|
+
|
|
194
|
+
## Reproducing the paper
|
|
195
|
+
|
|
196
|
+
The experiment scripts, the CarParts data and the results for the public
|
|
197
|
+
benchmarks are in this repository. See
|
|
198
|
+
[docs/reproducing.md](https://github.com/FH-Prevail/MetaFilm/blob/main/docs/reproducing.md).
|
|
199
|
+
|
|
200
|
+
## Citation
|
|
201
|
+
|
|
202
|
+
If you use MetaFiLM, please cite:
|
|
203
|
+
|
|
204
|
+
```bibtex
|
|
205
|
+
@inproceedings{mirshahi2026metafilm,
|
|
206
|
+
title = {{MetaFiLM}: Metadata-Conditioned {FiLM} Modulation for Cold-Start Demand Forecasting},
|
|
207
|
+
author = {Mirshahi, Sina and Falatouri, Taha and Brandtner, Patrick and Nasseri, Mehran and Kominkova Oplatkova, Zuzana},
|
|
208
|
+
booktitle = {CENTERIS 2026: International Conference on ENTERprise Information Systems},
|
|
209
|
+
series = {Procedia Computer Science},
|
|
210
|
+
year = {2026},
|
|
211
|
+
note = {To appear}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
GitHub also offers this citation through "Cite this repository"
|
|
216
|
+
([CITATION.cff](https://github.com/FH-Prevail/MetaFilm/blob/main/CITATION.cff)).
|
|
217
|
+
|
|
218
|
+
## Authors and acknowledgements
|
|
219
|
+
|
|
220
|
+
- Sina Mirshahi: University of Applied Sciences Upper Austria (Steyr), Josef
|
|
221
|
+
Ressel Centre for Predictive Value Network Intelligence, and Tomas Bata
|
|
222
|
+
University in Zlin
|
|
223
|
+
- Taha Falatouri, Patrick Brandtner and Mehran Nasseri: University of Applied
|
|
224
|
+
Sciences Upper Austria (Steyr) and Josef Ressel Centre for Predictive Value
|
|
225
|
+
Network Intelligence
|
|
226
|
+
- Zuzana Kominkova Oplatkova: Tomas Bata University in Zlin
|
|
227
|
+
|
|
228
|
+
This research was funded by the Christian Doppler Research Association as part
|
|
229
|
+
of the Josef Ressel Centre for Predictive Value Network Intelligence (JRC
|
|
230
|
+
PREVAIL), and partially supported by the Internal Grant Agency of Tomas Bata
|
|
231
|
+
University in Zlin under project number IGA/CebiaTech/2026/004.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT. See [LICENSE](https://github.com/FH-Prevail/MetaFilm/blob/main/LICENSE).
|
metafilm-0.1.0/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# MetaFiLM
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/metafilm/)
|
|
4
|
+
[](https://pypi.org/project/metafilm/)
|
|
5
|
+
[](https://github.com/FH-Prevail/MetaFilm/actions/workflows/tests.yml)
|
|
6
|
+
[](https://github.com/FH-Prevail/MetaFilm/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**Metadata-conditioned FiLM modulation for cold-start demand forecasting.**
|
|
9
|
+
|
|
10
|
+
MetaFiLM forecasts demand for products, stores and item-location pairs that a
|
|
11
|
+
model has never seen, by conditioning a compact neural forecaster on the
|
|
12
|
+
metadata that is known at launch: category, department, store, location. It is
|
|
13
|
+
the reference implementation of the paper *MetaFiLM: Metadata-Conditioned FiLM
|
|
14
|
+
Modulation for Cold-Start Demand Forecasting* (CENTERIS 2026).
|
|
15
|
+
|
|
16
|
+
- **Two metadata pathways.** A level head sets the demand scale; a bounded
|
|
17
|
+
FiLM block modulates how the forecaster reads the calendar and history.
|
|
18
|
+
- **Built for cold start.** Forecasts new series from a short history, or from
|
|
19
|
+
metadata alone. Unseen category values map to a reserved "unknown" code.
|
|
20
|
+
- **Small and fast.** 78,722 parameters in the paper's company configuration;
|
|
21
|
+
trains and serves on a CPU, about 50 microseconds per series in batches on
|
|
22
|
+
one thread.
|
|
23
|
+
- **Familiar API.** `fit`, `predict`, `save` and `load` on a long-format
|
|
24
|
+
pandas frame.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install metafilm
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requires Python 3.10 or newer and PyTorch 2.0 or newer. If you need a specific
|
|
33
|
+
PyTorch build (CPU-only or a particular CUDA version), install it first as
|
|
34
|
+
described on [pytorch.org](https://pytorch.org/get-started/locally/).
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
The input is a long-format frame with one row per series and period: an id, a
|
|
39
|
+
date, the target and the metadata columns (constant within a series).
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import pandas as pd
|
|
43
|
+
from metafilm import MetaFiLMConfig, MetaFiLMForecaster
|
|
44
|
+
|
|
45
|
+
config = MetaFiLMConfig(
|
|
46
|
+
n_steps_lag=8, # history window
|
|
47
|
+
forecast_horizon=4, # steps forecast at once
|
|
48
|
+
context_features=["category", "store"],
|
|
49
|
+
item_id_col="item", date_col="week", target_col="sales",
|
|
50
|
+
freq="W-SUN", # weekly; use "MS" for monthly data
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
model = MetaFiLMForecaster(config).fit(train_df)
|
|
54
|
+
|
|
55
|
+
forecasts = model.predict(new_items_df) # {item: array of 4 forecasts}
|
|
56
|
+
model.save("metafilm.pt")
|
|
57
|
+
model = MetaFiLMForecaster.load("metafilm.pt")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A series with at least `n_steps_lag` rows is forecast from its latest history;
|
|
61
|
+
a shorter one from its metadata and the calendar. For items with **no sales
|
|
62
|
+
history at all**, train with `use_history=False` so the model learns to forecast
|
|
63
|
+
from metadata alone. [`examples/quickstart.py`](https://github.com/FH-Prevail/MetaFilm/blob/main/examples/quickstart.py)
|
|
64
|
+
runs both cases end to end on synthetic data:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
New items, 8 weeks of history (MAE 1.12)
|
|
68
|
+
item50 forecast [30.9 28.7 26.3 25.4] actual [34.4 27.5 27.9 25.4]
|
|
69
|
+
item52 forecast [9.5 7.3 4.9 4. ] actual [8.8 7.1 5.5 6.8]
|
|
70
|
+
New items, metadata only (MAE 1.42)
|
|
71
|
+
item50 forecast [32.5 31.1 27.1 26.7] actual [34.4 27.5 27.9 25.4]
|
|
72
|
+
item52 forecast [11. 9.6 5.6 5.2] actual [8.8 7.1 5.5 6.8]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## How it works
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
For a series with metadata `c`, recent history `y` and future calendar
|
|
80
|
+
features `phi_k` (k = 1 ... H):
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
level l(c) = MLP(c) base demand level
|
|
84
|
+
history h_enc = LSTM_2(y, calendar)[final state] zero if no history
|
|
85
|
+
decoder d_k = LSTM_1(phi_k), zero initial state
|
|
86
|
+
fusion g_k = d_k + 0.3 * h_enc
|
|
87
|
+
FiLM z(c) = ReLU(W_1 c)
|
|
88
|
+
gamma = 1 + 0.1 * tanh(W_g z(c)), beta = 0.1 * tanh(W_b z(c))
|
|
89
|
+
h_k = gamma * g_k + beta + 0.5 * g_k
|
|
90
|
+
output y_hat_k = w . h_k + b + l(c)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
FiLM (Feature-wise Linear Modulation) comes from computer vision, where a
|
|
94
|
+
question about an image scales and shifts the image network's features
|
|
95
|
+
([Perez et al., AAAI 2018](https://doi.org/10.1609/aaai.v32i1.11671)). MetaFiLM
|
|
96
|
+
applies the same mechanism with product metadata in place of the question. The
|
|
97
|
+
modulation is bounded to stay close to the identity, so conditioning starts as
|
|
98
|
+
almost no change and grows only where training supports it.
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
`MetaFiLMConfig` fields, with defaults:
|
|
103
|
+
|
|
104
|
+
| Field | Default | Meaning |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `n_steps_lag` | 12 | history window fed to the encoder |
|
|
107
|
+
| `forecast_horizon` | 12 | steps forecast at once |
|
|
108
|
+
| `context_features` | `[]` | metadata columns (categorical or numeric) |
|
|
109
|
+
| `temporal_features` | `[]` | optional extra per-step inputs for the encoder |
|
|
110
|
+
| `item_id_col`, `date_col`, `target_col` | `compoundkey`, `orderweek_dt`, `quantity` | column names |
|
|
111
|
+
| `freq` | `"W-SUN"` | pandas frequency of the series (`"MS"` for monthly) |
|
|
112
|
+
| `hidden_dim`, `num_lstm_layers`, `dropout` | 128, 2, 0.3 | network size |
|
|
113
|
+
| `loss` | `"mse"` | training loss: `"mse"`, `"l1"` (absolute error) or `"huber"` |
|
|
114
|
+
| `film_mode` | `"bounded"` | `"bounded"`, `"unbounded"`, `"concat"` or `"off"` |
|
|
115
|
+
| `use_level`, `use_history`, `use_residual` | `True` | switch individual pathways off |
|
|
116
|
+
| `batch_size`, `learning_rate`, `weight_decay` | 32, 0.001, 0.01 | optimiser settings |
|
|
117
|
+
| `num_epochs`, `patience` | 100, 10 | training length and early stopping |
|
|
118
|
+
| `restore_best_weights` | `True` | keep the best-validation epoch after early stopping |
|
|
119
|
+
| `random_seed`, `device` | 42, CUDA if available | reproducibility and placement |
|
|
120
|
+
|
|
121
|
+
**Match the loss to how you are measured.** If forecasts are judged by mean
|
|
122
|
+
absolute error, train with `loss="l1"` or `loss="huber"`. In the paper this
|
|
123
|
+
lowered cold-start MAE by up to about 5% and moved accuracy more reliably than
|
|
124
|
+
any architectural change; `"huber"` is the more balanced choice for
|
|
125
|
+
intermittent demand, because `"l1"` tends to under-forecast.
|
|
126
|
+
|
|
127
|
+
## Results from the paper
|
|
128
|
+
|
|
129
|
+
Cold-start MAE on series held out from training, mean ± standard deviation over
|
|
130
|
+
five seeds (lower is better):
|
|
131
|
+
|
|
132
|
+
| Method | M5 weekly (Walmart) | Company partner | Monash CarParts |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| Zero forecast | 61.33 ± 6.13 | 124.75 ± 9.11 | 0.364 ± 0.019 |
|
|
135
|
+
| Persistence | 20.69 ± 2.21 | 95.95 ± 6.68 | 0.512 ± 0.019 |
|
|
136
|
+
| Ridge | 19.19 ± 1.85 | 87.18 ± 5.07 | 0.439 ± 0.025 |
|
|
137
|
+
| XGBoost (global) | 20.69 ± 2.34 | 80.97 ± 5.97 | 0.387 ± 0.020 |
|
|
138
|
+
| **MetaFiLM** | 19.64 ± 2.20 | **79.71 ± 4.37** | **0.377 ± 0.021** |
|
|
139
|
+
|
|
140
|
+
MetaFiLM has the lowest error of all learned models on the company and CarParts
|
|
141
|
+
panels, and is on par with the strongest baseline on M5 and the company panel
|
|
142
|
+
(paired tests over five seeds: p = .172 against Ridge, p = .328 against
|
|
143
|
+
XGBoost). On CarParts, where 81% of the test values are zero, the constant zero
|
|
144
|
+
forecast is hard to beat under squared-error training; trained with the Huber
|
|
145
|
+
loss, MetaFiLM reaches 0.359.
|
|
146
|
+
|
|
147
|
+
The benchmarks: M5 weekly (1,000 Walmart item-store series, 150 held out,
|
|
148
|
+
horizon 4), an industrial company-partner panel (3,000 item-location pairs,
|
|
149
|
+
monthly, horizon 6; proprietary and not distributed) and Monash CarParts
|
|
150
|
+
(2,674 intermittent monthly series, horizon 6, no categorical metadata, used
|
|
151
|
+
as a control).
|
|
152
|
+
|
|
153
|
+
## Reproducing the paper
|
|
154
|
+
|
|
155
|
+
The experiment scripts, the CarParts data and the results for the public
|
|
156
|
+
benchmarks are in this repository. See
|
|
157
|
+
[docs/reproducing.md](https://github.com/FH-Prevail/MetaFilm/blob/main/docs/reproducing.md).
|
|
158
|
+
|
|
159
|
+
## Citation
|
|
160
|
+
|
|
161
|
+
If you use MetaFiLM, please cite:
|
|
162
|
+
|
|
163
|
+
```bibtex
|
|
164
|
+
@inproceedings{mirshahi2026metafilm,
|
|
165
|
+
title = {{MetaFiLM}: Metadata-Conditioned {FiLM} Modulation for Cold-Start Demand Forecasting},
|
|
166
|
+
author = {Mirshahi, Sina and Falatouri, Taha and Brandtner, Patrick and Nasseri, Mehran and Kominkova Oplatkova, Zuzana},
|
|
167
|
+
booktitle = {CENTERIS 2026: International Conference on ENTERprise Information Systems},
|
|
168
|
+
series = {Procedia Computer Science},
|
|
169
|
+
year = {2026},
|
|
170
|
+
note = {To appear}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
GitHub also offers this citation through "Cite this repository"
|
|
175
|
+
([CITATION.cff](https://github.com/FH-Prevail/MetaFilm/blob/main/CITATION.cff)).
|
|
176
|
+
|
|
177
|
+
## Authors and acknowledgements
|
|
178
|
+
|
|
179
|
+
- Sina Mirshahi: University of Applied Sciences Upper Austria (Steyr), Josef
|
|
180
|
+
Ressel Centre for Predictive Value Network Intelligence, and Tomas Bata
|
|
181
|
+
University in Zlin
|
|
182
|
+
- Taha Falatouri, Patrick Brandtner and Mehran Nasseri: University of Applied
|
|
183
|
+
Sciences Upper Austria (Steyr) and Josef Ressel Centre for Predictive Value
|
|
184
|
+
Network Intelligence
|
|
185
|
+
- Zuzana Kominkova Oplatkova: Tomas Bata University in Zlin
|
|
186
|
+
|
|
187
|
+
This research was funded by the Christian Doppler Research Association as part
|
|
188
|
+
of the Josef Ressel Centre for Predictive Value Network Intelligence (JRC
|
|
189
|
+
PREVAIL), and partially supported by the Internal Grant Agency of Tomas Bata
|
|
190
|
+
University in Zlin under project number IGA/CebiaTech/2026/004.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT. See [LICENSE](https://github.com/FH-Prevail/MetaFilm/blob/main/LICENSE).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Data
|
|
2
|
+
|
|
3
|
+
## Monash CarParts (included)
|
|
4
|
+
|
|
5
|
+
`carparts/car_parts_dataset_without_missing_values.tsf`: 2,674 intermittent
|
|
6
|
+
monthly series of car-part sales (January 1998 to March 2002), from the Monash
|
|
7
|
+
Time Series Forecasting Archive (Zenodo record 4656021), distributed there under
|
|
8
|
+
the CC BY 4.0 licence. Missing values were replaced by zeros by the archive.
|
|
9
|
+
|
|
10
|
+
- R. Godahewa, C. Bergmeir, G. I. Webb, R. J. Hyndman and P. Montero-Manso
|
|
11
|
+
(2021). Monash Time Series Forecasting Archive. NeurIPS Datasets and
|
|
12
|
+
Benchmarks Track.
|
|
13
|
+
- R. J. Hyndman (2015). expsmooth: Data Sets from "Forecasting with
|
|
14
|
+
Exponential Smoothing". R package version 2.3.
|
|
15
|
+
|
|
16
|
+
CarParts has no categorical metadata; in the paper it serves as a control
|
|
17
|
+
panel on which metadata conditioning should have no effect.
|
|
18
|
+
|
|
19
|
+
## M5 (download)
|
|
20
|
+
|
|
21
|
+
The Walmart M5 sales data is not redistributed here. See
|
|
22
|
+
[docs/reproducing.md](../docs/reproducing.md) for the download command.
|
|
23
|
+
|
|
24
|
+
## Company partner (not public)
|
|
25
|
+
|
|
26
|
+
The industrial panel used in the paper is proprietary and is not distributed.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Train MetaFiLM on a synthetic panel, then forecast items it has never seen.
|
|
2
|
+
|
|
3
|
+
Run: python examples/quickstart.py
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from metafilm import MetaFiLMConfig, MetaFiLMForecaster
|
|
9
|
+
|
|
10
|
+
# Long-format panel: one row per item and week, plus metadata columns.
|
|
11
|
+
# The demand level depends on the metadata, which is what MetaFiLM learns.
|
|
12
|
+
rng = np.random.default_rng(0)
|
|
13
|
+
weeks = pd.date_range("2024-01-07", periods=40, freq="W-SUN")
|
|
14
|
+
rows = []
|
|
15
|
+
for i in range(60):
|
|
16
|
+
category, store = f"cat{i % 4}", f"store{i % 3}"
|
|
17
|
+
level = 5 + 10 * (i % 4) + 3 * (i % 3)
|
|
18
|
+
for t, week in enumerate(weeks):
|
|
19
|
+
sales = level * (1 + 0.2 * np.sin(2 * np.pi * t / 13)) + rng.normal(0, 1)
|
|
20
|
+
rows.append({"item": f"item{i}", "week": week, "category": category,
|
|
21
|
+
"store": store, "sales": max(0.0, sales)})
|
|
22
|
+
panel = pd.DataFrame(rows)
|
|
23
|
+
|
|
24
|
+
# Train on items 0-49. Items 50-59 are new: never seen during training.
|
|
25
|
+
train = panel[panel["item"].isin([f"item{i}" for i in range(50)])]
|
|
26
|
+
new = panel[~panel["item"].isin(train["item"])]
|
|
27
|
+
start = weeks[32] # forecast weeks 33-36
|
|
28
|
+
actual = new[new["week"].between(start, weeks[35])].pivot(index="item", columns="week", values="sales")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def settings(**extra):
|
|
32
|
+
return MetaFiLMConfig(
|
|
33
|
+
n_steps_lag=8, forecast_horizon=4,
|
|
34
|
+
context_features=["category", "store"],
|
|
35
|
+
item_id_col="item", date_col="week", target_col="sales", freq="W-SUN",
|
|
36
|
+
hidden_dim=32, num_epochs=60, patience=10, device="cpu", **extra,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def report(title, forecasts):
|
|
41
|
+
mae = np.mean([np.abs(forecasts[item] - actual.loc[item].to_numpy()).mean() for item in actual.index])
|
|
42
|
+
print(f"\n{title} (MAE {mae:.2f})")
|
|
43
|
+
for item in actual.index:
|
|
44
|
+
print(f" {item:7s} forecast {np.array2string(forecasts[item], precision=1):26s}"
|
|
45
|
+
f" actual {np.array2string(actual.loc[item].to_numpy(), precision=1)}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# 1. New items with a short sales history (the paper's cold-start setting):
|
|
49
|
+
# the last 8 weeks are available, the item itself was never trained on.
|
|
50
|
+
model = MetaFiLMForecaster(settings()).fit(train)
|
|
51
|
+
history = new[new["week"] < start]
|
|
52
|
+
report("New items, 8 weeks of history", model.predict(history))
|
|
53
|
+
|
|
54
|
+
# 2. Items with no sales history at all: train without the history pathway,
|
|
55
|
+
# so the forecast comes from metadata and the calendar alone.
|
|
56
|
+
metadata_model = MetaFiLMForecaster(settings(use_history=False)).fit(train)
|
|
57
|
+
metadata_only = new.groupby("item").head(1) # one row each: just the metadata
|
|
58
|
+
report("New items, metadata only", metadata_model.predict(metadata_only, forecast_start_date=start))
|