mantis-tsfm 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.
- mantis_tsfm-0.1.0/LICENSE +21 -0
- mantis_tsfm-0.1.0/PKG-INFO +204 -0
- mantis_tsfm-0.1.0/README.md +172 -0
- mantis_tsfm-0.1.0/pyproject.toml +54 -0
- mantis_tsfm-0.1.0/src/mantis/__init__.py +7 -0
- mantis_tsfm-0.1.0/src/mantis/adapters/__init__.py +8 -0
- mantis_tsfm-0.1.0/src/mantis/adapters/diff_adapter.py +32 -0
- mantis_tsfm-0.1.0/src/mantis/adapters/projector.py +60 -0
- mantis_tsfm-0.1.0/src/mantis/adapters/var_selector.py +38 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/__init__.py +6 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/architecture.py +218 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/tokgen_utils/__init__.py +0 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/tokgen_utils/convolution.py +19 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/tokgen_utils/encoders.py +62 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/vit_utils/__init__.py +0 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/vit_utils/positional_encoding.py +30 -0
- mantis_tsfm-0.1.0/src/mantis/architecture/vit_utils/transformer.py +92 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/__init__.py +6 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/trainer.py +304 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/trainer_utils/__init__.py +0 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/trainer_utils/architecture.py +45 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/trainer_utils/dataset.py +22 -0
- mantis_tsfm-0.1.0/src/mantis/trainer/trainer_utils/scheduling.py +38 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Vasilii Feofanov
|
|
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.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mantis-tsfm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: Time Series Foundation Model,Classification,Transformer
|
|
7
|
+
Author: Vasilii Feofanov
|
|
8
|
+
Author-email: vasilii.feofanov@huawei.com
|
|
9
|
+
Maintainer: Vasilii Feofanov
|
|
10
|
+
Maintainer-email: vasilii.feofanov@huawei.com
|
|
11
|
+
Requires-Python: >=3.9,<4.0
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Dist: einops (>=0.8,<0.9)
|
|
23
|
+
Requires-Dist: huggingface-hub (>=0.23,<0.24)
|
|
24
|
+
Requires-Dist: numpy (>=1.23,<3.0)
|
|
25
|
+
Requires-Dist: pandas (>=1.5,<3.0)
|
|
26
|
+
Requires-Dist: safetensors (>=0.4,<0.5)
|
|
27
|
+
Requires-Dist: scikit-learn (>=1.2,<2.0)
|
|
28
|
+
Requires-Dist: torch (>=1.12,<3.0)
|
|
29
|
+
Requires-Dist: tqdm (>=4.64,<5.0)
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<img src="figures/mantis_logo_white_with_font.png" alt="Logo" height="300"/>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
## Overview
|
|
39
|
+
|
|
40
|
+
**Mantis** is an open-source time series classification foundation model implemented by [Huawei Noah's Ark Lab](https://huggingface.co/paris-noah).\
|
|
41
|
+
The key features are:
|
|
42
|
+
|
|
43
|
+
- *Zero-shot feature extraction:* The model can be used in a frozen state to extract deep features and train a classifier on them.
|
|
44
|
+
- *Fine-tuning:* To achieve the highest performance, the model can be further fine-tuned for a new task.
|
|
45
|
+
- *Lightweight:* The model contains 8 million parameters, which allows it to be fine-tuned on a single GPU (even feasible on a CPU).
|
|
46
|
+
- *Calibration:* In our studies, we have shown that Mantis is the most calibrated foundation model for classification so far.
|
|
47
|
+
- *Adaptable to large-scale datasets:* For datasets with a large number of channels, we propose additional adapters that reduce memory requirements.
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="figures/zero-shot-exp-results.png" alt="Logo" height="300"/>
|
|
51
|
+
|
|
52
|
+
<img src="figures/fine-tuning-exp-results.png" alt="Logo" height="300"/>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
Please find out technical report on [arXiv](https://arxiv.org/abs/2502.15637). Our pre-trained weights can be found on [Hugging Face](https://huggingface.co/paris-noah/Mantis-8M).
|
|
56
|
+
Below we give instructions how the package can be installed and used.
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
### Pip installation
|
|
61
|
+
|
|
62
|
+
> [!WARNING]
|
|
63
|
+
> The package will be released to PyPI very soon. Meanwhile, please use editable mode intallation given below.
|
|
64
|
+
>
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
pip install mantis
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Editable mode using Poetry
|
|
71
|
+
|
|
72
|
+
First, install Poetry and add the path to the binary file to your shell configuration file.
|
|
73
|
+
For example, on Linux systems, you can do this by running:
|
|
74
|
+
```bash
|
|
75
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
|
76
|
+
export PATH="/home/username/.local/bin:$PATH"
|
|
77
|
+
```
|
|
78
|
+
Now you can create a virtual environment that is based on one of your already installed Python interpreters.
|
|
79
|
+
For example, if your default Python is 3.9, then create the environment by running:
|
|
80
|
+
```bash
|
|
81
|
+
poetry env use 3.9
|
|
82
|
+
```
|
|
83
|
+
Alternatively, you can specify a path to the interpreter. For example, to use an Anaconda Python interpreter:
|
|
84
|
+
```bash
|
|
85
|
+
poetry env use /path/to/anaconda3/envs/my_env/bin/python
|
|
86
|
+
```
|
|
87
|
+
If you want to run any command within the environment, instead of activating the environment manually, you can use `poetry run`:
|
|
88
|
+
```bash
|
|
89
|
+
poetry run <command>
|
|
90
|
+
```
|
|
91
|
+
For example, to install the dependencies and run tests:
|
|
92
|
+
```bash
|
|
93
|
+
poetry install
|
|
94
|
+
poetry run pytest
|
|
95
|
+
```
|
|
96
|
+
If dependencies are not resolving correctly, try re-generating the lock file:
|
|
97
|
+
```bash
|
|
98
|
+
poetry lock
|
|
99
|
+
poetry install
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Getting started
|
|
104
|
+
|
|
105
|
+
Please refer to `getting_started/` folder to see reproducible examples of how the package can be used.
|
|
106
|
+
|
|
107
|
+
Below we summarize the basic commands needed to use the package.
|
|
108
|
+
|
|
109
|
+
### Initialization.
|
|
110
|
+
|
|
111
|
+
To load our pre-trained model with 8M parameters from the Hugging Face, it is sufficient to run:
|
|
112
|
+
|
|
113
|
+
``` python
|
|
114
|
+
from mantis.architecture import Mantis8M
|
|
115
|
+
|
|
116
|
+
network = Mantis8M(device='cuda')
|
|
117
|
+
network = network.from_pretrained("paris-noah/Mantis-8M")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Feature Extraction.
|
|
121
|
+
|
|
122
|
+
We provide a scikit-learn-like wrapper `MantisTrainer` that allows to use Mantis as a feature extractor by running the following commands:
|
|
123
|
+
|
|
124
|
+
``` python
|
|
125
|
+
from mantis.trainer import MantisTrainer
|
|
126
|
+
|
|
127
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
128
|
+
Z = model.transform(X) # X is your time series dataset
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Fine-tuning.
|
|
132
|
+
|
|
133
|
+
If you want to fine-tune the model on your supervised dataset, you can use `fit` method of `MantisTrainer`:
|
|
134
|
+
|
|
135
|
+
``` python
|
|
136
|
+
from mantis.trainer import MantisTrainer
|
|
137
|
+
|
|
138
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
139
|
+
model.fit(X, y) # y is a vector with class labels
|
|
140
|
+
probs = model.predict_proba(X)
|
|
141
|
+
y_pred = model.predict(X)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Adapters.
|
|
145
|
+
|
|
146
|
+
We have integrated into the framework the possibility to pass the input to an adapter before sending it to the foundation model. This may be useful for time series data sets with a large number of channels. More specifically, large number of channels may induce the curse of dimensionality or make model's fine-tuning unfeasible.
|
|
147
|
+
|
|
148
|
+
A straightforward way to overcome these issues is to use a dimension reduction approach like PCA:
|
|
149
|
+
``` python
|
|
150
|
+
from mantis.adapters import MultichannelProjector
|
|
151
|
+
|
|
152
|
+
adapter = MultichannelProjector(new_num_channels=5, base_projector='pca')
|
|
153
|
+
adapter.fit(X)
|
|
154
|
+
X_transformed = adapter.transform(X)
|
|
155
|
+
|
|
156
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
157
|
+
Z = model.transform(X_transformed)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Another wat is to add learnable layers before the foundation model and fine-tune them with the prediction head:
|
|
161
|
+
``` python
|
|
162
|
+
from mantis.adapters import LinearChannelCombiner
|
|
163
|
+
|
|
164
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
165
|
+
adapter = LinearChannelCombiner(num_channels=X.shape[1], new_num_channels=5)
|
|
166
|
+
model.fit(X, y, adapter=adapter, fine_tuning_type='adapter_head')
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Structure
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
├── data/ <-- two datasets for demonstration
|
|
173
|
+
├── getting_started/ <-- jupyter notebooks with tutorials
|
|
174
|
+
└── src/mantis/ <-- the main package
|
|
175
|
+
├── adapters/ <-- adapters for multichannel time series
|
|
176
|
+
├── architecture/ <-- foundation model architectures
|
|
177
|
+
└── trainer/ <-- a scikit-learn-like wrapper for feature extraction or fine-tuning
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
|
184
|
+
|
|
185
|
+
## Open-source Participation
|
|
186
|
+
|
|
187
|
+
We would be happy to receive feedback and integrate any suggestion, so do not hesitate to contribute to this project by raising a GitHub issue or contacting us by email:
|
|
188
|
+
|
|
189
|
+
- Vasilii Feofanov - vasilii [dot] feofanov [at] huawei [dot] com
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
## Citing Mantis 📚
|
|
193
|
+
|
|
194
|
+
If you use Mantis in your work, please cite this technical report:
|
|
195
|
+
|
|
196
|
+
```bibtex
|
|
197
|
+
@article{feofanov2025mantis,
|
|
198
|
+
title={Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification},
|
|
199
|
+
author={Vasilii Feofanov and Songkang Wen and Marius Alonso and Romain Ilbert and Hongbo Guo and Malik Tiomoko and Lujia Pan and Jianfeng Zhang and Ievgen Redko},
|
|
200
|
+
journal={arXiv preprint arXiv:2502.15637},
|
|
201
|
+
year={2025},
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="figures/mantis_logo_white_with_font.png" alt="Logo" height="300"/>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**Mantis** is an open-source time series classification foundation model implemented by [Huawei Noah's Ark Lab](https://huggingface.co/paris-noah).\
|
|
10
|
+
The key features are:
|
|
11
|
+
|
|
12
|
+
- *Zero-shot feature extraction:* The model can be used in a frozen state to extract deep features and train a classifier on them.
|
|
13
|
+
- *Fine-tuning:* To achieve the highest performance, the model can be further fine-tuned for a new task.
|
|
14
|
+
- *Lightweight:* The model contains 8 million parameters, which allows it to be fine-tuned on a single GPU (even feasible on a CPU).
|
|
15
|
+
- *Calibration:* In our studies, we have shown that Mantis is the most calibrated foundation model for classification so far.
|
|
16
|
+
- *Adaptable to large-scale datasets:* For datasets with a large number of channels, we propose additional adapters that reduce memory requirements.
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="figures/zero-shot-exp-results.png" alt="Logo" height="300"/>
|
|
20
|
+
|
|
21
|
+
<img src="figures/fine-tuning-exp-results.png" alt="Logo" height="300"/>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
Please find out technical report on [arXiv](https://arxiv.org/abs/2502.15637). Our pre-trained weights can be found on [Hugging Face](https://huggingface.co/paris-noah/Mantis-8M).
|
|
25
|
+
Below we give instructions how the package can be installed and used.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Pip installation
|
|
30
|
+
|
|
31
|
+
> [!WARNING]
|
|
32
|
+
> The package will be released to PyPI very soon. Meanwhile, please use editable mode intallation given below.
|
|
33
|
+
>
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pip install mantis
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Editable mode using Poetry
|
|
40
|
+
|
|
41
|
+
First, install Poetry and add the path to the binary file to your shell configuration file.
|
|
42
|
+
For example, on Linux systems, you can do this by running:
|
|
43
|
+
```bash
|
|
44
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
|
45
|
+
export PATH="/home/username/.local/bin:$PATH"
|
|
46
|
+
```
|
|
47
|
+
Now you can create a virtual environment that is based on one of your already installed Python interpreters.
|
|
48
|
+
For example, if your default Python is 3.9, then create the environment by running:
|
|
49
|
+
```bash
|
|
50
|
+
poetry env use 3.9
|
|
51
|
+
```
|
|
52
|
+
Alternatively, you can specify a path to the interpreter. For example, to use an Anaconda Python interpreter:
|
|
53
|
+
```bash
|
|
54
|
+
poetry env use /path/to/anaconda3/envs/my_env/bin/python
|
|
55
|
+
```
|
|
56
|
+
If you want to run any command within the environment, instead of activating the environment manually, you can use `poetry run`:
|
|
57
|
+
```bash
|
|
58
|
+
poetry run <command>
|
|
59
|
+
```
|
|
60
|
+
For example, to install the dependencies and run tests:
|
|
61
|
+
```bash
|
|
62
|
+
poetry install
|
|
63
|
+
poetry run pytest
|
|
64
|
+
```
|
|
65
|
+
If dependencies are not resolving correctly, try re-generating the lock file:
|
|
66
|
+
```bash
|
|
67
|
+
poetry lock
|
|
68
|
+
poetry install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Getting started
|
|
73
|
+
|
|
74
|
+
Please refer to `getting_started/` folder to see reproducible examples of how the package can be used.
|
|
75
|
+
|
|
76
|
+
Below we summarize the basic commands needed to use the package.
|
|
77
|
+
|
|
78
|
+
### Initialization.
|
|
79
|
+
|
|
80
|
+
To load our pre-trained model with 8M parameters from the Hugging Face, it is sufficient to run:
|
|
81
|
+
|
|
82
|
+
``` python
|
|
83
|
+
from mantis.architecture import Mantis8M
|
|
84
|
+
|
|
85
|
+
network = Mantis8M(device='cuda')
|
|
86
|
+
network = network.from_pretrained("paris-noah/Mantis-8M")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Feature Extraction.
|
|
90
|
+
|
|
91
|
+
We provide a scikit-learn-like wrapper `MantisTrainer` that allows to use Mantis as a feature extractor by running the following commands:
|
|
92
|
+
|
|
93
|
+
``` python
|
|
94
|
+
from mantis.trainer import MantisTrainer
|
|
95
|
+
|
|
96
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
97
|
+
Z = model.transform(X) # X is your time series dataset
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Fine-tuning.
|
|
101
|
+
|
|
102
|
+
If you want to fine-tune the model on your supervised dataset, you can use `fit` method of `MantisTrainer`:
|
|
103
|
+
|
|
104
|
+
``` python
|
|
105
|
+
from mantis.trainer import MantisTrainer
|
|
106
|
+
|
|
107
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
108
|
+
model.fit(X, y) # y is a vector with class labels
|
|
109
|
+
probs = model.predict_proba(X)
|
|
110
|
+
y_pred = model.predict(X)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Adapters.
|
|
114
|
+
|
|
115
|
+
We have integrated into the framework the possibility to pass the input to an adapter before sending it to the foundation model. This may be useful for time series data sets with a large number of channels. More specifically, large number of channels may induce the curse of dimensionality or make model's fine-tuning unfeasible.
|
|
116
|
+
|
|
117
|
+
A straightforward way to overcome these issues is to use a dimension reduction approach like PCA:
|
|
118
|
+
``` python
|
|
119
|
+
from mantis.adapters import MultichannelProjector
|
|
120
|
+
|
|
121
|
+
adapter = MultichannelProjector(new_num_channels=5, base_projector='pca')
|
|
122
|
+
adapter.fit(X)
|
|
123
|
+
X_transformed = adapter.transform(X)
|
|
124
|
+
|
|
125
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
126
|
+
Z = model.transform(X_transformed)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Another wat is to add learnable layers before the foundation model and fine-tune them with the prediction head:
|
|
130
|
+
``` python
|
|
131
|
+
from mantis.adapters import LinearChannelCombiner
|
|
132
|
+
|
|
133
|
+
model = MantisTrainer(device='cuda', network=network)
|
|
134
|
+
adapter = LinearChannelCombiner(num_channels=X.shape[1], new_num_channels=5)
|
|
135
|
+
model.fit(X, y, adapter=adapter, fine_tuning_type='adapter_head')
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Structure
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
├── data/ <-- two datasets for demonstration
|
|
142
|
+
├── getting_started/ <-- jupyter notebooks with tutorials
|
|
143
|
+
└── src/mantis/ <-- the main package
|
|
144
|
+
├── adapters/ <-- adapters for multichannel time series
|
|
145
|
+
├── architecture/ <-- foundation model architectures
|
|
146
|
+
└── trainer/ <-- a scikit-learn-like wrapper for feature extraction or fine-tuning
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
|
153
|
+
|
|
154
|
+
## Open-source Participation
|
|
155
|
+
|
|
156
|
+
We would be happy to receive feedback and integrate any suggestion, so do not hesitate to contribute to this project by raising a GitHub issue or contacting us by email:
|
|
157
|
+
|
|
158
|
+
- Vasilii Feofanov - vasilii [dot] feofanov [at] huawei [dot] com
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Citing Mantis 📚
|
|
162
|
+
|
|
163
|
+
If you use Mantis in your work, please cite this technical report:
|
|
164
|
+
|
|
165
|
+
```bibtex
|
|
166
|
+
@article{feofanov2025mantis,
|
|
167
|
+
title={Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification},
|
|
168
|
+
author={Vasilii Feofanov and Songkang Wen and Marius Alonso and Romain Ilbert and Hongbo Guo and Malik Tiomoko and Lujia Pan and Jianfeng Zhang and Ievgen Redko},
|
|
169
|
+
journal={arXiv preprint arXiv:2502.15637},
|
|
170
|
+
year={2025},
|
|
171
|
+
}
|
|
172
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "mantis-tsfm"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Mantis: Lightweight Calibrated Foundation Model for User-Friendly Time Series Classification"
|
|
5
|
+
authors = [
|
|
6
|
+
"Vasilii Feofanov <vasilii.feofanov@huawei.com>",
|
|
7
|
+
"Songkang Wen",
|
|
8
|
+
"Marius Alonso",
|
|
9
|
+
"Romain Ilbert",
|
|
10
|
+
"Hongbo Guo",
|
|
11
|
+
"Malik Tiomoko",
|
|
12
|
+
"Lujia Pan",
|
|
13
|
+
"Jianfeng Zhang",
|
|
14
|
+
"Ievgen Redko",
|
|
15
|
+
]
|
|
16
|
+
maintainers = [
|
|
17
|
+
"Vasilii Feofanov <vasilii.feofanov@huawei.com>",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
readme = "README.md"
|
|
21
|
+
keywords = ["Time Series Foundation Model", "Classification", "Transformer"]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Intended Audience :: Science/Research",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence"
|
|
32
|
+
|
|
33
|
+
]
|
|
34
|
+
packages = [
|
|
35
|
+
{ include = "mantis", from = "src" }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.poetry.dependencies]
|
|
39
|
+
python = "^3.9"
|
|
40
|
+
einops = "^0.8"
|
|
41
|
+
huggingface-hub = "^0.23"
|
|
42
|
+
numpy = ">=1.23,<3.0"
|
|
43
|
+
pandas = ">=1.5,<3.0"
|
|
44
|
+
scikit-learn = "^1.2"
|
|
45
|
+
torch = ">=1.12,<3.0"
|
|
46
|
+
tqdm = "^4.64"
|
|
47
|
+
safetensors = "^0.4"
|
|
48
|
+
|
|
49
|
+
[tool.poetry.group.dev.dependencies]
|
|
50
|
+
pytest = ">=7.1,<9.0"
|
|
51
|
+
|
|
52
|
+
[build-system]
|
|
53
|
+
requires = ["poetry-core"]
|
|
54
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Init file for adapters."""
|
|
2
|
+
|
|
3
|
+
from .projector import MultichannelProjector
|
|
4
|
+
from .var_selector import VarianceBasedSelector
|
|
5
|
+
from .diff_adapter import LinearChannelCombiner
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
__all__ = ['MultichannelProjector', 'VarianceBasedSelector', 'LinearChannelCombiner']
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from torch import nn
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LinearChannelCombiner(nn.Module):
|
|
7
|
+
"""
|
|
8
|
+
A differentiable adapter that implements a linear projector along the channel axis.
|
|
9
|
+
Given time series dataset with `num_channels`, it transforms it into a dataset with `new_num_channels`,
|
|
10
|
+
where each new channel is a linear combination of the original ones.
|
|
11
|
+
This adapter is a pytorch module, which can be trained together with the prediction head
|
|
12
|
+
or during the full fine-tuning of Mantis.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
num_channels: int
|
|
17
|
+
The original number of channels in a time series dataset.
|
|
18
|
+
new_num_channels: int
|
|
19
|
+
The number of channels after transformation.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, num_channels, new_num_channels):
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.num_channels = num_channels
|
|
24
|
+
self.new_num_channels = new_num_channels
|
|
25
|
+
self.transformation = nn.Linear(num_channels, new_num_channels)
|
|
26
|
+
|
|
27
|
+
def forward(self, x):
|
|
28
|
+
# transpose time and channel dimensions to apply linear layer
|
|
29
|
+
x_transposed = x.transpose(1, 2)
|
|
30
|
+
x_transformed = self.transformation(x_transposed)
|
|
31
|
+
# return the output transposing back the dimensions
|
|
32
|
+
return x_transformed.transpose(1, 2)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from sklearn.decomposition import PCA, TruncatedSVD
|
|
4
|
+
from sklearn.random_projection import SparseRandomProjection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MultichannelProjector:
|
|
8
|
+
"""
|
|
9
|
+
A generic class to use various scikit-learn dimension reduction methods to reduce
|
|
10
|
+
the number of channels in a multichannel time series dataset.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
new_num_channels : int
|
|
15
|
+
The number of channels after projection.
|
|
16
|
+
patch_window_size : int, default=1
|
|
17
|
+
The size of the patch window. By default, it is equal to 1, i.e., no patching.
|
|
18
|
+
base_projector : str or object, default=None
|
|
19
|
+
The base projector to use. Can be 'pca', 'svd', 'rand', or a custom projector that accepts
|
|
20
|
+
`n_components` at the initialization and has `fit` and `transform` methods.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, new_num_channels, patch_window_size=1, base_projector=None):
|
|
23
|
+
# init dimensions
|
|
24
|
+
self.new_num_channels = new_num_channels
|
|
25
|
+
self.patch_window_size = patch_window_size
|
|
26
|
+
# init base projector
|
|
27
|
+
self.base_projector = base_projector
|
|
28
|
+
n_components = patch_window_size * new_num_channels
|
|
29
|
+
if base_projector in [None, 'pca']:
|
|
30
|
+
self.base_projector_ = PCA(n_components=n_components)
|
|
31
|
+
elif base_projector == 'svd':
|
|
32
|
+
self.base_projector_ = TruncatedSVD(n_components=n_components)
|
|
33
|
+
elif base_projector == 'rand':
|
|
34
|
+
self.base_projector_ = SparseRandomProjection(n_components=n_components)
|
|
35
|
+
# you can give your own base_projector with fit() and transform() methods, and it should have the argument `n_components`.
|
|
36
|
+
else:
|
|
37
|
+
self.base_projector_ = base_projector(n_components=n_components)
|
|
38
|
+
|
|
39
|
+
def fit(self, x):
|
|
40
|
+
x_transposed = np.swapaxes(x, 1, 2)
|
|
41
|
+
|
|
42
|
+
num_samples, seq_len, num_channels = x_transposed.shape
|
|
43
|
+
num_patches = seq_len // self.patch_window_size
|
|
44
|
+
assert num_patches * self.patch_window_size == seq_len
|
|
45
|
+
|
|
46
|
+
x_2d = x_transposed.reshape(num_samples * num_patches, self.patch_window_size * num_channels)
|
|
47
|
+
return self.base_projector_.fit(x_2d)
|
|
48
|
+
|
|
49
|
+
def transform(self, x):
|
|
50
|
+
x_transposed = np.swapaxes(x, 1, 2)
|
|
51
|
+
|
|
52
|
+
num_samples, seq_len, num_channels = x_transposed.shape
|
|
53
|
+
num_patches = seq_len // self.patch_window_size
|
|
54
|
+
assert num_patches * self.patch_window_size == seq_len
|
|
55
|
+
|
|
56
|
+
x_2d = x_transposed.reshape(num_samples * num_patches, self.patch_window_size * num_channels)
|
|
57
|
+
|
|
58
|
+
x_transformed = self.base_projector_.transform(x_2d)
|
|
59
|
+
x_transformed = x_transformed.reshape([num_samples, seq_len, self.new_num_channels])
|
|
60
|
+
return np.swapaxes(x_transformed, 1, 2)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VarianceBasedSelector:
|
|
5
|
+
"""
|
|
6
|
+
Perform a filter feature selection approach to reduce the number of channels in a multichannel time series data set.
|
|
7
|
+
More specifically, it selects those channels that have the highest variance.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
new_num_channels: int
|
|
12
|
+
The number of selected channels.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, new_num_channels):
|
|
15
|
+
self.new_num_channels = new_num_channels
|
|
16
|
+
self.support_ = None
|
|
17
|
+
|
|
18
|
+
def fit(self, x):
|
|
19
|
+
# flatten the tensor to 2D
|
|
20
|
+
x_transposed = np.swapaxes(x, 1, 2)
|
|
21
|
+
num_samples, seq_len, num_channels = x_transposed.shape
|
|
22
|
+
x_2d = x_transposed.reshape(num_samples * seq_len, num_channels)
|
|
23
|
+
|
|
24
|
+
# calculate variances and select top features
|
|
25
|
+
variances = np.var(x_2d, axis=0)
|
|
26
|
+
|
|
27
|
+
# select top features by variance
|
|
28
|
+
self.support_ = np.argsort(variances)[::-1][:self.new_num_channels]
|
|
29
|
+
return self.support_
|
|
30
|
+
|
|
31
|
+
def transform(self, x):
|
|
32
|
+
if self.support_ is None:
|
|
33
|
+
raise RuntimeError("You must call fit at least once before calling transform.")
|
|
34
|
+
|
|
35
|
+
# select features based on precomputed indices
|
|
36
|
+
selected_features = x[:, self.support_, :]
|
|
37
|
+
|
|
38
|
+
return selected_features
|