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.
Files changed (51) hide show
  1. torchgmm-0.1.0/LICENSE +19 -0
  2. torchgmm-0.1.0/PKG-INFO +138 -0
  3. torchgmm-0.1.0/README.md +112 -0
  4. torchgmm-0.1.0/pyproject.toml +95 -0
  5. torchgmm-0.1.0/torchgmm/__init__.py +38 -0
  6. torchgmm-0.1.0/torchgmm/base/__init__.py +23 -0
  7. torchgmm-0.1.0/torchgmm/base/data/__init__.py +14 -0
  8. torchgmm-0.1.0/torchgmm/base/data/collation.py +22 -0
  9. torchgmm-0.1.0/torchgmm/base/data/loader.py +78 -0
  10. torchgmm-0.1.0/torchgmm/base/data/sampler.py +41 -0
  11. torchgmm-0.1.0/torchgmm/base/data/types.py +46 -0
  12. torchgmm-0.1.0/torchgmm/base/estimator/__init__.py +12 -0
  13. torchgmm-0.1.0/torchgmm/base/estimator/_protocols.py +22 -0
  14. torchgmm-0.1.0/torchgmm/base/estimator/base.py +352 -0
  15. torchgmm-0.1.0/torchgmm/base/estimator/configurable.py +40 -0
  16. torchgmm-0.1.0/torchgmm/base/estimator/exception.py +5 -0
  17. torchgmm-0.1.0/torchgmm/base/estimator/mixins.py +47 -0
  18. torchgmm-0.1.0/torchgmm/base/nn/__init__.py +3 -0
  19. torchgmm-0.1.0/torchgmm/base/nn/_protocols.py +33 -0
  20. torchgmm-0.1.0/torchgmm/base/nn/configurable.py +165 -0
  21. torchgmm-0.1.0/torchgmm/base/utils/__init__.py +4 -0
  22. torchgmm-0.1.0/torchgmm/base/utils/generics.py +25 -0
  23. torchgmm-0.1.0/torchgmm/base/utils/path.py +9 -0
  24. torchgmm-0.1.0/torchgmm/bayes/__init__.py +7 -0
  25. torchgmm-0.1.0/torchgmm/bayes/core/__init__.py +13 -0
  26. torchgmm-0.1.0/torchgmm/bayes/core/_jit.py +88 -0
  27. torchgmm-0.1.0/torchgmm/bayes/core/normal.py +112 -0
  28. torchgmm-0.1.0/torchgmm/bayes/core/types.py +16 -0
  29. torchgmm-0.1.0/torchgmm/bayes/core/utils.py +44 -0
  30. torchgmm-0.1.0/torchgmm/bayes/gmm/__init__.py +8 -0
  31. torchgmm-0.1.0/torchgmm/bayes/gmm/estimator.py +308 -0
  32. torchgmm-0.1.0/torchgmm/bayes/gmm/lightning_module.py +334 -0
  33. torchgmm-0.1.0/torchgmm/bayes/gmm/metrics.py +148 -0
  34. torchgmm-0.1.0/torchgmm/bayes/gmm/model.py +147 -0
  35. torchgmm-0.1.0/torchgmm/bayes/gmm/types.py +16 -0
  36. torchgmm-0.1.0/torchgmm/bayes/markov_chain/__init__.py +8 -0
  37. torchgmm-0.1.0/torchgmm/bayes/markov_chain/estimator.py +176 -0
  38. torchgmm-0.1.0/torchgmm/bayes/markov_chain/lightning_module.py +51 -0
  39. torchgmm-0.1.0/torchgmm/bayes/markov_chain/metrics.py +61 -0
  40. torchgmm-0.1.0/torchgmm/bayes/markov_chain/model.py +158 -0
  41. torchgmm-0.1.0/torchgmm/bayes/markov_chain/types.py +53 -0
  42. torchgmm-0.1.0/torchgmm/clustering/__init__.py +5 -0
  43. torchgmm-0.1.0/torchgmm/clustering/kmeans/__init__.py +8 -0
  44. torchgmm-0.1.0/torchgmm/clustering/kmeans/estimator.py +261 -0
  45. torchgmm-0.1.0/torchgmm/clustering/kmeans/lightning_module.py +306 -0
  46. torchgmm-0.1.0/torchgmm/clustering/kmeans/metrics.py +222 -0
  47. torchgmm-0.1.0/torchgmm/clustering/kmeans/model.py +72 -0
  48. torchgmm-0.1.0/torchgmm/clustering/kmeans/types.py +15 -0
  49. torchgmm-0.1.0/torchgmm/py.typed +0 -0
  50. torchgmm-0.1.0/torchgmm/utils/__init__.py +3 -0
  51. 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.
@@ -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
+ <!-- ![PyPi](https://img.shields.io/pypi/v/torchgmm?label=version)
29
+ ![License](https://img.shields.io/pypi/l/torchgmm) -->
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
+
@@ -0,0 +1,112 @@
1
+ # TorchGMM
2
+
3
+ <!-- ![PyPi](https://img.shields.io/pypi/v/torchgmm?label=version)
4
+ ![License](https://img.shields.io/pypi/l/torchgmm) -->
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
+ ...