learnergy 2.0.0__tar.gz → 2.0.2__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.
- learnergy-2.0.2/AGENTS.md +5 -0
- learnergy-2.0.2/CONVENTIONS.md +75 -0
- learnergy-2.0.2/MANIFEST.in +1 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/PKG-INFO +32 -4
- {learnergy-2.0.0 → learnergy-2.0.2}/README.md +31 -3
- learnergy-2.0.2/learnergy/__init__.py +6 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/core/__init__.py +3 -0
- learnergy-2.0.2/learnergy/core/dataset.py +68 -0
- learnergy-2.0.2/learnergy/core/model.py +74 -0
- learnergy-2.0.2/learnergy/math/__init__.py +4 -0
- learnergy-2.0.2/learnergy/math/metrics.py +39 -0
- learnergy-2.0.2/learnergy/math/scale.py +27 -0
- learnergy-2.0.2/learnergy/models/__init__.py +4 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/bernoulli/__init__.py +3 -0
- learnergy-2.0.2/learnergy/models/bernoulli/conv_rbm.py +346 -0
- learnergy-2.0.2/learnergy/models/bernoulli/discriminative_rbm.py +400 -0
- learnergy-2.0.2/learnergy/models/bernoulli/dropout_rbm.py +164 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/bernoulli/e_dropout_rbm.py +61 -77
- learnergy-2.0.2/learnergy/models/bernoulli/rbm.py +357 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/deep/__init__.py +3 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/deep/conv_dbn.py +95 -42
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/deep/dbn.py +94 -38
- learnergy-2.0.2/learnergy/models/deep/residual_dbn.py +167 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/extra/__init__.py +3 -0
- learnergy-2.0.2/learnergy/models/extra/sigmoid_rbm.py +63 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/models/gaussian/__init__.py +3 -0
- learnergy-2.0.2/learnergy/models/gaussian/_normalization.py +13 -0
- learnergy-2.0.2/learnergy/models/gaussian/gaussian_conv_rbm.py +195 -0
- learnergy-2.0.2/learnergy/models/gaussian/gaussian_rbm.py +506 -0
- learnergy-2.0.2/learnergy/utils/__init__.py +4 -0
- learnergy-2.0.2/learnergy/utils/constants.py +6 -0
- learnergy-2.0.2/learnergy/utils/exception.py +99 -0
- learnergy-2.0.2/learnergy/utils/logging.py +67 -0
- learnergy-2.0.2/learnergy/visual/__init__.py +4 -0
- learnergy-2.0.2/learnergy/visual/convergence.py +65 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy/visual/image.py +43 -14
- learnergy-2.0.2/learnergy/visual/tensor.py +66 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy.egg-info/PKG-INFO +32 -4
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy.egg-info/SOURCES.txt +4 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/pyproject.toml +3 -2
- {learnergy-2.0.0 → learnergy-2.0.2}/tests/test_bernoulli.py +65 -3
- {learnergy-2.0.0 → learnergy-2.0.2}/tests/test_core.py +9 -1
- {learnergy-2.0.0 → learnergy-2.0.2}/tests/test_deep.py +51 -3
- learnergy-2.0.2/tests/test_gaussian.py +215 -0
- learnergy-2.0.2/tests/test_utilities.py +144 -0
- learnergy-2.0.0/learnergy/__init__.py +0 -5
- learnergy-2.0.0/learnergy/core/dataset.py +0 -48
- learnergy-2.0.0/learnergy/core/model.py +0 -58
- learnergy-2.0.0/learnergy/math/__init__.py +0 -1
- learnergy-2.0.0/learnergy/math/metrics.py +0 -21
- learnergy-2.0.0/learnergy/math/scale.py +0 -14
- learnergy-2.0.0/learnergy/models/__init__.py +0 -1
- learnergy-2.0.0/learnergy/models/bernoulli/conv_rbm.py +0 -359
- learnergy-2.0.0/learnergy/models/bernoulli/discriminative_rbm.py +0 -404
- learnergy-2.0.0/learnergy/models/bernoulli/dropout_rbm.py +0 -191
- learnergy-2.0.0/learnergy/models/bernoulli/rbm.py +0 -364
- learnergy-2.0.0/learnergy/models/deep/residual_dbn.py +0 -130
- learnergy-2.0.0/learnergy/models/extra/sigmoid_rbm.py +0 -34
- learnergy-2.0.0/learnergy/models/gaussian/gaussian_conv_rbm.py +0 -131
- learnergy-2.0.0/learnergy/models/gaussian/gaussian_rbm.py +0 -567
- learnergy-2.0.0/learnergy/utils/__init__.py +0 -1
- learnergy-2.0.0/learnergy/utils/constants.py +0 -3
- learnergy-2.0.0/learnergy/utils/exception.py +0 -40
- learnergy-2.0.0/learnergy/utils/logging.py +0 -37
- learnergy-2.0.0/learnergy/visual/__init__.py +0 -1
- learnergy-2.0.0/learnergy/visual/convergence.py +0 -41
- learnergy-2.0.0/learnergy/visual/tensor.py +0 -32
- learnergy-2.0.0/tests/test_gaussian.py +0 -73
- learnergy-2.0.0/tests/test_utilities.py +0 -75
- {learnergy-2.0.0 → learnergy-2.0.2}/LICENSE +0 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy.egg-info/dependency_links.txt +0 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy.egg-info/requires.txt +0 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/learnergy.egg-info/top_level.txt +0 -0
- {learnergy-2.0.0 → learnergy-2.0.2}/setup.cfg +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Contributor and agent guidance
|
|
2
|
+
|
|
3
|
+
Read and follow [CONVENTIONS.md](CONVENTIONS.md) before changing this repository.
|
|
4
|
+
Preserve public behavior and checkpoint compatibility unless the task explicitly changes them.
|
|
5
|
+
Use the existing validation tools and keep unrelated or concurrent work intact.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Learnergy conventions
|
|
2
|
+
|
|
3
|
+
Learnergy adopts the applicable code-style rules from cpmux's phitrain-derived conventions.
|
|
4
|
+
These rules govern new code and convention updates without authorizing unrelated algorithm or API changes.
|
|
5
|
+
|
|
6
|
+
## Compatibility and scope
|
|
7
|
+
|
|
8
|
+
- Keep the current Python 3.11 minimum. Use the requested modern Python idioms where they are compatible with it.
|
|
9
|
+
- Preserve public imports, model names, constructor options, return values, normalization policies, and checkpoint state.
|
|
10
|
+
- Keep Learnergy's domain-oriented package layout and public package exports. cpmux's empty package initializers,
|
|
11
|
+
CLI architecture, Pydantic choices, and test-layout migration are not part of this adoption.
|
|
12
|
+
- Tests remain grouped by the existing model families. Runtime validation rules do not prohibit test assertions.
|
|
13
|
+
|
|
14
|
+
## Code style
|
|
15
|
+
|
|
16
|
+
- Use `X | None`, builtin generics such as `list[str]`, and ABCs from `collections.abc`.
|
|
17
|
+
Import only typing-specific constructs such as `Any` and `Literal` from `typing`. (R2)
|
|
18
|
+
- Start every Python file with the project header:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
# Copyright (c) 2020-2026 Mateus Roder and Gustavo de Rosa.
|
|
22
|
+
# Licensed under the Apache License, Version 2.0.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Keep imports top-level, absolute, and grouped as standard library, third-party, then Learnergy imports.
|
|
26
|
+
- Use double-quoted strings. Code formatting and readable prose use a 120-character limit. (R9)
|
|
27
|
+
- Public functions, regular classes, and explicit constructors have Google-style docstrings.
|
|
28
|
+
Use a single-sentence summary and one-line `Args:`, `Returns:`, and `Raises:` entries as applicable.
|
|
29
|
+
Do not put semicolons or `defaults to ...` tails in entries. (R3, R13)
|
|
30
|
+
- A regular class has a one-sentence class summary. Constructor arguments belong on `__init__`, not the class.
|
|
31
|
+
Preserve mathematical references and substantive behavioral notes in module or operation documentation.
|
|
32
|
+
- Multiline docstrings have one blank line before the closing triple quotes and one blank line after them
|
|
33
|
+
before code or fields. Keep class and module summaries on one line when they need no other content,
|
|
34
|
+
matching cpmux's examples and Black's normalization.
|
|
35
|
+
- Private helpers have no docstrings. Framework-dispatched overrides such as `forward`, `__getitem__`, and
|
|
36
|
+
`__len__` have no docstrings. Document their relevant contracts in public sampling methods, constructors,
|
|
37
|
+
module documentation, or the user guide instead.
|
|
38
|
+
- Data classes without an explicit constructor document each field on one line in an `Attributes:` section.
|
|
39
|
+
This rule does not require introducing data classes into existing models.
|
|
40
|
+
- Public contracts describe tensor shapes, tuple element order, mutation, history, normalization, gradient
|
|
41
|
+
boundaries, and failure behavior where relevant. An annotation is not runtime validation.
|
|
42
|
+
- Obtain library loggers with `get_logger(__name__)` from `learnergy.utils.logging`.
|
|
43
|
+
Do not use `print()` in library code. Application examples may print their results.
|
|
44
|
+
- Warning and error diagnostics identify a backticked offender and end with a period, for example
|
|
45
|
+
`` f"`name={value}` could not be loaded." ``. Info and debug messages stay plain. (R14)
|
|
46
|
+
- Raised messages use `` "`name` <verb phrase>[, but got <value>]." ``.
|
|
47
|
+
Use `is None` and `is True` prose rather than comparison operators in messages. (R1)
|
|
48
|
+
- Validate with `if` and a specific raised exception, never a runtime `assert`. Do not use bare `except:`.
|
|
49
|
+
- Comments explain why rather than narrating the next statement. Prefer no comment or one line, with a
|
|
50
|
+
three-line maximum, no banners, and no trailing period. Copyright and license notices are exempt. (R8)
|
|
51
|
+
- Separate logical phases with one blank line in function bodies of at least 12 lines. (R11)
|
|
52
|
+
- Inline new single-use implementation details. Extract a helper, constant, or configurable parameter when
|
|
53
|
+
a second call-site establishes reuse. Do not delete published APIs or required callbacks by counting only
|
|
54
|
+
their internal call-sites. (R16)
|
|
55
|
+
|
|
56
|
+
## Tests and review
|
|
57
|
+
|
|
58
|
+
- Test functions and fixtures are plain functions without docstrings. Test functions have no type annotations.
|
|
59
|
+
- Use bare assertions without failure-message strings. Names should describe the observable behavior.
|
|
60
|
+
- Preserve seeded behavior when claiming a behavior-preserving refactor. Treat deliberate numerical changes
|
|
61
|
+
as separate, explicit changes rather than hiding them in a style pass.
|
|
62
|
+
- Regression tests compare actual model output with an independently justified expectation and should fail
|
|
63
|
+
for the defect they claim to prevent.
|
|
64
|
+
|
|
65
|
+
## Tooling
|
|
66
|
+
|
|
67
|
+
Use the existing pinned Black, isort, and flake8 tools at line length 120.
|
|
68
|
+
The supported interpreter range, dependencies, and build backend do not change as part of this style adoption.
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
uv sync --locked --extra docs
|
|
72
|
+
uv run pre-commit run --all-files
|
|
73
|
+
uv run pytest
|
|
74
|
+
uv run python -m sphinx -W -b html docs build
|
|
75
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include AGENTS.md CONVENTIONS.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: learnergy
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Energy-based machine learners built with PyTorch
|
|
5
5
|
Author-email: Mateus Roder <mateus.roder@unesp.br>, Gustavo de Rosa <gustavo.rosa@unesp.br>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -49,15 +49,22 @@ image-quality metrics, and visualization helpers.
|
|
|
49
49
|
|
|
50
50
|
## Installation
|
|
51
51
|
|
|
52
|
-
Learnergy requires Python 3.11 or newer.
|
|
52
|
+
Learnergy requires Python 3.11 or newer. Add it to a project managed by uv with:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
55
|
+
uv add learnergy
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Add the optional torchvision dependency to run the examples:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv add "learnergy[examples]"
|
|
56
62
|
```
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
For a consumer installation in an existing Python environment, pip is also supported:
|
|
59
65
|
|
|
60
66
|
```bash
|
|
67
|
+
pip install learnergy
|
|
61
68
|
pip install "learnergy[examples]"
|
|
62
69
|
```
|
|
63
70
|
|
|
@@ -112,8 +119,29 @@ plots, image mosaics, and tensor rendering.
|
|
|
112
119
|
See [`examples/applications`](examples/applications) for complete training and
|
|
113
120
|
classification programs.
|
|
114
121
|
|
|
122
|
+
### Numerical behavior
|
|
123
|
+
|
|
124
|
+
When enabled, Gaussian normalization uses statistics from the current batch,
|
|
125
|
+
not stored training statistics. Batches of two or more samples use sample
|
|
126
|
+
standard deviation; a singleton batch is centered to zero. Representations
|
|
127
|
+
therefore depend on batch composition. Disable the corresponding normalization
|
|
128
|
+
flags when supplying externally standardized features.
|
|
129
|
+
|
|
130
|
+
`VarianceGaussianRBM.sigma` is a learnable scale: the effective visible variance
|
|
131
|
+
is `sigma**2` plus a dtype-dependent epsilon. Its `visible_sampling` method
|
|
132
|
+
returns conditional means followed by sampled states, and Gibbs sampling uses
|
|
133
|
+
those states.
|
|
134
|
+
|
|
135
|
+
Gaussian convolutional representations support gradient-based fine-tuning.
|
|
136
|
+
Use `torch.no_grad()` when extracting frozen features without an autograd graph.
|
|
137
|
+
|
|
138
|
+
The corrected variance-Gaussian sampling and stabilized likelihood calculations
|
|
139
|
+
can change training trajectories, including with a fixed random seed.
|
|
140
|
+
|
|
115
141
|
## Development
|
|
116
142
|
|
|
143
|
+
Follow [the coding conventions](CONVENTIONS.md) when changing the library or its examples.
|
|
144
|
+
|
|
117
145
|
The repository uses [uv](https://docs.astral.sh/uv/) for reproducible
|
|
118
146
|
environments and packaging:
|
|
119
147
|
|
|
@@ -12,15 +12,22 @@ image-quality metrics, and visualization helpers.
|
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
-
Learnergy requires Python 3.11 or newer.
|
|
15
|
+
Learnergy requires Python 3.11 or newer. Add it to a project managed by uv with:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
18
|
+
uv add learnergy
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Add the optional torchvision dependency to run the examples:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv add "learnergy[examples]"
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
For a consumer installation in an existing Python environment, pip is also supported:
|
|
22
28
|
|
|
23
29
|
```bash
|
|
30
|
+
pip install learnergy
|
|
24
31
|
pip install "learnergy[examples]"
|
|
25
32
|
```
|
|
26
33
|
|
|
@@ -75,8 +82,29 @@ plots, image mosaics, and tensor rendering.
|
|
|
75
82
|
See [`examples/applications`](examples/applications) for complete training and
|
|
76
83
|
classification programs.
|
|
77
84
|
|
|
85
|
+
### Numerical behavior
|
|
86
|
+
|
|
87
|
+
When enabled, Gaussian normalization uses statistics from the current batch,
|
|
88
|
+
not stored training statistics. Batches of two or more samples use sample
|
|
89
|
+
standard deviation; a singleton batch is centered to zero. Representations
|
|
90
|
+
therefore depend on batch composition. Disable the corresponding normalization
|
|
91
|
+
flags when supplying externally standardized features.
|
|
92
|
+
|
|
93
|
+
`VarianceGaussianRBM.sigma` is a learnable scale: the effective visible variance
|
|
94
|
+
is `sigma**2` plus a dtype-dependent epsilon. Its `visible_sampling` method
|
|
95
|
+
returns conditional means followed by sampled states, and Gibbs sampling uses
|
|
96
|
+
those states.
|
|
97
|
+
|
|
98
|
+
Gaussian convolutional representations support gradient-based fine-tuning.
|
|
99
|
+
Use `torch.no_grad()` when extracting frozen features without an autograd graph.
|
|
100
|
+
|
|
101
|
+
The corrected variance-Gaussian sampling and stabilized likelihood calculations
|
|
102
|
+
can change training trajectories, including with a fixed random seed.
|
|
103
|
+
|
|
78
104
|
## Development
|
|
79
105
|
|
|
106
|
+
Follow [the coding conventions](CONVENTIONS.md) when changing the library or its examples.
|
|
107
|
+
|
|
80
108
|
The repository uses [uv](https://docs.astral.sh/uv/) for reproducible
|
|
81
109
|
environments and packaging:
|
|
82
110
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Mateus Roder and Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
"""Adapt array-backed samples to the PyTorch dataset interface."""
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import torch
|
|
10
|
+
|
|
11
|
+
import learnergy.utils.exception as e
|
|
12
|
+
from learnergy.core.model import _validated_property
|
|
13
|
+
from learnergy.utils.logging import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Dataset(torch.utils.data.Dataset):
|
|
19
|
+
"""Expose samples and targets with an optional sample transform."""
|
|
20
|
+
|
|
21
|
+
data = _validated_property("data", doc="Backing sample collection, retained without copying.")
|
|
22
|
+
targets = _validated_property("targets", doc="Targets indexed alongside the sample collection.")
|
|
23
|
+
transform = _validated_property(
|
|
24
|
+
"transform",
|
|
25
|
+
lambda _, value: value is None or callable(value),
|
|
26
|
+
e.TypeError,
|
|
27
|
+
"`transform` should be callable or None.",
|
|
28
|
+
doc="Optional transformation applied when a sample is accessed.",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
data: Any,
|
|
34
|
+
targets: Any,
|
|
35
|
+
transform: Callable[[Any], Any] | None = None,
|
|
36
|
+
show_log: bool = True,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Store sample and target references for indexed access.
|
|
39
|
+
|
|
40
|
+
Data is not copied and transforms are applied on access rather than during initialization.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data: Indexable sample collection.
|
|
44
|
+
targets: Indexable target collection aligned with the samples.
|
|
45
|
+
transform: Optional callable that transforms an individual sample.
|
|
46
|
+
show_log: Whether to log dataset creation.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
TypeError: The transform is neither callable nor None.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
self.data = data
|
|
54
|
+
self.targets = targets
|
|
55
|
+
self.transform = transform
|
|
56
|
+
|
|
57
|
+
if show_log:
|
|
58
|
+
logger.info("Creating class: Dataset.")
|
|
59
|
+
logger.info("Class created.")
|
|
60
|
+
|
|
61
|
+
def __getitem__(self, idx: int) -> tuple[Any, Any]:
|
|
62
|
+
sample = self.data[idx]
|
|
63
|
+
if self.transform:
|
|
64
|
+
sample = self.transform(sample)
|
|
65
|
+
return sample, self.targets[idx]
|
|
66
|
+
|
|
67
|
+
def __len__(self) -> int:
|
|
68
|
+
return len(self.data)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Mateus Roder and Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
"""Provide shared model state and validated attributes."""
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import torch
|
|
10
|
+
|
|
11
|
+
import learnergy.utils.exception as e
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _validated_property(
|
|
15
|
+
name: str,
|
|
16
|
+
validator: Callable[[Any, Any], bool] | None = None,
|
|
17
|
+
error: type[Exception] = ValueError,
|
|
18
|
+
message: str = "`value` is invalid.",
|
|
19
|
+
*,
|
|
20
|
+
doc: str | None = None,
|
|
21
|
+
) -> property:
|
|
22
|
+
storage_name = f"_{name}"
|
|
23
|
+
|
|
24
|
+
def _get(instance: Any) -> Any:
|
|
25
|
+
return getattr(instance, storage_name)
|
|
26
|
+
|
|
27
|
+
def _set(instance: Any, value: Any) -> None:
|
|
28
|
+
if validator is not None and not validator(instance, value):
|
|
29
|
+
raise error(message)
|
|
30
|
+
|
|
31
|
+
setattr(instance, storage_name, value)
|
|
32
|
+
|
|
33
|
+
return property(_get, _set, doc=doc)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Model(torch.nn.Module):
|
|
37
|
+
"""Provide device selection and metric history for energy-based models."""
|
|
38
|
+
|
|
39
|
+
device = _validated_property(
|
|
40
|
+
"device",
|
|
41
|
+
lambda _, value: value in ("cpu", "cuda"),
|
|
42
|
+
e.TypeError,
|
|
43
|
+
"`device` should be `cpu` or `cuda`.",
|
|
44
|
+
doc="Computation device selected during initialization.",
|
|
45
|
+
)
|
|
46
|
+
history = _validated_property("history", doc="Recorded values grouped into lists by metric name.")
|
|
47
|
+
|
|
48
|
+
def __init__(self, use_gpu: bool = False) -> None:
|
|
49
|
+
"""Initialize device selection and an empty metric history.
|
|
50
|
+
|
|
51
|
+
Initialization sets PyTorch's process-wide default floating-point dtype to float32.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
use_gpu: Whether to select CUDA when it is available.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
super().__init__()
|
|
59
|
+
torch.set_default_dtype(torch.float32)
|
|
60
|
+
self.device = "cuda" if use_gpu and torch.cuda.is_available() else "cpu"
|
|
61
|
+
self.history = {}
|
|
62
|
+
|
|
63
|
+
def dump(self, **kwargs: Any) -> None:
|
|
64
|
+
"""Append values to their per-metric history lists.
|
|
65
|
+
|
|
66
|
+
Existing history is retained and values are stored without conversion or copying.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
**kwargs: Values keyed by the metric names to update.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
for k, v in kwargs.items():
|
|
74
|
+
self.history.setdefault(k, []).append(v)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Mateus Roder and Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
"""Calculate image similarity metrics."""
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
from skimage.metrics import structural_similarity
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def calculate_ssim(v: torch.Tensor, x: torch.Tensor) -> float:
|
|
11
|
+
"""Calculate the mean structural similarity of reconstructed images.
|
|
12
|
+
|
|
13
|
+
Tensors are detached and copied to CPU as needed before comparison.
|
|
14
|
+
Each original image supplies its own intensity range to the SSIM calculation.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
v: Reconstructed images, with each image flattened or shaped like its original.
|
|
18
|
+
x: Original grayscale images with shape (batch, height, width).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Mean SSIM over the paired images.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
ValueError: Batch lengths, image shapes, or SSIM window requirements are incompatible.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
originals = x.detach().cpu().numpy()
|
|
29
|
+
reconstructed = v.detach().cpu().numpy()
|
|
30
|
+
height, width = originals.shape[1:3]
|
|
31
|
+
|
|
32
|
+
return sum(
|
|
33
|
+
structural_similarity(
|
|
34
|
+
original,
|
|
35
|
+
rebuilt.reshape(height, width),
|
|
36
|
+
data_range=original.max() - original.min(),
|
|
37
|
+
)
|
|
38
|
+
for original, rebuilt in zip(originals, reconstructed, strict=True)
|
|
39
|
+
) / len(reconstructed)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Mateus Roder and Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
"""Normalize numerical arrays."""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from learnergy.utils.constants import EPSILON
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def unitary_scale(x: np.ndarray) -> np.ndarray:
|
|
12
|
+
"""Return a float32 copy scaled by its value range.
|
|
13
|
+
|
|
14
|
+
The input is not modified and finite constant arrays become zero arrays.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
x: Numeric array to scale.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Float32 array of the same shape, normalized with an epsilon guard.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
scaled = x.astype("float32")
|
|
25
|
+
scaled -= scaled.min()
|
|
26
|
+
scaled /= scaled.max() + EPSILON
|
|
27
|
+
return scaled
|