frogbox 0.5.3__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.
- frogbox-0.5.3/LICENSE +21 -0
- frogbox-0.5.3/MANIFEST.in +1 -0
- frogbox-0.5.3/PKG-INFO +48 -0
- frogbox-0.5.3/README.md +26 -0
- frogbox-0.5.3/frogbox/__init__.py +12 -0
- frogbox-0.5.3/frogbox/callbacks/__init__.py +22 -0
- frogbox-0.5.3/frogbox/callbacks/callback.py +7 -0
- frogbox-0.5.3/frogbox/callbacks/image_logger.py +231 -0
- frogbox-0.5.3/frogbox/cli.py +147 -0
- frogbox-0.5.3/frogbox/config.py +332 -0
- frogbox-0.5.3/frogbox/data/__init__.py +1 -0
- frogbox-0.5.3/frogbox/data/dataset.py +12 -0
- frogbox-0.5.3/frogbox/data/model.py +9 -0
- frogbox-0.5.3/frogbox/data/train_supervised.py +42 -0
- frogbox-0.5.3/frogbox/engines/__init__.py +0 -0
- frogbox-0.5.3/frogbox/engines/engine.py +170 -0
- frogbox-0.5.3/frogbox/engines/events.py +64 -0
- frogbox-0.5.3/frogbox/engines/gan.py +125 -0
- frogbox-0.5.3/frogbox/engines/supervised.py +106 -0
- frogbox-0.5.3/frogbox/handlers/__init__.py +0 -0
- frogbox-0.5.3/frogbox/handlers/checkpoint.py +165 -0
- frogbox-0.5.3/frogbox/handlers/composite_loss_logger.py +30 -0
- frogbox-0.5.3/frogbox/handlers/metric_logger.py +43 -0
- frogbox-0.5.3/frogbox/handlers/optimizer_logger.py +37 -0
- frogbox-0.5.3/frogbox/handlers/output_logger.py +21 -0
- frogbox-0.5.3/frogbox/metrics/__init__.py +4 -0
- frogbox-0.5.3/frogbox/pipelines/__init__.py +14 -0
- frogbox-0.5.3/frogbox/pipelines/composite_loss.py +50 -0
- frogbox-0.5.3/frogbox/pipelines/gan.py +366 -0
- frogbox-0.5.3/frogbox/pipelines/lr_scheduler.py +45 -0
- frogbox-0.5.3/frogbox/pipelines/name_generation.py +457 -0
- frogbox-0.5.3/frogbox/pipelines/pipeline.py +234 -0
- frogbox-0.5.3/frogbox/pipelines/supervised.py +283 -0
- frogbox-0.5.3/frogbox/utils.py +119 -0
- frogbox-0.5.3/frogbox.egg-info/PKG-INFO +48 -0
- frogbox-0.5.3/frogbox.egg-info/SOURCES.txt +42 -0
- frogbox-0.5.3/frogbox.egg-info/dependency_links.txt +1 -0
- frogbox-0.5.3/frogbox.egg-info/entry_points.txt +2 -0
- frogbox-0.5.3/frogbox.egg-info/requires.txt +9 -0
- frogbox-0.5.3/frogbox.egg-info/top_level.txt +1 -0
- frogbox-0.5.3/pyproject.toml +36 -0
- frogbox-0.5.3/setup.cfg +4 -0
- frogbox-0.5.3/tests/test_config.py +12 -0
- frogbox-0.5.3/tests/test_package.py +12 -0
frogbox-0.5.3/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Simon Larsen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include frogbox/data/*.py
|
frogbox-0.5.3/PKG-INFO
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frogbox
|
|
3
|
+
Version: 0.5.3
|
|
4
|
+
Summary: An opinionated machine learning framework
|
|
5
|
+
Author-email: "Simon J. Larsen" <simonhffh@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://hisimon.dk/frogbox
|
|
8
|
+
Project-URL: Issues, https://github.com/SimonLarsen/frogbox/issues
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: torch>=2.5.1
|
|
13
|
+
Requires-Dist: torchvision>=0.20.1
|
|
14
|
+
Requires-Dist: accelerate>=1.2.0
|
|
15
|
+
Requires-Dist: torchmetrics>=1.6.0
|
|
16
|
+
Requires-Dist: pydantic>=2.10.3
|
|
17
|
+
Requires-Dist: tqdm>=4.67.1
|
|
18
|
+
Requires-Dist: wandb>=0.18.7
|
|
19
|
+
Requires-Dist: jinja2>=3.1.4
|
|
20
|
+
Requires-Dist: click>=8.1.7
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<img src="https://simonlarsen.github.io/frogbox/logo.png" width="224">
|
|
25
|
+
</p>
|
|
26
|
+
<h1 align="center">
|
|
27
|
+
frogbox
|
|
28
|
+
</h1>
|
|
29
|
+
|
|
30
|
+
Frogbox is an opinionated PyTorch machine learning framework built for rapid prototyping and research.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
* Experiments are defined using JSON files and support [jinja2](https://jinja.palletsprojects.com) templates.
|
|
35
|
+
* Flexible event system inspired by [Ignite](https://pytorch.org/ignite).
|
|
36
|
+
* Automatic experiment tracking. Currently only [Weights & Biases](https://wandb.ai/) is supported with other platforms planned.
|
|
37
|
+
* CLI tool for easy project management. Just type `frogbox project new -t supervised` to get started.
|
|
38
|
+
* Integrates [Accelerate](https://huggingface.co/docs/accelerate/index) to support automatic mixed precision (AMP) and distributed training.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pip install git+https://SimonLarsen@github.com/SimonLarsen/frogbox.git@v0.5.3
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Getting started
|
|
47
|
+
|
|
48
|
+
See [documentation](https://simonlarsen.github.io/frogbox).
|
frogbox-0.5.3/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://simonlarsen.github.io/frogbox/logo.png" width="224">
|
|
3
|
+
</p>
|
|
4
|
+
<h1 align="center">
|
|
5
|
+
frogbox
|
|
6
|
+
</h1>
|
|
7
|
+
|
|
8
|
+
Frogbox is an opinionated PyTorch machine learning framework built for rapid prototyping and research.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
* Experiments are defined using JSON files and support [jinja2](https://jinja.palletsprojects.com) templates.
|
|
13
|
+
* Flexible event system inspired by [Ignite](https://pytorch.org/ignite).
|
|
14
|
+
* Automatic experiment tracking. Currently only [Weights & Biases](https://wandb.ai/) is supported with other platforms planned.
|
|
15
|
+
* CLI tool for easy project management. Just type `frogbox project new -t supervised` to get started.
|
|
16
|
+
* Integrates [Accelerate](https://huggingface.co/docs/accelerate/index) to support automatic mixed precision (AMP) and distributed training.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pip install git+https://SimonLarsen@github.com/SimonLarsen/frogbox.git@v0.5.3
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Getting started
|
|
25
|
+
|
|
26
|
+
See [documentation](https://simonlarsen.github.io/frogbox).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. include:: ./intro.md
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.5.3"
|
|
6
|
+
|
|
7
|
+
from accelerate.utils import set_seed # noqa: F401
|
|
8
|
+
from .config import read_json_config, SupervisedConfig, GANConfig # noqa: F401
|
|
9
|
+
from .engines.events import Event # noqa: F401
|
|
10
|
+
from .pipelines.supervised import SupervisedPipeline # noqa: F401
|
|
11
|
+
from .pipelines.gan import GANPipeline # noqa: F401
|
|
12
|
+
from .utils import load_model_checkpoint # noqa: F401
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Custom callbacks
|
|
3
|
+
|
|
4
|
+
Custom callbacks can be created by implementing a function that accepts the pipeline as its first argument.
|
|
5
|
+
|
|
6
|
+
For instance, in the following example a callback is added to unfreeze the model's encoder after 20 epochs:
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from frogbox import Event
|
|
10
|
+
|
|
11
|
+
def unfreeze_encoder(pipeline)
|
|
12
|
+
model = pipeline.model
|
|
13
|
+
model.encoder.requires_grad_(True)
|
|
14
|
+
|
|
15
|
+
pipeline.install_callback(
|
|
16
|
+
event=Event("epoch_started", first=20, last=20),
|
|
17
|
+
callback=unfreeze_encoder,
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
""" # noqa: E501
|
|
21
|
+
|
|
22
|
+
from .image_logger import ImageLogger # noqa: F401
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Logging images
|
|
3
|
+
|
|
4
|
+
The simplest way to log images during training is to create a callback with
|
|
5
|
+
`frogbox.callbacks.image_logger.ImageLogger`:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from frogbox import Events
|
|
9
|
+
from frogbox.callbacks import ImageLogger
|
|
10
|
+
|
|
11
|
+
image_logger = ImageLogger()
|
|
12
|
+
|
|
13
|
+
pipeline.install_callback(
|
|
14
|
+
event="epoch_completed",
|
|
15
|
+
callback=image_logger,
|
|
16
|
+
)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Images can automatically be denormalized by setting `denormalize_input`/`denormalize_output`
|
|
20
|
+
and providing the mean and standard deviation used for normalization.
|
|
21
|
+
|
|
22
|
+
For instance, if input images are normalized with ImageNet parameters and outputs are in [0, 1]:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
image_logger = ImageLogger(
|
|
26
|
+
normalize_mean=[0.485, 0.456, 0.406],
|
|
27
|
+
normalize_std=[0.229, 0.224, 0.225],
|
|
28
|
+
denormalize_input=True,
|
|
29
|
+
)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
More advanced transformations can be made by overriding `input_transform`, `model_transform`, or `output_transform`:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from torchvision.transforms.functional import hflip
|
|
36
|
+
|
|
37
|
+
def flip_input(x, y, y_pred):
|
|
38
|
+
x = hflip(x)
|
|
39
|
+
return x, y_pred, y
|
|
40
|
+
|
|
41
|
+
image_logger = ImageLogger(
|
|
42
|
+
output_transform=flip_input,
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
""" # noqa: E501
|
|
46
|
+
|
|
47
|
+
from typing import Sequence, Callable, Any, Optional
|
|
48
|
+
import torch
|
|
49
|
+
from torchvision.transforms.functional import (
|
|
50
|
+
center_crop,
|
|
51
|
+
resize,
|
|
52
|
+
InterpolationMode,
|
|
53
|
+
to_pil_image,
|
|
54
|
+
)
|
|
55
|
+
from torchvision.utils import make_grid
|
|
56
|
+
import tqdm
|
|
57
|
+
import wandb
|
|
58
|
+
from .callback import Callback
|
|
59
|
+
from ..pipelines.pipeline import Pipeline
|
|
60
|
+
from ..utils import convert_tensor
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ImageLogger(Callback):
|
|
64
|
+
"""Callback for logging images."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
split: str = "test",
|
|
69
|
+
log_label: str = "test/images",
|
|
70
|
+
resize_to_fit: bool = True,
|
|
71
|
+
interpolation: str | InterpolationMode = "nearest",
|
|
72
|
+
num_cols: Optional[int] = None,
|
|
73
|
+
denormalize_input: bool = False,
|
|
74
|
+
denormalize_target: bool = False,
|
|
75
|
+
normalize_mean: Sequence[float] = (0.0, 0.0, 0.0),
|
|
76
|
+
normalize_std: Sequence[float] = (1.0, 1.0, 1.0),
|
|
77
|
+
show_progress: bool = False,
|
|
78
|
+
input_transform: Callable[[Any, Any], Any] = lambda x, y: (x, y),
|
|
79
|
+
model_transform: Callable[[Any], Any] = lambda output: output,
|
|
80
|
+
output_transform: Callable[
|
|
81
|
+
[Any, Any, Any], Any
|
|
82
|
+
] = lambda x, y, y_pred: (x, y_pred, y),
|
|
83
|
+
):
|
|
84
|
+
"""
|
|
85
|
+
Create ImageLogger.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
split : str
|
|
90
|
+
Dataset split to evaluate on. Defaults to "test".
|
|
91
|
+
log_label : str
|
|
92
|
+
Label to log images under in Weights & Biases.
|
|
93
|
+
resize_to_fit : bool
|
|
94
|
+
If `true` smaller images are resized to fit canvas.
|
|
95
|
+
interpolation : torchvision.transforms.functional.InterpolationMode
|
|
96
|
+
Interpolation to use for resizing images.
|
|
97
|
+
num_cols : int
|
|
98
|
+
Number of columns in image grid.
|
|
99
|
+
Defaults to number of elements in returned tuple.
|
|
100
|
+
denormalize_input : bool
|
|
101
|
+
If `true` input images (x) a denormalized after inference.
|
|
102
|
+
denormalize_target : bool
|
|
103
|
+
If `true` target images (y and y_pred) are denormalized after
|
|
104
|
+
inference.
|
|
105
|
+
normalize_mean : (float, float, float)
|
|
106
|
+
RGB mean values used in image normalization.
|
|
107
|
+
normalize_std : (float, float, float)
|
|
108
|
+
RGB std.dev. values used in image normalization.
|
|
109
|
+
show_progress : bool
|
|
110
|
+
Show progress bar.
|
|
111
|
+
input_transform : Callable
|
|
112
|
+
Function that receives tensors `y` and `y` and outputs tuple of
|
|
113
|
+
tensors `(x, y)`.
|
|
114
|
+
model_transform : Callable
|
|
115
|
+
Function that receives the output from the model during evaluation
|
|
116
|
+
and converts it into the predictions:
|
|
117
|
+
`y_pred = model_transform(model(x))`.
|
|
118
|
+
output_transform : Callable
|
|
119
|
+
Function that receives `x`, `y`, `y_pred` and returns tensors to be
|
|
120
|
+
logged as images. Default is returning `(x, y_pred, y)`.
|
|
121
|
+
"""
|
|
122
|
+
self._split = split
|
|
123
|
+
self._log_label = log_label
|
|
124
|
+
self._resize_to_fit = resize_to_fit
|
|
125
|
+
self._interpolation = InterpolationMode(interpolation)
|
|
126
|
+
self._num_cols = num_cols
|
|
127
|
+
self._denormalize_input = denormalize_input
|
|
128
|
+
self._denormalize_target = denormalize_target
|
|
129
|
+
self._normalize_mean = normalize_mean
|
|
130
|
+
self._normalize_std = normalize_std
|
|
131
|
+
self._show_progress = show_progress
|
|
132
|
+
self._input_transform = input_transform
|
|
133
|
+
self._model_transform = model_transform
|
|
134
|
+
self._output_transform = output_transform
|
|
135
|
+
|
|
136
|
+
def _denormalize(self, x: torch.Tensor) -> torch.Tensor:
|
|
137
|
+
mean = torch.as_tensor(
|
|
138
|
+
self._normalize_mean, device=x.device, dtype=x.dtype
|
|
139
|
+
).reshape(1, -1, 1, 1)
|
|
140
|
+
|
|
141
|
+
std = torch.as_tensor(
|
|
142
|
+
self._normalize_std, device=x.device, dtype=x.dtype
|
|
143
|
+
).reshape(1, -1, 1, 1)
|
|
144
|
+
|
|
145
|
+
return (x * std) + mean
|
|
146
|
+
|
|
147
|
+
def __call__(self, pipeline: Pipeline) -> None:
|
|
148
|
+
if not hasattr(pipeline, "model") or not hasattr(pipeline, "loaders"):
|
|
149
|
+
raise RuntimeError(
|
|
150
|
+
f"ImageLogger not compatible with pipeline {pipeline}."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
model = pipeline.model
|
|
154
|
+
loaders = pipeline.loaders
|
|
155
|
+
accelerator = pipeline.accelerator
|
|
156
|
+
|
|
157
|
+
model.eval()
|
|
158
|
+
|
|
159
|
+
data_iter = loaders[self._split]
|
|
160
|
+
if self._show_progress:
|
|
161
|
+
data_iter = tqdm.tqdm(
|
|
162
|
+
data_iter,
|
|
163
|
+
desc="Images",
|
|
164
|
+
ncols=80,
|
|
165
|
+
leave=False,
|
|
166
|
+
total=len(data_iter),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
images = []
|
|
170
|
+
for batch in data_iter:
|
|
171
|
+
x, y = batch
|
|
172
|
+
x, y = self._input_transform(x, y)
|
|
173
|
+
|
|
174
|
+
with torch.inference_mode():
|
|
175
|
+
y_pred = self._model_transform(model(x))
|
|
176
|
+
|
|
177
|
+
x, y, y_pred = accelerator.gather_for_metrics((x, y, y_pred))
|
|
178
|
+
|
|
179
|
+
x = convert_tensor(x, device=torch.device("cpu"))
|
|
180
|
+
y = convert_tensor(y, device=torch.device("cpu"))
|
|
181
|
+
y_pred = convert_tensor(y_pred, device=torch.device("cpu"))
|
|
182
|
+
|
|
183
|
+
if self._denormalize_input:
|
|
184
|
+
x = self._denormalize(x)
|
|
185
|
+
if self._denormalize_target:
|
|
186
|
+
y = self._denormalize(y)
|
|
187
|
+
y_pred = self._denormalize(y_pred)
|
|
188
|
+
|
|
189
|
+
output = self._output_transform(x, y, y_pred)
|
|
190
|
+
|
|
191
|
+
batch_sizes = [len(e) for e in output]
|
|
192
|
+
assert all(s == batch_sizes[0] for s in batch_sizes)
|
|
193
|
+
for i in range(batch_sizes[0]):
|
|
194
|
+
grid = self._combine_test_images([e[i] for e in output])
|
|
195
|
+
images.append(grid)
|
|
196
|
+
|
|
197
|
+
wandb_images = [wandb.Image(to_pil_image(image)) for image in images]
|
|
198
|
+
pipeline.log({self._log_label: wandb_images})
|
|
199
|
+
|
|
200
|
+
def _combine_test_images(
|
|
201
|
+
self, images: Sequence[torch.Tensor]
|
|
202
|
+
) -> torch.Tensor:
|
|
203
|
+
for image in images:
|
|
204
|
+
assert len(image.shape) == 3
|
|
205
|
+
assert image.size(0) in (1, 3)
|
|
206
|
+
|
|
207
|
+
max_h = max(image.size(1) for image in images)
|
|
208
|
+
max_w = max(image.size(2) for image in images)
|
|
209
|
+
|
|
210
|
+
transformed = []
|
|
211
|
+
for image in images:
|
|
212
|
+
c, h, w = image.shape
|
|
213
|
+
if (h, w) != (max_h, max_w):
|
|
214
|
+
if self._resize_to_fit:
|
|
215
|
+
image = resize(
|
|
216
|
+
image,
|
|
217
|
+
size=(max_h, max_w),
|
|
218
|
+
interpolation=self._interpolation,
|
|
219
|
+
)
|
|
220
|
+
else:
|
|
221
|
+
image = center_crop(image, output_size=(max_h, max_w))
|
|
222
|
+
if c == 1:
|
|
223
|
+
image = image.repeat((3, 1, 1))
|
|
224
|
+
image = image.clamp(0.0, 1.0)
|
|
225
|
+
transformed.append(image)
|
|
226
|
+
|
|
227
|
+
return make_grid(
|
|
228
|
+
tensor=transformed,
|
|
229
|
+
normalize=False,
|
|
230
|
+
nrow=self._num_cols or len(transformed),
|
|
231
|
+
)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""@private"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import importlib
|
|
7
|
+
import subprocess
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group()
|
|
12
|
+
def cli():
|
|
13
|
+
"""
|
|
14
|
+
An opinionated machine learning framework.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@cli.command(
|
|
19
|
+
context_settings=dict(
|
|
20
|
+
ignore_unknown_options=True,
|
|
21
|
+
help_option_names=[],
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
|
|
25
|
+
def launch(args):
|
|
26
|
+
"""
|
|
27
|
+
Launch script.
|
|
28
|
+
|
|
29
|
+
Alias for `accelerate launch`.
|
|
30
|
+
"""
|
|
31
|
+
exec_path = shutil.which("accelerate")
|
|
32
|
+
cmd = [exec_path, "launch"] + list(args)
|
|
33
|
+
env = os.environ.copy()
|
|
34
|
+
subprocess.run(cmd, env=env, check=False)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@cli.command(
|
|
38
|
+
context_settings=dict(
|
|
39
|
+
ignore_unknown_options=True,
|
|
40
|
+
help_option_names=[],
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
|
|
44
|
+
def config(args):
|
|
45
|
+
"""
|
|
46
|
+
Configure training system.
|
|
47
|
+
|
|
48
|
+
Alias for `accelerate config`.
|
|
49
|
+
"""
|
|
50
|
+
exec_path = shutil.which("accelerate")
|
|
51
|
+
cmd = [exec_path, "config"] + list(args)
|
|
52
|
+
env = os.environ.copy()
|
|
53
|
+
subprocess.run(cmd, env=env, check=False)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@cli.group()
|
|
57
|
+
def project():
|
|
58
|
+
"""Manage project."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@project.command(name="new")
|
|
62
|
+
@click.option(
|
|
63
|
+
"--type",
|
|
64
|
+
"-t",
|
|
65
|
+
"type_",
|
|
66
|
+
type=click.Choice(["supervised"]),
|
|
67
|
+
default="supervised",
|
|
68
|
+
help="Pipeline type.",
|
|
69
|
+
)
|
|
70
|
+
@click.option(
|
|
71
|
+
"--dir",
|
|
72
|
+
"-d",
|
|
73
|
+
"dir_",
|
|
74
|
+
type=click.Path(
|
|
75
|
+
exists=False,
|
|
76
|
+
file_okay=False,
|
|
77
|
+
dir_okay=True,
|
|
78
|
+
path_type=Path,
|
|
79
|
+
),
|
|
80
|
+
default=Path("."),
|
|
81
|
+
help="Project root directory.",
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--overwrite",
|
|
85
|
+
is_flag=True,
|
|
86
|
+
help="Overwrite existing files if present.",
|
|
87
|
+
)
|
|
88
|
+
def new_project(type_: str, dir_: Path, overwrite: bool = False):
|
|
89
|
+
"""Create a new project from template."""
|
|
90
|
+
|
|
91
|
+
template_inputs = [
|
|
92
|
+
f"train_{type_}.py",
|
|
93
|
+
"model.py",
|
|
94
|
+
"dataset.py",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
template_outputs = [
|
|
98
|
+
dir_ / "train.py",
|
|
99
|
+
dir_ / "models" / "example.py",
|
|
100
|
+
dir_ / "datasets" / "example.py",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
overwrite_checks = template_outputs + [dir_ / "configs" / "example.json"]
|
|
104
|
+
|
|
105
|
+
# Check if files already exist
|
|
106
|
+
if not overwrite:
|
|
107
|
+
for path in overwrite_checks:
|
|
108
|
+
if path.exists():
|
|
109
|
+
raise RuntimeError(
|
|
110
|
+
f"File '{path}' already exists."
|
|
111
|
+
" Use flag --overwrite to overwrite."
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Create folders and copy template files
|
|
115
|
+
resource_files = importlib.resources.files("frogbox.data")
|
|
116
|
+
for input_resource, output_path in zip(template_inputs, template_outputs):
|
|
117
|
+
file_data = resource_files.joinpath(input_resource).read_text()
|
|
118
|
+
output_path.parent.mkdir(exist_ok=True, parents=True)
|
|
119
|
+
output_path.write_text(file_data)
|
|
120
|
+
|
|
121
|
+
# Create config template
|
|
122
|
+
if type_ == "supervised":
|
|
123
|
+
from .config import SupervisedConfig, ObjectDefinition
|
|
124
|
+
|
|
125
|
+
config_json = SupervisedConfig(
|
|
126
|
+
type="supervised",
|
|
127
|
+
project="example",
|
|
128
|
+
model=ObjectDefinition(class_name="models.example.ExampleModel"),
|
|
129
|
+
datasets={
|
|
130
|
+
"train": ObjectDefinition(
|
|
131
|
+
class_name="datasets.example.ExampleDataset"
|
|
132
|
+
),
|
|
133
|
+
"val": ObjectDefinition(
|
|
134
|
+
class_name="datasets.example.ExampleDataset"
|
|
135
|
+
),
|
|
136
|
+
},
|
|
137
|
+
).model_dump_json(indent=4, exclude_none=True)
|
|
138
|
+
else:
|
|
139
|
+
raise RuntimeError(f"Unknown pipeline type {type_}.")
|
|
140
|
+
|
|
141
|
+
output_path = dir_ / "configs" / "example.json"
|
|
142
|
+
output_path.parent.mkdir(exist_ok=True, parents=True)
|
|
143
|
+
output_path.write_text(config_json)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
if __name__ == "__main__":
|
|
147
|
+
cli()
|