mimosa-ml 0.1.0a0__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.
- mimosa_ml-0.1.0a0/.github/workflows/publish.yml +32 -0
- mimosa_ml-0.1.0a0/LICENSE +21 -0
- mimosa_ml-0.1.0a0/PKG-INFO +240 -0
- mimosa_ml-0.1.0a0/README.md +215 -0
- mimosa_ml-0.1.0a0/docs/images/mean_processes.png +0 -0
- mimosa_ml-0.1.0a0/docs/images/task0_prediction_samples.png +0 -0
- mimosa_ml-0.1.0a0/examples/basic_example.py +140 -0
- mimosa_ml-0.1.0a0/examples/readme_example.py +57 -0
- mimosa_ml-0.1.0a0/mimosa/__init__.py +38 -0
- mimosa_ml-0.1.0a0/mimosa/data_structures.py +333 -0
- mimosa_ml-0.1.0a0/mimosa/grid.py +88 -0
- mimosa_ml-0.1.0a0/mimosa/hyperpost.py +213 -0
- mimosa_ml-0.1.0a0/mimosa/io.py +83 -0
- mimosa_ml-0.1.0a0/mimosa/kmeans.py +102 -0
- mimosa_ml-0.1.0a0/mimosa/laplace.py +21 -0
- mimosa_ml-0.1.0a0/mimosa/linalg.py +171 -0
- mimosa_ml-0.1.0a0/mimosa/mixture.py +111 -0
- mimosa_ml-0.1.0a0/mimosa/models.py +209 -0
- mimosa_ml-0.1.0a0/mimosa/nll.py +257 -0
- mimosa_ml-0.1.0a0/mimosa/optimisers.py +200 -0
- mimosa_ml-0.1.0a0/mimosa/plot.py +662 -0
- mimosa_ml-0.1.0a0/mimosa/prediction.py +241 -0
- mimosa_ml-0.1.0a0/mimosa/sampling.py +31 -0
- mimosa_ml-0.1.0a0/mimosa/synthetic.py +488 -0
- mimosa_ml-0.1.0a0/pyproject.toml +39 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Publish Python 🐍📦 to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*" # déclenche quand on push un tag qui commence par v
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.10"
|
|
22
|
+
|
|
23
|
+
- name: Install build dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip
|
|
26
|
+
pip install build twine
|
|
27
|
+
|
|
28
|
+
- name: Build package
|
|
29
|
+
run: python -m build
|
|
30
|
+
|
|
31
|
+
- name: Publish to PyPI
|
|
32
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon Lejoly
|
|
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,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mimosa-ml
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: MIMOSA: Multi-Input Multi-Output Sample Analysis - A fully-featured multi-task Gaussian process framework for analysing functional data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/SimLej18/mimosa
|
|
6
|
+
Author-email: Simon Lejoly <simon.lejoly@unamur.be>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: beartype
|
|
14
|
+
Requires-Dist: equinox
|
|
15
|
+
Requires-Dist: jax
|
|
16
|
+
Requires-Dist: jaxtyping
|
|
17
|
+
Requires-Dist: kernax-ml
|
|
18
|
+
Requires-Dist: matplotlib
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: optimistix
|
|
21
|
+
Requires-Dist: polars
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# MIMOSA: Multi-Input Multi-Output Sample Analysis
|
|
27
|
+
|
|
28
|
+
A fully-featured *multi-task Gaussian process framework* for analysing functional data.
|
|
29
|
+
It is based on the [MagmaClust framework](https://jmlr.org/papers/v24/20-1321.html), with many enhancements for efficiency and modularity.
|
|
30
|
+
|
|
31
|
+
Its core features currently include:
|
|
32
|
+
* Joint learning across multiple samples (Multi-Task GPs) with unaligned sampling grids
|
|
33
|
+
* Clustering of samples
|
|
34
|
+
* Multi-dimensional inputs and outputs
|
|
35
|
+
* Probabilistic predictions with uncertainty quantification
|
|
36
|
+
* A multitude of training configuration to model complex relationships between tasks, clusters and inputs
|
|
37
|
+
* Deep kernax integration for complex kernels/means functions
|
|
38
|
+
* Support for training and predicting on GPUs and TPUs
|
|
39
|
+
* Full Jax/Equinox compatibility for vmap/grad/jit
|
|
40
|
+
|
|
41
|
+
Towards v1.0.0, we plan to add:
|
|
42
|
+
* Inter-output correlation learning
|
|
43
|
+
* Adaptation to non-gaussian data and classification tasks
|
|
44
|
+
* Sparse learning and approximations for scalability
|
|
45
|
+
* Visualisation tools for data and model outputs
|
|
46
|
+
* Clever initialisation for hyper-parameters of the models
|
|
47
|
+
|
|
48
|
+
> **⚠️ Project Status**: MIMOSA is currently in early development.
|
|
49
|
+
> While current features are functional in most cases, bugs can happen.
|
|
50
|
+
> The API and features are still subject to change.
|
|
51
|
+
> If you use the package for research/production, we encourage you to version-lock your mimosa dependency.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
You can install mimosa through pip:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install mimosa-ml
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You can then import components of the package as:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from mimosa.hyperpost import Hyperpost # Import example
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
N.b.: notice the difference between `mimosa-ml` (the PyPI ID of the package) and `mimosa` (the internal package name).
|
|
69
|
+
|
|
70
|
+
Python compatibility: mimosa is compatible with Python 3.10+.
|
|
71
|
+
We encourage using a more recent version to be compatible with latest releases of Jax and Equinox.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
## Philosophy
|
|
75
|
+
|
|
76
|
+
We basically took Gaussian processes, and combined them with a ton of ✨*fancy components*✨
|
|
77
|
+
* ➡️ Multi-task learning based on [Magma](https://jmlr.org/papers/v24/20-1321.html)
|
|
78
|
+
* ➡️ Clustering as a mixture of Magma GPs
|
|
79
|
+
* ➡️ Multi-dimensional inputs thanks to efficient kernels from [Kernax](https://github.com/SimLej18/kernax-ml)
|
|
80
|
+
* ➡️ Multi-dimensional uncorrelated outputs by simply broadcasting the algorithm to another dimension
|
|
81
|
+
* ➡️ Multi-output correlation discovery by learning a [convolution process]([TODO]) with specific kernel
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
We use **JAX** as a back-end for every computation!
|
|
85
|
+
`jit` compilation saves us a lot of time,
|
|
86
|
+
`vmap` allows us to efficiently batch over all dimensions of the data and parameters,
|
|
87
|
+
`grad` spares us the hastle of implementing gradients for every component of the model,
|
|
88
|
+
and XLA support allows us to train and predict on GPUs and TPUs for scalability.
|
|
89
|
+
|
|
90
|
+
A GP library is nothing without a fast and modular **Kernel library**.
|
|
91
|
+
MIMOSA relies on the efficiency-focused [Kernax package](https://github.com/SimLej18/kernax-ml) for every kernel use
|
|
92
|
+
throughout the algorithms.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
## Examples
|
|
96
|
+
|
|
97
|
+
Basic examples can be found in the `examples` directory. A quick example using the top-level API from Model can be found here:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import jax.random as jr
|
|
101
|
+
from jax import vmap
|
|
102
|
+
from kernax import ZeroMean, VarianceKernel, SEKernel, WhiteNoiseKernel
|
|
103
|
+
|
|
104
|
+
from mimosa.data_structures import Dimensions, Parameters, ModelConfig
|
|
105
|
+
from mimosa.synthetic import generate_data
|
|
106
|
+
from mimosa.models import BasicModel
|
|
107
|
+
from mimosa.grid import UnionGrid
|
|
108
|
+
from mimosa.plot import plot_dataset, plot_clusters, plot_single_task_prediction
|
|
109
|
+
from mimosa.sampling import sample_gp
|
|
110
|
+
|
|
111
|
+
key = jr.PRNGKey(0)
|
|
112
|
+
|
|
113
|
+
# 20 tasks, 2 clusters, 1D inputs/outputs, 25 points/task
|
|
114
|
+
dims = Dimensions(T=20, K=2, I=1, O=1, F=1, N=25, G=50)
|
|
115
|
+
config = ModelConfig(isotopic_tasks=False)
|
|
116
|
+
|
|
117
|
+
# Ground-truth parameters used to generate the synthetic dataset
|
|
118
|
+
init_params = Parameters(
|
|
119
|
+
cluster_mean=ZeroMean(),
|
|
120
|
+
cluster_kernel=VarianceKernel(1.0) * SEKernel(length_scale=1.0),
|
|
121
|
+
task_kernel=VarianceKernel(0.1) * SEKernel(length_scale=2.0),
|
|
122
|
+
noise_kernel=WhiteNoiseKernel(noise=0.01),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
key, subkey = jr.split(key)
|
|
126
|
+
dataset, grid, hyperprior, mixture, parameters, cluster_means, tasks = generate_data(
|
|
127
|
+
subkey, dims, init_params, config, input_range=(-2.5, 2.5))
|
|
128
|
+
|
|
129
|
+
# Fit a model on the generated dataset, starting from the same parameters
|
|
130
|
+
fit_grid = UnionGrid()(dataset.inputs)
|
|
131
|
+
model = BasicModel(jr.PRNGKey(1), n_clusters=dims.K)
|
|
132
|
+
fitted_params, fitted_mixture = model.fit(dataset, fit_grid, mixture.proportions, parameters, n_iter=50)
|
|
133
|
+
|
|
134
|
+
# Predict the posterior distribution of every task, in every cluster
|
|
135
|
+
predictions = model.predict(dataset, fit_grid, fitted_mixture, fitted_params)
|
|
136
|
+
hyperposterior = model.hyperpost(dataset, fit_grid, fitted_mixture, fitted_params)
|
|
137
|
+
|
|
138
|
+
# Plot the fitted mean-processes over the dataset
|
|
139
|
+
fig, ax = plot_dataset(dataset, mixture=mixture, alpha=.3)
|
|
140
|
+
fig, ax = plot_clusters(fit_grid, hyperposterior=hyperposterior, fig=fig, ax=ax, legend=False)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+

|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# Draw samples from task 0's predictive distribution and plot them
|
|
147
|
+
t_id, o_id = 0, 0
|
|
148
|
+
k_id = int(fitted_mixture.assignments[t_id]) # task's dominant cluster
|
|
149
|
+
prediction = predictions[t_id, k_id, o_id]
|
|
150
|
+
|
|
151
|
+
key, sample_key = jr.split(key)
|
|
152
|
+
sample_keys = jr.split(sample_key, 64)
|
|
153
|
+
samples = vmap(lambda k: sample_gp(k, prediction.mean, prediction.covariance))(sample_keys)
|
|
154
|
+
|
|
155
|
+
fig, ax = plot_single_task_prediction(
|
|
156
|
+
dataset, fit_grid, hyperposterior, fitted_mixture, t_id, o_id, samples=samples)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+

|
|
160
|
+
|
|
161
|
+
MIMOSA is designed to have multiple layer of customisation. The "Model" API covers the most general use case of the
|
|
162
|
+
algorithm, but you should consider building your own training loops and combining/developping your own sub-modules to
|
|
163
|
+
create tailored models and pipelines.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
## About configurations
|
|
167
|
+
|
|
168
|
+
MIMOSA has many training configurations to make it as precise as possible when modelling your specific data.
|
|
169
|
+
These configurations are stored in a `ModelConfig` dataclass.
|
|
170
|
+
Here is a quick overview of what each configuration option does.
|
|
171
|
+
|
|
172
|
+
* `shared_task_hps` (default `True`): whether task kernel/noise hyper-parameters are shared across all tasks, or learnt independently per task.
|
|
173
|
+
* `shared_cluster_hps` (default `True`): whether cluster mean/kernel hyper-parameters are shared across all clusters (mean-processes), or learnt independently per cluster.
|
|
174
|
+
* `shared_output_hps` (default `True`): whether hyper-parameters are shared across output dimensions, or learnt independently per output.
|
|
175
|
+
* `shared_features_hps` (default `True`): whether hyper-parameters are shared across features, or learnt independently per feature.
|
|
176
|
+
* `cluster_specific_task_hps` (default `True`): whether the task/noise kernels themselves vary depending on the cluster a task belongs to, or are shared across clusters.
|
|
177
|
+
* `isotopic_tasks` (default `True`): whether all tasks are observed at the same input locations, or each task has its own set of inputs.
|
|
178
|
+
* `isotopic_features` (default `True`): whether all features are observed at the same input locations, or each feature has its own set of inputs.
|
|
179
|
+
|
|
180
|
+
These configurations are independent of your choices of parameters (mean functions/kernels), leading to a large
|
|
181
|
+
number of ways to model your data.
|
|
182
|
+
|
|
183
|
+
The best way to get a feeling for which setup might be the best for your specific needs is to play
|
|
184
|
+
around with `generate_data` and varying configurations and parameters until you get something that looks like
|
|
185
|
+
your specific dataset.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
## Troubleshooting
|
|
189
|
+
|
|
190
|
+
As with any GP-based framework, mimosa is subject to numerical stability issues.
|
|
191
|
+
|
|
192
|
+
We highly recommend to use Jax' 64bit precision:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
import jax
|
|
196
|
+
jax.config.update("jax_enable_x64", True) # Should be called before any other jax/kernax/mimosa import
|
|
197
|
+
# ...other imports
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
If you still have troubles (NaN in hyperpost/likelihoods, optimisation does not converge, odd-looking mean
|
|
201
|
+
processes/predictions/samples, ...), consider increasing the `jitter` used in the specific component or
|
|
202
|
+
directly at the Model level:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
model = BasicModel(prng_key=key, n_clusters=K, jitter=jnp.asarray(1e-5))
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
By default, Mimosa's jitter is fixed at 1e-8 for every component of the framework.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
## Authors and citation
|
|
212
|
+
|
|
213
|
+
MIMOSA is primarily developed by the *Magma Task Force*, composed of:
|
|
214
|
+
|
|
215
|
+
* [Arthur Leroy](https://arthur-leroy.netlify.app/), researcher at Paris Saclay and INRAe (FR), main author of the original tool [MagmaClustR](https://arthurleroy.github.io/MagmaClustR/) and coordinator of the Task Force.
|
|
216
|
+
* [Simon Lejoly](https://researchportal.unamur.be/fr/persons/slejoly/), PhD student at UNamur (BE), main developper of the package and author of the [Kernax package](https://github.com/SimLej18/kernax-ml)
|
|
217
|
+
* [Alexia Grenouillat]([TODO]), PhD student at INSA Toulouse (FR), developper of multi-feature correlation discovery.
|
|
218
|
+
* [Térence Viellard]([TODO]), PhD student at INRAe (FR), working on sparse approximations and scalability.
|
|
219
|
+
|
|
220
|
+
If you use the package for your research, please consider citing the following paper:
|
|
221
|
+
|
|
222
|
+
[TODO]
|
|
223
|
+
|
|
224
|
+
If your research field could provide an interesting example of using MIMOSA, please consider publishing a toy example in
|
|
225
|
+
the documentation [TODO].
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
At this stage of development, we are not yet accepting contributions to the codebase. However, if you are interested
|
|
231
|
+
in contributing to the project, please feel free to reach out to us via email or through our GitHub repository.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
## License
|
|
235
|
+
|
|
236
|
+
This work is distributed under the [MIT License](https://opensource.org/licenses/MIT). See `LICENSE` for more information.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# MIMOSA: Multi-Input Multi-Output Sample Analysis
|
|
2
|
+
|
|
3
|
+
A fully-featured *multi-task Gaussian process framework* for analysing functional data.
|
|
4
|
+
It is based on the [MagmaClust framework](https://jmlr.org/papers/v24/20-1321.html), with many enhancements for efficiency and modularity.
|
|
5
|
+
|
|
6
|
+
Its core features currently include:
|
|
7
|
+
* Joint learning across multiple samples (Multi-Task GPs) with unaligned sampling grids
|
|
8
|
+
* Clustering of samples
|
|
9
|
+
* Multi-dimensional inputs and outputs
|
|
10
|
+
* Probabilistic predictions with uncertainty quantification
|
|
11
|
+
* A multitude of training configuration to model complex relationships between tasks, clusters and inputs
|
|
12
|
+
* Deep kernax integration for complex kernels/means functions
|
|
13
|
+
* Support for training and predicting on GPUs and TPUs
|
|
14
|
+
* Full Jax/Equinox compatibility for vmap/grad/jit
|
|
15
|
+
|
|
16
|
+
Towards v1.0.0, we plan to add:
|
|
17
|
+
* Inter-output correlation learning
|
|
18
|
+
* Adaptation to non-gaussian data and classification tasks
|
|
19
|
+
* Sparse learning and approximations for scalability
|
|
20
|
+
* Visualisation tools for data and model outputs
|
|
21
|
+
* Clever initialisation for hyper-parameters of the models
|
|
22
|
+
|
|
23
|
+
> **⚠️ Project Status**: MIMOSA is currently in early development.
|
|
24
|
+
> While current features are functional in most cases, bugs can happen.
|
|
25
|
+
> The API and features are still subject to change.
|
|
26
|
+
> If you use the package for research/production, we encourage you to version-lock your mimosa dependency.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
You can install mimosa through pip:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install mimosa-ml
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You can then import components of the package as:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from mimosa.hyperpost import Hyperpost # Import example
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
N.b.: notice the difference between `mimosa-ml` (the PyPI ID of the package) and `mimosa` (the internal package name).
|
|
44
|
+
|
|
45
|
+
Python compatibility: mimosa is compatible with Python 3.10+.
|
|
46
|
+
We encourage using a more recent version to be compatible with latest releases of Jax and Equinox.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
## Philosophy
|
|
50
|
+
|
|
51
|
+
We basically took Gaussian processes, and combined them with a ton of ✨*fancy components*✨
|
|
52
|
+
* ➡️ Multi-task learning based on [Magma](https://jmlr.org/papers/v24/20-1321.html)
|
|
53
|
+
* ➡️ Clustering as a mixture of Magma GPs
|
|
54
|
+
* ➡️ Multi-dimensional inputs thanks to efficient kernels from [Kernax](https://github.com/SimLej18/kernax-ml)
|
|
55
|
+
* ➡️ Multi-dimensional uncorrelated outputs by simply broadcasting the algorithm to another dimension
|
|
56
|
+
* ➡️ Multi-output correlation discovery by learning a [convolution process]([TODO]) with specific kernel
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
We use **JAX** as a back-end for every computation!
|
|
60
|
+
`jit` compilation saves us a lot of time,
|
|
61
|
+
`vmap` allows us to efficiently batch over all dimensions of the data and parameters,
|
|
62
|
+
`grad` spares us the hastle of implementing gradients for every component of the model,
|
|
63
|
+
and XLA support allows us to train and predict on GPUs and TPUs for scalability.
|
|
64
|
+
|
|
65
|
+
A GP library is nothing without a fast and modular **Kernel library**.
|
|
66
|
+
MIMOSA relies on the efficiency-focused [Kernax package](https://github.com/SimLej18/kernax-ml) for every kernel use
|
|
67
|
+
throughout the algorithms.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
## Examples
|
|
71
|
+
|
|
72
|
+
Basic examples can be found in the `examples` directory. A quick example using the top-level API from Model can be found here:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import jax.random as jr
|
|
76
|
+
from jax import vmap
|
|
77
|
+
from kernax import ZeroMean, VarianceKernel, SEKernel, WhiteNoiseKernel
|
|
78
|
+
|
|
79
|
+
from mimosa.data_structures import Dimensions, Parameters, ModelConfig
|
|
80
|
+
from mimosa.synthetic import generate_data
|
|
81
|
+
from mimosa.models import BasicModel
|
|
82
|
+
from mimosa.grid import UnionGrid
|
|
83
|
+
from mimosa.plot import plot_dataset, plot_clusters, plot_single_task_prediction
|
|
84
|
+
from mimosa.sampling import sample_gp
|
|
85
|
+
|
|
86
|
+
key = jr.PRNGKey(0)
|
|
87
|
+
|
|
88
|
+
# 20 tasks, 2 clusters, 1D inputs/outputs, 25 points/task
|
|
89
|
+
dims = Dimensions(T=20, K=2, I=1, O=1, F=1, N=25, G=50)
|
|
90
|
+
config = ModelConfig(isotopic_tasks=False)
|
|
91
|
+
|
|
92
|
+
# Ground-truth parameters used to generate the synthetic dataset
|
|
93
|
+
init_params = Parameters(
|
|
94
|
+
cluster_mean=ZeroMean(),
|
|
95
|
+
cluster_kernel=VarianceKernel(1.0) * SEKernel(length_scale=1.0),
|
|
96
|
+
task_kernel=VarianceKernel(0.1) * SEKernel(length_scale=2.0),
|
|
97
|
+
noise_kernel=WhiteNoiseKernel(noise=0.01),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
key, subkey = jr.split(key)
|
|
101
|
+
dataset, grid, hyperprior, mixture, parameters, cluster_means, tasks = generate_data(
|
|
102
|
+
subkey, dims, init_params, config, input_range=(-2.5, 2.5))
|
|
103
|
+
|
|
104
|
+
# Fit a model on the generated dataset, starting from the same parameters
|
|
105
|
+
fit_grid = UnionGrid()(dataset.inputs)
|
|
106
|
+
model = BasicModel(jr.PRNGKey(1), n_clusters=dims.K)
|
|
107
|
+
fitted_params, fitted_mixture = model.fit(dataset, fit_grid, mixture.proportions, parameters, n_iter=50)
|
|
108
|
+
|
|
109
|
+
# Predict the posterior distribution of every task, in every cluster
|
|
110
|
+
predictions = model.predict(dataset, fit_grid, fitted_mixture, fitted_params)
|
|
111
|
+
hyperposterior = model.hyperpost(dataset, fit_grid, fitted_mixture, fitted_params)
|
|
112
|
+
|
|
113
|
+
# Plot the fitted mean-processes over the dataset
|
|
114
|
+
fig, ax = plot_dataset(dataset, mixture=mixture, alpha=.3)
|
|
115
|
+
fig, ax = plot_clusters(fit_grid, hyperposterior=hyperposterior, fig=fig, ax=ax, legend=False)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# Draw samples from task 0's predictive distribution and plot them
|
|
122
|
+
t_id, o_id = 0, 0
|
|
123
|
+
k_id = int(fitted_mixture.assignments[t_id]) # task's dominant cluster
|
|
124
|
+
prediction = predictions[t_id, k_id, o_id]
|
|
125
|
+
|
|
126
|
+
key, sample_key = jr.split(key)
|
|
127
|
+
sample_keys = jr.split(sample_key, 64)
|
|
128
|
+
samples = vmap(lambda k: sample_gp(k, prediction.mean, prediction.covariance))(sample_keys)
|
|
129
|
+
|
|
130
|
+
fig, ax = plot_single_task_prediction(
|
|
131
|
+
dataset, fit_grid, hyperposterior, fitted_mixture, t_id, o_id, samples=samples)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+

|
|
135
|
+
|
|
136
|
+
MIMOSA is designed to have multiple layer of customisation. The "Model" API covers the most general use case of the
|
|
137
|
+
algorithm, but you should consider building your own training loops and combining/developping your own sub-modules to
|
|
138
|
+
create tailored models and pipelines.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
## About configurations
|
|
142
|
+
|
|
143
|
+
MIMOSA has many training configurations to make it as precise as possible when modelling your specific data.
|
|
144
|
+
These configurations are stored in a `ModelConfig` dataclass.
|
|
145
|
+
Here is a quick overview of what each configuration option does.
|
|
146
|
+
|
|
147
|
+
* `shared_task_hps` (default `True`): whether task kernel/noise hyper-parameters are shared across all tasks, or learnt independently per task.
|
|
148
|
+
* `shared_cluster_hps` (default `True`): whether cluster mean/kernel hyper-parameters are shared across all clusters (mean-processes), or learnt independently per cluster.
|
|
149
|
+
* `shared_output_hps` (default `True`): whether hyper-parameters are shared across output dimensions, or learnt independently per output.
|
|
150
|
+
* `shared_features_hps` (default `True`): whether hyper-parameters are shared across features, or learnt independently per feature.
|
|
151
|
+
* `cluster_specific_task_hps` (default `True`): whether the task/noise kernels themselves vary depending on the cluster a task belongs to, or are shared across clusters.
|
|
152
|
+
* `isotopic_tasks` (default `True`): whether all tasks are observed at the same input locations, or each task has its own set of inputs.
|
|
153
|
+
* `isotopic_features` (default `True`): whether all features are observed at the same input locations, or each feature has its own set of inputs.
|
|
154
|
+
|
|
155
|
+
These configurations are independent of your choices of parameters (mean functions/kernels), leading to a large
|
|
156
|
+
number of ways to model your data.
|
|
157
|
+
|
|
158
|
+
The best way to get a feeling for which setup might be the best for your specific needs is to play
|
|
159
|
+
around with `generate_data` and varying configurations and parameters until you get something that looks like
|
|
160
|
+
your specific dataset.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
## Troubleshooting
|
|
164
|
+
|
|
165
|
+
As with any GP-based framework, mimosa is subject to numerical stability issues.
|
|
166
|
+
|
|
167
|
+
We highly recommend to use Jax' 64bit precision:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
import jax
|
|
171
|
+
jax.config.update("jax_enable_x64", True) # Should be called before any other jax/kernax/mimosa import
|
|
172
|
+
# ...other imports
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
If you still have troubles (NaN in hyperpost/likelihoods, optimisation does not converge, odd-looking mean
|
|
176
|
+
processes/predictions/samples, ...), consider increasing the `jitter` used in the specific component or
|
|
177
|
+
directly at the Model level:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
model = BasicModel(prng_key=key, n_clusters=K, jitter=jnp.asarray(1e-5))
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
By default, Mimosa's jitter is fixed at 1e-8 for every component of the framework.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
## Authors and citation
|
|
187
|
+
|
|
188
|
+
MIMOSA is primarily developed by the *Magma Task Force*, composed of:
|
|
189
|
+
|
|
190
|
+
* [Arthur Leroy](https://arthur-leroy.netlify.app/), researcher at Paris Saclay and INRAe (FR), main author of the original tool [MagmaClustR](https://arthurleroy.github.io/MagmaClustR/) and coordinator of the Task Force.
|
|
191
|
+
* [Simon Lejoly](https://researchportal.unamur.be/fr/persons/slejoly/), PhD student at UNamur (BE), main developper of the package and author of the [Kernax package](https://github.com/SimLej18/kernax-ml)
|
|
192
|
+
* [Alexia Grenouillat]([TODO]), PhD student at INSA Toulouse (FR), developper of multi-feature correlation discovery.
|
|
193
|
+
* [Térence Viellard]([TODO]), PhD student at INRAe (FR), working on sparse approximations and scalability.
|
|
194
|
+
|
|
195
|
+
If you use the package for your research, please consider citing the following paper:
|
|
196
|
+
|
|
197
|
+
[TODO]
|
|
198
|
+
|
|
199
|
+
If your research field could provide an interesting example of using MIMOSA, please consider publishing a toy example in
|
|
200
|
+
the documentation [TODO].
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
At this stage of development, we are not yet accepting contributions to the codebase. However, if you are interested
|
|
206
|
+
in contributing to the project, please feel free to reach out to us via email or through our GitHub repository.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
This work is distributed under the [MIT License](https://opensource.org/licenses/MIT). See `LICENSE` for more information.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic usage example of mimosa-ml.
|
|
3
|
+
|
|
4
|
+
Walks through the full pipeline: configure dimensions -> generate a synthetic dataset and remove
|
|
5
|
+
some points at random -> plot the raw dataset -> fit a BasicModel -> plot the fitted clusters ->
|
|
6
|
+
predict a task's output -> plot the prediction (mean + confidence interval) and samples from it.
|
|
7
|
+
|
|
8
|
+
Meant to be run cell-by-cell (e.g. in PyCharm/VSCode's "#%%" notebook mode), or as a plain script.
|
|
9
|
+
"""
|
|
10
|
+
import jax
|
|
11
|
+
|
|
12
|
+
jax.config.update("jax_enable_x64", True)
|
|
13
|
+
jax.config.update("jax_disable_jit", False)
|
|
14
|
+
import jax.random as jr
|
|
15
|
+
import jax.numpy as jnp
|
|
16
|
+
from jax import vmap
|
|
17
|
+
import matplotlib.pyplot as plt
|
|
18
|
+
|
|
19
|
+
from kernax import ZeroMean, VarianceKernel, SEKernel, WhiteNoiseKernel
|
|
20
|
+
|
|
21
|
+
from mimosa import (
|
|
22
|
+
Dimensions, ModelConfig, DataRemovalConfig, Parameters,
|
|
23
|
+
BasicModel, generate_data, RandomDataRemover, save_csv, load_csv, build_parameters,
|
|
24
|
+
)
|
|
25
|
+
from mimosa.grid import UnionGrid
|
|
26
|
+
from mimosa.plot import plot_dataset, plot_clusters, plot_single_task_prediction
|
|
27
|
+
from mimosa.sampling import sample_gp
|
|
28
|
+
|
|
29
|
+
key = jr.PRNGKey(42)
|
|
30
|
+
plt.rcParams['figure.dpi']=300
|
|
31
|
+
|
|
32
|
+
#%% 1. Configuration
|
|
33
|
+
# Dimensions: T tasks, K clusters, I/O input/output dims, N points observed
|
|
34
|
+
# per task, G points in the full grid.
|
|
35
|
+
dims = Dimensions(T=32, K=2, I=1, O=2, F=1, N=50, G=150)
|
|
36
|
+
|
|
37
|
+
# ModelConfig controls which hyperparameters are shared (across tasks/clusters/outputs/features) and
|
|
38
|
+
# whether tasks/features share input locations.
|
|
39
|
+
model_config = ModelConfig(
|
|
40
|
+
shared_task_hps=True,
|
|
41
|
+
shared_cluster_hps=True,
|
|
42
|
+
shared_output_hps=True,
|
|
43
|
+
cluster_specific_task_hps=False,
|
|
44
|
+
isotopic_tasks=False,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# How many points to remove at random per task, to simulate missing data.
|
|
48
|
+
removal_config = DataRemovalConfig(max_missing=5, random_missing_count=True, same_missing_across_outputs=False)
|
|
49
|
+
|
|
50
|
+
#%% 2. Generative parameters
|
|
51
|
+
# These are the "true" parameters used to synthesise the toy dataset below. Swap any kernel/mean for
|
|
52
|
+
# another kernax one (e.g. MaternKernel, PeriodicKernel, LinearMean, ...) to change the shape of the
|
|
53
|
+
# data generated.
|
|
54
|
+
true_params = Parameters(
|
|
55
|
+
cluster_mean=ZeroMean(),
|
|
56
|
+
cluster_kernel=VarianceKernel(5.0) * SEKernel(length_scale=.5),
|
|
57
|
+
task_kernel=VarianceKernel(1.0) * SEKernel(length_scale=.4),
|
|
58
|
+
noise_kernel=WhiteNoiseKernel(noise=.05),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
#%% 3. Generate synthetic data, then remove points at random
|
|
62
|
+
key, gen_key, removal_key = jr.split(key, 3)
|
|
63
|
+
|
|
64
|
+
dataset, grid, hyperprior, true_mixture, true_params, cluster_means, tasks = generate_data(
|
|
65
|
+
gen_key, dims, true_params, model_config, input_range=(-2.5, 2.5)
|
|
66
|
+
)
|
|
67
|
+
dataset = RandomDataRemover()(removal_key, dataset, removal_config)
|
|
68
|
+
|
|
69
|
+
#%% 3bis. Alternatively, you can load a dataset from a local file through load_csv.
|
|
70
|
+
# Check load_csv's doc or open the csv file to see the expected file format.
|
|
71
|
+
save_csv("./dummy.csv", dataset)
|
|
72
|
+
dataset = load_csv("./dummy.csv")
|
|
73
|
+
|
|
74
|
+
#%% 4. Plot the raw dataset (coloured by each task's true cluster)
|
|
75
|
+
fig, ax = plot_dataset(dataset, mixture=true_mixture, figsize=(8*dims.O, 6))
|
|
76
|
+
fig.suptitle("Synthetic dataset (colored by true cluster)")
|
|
77
|
+
plt.show()
|
|
78
|
+
|
|
79
|
+
#%% 5. Instantiate the model
|
|
80
|
+
# n_clusters can differ from the true K above (the model doesn't know it); jitter is the numerical
|
|
81
|
+
# stabiliser added before Cholesky factorizations, only increase it if you hit factorization errors.
|
|
82
|
+
key, model_key = jr.split(key)
|
|
83
|
+
model = BasicModel(prng_key=model_key, n_clusters=dims.K)
|
|
84
|
+
|
|
85
|
+
# Starting guess for the parameters to fit. In practice these would be a rough, uninformed guess
|
|
86
|
+
# rather than the true generative ones — feel free to try different starting kernels/values here.
|
|
87
|
+
init_params = Parameters(
|
|
88
|
+
cluster_mean=ZeroMean(),
|
|
89
|
+
cluster_kernel=VarianceKernel(2.0) * SEKernel(length_scale=1.),
|
|
90
|
+
task_kernel=VarianceKernel(.5) * SEKernel(length_scale=.5),
|
|
91
|
+
noise_kernel=WhiteNoiseKernel(noise=0.5))
|
|
92
|
+
|
|
93
|
+
# build_parameters batches the base kernels/mean below to match model_config's sharing structure
|
|
94
|
+
# (same helper generate_data uses internally), so their shapes line up with what model.fit expects.
|
|
95
|
+
init_params = build_parameters(init_params, dims, model_config)
|
|
96
|
+
|
|
97
|
+
# proportions of the dataset in each cluster, a priori
|
|
98
|
+
mixture_proportions = jnp.repeat(1 / dims.K, dims.K) # fixed, equal weight per cluster
|
|
99
|
+
|
|
100
|
+
#%% 6. Fit
|
|
101
|
+
# Grid construction (union of every task's input points) isn't jit-compatible, so it's built once
|
|
102
|
+
# here by the caller, outside of fit/predict, rather than owned by the model — see
|
|
103
|
+
# mimosa.grid.GridBuilder. Swap UnionGrid for another GridBuilder to change how the grid is built.
|
|
104
|
+
fitted_grid = UnionGrid()(dataset.inputs)
|
|
105
|
+
|
|
106
|
+
fitted_params, fitted_mixture = model.fit(dataset, fitted_grid, mixture_proportions, init_params, n_iter=50)
|
|
107
|
+
|
|
108
|
+
#%% 7. Plot the fitted clusters (mean-processes)
|
|
109
|
+
hyperposterior = model.hyperpost(dataset, fitted_grid, fitted_mixture, fitted_params, jitter=model.jitter)
|
|
110
|
+
|
|
111
|
+
fig, ax = plot_dataset(dataset, mixture=true_mixture, figsize=(8*dims.O, 6), alpha=.1)
|
|
112
|
+
fig, ax = plot_clusters(fitted_grid, hyperposterior=hyperposterior, figsize=(8*dims.O, 6), fig=fig, ax=ax)
|
|
113
|
+
fig.suptitle("Fitted clusters (mean-processes) on the dataset")
|
|
114
|
+
plt.show()
|
|
115
|
+
|
|
116
|
+
#%% 8. Predict
|
|
117
|
+
predictions = model.predict(dataset, fitted_grid, fitted_mixture, fitted_params) # MultivariateNormal, batched (T, K, O, G)
|
|
118
|
+
|
|
119
|
+
t_id, o_id = 0, 0
|
|
120
|
+
k_id = int(fitted_mixture.assignments[t_id]) # task's dominant cluster
|
|
121
|
+
prediction = predictions[t_id, k_id, o_id]
|
|
122
|
+
|
|
123
|
+
#%% 9. Plot the prediction: observed points, cluster means, and predictive mean + confidence interval
|
|
124
|
+
fig, ax = plot_single_task_prediction(
|
|
125
|
+
dataset, fitted_grid, hyperposterior, fitted_mixture, t_id, o_id, prediction=prediction, figsize=(8*dims.O, 6)
|
|
126
|
+
)
|
|
127
|
+
fig.suptitle(f"Prediction — task {t_id}, output {o_id}")
|
|
128
|
+
plt.show()
|
|
129
|
+
|
|
130
|
+
#%% 10. Draw samples from the prediction and plot them alongside it
|
|
131
|
+
key, sample_key = jr.split(key)
|
|
132
|
+
n_samples = 64
|
|
133
|
+
sample_keys = jr.split(sample_key, n_samples)
|
|
134
|
+
samples = vmap(lambda k: sample_gp(k, prediction.mean, prediction.covariance))(sample_keys) # (S, G)
|
|
135
|
+
|
|
136
|
+
fig, ax = plot_single_task_prediction(
|
|
137
|
+
dataset, fitted_grid, hyperposterior, fitted_mixture, t_id, o_id, samples=samples, figsize=(8*dims.O, 6)
|
|
138
|
+
)
|
|
139
|
+
fig.suptitle(f"Prediction samples — task {t_id}, output {o_id}")
|
|
140
|
+
plt.show()
|