torchgmm 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.
- torchgmm-0.1.0/LICENSE +19 -0
- torchgmm-0.1.0/PKG-INFO +138 -0
- torchgmm-0.1.0/README.md +112 -0
- torchgmm-0.1.0/pyproject.toml +95 -0
- torchgmm-0.1.0/torchgmm/__init__.py +38 -0
- torchgmm-0.1.0/torchgmm/base/__init__.py +23 -0
- torchgmm-0.1.0/torchgmm/base/data/__init__.py +14 -0
- torchgmm-0.1.0/torchgmm/base/data/collation.py +22 -0
- torchgmm-0.1.0/torchgmm/base/data/loader.py +78 -0
- torchgmm-0.1.0/torchgmm/base/data/sampler.py +41 -0
- torchgmm-0.1.0/torchgmm/base/data/types.py +46 -0
- torchgmm-0.1.0/torchgmm/base/estimator/__init__.py +12 -0
- torchgmm-0.1.0/torchgmm/base/estimator/_protocols.py +22 -0
- torchgmm-0.1.0/torchgmm/base/estimator/base.py +352 -0
- torchgmm-0.1.0/torchgmm/base/estimator/configurable.py +40 -0
- torchgmm-0.1.0/torchgmm/base/estimator/exception.py +5 -0
- torchgmm-0.1.0/torchgmm/base/estimator/mixins.py +47 -0
- torchgmm-0.1.0/torchgmm/base/nn/__init__.py +3 -0
- torchgmm-0.1.0/torchgmm/base/nn/_protocols.py +33 -0
- torchgmm-0.1.0/torchgmm/base/nn/configurable.py +165 -0
- torchgmm-0.1.0/torchgmm/base/utils/__init__.py +4 -0
- torchgmm-0.1.0/torchgmm/base/utils/generics.py +25 -0
- torchgmm-0.1.0/torchgmm/base/utils/path.py +9 -0
- torchgmm-0.1.0/torchgmm/bayes/__init__.py +7 -0
- torchgmm-0.1.0/torchgmm/bayes/core/__init__.py +13 -0
- torchgmm-0.1.0/torchgmm/bayes/core/_jit.py +88 -0
- torchgmm-0.1.0/torchgmm/bayes/core/normal.py +112 -0
- torchgmm-0.1.0/torchgmm/bayes/core/types.py +16 -0
- torchgmm-0.1.0/torchgmm/bayes/core/utils.py +44 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/__init__.py +8 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/estimator.py +308 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/lightning_module.py +334 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/metrics.py +148 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/model.py +147 -0
- torchgmm-0.1.0/torchgmm/bayes/gmm/types.py +16 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/__init__.py +8 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/estimator.py +176 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/lightning_module.py +51 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/metrics.py +61 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/model.py +158 -0
- torchgmm-0.1.0/torchgmm/bayes/markov_chain/types.py +53 -0
- torchgmm-0.1.0/torchgmm/clustering/__init__.py +5 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/__init__.py +8 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/estimator.py +261 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/lightning_module.py +306 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/metrics.py +222 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/model.py +72 -0
- torchgmm-0.1.0/torchgmm/clustering/kmeans/types.py +15 -0
- torchgmm-0.1.0/torchgmm/py.typed +0 -0
- torchgmm-0.1.0/torchgmm/utils/__init__.py +3 -0
- torchgmm-0.1.0/torchgmm/utils/lightning_module.py +53 -0
torchgmm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2020 Oliver Borchert
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
torchgmm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: torchgmm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Gaussian Mixture Models in PyTorch.
|
|
5
|
+
Home-page: https://github.com/marcovarrone/torchgmm
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Oliver Borchert
|
|
8
|
+
Author-email: me@borchero.com
|
|
9
|
+
Requires-Python: >=3.8,<4.0
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Dist: numpy (>=1.20.3,<2.0.0)
|
|
20
|
+
Requires-Dist: pytorch-lightning (>2.0)
|
|
21
|
+
Requires-Dist: torch (>1.11.0)
|
|
22
|
+
Requires-Dist: torchmetrics (>=0.6)
|
|
23
|
+
Project-URL: Repository, https://github.com/marcovarrone/torchgmm
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# TorchGMM
|
|
27
|
+
|
|
28
|
+
<!-- 
|
|
29
|
+
 -->
|
|
30
|
+
|
|
31
|
+
TorchGMM allows to run Gaussian Mixture Models on single or multiple CPUs/GPUs.
|
|
32
|
+
The repository is a fork from [PyCave](https://github.com/borchero/pycave) and [LightKit](https://github.com/borchero/lightkit), two amazing packages developed by [Olivier Borchert](https://github.com/borchero) that are not being maintained anymore.
|
|
33
|
+
While PyCave implements additional models such as Markov Chains, TorchGMM focuses only on Gaussian Mixture Models.
|
|
34
|
+
|
|
35
|
+
The models are implemented in [PyTorch](https://pytorch.org/) and [PyTorch Lightning](https://lightning.ai/docs/pytorch/stable/), and provide an `Estimator` API
|
|
36
|
+
that is fully compatible with [scikit-learn](https://scikit-learn.org/stable/).
|
|
37
|
+
|
|
38
|
+
For Gaussian mixture model, TorchGMM allows for 100x speed ups when using a GPU and enables to train
|
|
39
|
+
on markedly larger datasets via mini-batch training. The full suite of benchmarks run to compare
|
|
40
|
+
TorchGMM models against scikit-learn models is available on the
|
|
41
|
+
[documentation website](https://pycave.borchero.com/sites/benchmark.html).
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- Support for GPU and multi-node training by implementing models in PyTorch and relying on
|
|
46
|
+
[PyTorch Lightning](https://lightning.ai/docs/pytorch/stable/)
|
|
47
|
+
- Mini-batch training for all models such that they can be used on huge datasets
|
|
48
|
+
- Well-structured implementation of models
|
|
49
|
+
|
|
50
|
+
- High-level `Estimator` API allows for easy usage such that models feel and behave like in
|
|
51
|
+
scikit-learn
|
|
52
|
+
- Medium-level `LightingModule` implements the training algorithm
|
|
53
|
+
- Low-level PyTorch `Module` manages the model parameters
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
TorchGMM is available via `pip`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install torchgmm
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If you are using [Poetry](https://python-poetry.org/):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
poetry add torchgmm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
If you've ever used scikit-learn, you'll feel right at home when using TorchGMM. First, let's create
|
|
72
|
+
some artificial data to work with:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import torch
|
|
76
|
+
|
|
77
|
+
X = torch.cat([
|
|
78
|
+
torch.randn(10000, 8) - 5,
|
|
79
|
+
torch.randn(10000, 8),
|
|
80
|
+
torch.randn(10000, 8) + 5,
|
|
81
|
+
])
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This dataset consists of three clusters with 8-dimensional datapoints. If you want to fit a K-Means
|
|
85
|
+
model, to find the clusters' centroids, it's as easy as:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from torchgmm.clustering import KMeans
|
|
89
|
+
|
|
90
|
+
estimator = KMeans(3)
|
|
91
|
+
estimator.fit(X)
|
|
92
|
+
|
|
93
|
+
# Once the estimator is fitted, it provides various properties. One of them is
|
|
94
|
+
# the `model_` property which yields the PyTorch module with the fitted parameters.
|
|
95
|
+
print("Centroids are:")
|
|
96
|
+
print(estimator.model_.centroids)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Due to the high-level estimator API, the usage for all machine learning models is similar. The API
|
|
100
|
+
documentation provides more detailed information about parameters that can be passed to estimators
|
|
101
|
+
and which methods are available.
|
|
102
|
+
|
|
103
|
+
### GPU and Multi-Node training
|
|
104
|
+
|
|
105
|
+
For GPU- and multi-node training, TorchGMM leverages PyTorch Lightning. The hardware that training
|
|
106
|
+
runs on is determined by the
|
|
107
|
+
[Trainer](https://pytorch-lightning.readthedocs.io/en/latest/api/pytorch_lightning.trainer.trainer.html#pytorch_lightning.trainer.trainer.Trainer)
|
|
108
|
+
class. It's
|
|
109
|
+
[**init**](https://pytorch-lightning.readthedocs.io/en/latest/api/pytorch_lightning.trainer.trainer.html#pytorch_lightning.trainer.trainer.Trainer.__init__)
|
|
110
|
+
method provides various configuration options.
|
|
111
|
+
|
|
112
|
+
If you want to run K-Means with a GPU, you can pass the options `accelerator='gpu'` and `devices=1`
|
|
113
|
+
to the estimator's initializer:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
estimator = KMeans(3, trainer_params=dict(accelerator='gpu', devices=1))
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Similarly, if you want to train on 4 nodes simultaneously where each node has one GPU available,
|
|
120
|
+
you can specify this as follows:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
estimator = KMeans(3, trainer_params=dict(num_nodes=4, accelerator='gpu', devices=1))
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
In fact, **you do not need to change anything else in your code**.
|
|
127
|
+
|
|
128
|
+
### Implemented Models
|
|
129
|
+
|
|
130
|
+
Currently, TorchGMM implements two different models:
|
|
131
|
+
|
|
132
|
+
- [GaussianMixture](https://pycave.borchero.com/sites/generated/bayes/gmm/pycave.bayes.GaussianMixture.html)
|
|
133
|
+
- [K-Means](https://pycave.borchero.com/sites/generated/clustering/kmeans/pycave.clustering.KMeans.html)
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
TorchGMM is licensed under the [MIT License](https://github.com/marcovarrone/torchgmm/blob/main/LICENSE).
|
|
138
|
+
|
torchgmm-0.1.0/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# TorchGMM
|
|
2
|
+
|
|
3
|
+
<!-- 
|
|
4
|
+
 -->
|
|
5
|
+
|
|
6
|
+
TorchGMM allows to run Gaussian Mixture Models on single or multiple CPUs/GPUs.
|
|
7
|
+
The repository is a fork from [PyCave](https://github.com/borchero/pycave) and [LightKit](https://github.com/borchero/lightkit), two amazing packages developed by [Olivier Borchert](https://github.com/borchero) that are not being maintained anymore.
|
|
8
|
+
While PyCave implements additional models such as Markov Chains, TorchGMM focuses only on Gaussian Mixture Models.
|
|
9
|
+
|
|
10
|
+
The models are implemented in [PyTorch](https://pytorch.org/) and [PyTorch Lightning](https://lightning.ai/docs/pytorch/stable/), and provide an `Estimator` API
|
|
11
|
+
that is fully compatible with [scikit-learn](https://scikit-learn.org/stable/).
|
|
12
|
+
|
|
13
|
+
For Gaussian mixture model, TorchGMM allows for 100x speed ups when using a GPU and enables to train
|
|
14
|
+
on markedly larger datasets via mini-batch training. The full suite of benchmarks run to compare
|
|
15
|
+
TorchGMM models against scikit-learn models is available on the
|
|
16
|
+
[documentation website](https://pycave.borchero.com/sites/benchmark.html).
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Support for GPU and multi-node training by implementing models in PyTorch and relying on
|
|
21
|
+
[PyTorch Lightning](https://lightning.ai/docs/pytorch/stable/)
|
|
22
|
+
- Mini-batch training for all models such that they can be used on huge datasets
|
|
23
|
+
- Well-structured implementation of models
|
|
24
|
+
|
|
25
|
+
- High-level `Estimator` API allows for easy usage such that models feel and behave like in
|
|
26
|
+
scikit-learn
|
|
27
|
+
- Medium-level `LightingModule` implements the training algorithm
|
|
28
|
+
- Low-level PyTorch `Module` manages the model parameters
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
TorchGMM is available via `pip`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install torchgmm
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you are using [Poetry](https://python-poetry.org/):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
poetry add torchgmm
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
If you've ever used scikit-learn, you'll feel right at home when using TorchGMM. First, let's create
|
|
47
|
+
some artificial data to work with:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import torch
|
|
51
|
+
|
|
52
|
+
X = torch.cat([
|
|
53
|
+
torch.randn(10000, 8) - 5,
|
|
54
|
+
torch.randn(10000, 8),
|
|
55
|
+
torch.randn(10000, 8) + 5,
|
|
56
|
+
])
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This dataset consists of three clusters with 8-dimensional datapoints. If you want to fit a K-Means
|
|
60
|
+
model, to find the clusters' centroids, it's as easy as:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from torchgmm.clustering import KMeans
|
|
64
|
+
|
|
65
|
+
estimator = KMeans(3)
|
|
66
|
+
estimator.fit(X)
|
|
67
|
+
|
|
68
|
+
# Once the estimator is fitted, it provides various properties. One of them is
|
|
69
|
+
# the `model_` property which yields the PyTorch module with the fitted parameters.
|
|
70
|
+
print("Centroids are:")
|
|
71
|
+
print(estimator.model_.centroids)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Due to the high-level estimator API, the usage for all machine learning models is similar. The API
|
|
75
|
+
documentation provides more detailed information about parameters that can be passed to estimators
|
|
76
|
+
and which methods are available.
|
|
77
|
+
|
|
78
|
+
### GPU and Multi-Node training
|
|
79
|
+
|
|
80
|
+
For GPU- and multi-node training, TorchGMM leverages PyTorch Lightning. The hardware that training
|
|
81
|
+
runs on is determined by the
|
|
82
|
+
[Trainer](https://pytorch-lightning.readthedocs.io/en/latest/api/pytorch_lightning.trainer.trainer.html#pytorch_lightning.trainer.trainer.Trainer)
|
|
83
|
+
class. It's
|
|
84
|
+
[**init**](https://pytorch-lightning.readthedocs.io/en/latest/api/pytorch_lightning.trainer.trainer.html#pytorch_lightning.trainer.trainer.Trainer.__init__)
|
|
85
|
+
method provides various configuration options.
|
|
86
|
+
|
|
87
|
+
If you want to run K-Means with a GPU, you can pass the options `accelerator='gpu'` and `devices=1`
|
|
88
|
+
to the estimator's initializer:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
estimator = KMeans(3, trainer_params=dict(accelerator='gpu', devices=1))
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Similarly, if you want to train on 4 nodes simultaneously where each node has one GPU available,
|
|
95
|
+
you can specify this as follows:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
estimator = KMeans(3, trainer_params=dict(num_nodes=4, accelerator='gpu', devices=1))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
In fact, **you do not need to change anything else in your code**.
|
|
102
|
+
|
|
103
|
+
### Implemented Models
|
|
104
|
+
|
|
105
|
+
Currently, TorchGMM implements two different models:
|
|
106
|
+
|
|
107
|
+
- [GaussianMixture](https://pycave.borchero.com/sites/generated/bayes/gmm/pycave.bayes.GaussianMixture.html)
|
|
108
|
+
- [K-Means](https://pycave.borchero.com/sites/generated/clustering/kmeans/pycave.clustering.KMeans.html)
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
TorchGMM is licensed under the [MIT License](https://github.com/marcovarrone/torchgmm/blob/main/LICENSE).
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
authors = ["Oliver Borchert <me@borchero.com>"]
|
|
3
|
+
classifiers = [
|
|
4
|
+
"Development Status :: 5 - Production/Stable",
|
|
5
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
6
|
+
]
|
|
7
|
+
description = "Gaussian Mixture Models in PyTorch."
|
|
8
|
+
license = "MIT"
|
|
9
|
+
name = "torchgmm"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
repository = "https://github.com/marcovarrone/torchgmm"
|
|
12
|
+
version = "0.1.0"
|
|
13
|
+
|
|
14
|
+
[tool.poetry.dependencies]
|
|
15
|
+
numpy = "^1.20.3"
|
|
16
|
+
python = ">=3.8,<4.0"
|
|
17
|
+
pytorch-lightning = ">2.0"
|
|
18
|
+
torch = ">1.11.0"
|
|
19
|
+
torchmetrics = ">=0.6,"
|
|
20
|
+
|
|
21
|
+
[tool.poetry.group.pre-commit.dependencies]
|
|
22
|
+
black = "^22.12.0"
|
|
23
|
+
docformatter = "^1.5.0"
|
|
24
|
+
isort = "^5.10.1"
|
|
25
|
+
mypy = "^0.991"
|
|
26
|
+
pylint = "^2.12.2"
|
|
27
|
+
pyupgrade = "^3.3.1"
|
|
28
|
+
|
|
29
|
+
[tool.poetry.group.docs.dependencies]
|
|
30
|
+
Sphinx = "^5.0.0"
|
|
31
|
+
pydata-sphinx-theme = "^0.7.2"
|
|
32
|
+
scanpydoc = "^0.7.1"
|
|
33
|
+
sphinx-autodoc-typehints = "^1.12.0"
|
|
34
|
+
sphinx-automodapi = "^0.13"
|
|
35
|
+
sphinx-copybutton = "^0.3.3"
|
|
36
|
+
sphinxcontrib-spelling = "^7.2.1"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.group.testing.dependencies]
|
|
39
|
+
flaky = "^3.7.0"
|
|
40
|
+
pytest = "^6.2.4"
|
|
41
|
+
pytest-benchmark = "^3.4.1"
|
|
42
|
+
scikit-learn = ">=0.24.2"
|
|
43
|
+
|
|
44
|
+
[tool.poetry.group.dev.dependencies]
|
|
45
|
+
jupyter = "^1.0.0"
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
build-backend = "poetry.core.masonry.api"
|
|
49
|
+
requires = ["poetry-core>=1.0.0"]
|
|
50
|
+
|
|
51
|
+
[tool.pylint.messages_control]
|
|
52
|
+
disable = [
|
|
53
|
+
"arguments-differ",
|
|
54
|
+
"duplicate-code",
|
|
55
|
+
"missing-module-docstring",
|
|
56
|
+
"invalid-name",
|
|
57
|
+
"too-few-public-methods",
|
|
58
|
+
"too-many-ancestors",
|
|
59
|
+
"too-many-arguments",
|
|
60
|
+
"too-many-branches",
|
|
61
|
+
"too-many-locals",
|
|
62
|
+
"too-many-instance-attributes",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.pylint.typecheck]
|
|
66
|
+
generated-members = [
|
|
67
|
+
"torch.*",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.black]
|
|
71
|
+
line-length = 99
|
|
72
|
+
target-version = ["py38", "py39", "py310"]
|
|
73
|
+
|
|
74
|
+
[tool.isort]
|
|
75
|
+
force_alphabetical_sort_within_sections = true
|
|
76
|
+
include_trailing_comma = true
|
|
77
|
+
known_first_party = "torchgmm,tests"
|
|
78
|
+
line_length = 99
|
|
79
|
+
lines_between_sections = 0
|
|
80
|
+
profile = "black"
|
|
81
|
+
skip_gitignore = true
|
|
82
|
+
|
|
83
|
+
[tool.docformatter]
|
|
84
|
+
make-summary-multi-line = true
|
|
85
|
+
pre-summary-newline = true
|
|
86
|
+
recursive = true
|
|
87
|
+
wrap-descriptions = 99
|
|
88
|
+
wrap-summaries = 99
|
|
89
|
+
|
|
90
|
+
[tool.pytest.ini_options]
|
|
91
|
+
filterwarnings = [
|
|
92
|
+
"ignore:.*Create unlinked descriptors is going to go away.*:DeprecationWarning",
|
|
93
|
+
"ignore:.*this fit will run with no optimizer.*",
|
|
94
|
+
"ignore:.*Consider increasing the value of the `num_workers` argument.*",
|
|
95
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import warnings
|
|
3
|
+
import torchgmm.base
|
|
4
|
+
|
|
5
|
+
# This is taken from PyTorch Lightning and ensures that logging for this package is enabled
|
|
6
|
+
_root_logger = logging.getLogger()
|
|
7
|
+
_logger = logging.getLogger(__name__)
|
|
8
|
+
_logger.setLevel(logging.INFO)
|
|
9
|
+
if not _root_logger.hasHandlers():
|
|
10
|
+
_logger.addHandler(logging.StreamHandler())
|
|
11
|
+
_logger.propagate = False
|
|
12
|
+
|
|
13
|
+
# This disables most logs generated by PyTorch Lightning
|
|
14
|
+
logging.getLogger("pytorch_lightning").setLevel(logging.WARNING)
|
|
15
|
+
warnings.filterwarnings(
|
|
16
|
+
action="ignore", message=".*Consider increasing the value of the `num_workers` argument.*"
|
|
17
|
+
)
|
|
18
|
+
warnings.filterwarnings(
|
|
19
|
+
action="ignore", message=".*`LightningModule.configure_optimizers` returned `None`.*"
|
|
20
|
+
)
|
|
21
|
+
warnings.filterwarnings(
|
|
22
|
+
action="ignore", message=".*`LoggerConnector.gpus_metrics` was deprecated in v1.5.*"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# We also want to define a function which silences info logs
|
|
26
|
+
def set_logging_level(level: int) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Enables or disables logging for the entire module. By default, logging is enabled.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
enabled: Whether to enable logging.
|
|
32
|
+
"""
|
|
33
|
+
_logger.setLevel(level)
|
|
34
|
+
torchgmm.base.set_logging_level(level)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Export
|
|
38
|
+
__all__ = ["set_logging_level"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from .estimator import BaseEstimator, ConfigurableBaseEstimator
|
|
3
|
+
|
|
4
|
+
# This is taken from PyTorch Lightning and ensures that logging for this package is enabled
|
|
5
|
+
_root_logger = logging.getLogger()
|
|
6
|
+
_logger = logging.getLogger(__name__)
|
|
7
|
+
_logger.setLevel(logging.INFO)
|
|
8
|
+
if not _root_logger.hasHandlers():
|
|
9
|
+
_logger.addHandler(logging.StreamHandler())
|
|
10
|
+
_logger.propagate = False
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def set_logging_level(level: int) -> None:
|
|
14
|
+
"""
|
|
15
|
+
Enables or disables logging for the entire module. By default, logging is enabled.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
enabled: Whether to enable logging.
|
|
19
|
+
"""
|
|
20
|
+
_logger.setLevel(level)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ["BaseEstimator", "ConfigurableBaseEstimator"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .collation import collate_tensor, collate_tuple
|
|
2
|
+
from .loader import DataLoader
|
|
3
|
+
from .sampler import RangeBatchSampler
|
|
4
|
+
from .types import DataLoaderLike, dataset_from_tensors, TensorLike
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"DataLoader",
|
|
8
|
+
"DataLoaderLike",
|
|
9
|
+
"RangeBatchSampler",
|
|
10
|
+
"TensorLike",
|
|
11
|
+
"collate_tensor",
|
|
12
|
+
"collate_tuple",
|
|
13
|
+
"dataset_from_tensors",
|
|
14
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
import torch
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def collate_tuple(batch: Tuple[torch.Tensor, ...]) -> Tuple[torch.Tensor, ...]:
|
|
6
|
+
"""
|
|
7
|
+
Collate a tuple of batch items by returning the input tuple.
|
|
8
|
+
|
|
9
|
+
This is the default used by
|
|
10
|
+
:class:`~torchgmm.base.data.DataLoader` when slices are cut from the underlying data source.
|
|
11
|
+
"""
|
|
12
|
+
return batch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def collate_tensor(batch: Tuple[torch.Tensor, ...]) -> torch.Tensor:
|
|
16
|
+
"""
|
|
17
|
+
Collates a tuple of batch items into the first tensor.
|
|
18
|
+
|
|
19
|
+
Might be useful if only a single tensor is passed to
|
|
20
|
+
:class:`~torchgmm.base.data.DataLoader`.
|
|
21
|
+
"""
|
|
22
|
+
return batch[0]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from typing import Any, Iterator, TypeVar
|
|
2
|
+
from pytorch_lightning.overrides.distributed import _IndexBatchSamplerWrapper
|
|
3
|
+
from torch.utils.data import DataLoader as TorchDataLoader
|
|
4
|
+
from torch.utils.data import Dataset, TensorDataset
|
|
5
|
+
from torch.utils.data.sampler import SequentialSampler
|
|
6
|
+
from .collation import collate_tuple
|
|
7
|
+
from .sampler import RangeBatchSampler
|
|
8
|
+
|
|
9
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DataLoader(TorchDataLoader[T_co]):
|
|
13
|
+
"""Extension for PyTorch's builtin dataloader. This implementation allows
|
|
14
|
+
to retrieve contiguous indices from a
|
|
15
|
+
:class:`~torch.utils.data.TensorDataset` orders of magnitude faster. The
|
|
16
|
+
data loader, thus, enables to implement traditional machine learning
|
|
17
|
+
methods that exhibit a speed similar to the implementations found in
|
|
18
|
+
Scikit-learn.
|
|
19
|
+
|
|
20
|
+
Note:
|
|
21
|
+
Retrieving contiguous indices is only possible when all of the following conditions apply:
|
|
22
|
+
|
|
23
|
+
- ``shuffle=False`` or ``batch_sampler`` is of type
|
|
24
|
+
:class:`~torchgmm.base.data.RangeBatchSampler`
|
|
25
|
+
- ``sampler is None``
|
|
26
|
+
- ``num_workers=0``
|
|
27
|
+
- ``dataset`` is not iterable
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, dataset: Dataset[T_co], **kwargs: Any):
|
|
31
|
+
"""
|
|
32
|
+
Args:
|
|
33
|
+
dataset: The dataset from which to load the data.
|
|
34
|
+
kwargs: Keyword arguments passed to :meth:`torch.utils.data.DataLoader.__init__`.
|
|
35
|
+
"""
|
|
36
|
+
if (
|
|
37
|
+
not kwargs.get("shuffle", False)
|
|
38
|
+
and "sampler" not in kwargs
|
|
39
|
+
and "batch_sampler" not in kwargs
|
|
40
|
+
and kwargs.get("num_workers", 0) == 0
|
|
41
|
+
and isinstance(dataset, TensorDataset)
|
|
42
|
+
):
|
|
43
|
+
kwargs["batch_sampler"] = RangeBatchSampler(
|
|
44
|
+
SequentialSampler(dataset),
|
|
45
|
+
batch_size=kwargs.get("batch_size", 1),
|
|
46
|
+
drop_last=kwargs.get("drop_last", False),
|
|
47
|
+
)
|
|
48
|
+
kwargs.pop("batch_size", None)
|
|
49
|
+
kwargs.pop("shuffle", None)
|
|
50
|
+
kwargs.pop("drop_last", None)
|
|
51
|
+
kwargs.setdefault("collate_fn", collate_tuple)
|
|
52
|
+
|
|
53
|
+
super().__init__(dataset, **kwargs) # type: ignore
|
|
54
|
+
|
|
55
|
+
self.custom_batching = self.num_workers == 0 and (
|
|
56
|
+
isinstance(self.batch_sampler, RangeBatchSampler)
|
|
57
|
+
or (
|
|
58
|
+
self.batch_sampler is not None
|
|
59
|
+
and hasattr(self.batch_sampler, "sampler")
|
|
60
|
+
and isinstance(self.batch_sampler.sampler, RangeBatchSampler)
|
|
61
|
+
)
|
|
62
|
+
or (
|
|
63
|
+
isinstance(self.batch_sampler, _IndexBatchSamplerWrapper)
|
|
64
|
+
and isinstance(self.batch_sampler._batch_sampler, RangeBatchSampler) # type: ignore
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def __iter__(self) -> Iterator[Any]: # pylint: disable=inconsistent-return-statements
|
|
69
|
+
if not self.custom_batching:
|
|
70
|
+
yield from super().__iter__()
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
for indices in self.batch_sampler:
|
|
74
|
+
if isinstance(indices, range):
|
|
75
|
+
subscript = slice(indices.start, indices.stop)
|
|
76
|
+
yield self.collate_fn(tuple(t[subscript] for t in self.dataset.tensors))
|
|
77
|
+
else:
|
|
78
|
+
yield self.collate_fn(tuple(t[indices] for t in self.dataset.tensors))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Iterator
|
|
3
|
+
from torch.utils.data import Sampler
|
|
4
|
+
from torch.utils.data.sampler import SequentialSampler
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RangeBatchSampler(Sampler[range]):
|
|
8
|
+
"""
|
|
9
|
+
Sampler providing batches of contiguous indices.
|
|
10
|
+
|
|
11
|
+
This sampler can be used with
|
|
12
|
+
:class:`torchgmm.base.data.DataLoader` to provide significant speedups for tensor datasets.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, sampler: Sampler[int], batch_size: int, drop_last: bool = False):
|
|
16
|
+
"""
|
|
17
|
+
Args:
|
|
18
|
+
sampler: The sampler providing indices. Must be a sequential sampler. Note that the
|
|
19
|
+
only purpose of this sampler is to determine its length.
|
|
20
|
+
batch_size: The number of items to sample for each batch.
|
|
21
|
+
drop_last: Whether to drop the last batch if ``num_items`` is not divisible by
|
|
22
|
+
``batch_size``.
|
|
23
|
+
"""
|
|
24
|
+
assert isinstance(
|
|
25
|
+
sampler, SequentialSampler
|
|
26
|
+
), f"{self.__class__.__name__} only works with sequential samplers."
|
|
27
|
+
|
|
28
|
+
super().__init__(None)
|
|
29
|
+
self.dataset_size = len(sampler)
|
|
30
|
+
self.batch_size = batch_size
|
|
31
|
+
self.drop_last = drop_last
|
|
32
|
+
|
|
33
|
+
def __len__(self) -> int:
|
|
34
|
+
if self.drop_last:
|
|
35
|
+
return self.dataset_size // self.batch_size
|
|
36
|
+
return math.ceil(self.dataset_size / self.batch_size)
|
|
37
|
+
|
|
38
|
+
def __iter__(self) -> Iterator[range]:
|
|
39
|
+
for i in range(len(self)):
|
|
40
|
+
sample = range(i * self.batch_size, (i + 1) * self.batch_size)
|
|
41
|
+
yield sample
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Sequence, TypeVar, Union
|
|
2
|
+
import numpy as np
|
|
3
|
+
import numpy.typing as npt
|
|
4
|
+
import torch
|
|
5
|
+
from torch.utils.data import DataLoader, TensorDataset
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
TensorLike = Union[
|
|
10
|
+
npt.NDArray[np.float32],
|
|
11
|
+
torch.Tensor,
|
|
12
|
+
]
|
|
13
|
+
TensorLike.__doc__ = """
|
|
14
|
+
Type annotation for functions accepting any kind of tensor data as input. Consider using this
|
|
15
|
+
annotation if your methods in an estimator derived from :class:`torchgmm.base.BaseEstimator` work on
|
|
16
|
+
tensors.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
DataLoaderLike = Union[
|
|
20
|
+
DataLoader[T],
|
|
21
|
+
Sequence[DataLoader[T]],
|
|
22
|
+
]
|
|
23
|
+
DataLoaderLike.__doc__ = """
|
|
24
|
+
Generic type annotation for functions accepting any data loader as input. Consider using this
|
|
25
|
+
annotation for the implementation of methods in an estimator derived from
|
|
26
|
+
:class:`torchgmm.base.BaseEstimator`.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def dataset_from_tensors(*data: TensorLike) -> TensorDataset:
|
|
31
|
+
"""
|
|
32
|
+
Transforms a set of tensor-like items into a datasets.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
data: The tensor-like items.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The dataset.
|
|
39
|
+
"""
|
|
40
|
+
return TensorDataset(*[_to_tensor(t) for t in data])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _to_tensor(data: TensorLike) -> torch.Tensor:
|
|
44
|
+
if isinstance(data, np.ndarray):
|
|
45
|
+
return torch.from_numpy(data)
|
|
46
|
+
return data
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .base import BaseEstimator
|
|
2
|
+
from .configurable import ConfigurableBaseEstimator
|
|
3
|
+
from .exception import NotFittedError
|
|
4
|
+
from .mixins import PredictorMixin, TransformerMixin
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"BaseEstimator",
|
|
8
|
+
"ConfigurableBaseEstimator",
|
|
9
|
+
"NotFittedError",
|
|
10
|
+
"PredictorMixin",
|
|
11
|
+
"TransformerMixin",
|
|
12
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# pylint: disable=missing-class-docstring,missing-function-docstring
|
|
2
|
+
|
|
3
|
+
from typing import Protocol, TypeVar
|
|
4
|
+
|
|
5
|
+
D_contra = TypeVar("D_contra", contravariant=True)
|
|
6
|
+
R_co = TypeVar("R_co", covariant=True)
|
|
7
|
+
E = TypeVar("E", bound="Estimator") # type: ignore
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Estimator(Protocol[D_contra]):
|
|
11
|
+
def fit(self: E, data: D_contra) -> E:
|
|
12
|
+
...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Transformer(Estimator[D_contra], Protocol[D_contra, R_co]):
|
|
16
|
+
def transform(self, data: D_contra) -> R_co:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Predictor(Estimator[D_contra], Protocol[D_contra, R_co]):
|
|
21
|
+
def predict(self, data: D_contra) -> R_co:
|
|
22
|
+
...
|