laco-logging 1.0.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.
- laco_logging-1.0.0/PKG-INFO +47 -0
- laco_logging-1.0.0/README.md +28 -0
- laco_logging-1.0.0/pyproject.toml +25 -0
- laco_logging-1.0.0/setup.cfg +4 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/__init__.py +58 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/_backends/__init__.py +1 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/_backends/mlflow.py +69 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/_backends/tensorboard.py +89 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/_backends/wandb.py +46 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/_core.py +94 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/mlflow.py +7 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/tensorboard.py +6 -0
- laco_logging-1.0.0/sources/laco/integrations/logging/wandb.py +6 -0
- laco_logging-1.0.0/sources/laco_logging.egg-info/PKG-INFO +47 -0
- laco_logging-1.0.0/sources/laco_logging.egg-info/SOURCES.txt +17 -0
- laco_logging-1.0.0/sources/laco_logging.egg-info/dependency_links.txt +1 -0
- laco_logging-1.0.0/sources/laco_logging.egg-info/requires.txt +15 -0
- laco_logging-1.0.0/sources/laco_logging.egg-info/top_level.txt +1 -0
- laco_logging-1.0.0/tests/test_laco_logging.py +50 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: laco-logging
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Unified experiment logging (W&B, MLflow, TensorBoard) for laco.
|
|
5
|
+
Author-email: Kurt Stolle <kurt@khws.io>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: laco>=1.0.0
|
|
9
|
+
Provides-Extra: wandb
|
|
10
|
+
Requires-Dist: wandb>=0.19; extra == "wandb"
|
|
11
|
+
Provides-Extra: mlflow
|
|
12
|
+
Requires-Dist: mlflow>=2.0; extra == "mlflow"
|
|
13
|
+
Provides-Extra: tensorboard
|
|
14
|
+
Requires-Dist: tensorboard>=2.0; extra == "tensorboard"
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Requires-Dist: wandb>=0.19; extra == "all"
|
|
17
|
+
Requires-Dist: mlflow>=2.0; extra == "all"
|
|
18
|
+
Requires-Dist: tensorboard>=2.0; extra == "all"
|
|
19
|
+
|
|
20
|
+
# Laco-Logging
|
|
21
|
+
|
|
22
|
+
Unified experiment logging (W&B, MLflow, TensorBoard) for laco.
|
|
23
|
+
|
|
24
|
+
Part of the [laco](https://github.com/khwstolle/laco) project — see the [root README](../../README.md) for an overview.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install laco-logging[all]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Extras
|
|
33
|
+
|
|
34
|
+
| Extra | Installs |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `laco-logging[wandb]` | W&B backend |
|
|
37
|
+
| `laco-logging[mlflow]` | MLflow backend |
|
|
38
|
+
| `laco-logging[tensorboard]` | TensorBoard backend |
|
|
39
|
+
| `laco-logging[all]` | All backends |
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
`log_config()`, `run()`, submodules `wandb`, `mlflow`, `tensorboard`
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
See [`docs/index.md`](docs/index.md) for the full guide.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Laco-Logging
|
|
2
|
+
|
|
3
|
+
Unified experiment logging (W&B, MLflow, TensorBoard) for laco.
|
|
4
|
+
|
|
5
|
+
Part of the [laco](https://github.com/khwstolle/laco) project — see the [root README](../../README.md) for an overview.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install laco-logging[all]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Extras
|
|
14
|
+
|
|
15
|
+
| Extra | Installs |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `laco-logging[wandb]` | W&B backend |
|
|
18
|
+
| `laco-logging[mlflow]` | MLflow backend |
|
|
19
|
+
| `laco-logging[tensorboard]` | TensorBoard backend |
|
|
20
|
+
| `laco-logging[all]` | All backends |
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
`log_config()`, `run()`, submodules `wandb`, `mlflow`, `tensorboard`
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
See [`docs/index.md`](docs/index.md) for the full guide.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "laco-logging"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Unified experiment logging (W&B, MLflow, TensorBoard) for laco."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
authors = [{ name = "Kurt Stolle", email = "kurt@khws.io" }]
|
|
12
|
+
dependencies = ["laco>=1.0.0"]
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
wandb = ["wandb>=0.19"]
|
|
16
|
+
mlflow = ["mlflow>=2.0"]
|
|
17
|
+
tensorboard = ["tensorboard>=2.0"]
|
|
18
|
+
all = ["wandb>=0.19", "mlflow>=2.0", "tensorboard>=2.0"]
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["sources"]
|
|
22
|
+
namespaces = true
|
|
23
|
+
|
|
24
|
+
[tool.uv.sources]
|
|
25
|
+
laco = { workspace = true }
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Unified experiment logging for laco.
|
|
2
|
+
|
|
3
|
+
Supports Weights & Biases, MLflow, and TensorBoard through a common API.
|
|
4
|
+
Each backend is an optional extra — install only what you need::
|
|
5
|
+
|
|
6
|
+
pip install laco-logging[wandb]
|
|
7
|
+
pip install laco-logging[mlflow]
|
|
8
|
+
pip install laco-logging[tensorboard]
|
|
9
|
+
pip install laco-logging[all]
|
|
10
|
+
|
|
11
|
+
Top-level API
|
|
12
|
+
-------------
|
|
13
|
+
:func:`log_config`
|
|
14
|
+
Log a resolved DictConfig to one or more backends in a single call.
|
|
15
|
+
|
|
16
|
+
:func:`run`
|
|
17
|
+
Unified context manager — delegates to the backend of your choice.
|
|
18
|
+
|
|
19
|
+
Backend submodules
|
|
20
|
+
------------------
|
|
21
|
+
:mod:`laco.integrations.logging.wandb`
|
|
22
|
+
``run(cfg, *, project, name, tags, ...)``
|
|
23
|
+
``log_config(cfg, run=None)``
|
|
24
|
+
|
|
25
|
+
:mod:`laco.integrations.logging.mlflow`
|
|
26
|
+
``run(cfg, *, experiment, run_name, tags, ...)``
|
|
27
|
+
``log_config(cfg, run_id=None)``
|
|
28
|
+
``log_artifact(cfg)``
|
|
29
|
+
|
|
30
|
+
:mod:`laco.integrations.logging.tensorboard`
|
|
31
|
+
``run(cfg, *, log_dir, comment, ...)``
|
|
32
|
+
``log_config(cfg, writer)``
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
::
|
|
37
|
+
|
|
38
|
+
import laco
|
|
39
|
+
import laco.integrations.logging as laco_logging
|
|
40
|
+
|
|
41
|
+
cfg = laco.load("configs/train.py")
|
|
42
|
+
|
|
43
|
+
# Single backend:
|
|
44
|
+
with laco_logging.run(cfg, backend="wandb", project="my-project") as run:
|
|
45
|
+
train(cfg)
|
|
46
|
+
|
|
47
|
+
# Multiple backends simultaneously:
|
|
48
|
+
laco_logging.log_config(cfg, backends=["wandb", "mlflow"])
|
|
49
|
+
|
|
50
|
+
# Backend submodule directly:
|
|
51
|
+
with laco_logging.tensorboard.run(cfg, log_dir="tb/run1") as writer:
|
|
52
|
+
writer.add_scalar("loss", 0.1, 0)
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
from laco.integrations.logging._core import log_config as log_config
|
|
56
|
+
from laco.integrations.logging._core import run as run
|
|
57
|
+
|
|
58
|
+
__all__ = ["log_config", "mlflow", "run", "tensorboard", "wandb"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Backend implementations — imported lazily by the top-level API."""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""MLflow backend for laco-logging."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import pathlib
|
|
7
|
+
import typing
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
import mlflow
|
|
11
|
+
from omegaconf import DictConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _flatten(d: dict, prefix: str = "") -> dict[str, str]:
|
|
15
|
+
out: dict[str, str] = {}
|
|
16
|
+
for k, v in d.items():
|
|
17
|
+
key = f"{prefix}.{k}" if prefix else k
|
|
18
|
+
if isinstance(v, dict):
|
|
19
|
+
out.update(_flatten(v, key))
|
|
20
|
+
else:
|
|
21
|
+
out[key] = str(v)
|
|
22
|
+
return out
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def log_config(cfg: DictConfig, run_id: str | None = None) -> None:
|
|
26
|
+
"""Log *cfg* as MLflow params (flattened with dot-separated keys)."""
|
|
27
|
+
import mlflow as _mlflow
|
|
28
|
+
from omegaconf import OmegaConf
|
|
29
|
+
|
|
30
|
+
flat = OmegaConf.to_container(cfg, resolve=True, throw_on_missing=False)
|
|
31
|
+
params = _flatten(flat) # type: ignore[arg-type]
|
|
32
|
+
if run_id:
|
|
33
|
+
with _mlflow.start_run(run_id=run_id):
|
|
34
|
+
_mlflow.log_params(params)
|
|
35
|
+
else:
|
|
36
|
+
_mlflow.log_params(params)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def log_artifact(cfg: DictConfig) -> None:
|
|
40
|
+
"""Save *cfg* as a ``config.yaml`` artifact in the active MLflow run."""
|
|
41
|
+
import tempfile
|
|
42
|
+
|
|
43
|
+
import mlflow as _mlflow
|
|
44
|
+
from omegaconf import OmegaConf
|
|
45
|
+
|
|
46
|
+
yaml_text = OmegaConf.to_yaml(cfg, resolve=True)
|
|
47
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
48
|
+
p = pathlib.Path(tmp) / "config.yaml"
|
|
49
|
+
p.write_text(yaml_text)
|
|
50
|
+
_mlflow.log_artifact(str(p))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@contextlib.contextmanager
|
|
54
|
+
def run(
|
|
55
|
+
cfg: DictConfig,
|
|
56
|
+
*,
|
|
57
|
+
experiment: str | None = None,
|
|
58
|
+
run_name: str | None = None,
|
|
59
|
+
tags: dict[str, str] | None = None,
|
|
60
|
+
**kwargs: typing.Any,
|
|
61
|
+
) -> typing.Generator[mlflow.ActiveRun]:
|
|
62
|
+
"""Start an MLflow run, log *cfg* as params, and end on exit."""
|
|
63
|
+
import mlflow as _mlflow
|
|
64
|
+
|
|
65
|
+
if experiment:
|
|
66
|
+
_mlflow.set_experiment(experiment)
|
|
67
|
+
with _mlflow.start_run(run_name=run_name, tags=tags, **kwargs) as active_run:
|
|
68
|
+
log_config(cfg, run_id=active_run.info.run_id)
|
|
69
|
+
yield active_run
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""TensorBoard backend for laco-logging."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from omegaconf import DictConfig
|
|
10
|
+
from torch.utils.tensorboard import SummaryWriter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def log_config(cfg: DictConfig, writer: SummaryWriter) -> None:
|
|
14
|
+
"""Log *cfg* as TensorBoard hparams with no associated metrics.
|
|
15
|
+
|
|
16
|
+
Writes a ``hparams`` entry to the TensorBoard event file so the
|
|
17
|
+
HParams dashboard shows the config alongside any scalars logged later.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
cfg : DictConfig
|
|
22
|
+
Resolved laco DictConfig.
|
|
23
|
+
writer : SummaryWriter
|
|
24
|
+
An open ``SummaryWriter``.
|
|
25
|
+
"""
|
|
26
|
+
from omegaconf import OmegaConf
|
|
27
|
+
|
|
28
|
+
flat = OmegaConf.to_container(cfg, resolve=True, throw_on_missing=False)
|
|
29
|
+
|
|
30
|
+
def _flatten(d: dict, prefix: str = "") -> dict[str, typing.Any]:
|
|
31
|
+
out: dict[str, typing.Any] = {}
|
|
32
|
+
for k, v in d.items():
|
|
33
|
+
key = f"{prefix}.{k}" if prefix else k
|
|
34
|
+
if isinstance(v, dict):
|
|
35
|
+
out.update(_flatten(v, key))
|
|
36
|
+
elif isinstance(v, int | float | str | bool):
|
|
37
|
+
out[key] = v
|
|
38
|
+
else:
|
|
39
|
+
out[key] = str(v)
|
|
40
|
+
return out
|
|
41
|
+
|
|
42
|
+
hparams = _flatten(flat) # type: ignore[arg-type]
|
|
43
|
+
# TensorBoard's add_hparams requires a metric_dict; pass an empty one
|
|
44
|
+
# so the hparams are visible without artificial metric entries.
|
|
45
|
+
writer.add_hparams(hparams, metric_dict={})
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@contextlib.contextmanager
|
|
49
|
+
def run(
|
|
50
|
+
cfg: DictConfig,
|
|
51
|
+
*,
|
|
52
|
+
log_dir: str | None = None,
|
|
53
|
+
comment: str = "",
|
|
54
|
+
**kwargs: typing.Any,
|
|
55
|
+
) -> typing.Generator[SummaryWriter]:
|
|
56
|
+
"""Open a ``SummaryWriter``, log *cfg* as hparams, and close on exit.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
cfg : DictConfig
|
|
61
|
+
Resolved laco DictConfig.
|
|
62
|
+
log_dir : str | None
|
|
63
|
+
TensorBoard log directory. Defaults to ``runs/<timestamp>``.
|
|
64
|
+
comment : str
|
|
65
|
+
Appended to the auto-generated run name when *log_dir* is ``None``.
|
|
66
|
+
**kwargs
|
|
67
|
+
Extra kwargs forwarded to ``SummaryWriter.__init__``.
|
|
68
|
+
|
|
69
|
+
Yields
|
|
70
|
+
------
|
|
71
|
+
SummaryWriter
|
|
72
|
+
An open ``torch.utils.tensorboard.SummaryWriter``.
|
|
73
|
+
|
|
74
|
+
Examples
|
|
75
|
+
--------
|
|
76
|
+
::
|
|
77
|
+
|
|
78
|
+
with laco.integrations.logging.tensorboard.run(cfg, log_dir="tb_logs/run1") as writer:
|
|
79
|
+
for step, loss in enumerate(train(cfg)):
|
|
80
|
+
writer.add_scalar("loss/train", loss, step)
|
|
81
|
+
"""
|
|
82
|
+
from torch.utils.tensorboard import SummaryWriter as _SW
|
|
83
|
+
|
|
84
|
+
writer = _SW(log_dir=log_dir, comment=comment, **kwargs)
|
|
85
|
+
log_config(cfg, writer)
|
|
86
|
+
try:
|
|
87
|
+
yield writer
|
|
88
|
+
finally:
|
|
89
|
+
writer.close()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""W&B backend for laco-logging."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
import wandb
|
|
10
|
+
from omegaconf import DictConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def log_config(cfg: DictConfig, run: wandb.Run | None = None) -> None:
|
|
14
|
+
"""Log *cfg* as W&B hyperparameters on *run* (or the global run)."""
|
|
15
|
+
import wandb as _wandb
|
|
16
|
+
from omegaconf import OmegaConf
|
|
17
|
+
|
|
18
|
+
target = run or _wandb.run
|
|
19
|
+
if target is None:
|
|
20
|
+
msg = "No active W&B run. Call wandb.init() before log_config()."
|
|
21
|
+
raise RuntimeError(msg)
|
|
22
|
+
flat = OmegaConf.to_container(cfg, resolve=True, throw_on_missing=False)
|
|
23
|
+
target.config.update(flat, allow_val_change=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@contextlib.contextmanager
|
|
27
|
+
def run(
|
|
28
|
+
cfg: DictConfig,
|
|
29
|
+
*,
|
|
30
|
+
project: str | None = None,
|
|
31
|
+
name: str | None = None,
|
|
32
|
+
tags: list[str] | None = None,
|
|
33
|
+
**kwargs: typing.Any,
|
|
34
|
+
) -> typing.Generator[wandb.Run]:
|
|
35
|
+
"""Start a W&B run, log *cfg*, and finish on exit."""
|
|
36
|
+
import wandb as _wandb
|
|
37
|
+
from omegaconf import OmegaConf
|
|
38
|
+
|
|
39
|
+
flat = OmegaConf.to_container(cfg, resolve=True, throw_on_missing=False)
|
|
40
|
+
wandb_run = _wandb.init(
|
|
41
|
+
project=project, name=name, tags=tags, config=flat, **kwargs
|
|
42
|
+
)
|
|
43
|
+
try:
|
|
44
|
+
yield wandb_run
|
|
45
|
+
finally:
|
|
46
|
+
wandb_run.finish()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Implementation for laco-logging."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from omegaconf import DictConfig
|
|
10
|
+
|
|
11
|
+
_BACKEND_MODULES = {
|
|
12
|
+
"wandb": "laco.integrations.logging._backends.wandb",
|
|
13
|
+
"mlflow": "laco.integrations.logging._backends.mlflow",
|
|
14
|
+
"tensorboard": "laco.integrations.logging._backends.tensorboard",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_Backend = typing.Literal["wandb", "mlflow", "tensorboard"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def log_config(
|
|
21
|
+
cfg: DictConfig,
|
|
22
|
+
backends: list[_Backend] | _Backend,
|
|
23
|
+
**backend_kwargs: typing.Any,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Log a resolved laco DictConfig to one or more backends.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
cfg : DictConfig
|
|
30
|
+
Resolved OmegaConf DictConfig from ``laco.load()``.
|
|
31
|
+
backends : str | list[str]
|
|
32
|
+
One backend name or a list of backend names.
|
|
33
|
+
Valid values: ``"wandb"``, ``"mlflow"``, ``"tensorboard"``.
|
|
34
|
+
**backend_kwargs
|
|
35
|
+
Passed through to each backend's ``log_config()``. For TensorBoard
|
|
36
|
+
a ``writer`` kwarg is required.
|
|
37
|
+
|
|
38
|
+
Raises
|
|
39
|
+
------
|
|
40
|
+
ValueError
|
|
41
|
+
If an unknown backend name is provided.
|
|
42
|
+
"""
|
|
43
|
+
import importlib
|
|
44
|
+
|
|
45
|
+
targets: list[_Backend] = [backends] if isinstance(backends, str) else backends
|
|
46
|
+
for backend in targets:
|
|
47
|
+
if backend not in _BACKEND_MODULES:
|
|
48
|
+
msg = f"Unknown backend {backend!r}. Choose from: {list(_BACKEND_MODULES)}"
|
|
49
|
+
raise ValueError(msg)
|
|
50
|
+
mod = importlib.import_module(_BACKEND_MODULES[backend])
|
|
51
|
+
mod.log_config(cfg, **backend_kwargs)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@contextlib.contextmanager
|
|
55
|
+
def run(
|
|
56
|
+
cfg: DictConfig,
|
|
57
|
+
*,
|
|
58
|
+
backend: _Backend,
|
|
59
|
+
**backend_kwargs: typing.Any,
|
|
60
|
+
) -> typing.Generator[typing.Any]:
|
|
61
|
+
"""Unified context manager — starts a run on the chosen backend.
|
|
62
|
+
|
|
63
|
+
Delegates entirely to the backend's own ``run()`` context manager,
|
|
64
|
+
forwarding all backend-specific kwargs unchanged.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
cfg : DictConfig
|
|
69
|
+
Resolved laco DictConfig.
|
|
70
|
+
backend : str
|
|
71
|
+
One of ``"wandb"``, ``"mlflow"``, ``"tensorboard"``.
|
|
72
|
+
**backend_kwargs
|
|
73
|
+
Forwarded to the backend's ``run()`` (e.g. ``project=`` for W&B,
|
|
74
|
+
``experiment=`` for MLflow, ``log_dir=`` for TensorBoard).
|
|
75
|
+
|
|
76
|
+
Yields
|
|
77
|
+
------
|
|
78
|
+
Any
|
|
79
|
+
The backend's run handle (``wandb.Run``, ``mlflow.ActiveRun``, or
|
|
80
|
+
``torch.utils.tensorboard.SummaryWriter``).
|
|
81
|
+
|
|
82
|
+
Raises
|
|
83
|
+
------
|
|
84
|
+
ValueError
|
|
85
|
+
If an unknown backend name is provided.
|
|
86
|
+
"""
|
|
87
|
+
import importlib
|
|
88
|
+
|
|
89
|
+
if backend not in _BACKEND_MODULES:
|
|
90
|
+
msg = f"Unknown backend {backend!r}. Choose from: {list(_BACKEND_MODULES)}"
|
|
91
|
+
raise ValueError(msg)
|
|
92
|
+
mod = importlib.import_module(_BACKEND_MODULES[backend])
|
|
93
|
+
with mod.run(cfg, **backend_kwargs) as handle:
|
|
94
|
+
yield handle
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""MLflow backend — re-exported from laco.integrations.logging._backends.mlflow."""
|
|
2
|
+
|
|
3
|
+
from laco.integrations.logging._backends.mlflow import log_artifact as log_artifact
|
|
4
|
+
from laco.integrations.logging._backends.mlflow import log_config as log_config
|
|
5
|
+
from laco.integrations.logging._backends.mlflow import run as run
|
|
6
|
+
|
|
7
|
+
__all__ = ["log_artifact", "log_config", "run"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""TensorBoard backend — re-exported from laco.integrations.logging._backends.tensorboard."""
|
|
2
|
+
|
|
3
|
+
from laco.integrations.logging._backends.tensorboard import log_config as log_config
|
|
4
|
+
from laco.integrations.logging._backends.tensorboard import run as run
|
|
5
|
+
|
|
6
|
+
__all__ = ["log_config", "run"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: laco-logging
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Unified experiment logging (W&B, MLflow, TensorBoard) for laco.
|
|
5
|
+
Author-email: Kurt Stolle <kurt@khws.io>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: laco>=1.0.0
|
|
9
|
+
Provides-Extra: wandb
|
|
10
|
+
Requires-Dist: wandb>=0.19; extra == "wandb"
|
|
11
|
+
Provides-Extra: mlflow
|
|
12
|
+
Requires-Dist: mlflow>=2.0; extra == "mlflow"
|
|
13
|
+
Provides-Extra: tensorboard
|
|
14
|
+
Requires-Dist: tensorboard>=2.0; extra == "tensorboard"
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Requires-Dist: wandb>=0.19; extra == "all"
|
|
17
|
+
Requires-Dist: mlflow>=2.0; extra == "all"
|
|
18
|
+
Requires-Dist: tensorboard>=2.0; extra == "all"
|
|
19
|
+
|
|
20
|
+
# Laco-Logging
|
|
21
|
+
|
|
22
|
+
Unified experiment logging (W&B, MLflow, TensorBoard) for laco.
|
|
23
|
+
|
|
24
|
+
Part of the [laco](https://github.com/khwstolle/laco) project — see the [root README](../../README.md) for an overview.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install laco-logging[all]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Extras
|
|
33
|
+
|
|
34
|
+
| Extra | Installs |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `laco-logging[wandb]` | W&B backend |
|
|
37
|
+
| `laco-logging[mlflow]` | MLflow backend |
|
|
38
|
+
| `laco-logging[tensorboard]` | TensorBoard backend |
|
|
39
|
+
| `laco-logging[all]` | All backends |
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
`log_config()`, `run()`, submodules `wandb`, `mlflow`, `tensorboard`
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
See [`docs/index.md`](docs/index.md) for the full guide.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
sources/laco/integrations/logging/__init__.py
|
|
4
|
+
sources/laco/integrations/logging/_core.py
|
|
5
|
+
sources/laco/integrations/logging/mlflow.py
|
|
6
|
+
sources/laco/integrations/logging/tensorboard.py
|
|
7
|
+
sources/laco/integrations/logging/wandb.py
|
|
8
|
+
sources/laco/integrations/logging/_backends/__init__.py
|
|
9
|
+
sources/laco/integrations/logging/_backends/mlflow.py
|
|
10
|
+
sources/laco/integrations/logging/_backends/tensorboard.py
|
|
11
|
+
sources/laco/integrations/logging/_backends/wandb.py
|
|
12
|
+
sources/laco_logging.egg-info/PKG-INFO
|
|
13
|
+
sources/laco_logging.egg-info/SOURCES.txt
|
|
14
|
+
sources/laco_logging.egg-info/dependency_links.txt
|
|
15
|
+
sources/laco_logging.egg-info/requires.txt
|
|
16
|
+
sources/laco_logging.egg-info/top_level.txt
|
|
17
|
+
tests/test_laco_logging.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
laco
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Tests for laco-logging."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_import():
|
|
7
|
+
import laco.integrations.logging # noqa: F401
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_public_api():
|
|
11
|
+
import laco.integrations.logging as laco_logging
|
|
12
|
+
|
|
13
|
+
assert hasattr(laco_logging, "log_config")
|
|
14
|
+
assert hasattr(laco_logging, "run")
|
|
15
|
+
assert "log_config" in laco_logging.__all__
|
|
16
|
+
assert "run" in laco_logging.__all__
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_log_config_unknown_backend_raises():
|
|
20
|
+
import laco.integrations.logging as laco_logging
|
|
21
|
+
from omegaconf import OmegaConf
|
|
22
|
+
|
|
23
|
+
cfg = OmegaConf.create({"lr": 0.001})
|
|
24
|
+
with pytest.raises(ValueError, match="Unknown backend"):
|
|
25
|
+
laco_logging.log_config(cfg, "nonexistent_backend")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_run_unknown_backend_raises():
|
|
29
|
+
import laco.integrations.logging as laco_logging
|
|
30
|
+
from omegaconf import OmegaConf
|
|
31
|
+
|
|
32
|
+
cfg = OmegaConf.create({"lr": 0.001})
|
|
33
|
+
with pytest.raises(ValueError, match="Unknown backend"):
|
|
34
|
+
with laco_logging.run(cfg, backend="nonexistent_backend"):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_log_config_list_of_backends_invalid_raises():
|
|
39
|
+
import laco.integrations.logging as laco_logging
|
|
40
|
+
from omegaconf import OmegaConf
|
|
41
|
+
|
|
42
|
+
cfg = OmegaConf.create({})
|
|
43
|
+
with pytest.raises(ValueError, match="Unknown backend"):
|
|
44
|
+
laco_logging.log_config(cfg, ["wandb", "invalid"])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_backend_submodules_importable():
|
|
48
|
+
import laco.integrations.logging.mlflow # noqa: F401
|
|
49
|
+
import laco.integrations.logging.tensorboard # noqa: F401
|
|
50
|
+
import laco.integrations.logging.wandb # noqa: F401
|