torchabc 0.1.0__py3-none-any.whl

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.
torchabc/__init__.py ADDED
@@ -0,0 +1,415 @@
1
+ import abc
2
+ import torch
3
+ from functools import cached_property
4
+ from types import SimpleNamespace
5
+ from typing import Any, Union, Dict, List, Callable
6
+
7
+
8
+ class TorchABC(abc.ABC):
9
+ """
10
+ A minimal abstract class for training, evaluation, and inference of pytorch models.
11
+ """
12
+
13
+ def __init__(self, device: Union[str, torch.device] = None, logger: Callable = print, **hparams):
14
+ """Initialize the model.
15
+
16
+ Parameters
17
+ ----------
18
+ device : str or torch.device, optional
19
+ The device to use. Defaults to None, which will try CUDA, then MPS, and
20
+ finally fall back to CPU.
21
+ logger : Callable, optional
22
+ A logging function that takes a dictionary in input. Defaults to print.
23
+ **hparams :
24
+ Arbitrary keyword arguments that will be stored in the `self.hparams` namespace.
25
+
26
+ Attributes
27
+ ----------
28
+ device : torch.device
29
+ The device the model will operate on.
30
+ logger : Callable
31
+ The function used for logging.
32
+ hparams : SimpleNamespace
33
+ A namespace containing the hyperparameters.
34
+ epoch : int
35
+ The last epoch seen during training.
36
+ """
37
+ super().__init__()
38
+ if device is not None:
39
+ self.device = torch.device(device)
40
+ elif torch.cuda.is_available():
41
+ self.device = torch.device("cuda")
42
+ elif torch.backends.mps.is_available():
43
+ self.device = torch.device("mps")
44
+ else:
45
+ self.device = torch.device("cpu")
46
+ self.logger = logger
47
+ self.hparams = SimpleNamespace(**hparams)
48
+ self.epoch = 0
49
+
50
+ @abc.abstractmethod
51
+ @cached_property
52
+ def network(self) -> torch.nn.Module:
53
+ """The neural network.
54
+
55
+ Returns a `torch.nn.Module` whose input and output tensors assume the
56
+ batch size is the first dimension: (batch_size, ...).
57
+ """
58
+ pass
59
+
60
+ @abc.abstractmethod
61
+ @cached_property
62
+ def optimizer(self) -> torch.optim.Optimizer:
63
+ """The optimizer for training the network.
64
+
65
+ Returns a `torch.optim.Optimizer` configured for `self.network.parameters()`.
66
+ """
67
+ pass
68
+
69
+ @abc.abstractmethod
70
+ @cached_property
71
+ def scheduler(self) -> Union[None, torch.optim.lr_scheduler.LRScheduler, torch.optim.lr_scheduler.ReduceLROnPlateau]:
72
+ """The learning rate scheduler for the optimizer.
73
+
74
+ Returns a `torch.optim.lr_scheduler.LRScheduler` or `torch.optim.lr_scheduler.ReduceLROnPlateau`
75
+ configured for `self.optimizer`.
76
+ """
77
+ pass
78
+
79
+ @abc.abstractmethod
80
+ @cached_property
81
+ def dataloaders(self) -> Dict[str, torch.utils.data.DataLoader]:
82
+ """The dataloaders for training and evaluation.
83
+
84
+ This method defines and returns a dictionary containing the `DataLoader` instances
85
+ for the training, validation, and testing datasets. The keys of the dictionary
86
+ should correspond to the names of the datasets (e.g., 'train', 'val', 'test'),
87
+ and the values should be their respective `torch.utils.data.DataLoader` objects.
88
+
89
+ Any transformation of the raw input data for each dataset should be implemented
90
+ within the `preprocess` method of this class. The `preprocess` method should
91
+ then be passed as the `transform` argument of the `Dataset` instances.
92
+
93
+ If you require custom collation logic (i.e., a specific way to merge a list of
94
+ samples into a batch beyond the default behavior), you should implement this
95
+ logic in the `collate` method of this class. The `collate` method should then be
96
+ passed to the `collate_fn` argument when creating the `DataLoader` instances.
97
+ """
98
+ pass
99
+
100
+ @abc.abstractmethod
101
+ def preprocess(self, data: Any, flag: str = '') -> Any:
102
+ """Prepare the raw data for the network.
103
+
104
+ The way this method processes the `data` depends on the `flag`.
105
+ When `flag` is empty (the default), the `data` are assumed to represent the
106
+ model's input that is used for inference. When `flag` has a specific value,
107
+ the method may perform different preprocessing steps such as transforming
108
+ the target or augmenting the input for training.
109
+
110
+ Parameters
111
+ ----------
112
+ data : Any
113
+ The raw input data to be processed.
114
+ flag : str, optional
115
+ A string indicating the purpose of the preprocessing. The default
116
+ is an empty string, meaning preprocess the model's input for inference.
117
+
118
+ Returns
119
+ -------
120
+ Any
121
+ The preprocessed data.
122
+ """
123
+ pass
124
+
125
+ @abc.abstractmethod
126
+ def postprocess(self, outputs: torch.Tensor) -> Any:
127
+ """Postprocess the model's outputs.
128
+
129
+ This method transforms the outputs of the neural network to
130
+ generate the final predictions.
131
+
132
+ Parameters
133
+ ----------
134
+ outputs : torch.Tensor
135
+ The output tensor from `self.network`.
136
+
137
+ Returns
138
+ -------
139
+ Any
140
+ The postprocessed outputs.
141
+ """
142
+ pass
143
+
144
+ @abc.abstractmethod
145
+ def loss(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
146
+ """Loss function.
147
+
148
+ This method defines the loss function that quantifies the discrepancy
149
+ between the neural network `outputs` and the corresponding `targets`.
150
+ The loss function should be differentiable to enable backpropagation.
151
+
152
+ Parameters
153
+ ----------
154
+ outputs : torch.Tensor
155
+ The tensor containing the network's output.
156
+ targets : torch.Tensor
157
+ The targets corresponding to the outputs.
158
+
159
+ Returns
160
+ -------
161
+ torch.Tensor
162
+ A scalar tensor representing the computed loss value.
163
+ """
164
+ pass
165
+
166
+ @abc.abstractmethod
167
+ def metrics(self, outputs: torch.Tensor, targets: torch.Tensor) -> Dict[str, float]:
168
+ """Evaluation metrics.
169
+
170
+ This method calculates various metrics that quantify the discrepancy
171
+ between the neural network `outputs` and the corresponding `targets`.
172
+ Unlike `self.loss`, which is primarily used for training, these metrics
173
+ are only used for evaluation and they do not need to be differentiable.
174
+
175
+ Parameters
176
+ ----------
177
+ outputs : torch.Tensor
178
+ The tensor containing the network's output.
179
+ targets : torch.Tensor
180
+ The targets corresponding to the outputs.
181
+
182
+ Returns
183
+ -------
184
+ Dict[str, float]
185
+ A dictionary where the keys are the names of the metrics and the
186
+ values are the corresponding metric scores.
187
+ """
188
+ pass
189
+
190
+ def collate(self, batch: Any) -> Any:
191
+ """Collate a batch of data.
192
+
193
+ This method applies the `torch.utils.data.default_collate` function, which is
194
+ used as the default function for collation in dataloaders. For custom data types,
195
+ overwrite this function and pass it as the `collate_fn` argument to the dataloader.
196
+
197
+ Parameters
198
+ ----------
199
+ batch : Any
200
+ The batch of data to collate.
201
+
202
+ Returns
203
+ -------
204
+ Any
205
+ The collated batch of data.
206
+ """
207
+ return torch.utils.data.default_collate(batch)
208
+
209
+ def move(self, data: Any) -> Any:
210
+ """Move data to the current device.
211
+
212
+ This method moves the data to the device specified by `self.device`. It supports
213
+ moving tensors, lists, tuples, and dictionaries. For custom data types, overwrite
214
+ this function to implement the necessary logic for moving the data to the device.
215
+
216
+ Parameters
217
+ ----------
218
+ data : Any
219
+ The data to move to the current device.
220
+
221
+ Returns
222
+ -------
223
+ Any
224
+ The data moved to the current device.
225
+ """
226
+ if isinstance(data, torch.Tensor):
227
+ return data.to(self.device)
228
+ elif isinstance(data, list):
229
+ return [self.move(item) for item in data]
230
+ elif isinstance(data, tuple):
231
+ return tuple(self.move(item) for item in data)
232
+ elif isinstance(data, dict):
233
+ return {key: self.move(value) for key, value in data.items()}
234
+ else:
235
+ raise TypeError(
236
+ f"Unsupported data type: {type(data)}. "
237
+ "Please implement the move method for custom data types."
238
+ )
239
+
240
+ def train(self, epochs: int, on: str = 'train', val: str = 'val', gas: int = 1, callback: Callable = None) -> List[dict]:
241
+ """Train the model.
242
+
243
+ This method sets the network to training mode, iterates through the training dataloader
244
+ for the given number of epochs, performs forward and backward passes, optimizes the
245
+ model parameters, and logs the training loss and metrics. It optionally performs validation
246
+ after each epoch.
247
+
248
+ Parameters
249
+ ----------
250
+ epochs : int
251
+ The number of training epochs to perform.
252
+ on : str, optional
253
+ The name of the training dataloader. Defaults to 'train'.
254
+ val : str, optional
255
+ The name of the validation dataloader. Defaults to 'val'.
256
+ gas : int, optional
257
+ The number of gradient accumulation steps. Defaults to 1 (no gradient accumulation).
258
+ callback : Callable, optional
259
+ A callback function that is called after each epoch. It should accept two arguments:
260
+ the instance itself and a list of dictionaries containing the loss and evaluation metrics.
261
+ When this function returns True, training stops.
262
+
263
+ Returns
264
+ -------
265
+ list
266
+ A list of dictionaries containing the loss and evaluation metrics.
267
+ """
268
+ logs, log_batch, log_epoch = [], {}, {}
269
+ for epoch in range(self.epoch + 1, self.epoch + 1 + epochs):
270
+ self.epoch = epoch
271
+ self.network.train()
272
+ self.network.to(self.device)
273
+ self.optimizer.zero_grad()
274
+ loss_gas = 0
275
+ for batch, (inputs, targets) in enumerate(self.dataloaders[on], start=1):
276
+ inputs, targets = self.move((inputs, targets))
277
+ outputs = self.network(inputs)
278
+ loss = self.loss(outputs, targets)
279
+ loss = loss / gas
280
+ loss.backward()
281
+ loss_gas += loss.item()
282
+ if batch % gas == 0:
283
+ self.optimizer.step()
284
+ self.optimizer.zero_grad()
285
+ log_batch.update({on + "/epoch": epoch, on + "/batch": batch, on + "/loss": loss_gas})
286
+ log_batch.update({on + "/" + k: v for k, v in self.metrics(outputs, targets).items()})
287
+ self.logger(log_batch)
288
+ logs.append(log_batch.copy())
289
+ loss_gas = 0
290
+ if val:
291
+ log_epoch.update({val + "/epoch": epoch})
292
+ log_epoch.update({val + "/" + k: v for k, v in self.eval(on=val).items()})
293
+ if self.scheduler:
294
+ if isinstance(self.scheduler, torch.optim.lr_scheduler.ReduceLROnPlateau):
295
+ if not val:
296
+ raise ValueError(
297
+ "ReduceLROnPlateau scheduler requires validation metrics. "
298
+ "Please provide a validation dataloader. "
299
+ )
300
+ if not hasattr(self.hparams, "plateau"):
301
+ raise ValueError(
302
+ "ReduceLROnPlateau scheduler requires a metric to monitor. "
303
+ "Please specify the metric name during initialization by "
304
+ f"using {self.__class__.__name__}(plateau='name'), "
305
+ "where 'name' is either 'loss' or a key returned by `self.metrics`."
306
+ )
307
+ self.scheduler.step(log_epoch[val + "/" + self.hparams.plateau])
308
+ log_epoch.update({val + "/lr": self.scheduler.get_last_lr()})
309
+ else:
310
+ self.scheduler.step()
311
+ log_epoch.update({val + "/lr": self.scheduler.get_last_lr()})
312
+ if log_epoch:
313
+ self.logger(log_epoch)
314
+ logs.append(log_epoch.copy())
315
+ if callback:
316
+ stop = callback(self, logs)
317
+ if stop:
318
+ break
319
+ return logs
320
+
321
+ def eval(self, on: str) -> Dict[str, float]:
322
+ """Evaluate the model.
323
+
324
+ This method sets the network to evaluation mode, iterates through the
325
+ given dataloader, calculates the loss and metrics, and returns
326
+ the results. No gradients are computed during this process.
327
+
328
+ Parameters
329
+ ----------
330
+ on : str
331
+ The name of the dataloader to evaluate on. This should be one of
332
+ the keys in `self.dataloaders`.
333
+
334
+ Returns
335
+ -------
336
+ dict
337
+ A dictionary containing the loss and evaluation metrics.
338
+ """
339
+ self.network.eval()
340
+ tot_loss, num_batches = 0, 0
341
+ all_outputs, all_targets = [], []
342
+ with torch.no_grad():
343
+ self.network.to(self.device)
344
+ for inputs, targets in self.dataloaders[on]:
345
+ inputs, targets = self.move((inputs, targets))
346
+ outputs = self.network(inputs)
347
+ tot_loss += self.loss(outputs, targets).item()
348
+ all_outputs.append(outputs)
349
+ all_targets.append(targets)
350
+ num_batches += 1
351
+ metrics = self.metrics(torch.cat(all_outputs), torch.cat(all_targets))
352
+ metrics["loss"] = tot_loss / num_batches
353
+ return metrics
354
+
355
+ def predict(self, data: Any) -> Any:
356
+ """Predict the raw data.
357
+
358
+ This method sets the network to evaluation mode, preprocesses and
359
+ collates the input data into a batch of size 1, performs a forward pass
360
+ without tracking gradients, and then postprocesses the output to
361
+ return the final prediction.
362
+
363
+ Parameters
364
+ ----------
365
+ data : Any
366
+ The raw input data to predict.
367
+
368
+ Returns
369
+ -------
370
+ Any
371
+ The postprocessed prediction.
372
+ """
373
+ self.network.eval()
374
+ self.network.to(self.device)
375
+ with torch.no_grad():
376
+ preprocessed = self.preprocess(data)
377
+ batch = [preprocessed]
378
+ collated = self.collate(batch)
379
+ inputs = self.move(collated)
380
+ outputs = self.network(inputs)
381
+ predictions = self.postprocess(outputs)
382
+ return predictions[0]
383
+
384
+ def save(self, checkpoint: str) -> None:
385
+ """Save checkpoint.
386
+
387
+ Parameters
388
+ ----------
389
+ checkpoint : str
390
+ The path where to save the checkpoint.
391
+ """
392
+ torch.save({
393
+ 'hparams': self.hparams.__dict__,
394
+ 'epoch': self.epoch,
395
+ 'network_state_dict': self.network.state_dict(),
396
+ 'optimizer_state_dict': self.optimizer.state_dict(),
397
+ 'scheduler_state_dict': self.scheduler.state_dict() if self.scheduler else None,
398
+
399
+ }, checkpoint)
400
+
401
+ def load(self, checkpoint: str) -> None:
402
+ """Load checkpoint.
403
+
404
+ Parameters
405
+ ----------
406
+ checkpoint : str
407
+ The path from where to load the checkpoint.
408
+ """
409
+ checkpoint = torch.load(checkpoint, map_location=self.device)
410
+ self.hparams = SimpleNamespace(**checkpoint['hparams'])
411
+ self.epoch = checkpoint['epoch']
412
+ self.network.load_state_dict(checkpoint['network_state_dict'])
413
+ self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
414
+ if checkpoint['scheduler_state_dict']:
415
+ self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
torchabc/cli.py ADDED
@@ -0,0 +1,91 @@
1
+ import argparse
2
+ import inspect
3
+ from functools import cached_property
4
+ from . import TorchABC
5
+
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser(description="Generate template.")
9
+ parser.add_argument('--create', type=str, required=True, help='The path to create the template file.')
10
+ parser.add_argument('--minimal', action='store_true', help='Generate a minimal template without docstrings.')
11
+ args = parser.parse_args()
12
+
13
+ cached_properties = {}
14
+ methods = {}
15
+ defaults = {
16
+ 'scheduler': 'return None',
17
+ 'metrics': 'return {}',
18
+ 'postprocess': 'return outputs',
19
+ 'preprocess': 'return data',
20
+ }
21
+
22
+ for name, member in inspect.getmembers(TorchABC):
23
+ if hasattr(member, '__isabstractmethod__'):
24
+ if isinstance(TorchABC.__dict__.get(name), cached_property):
25
+ cached_properties[name] = member.__doc__ or ""
26
+ elif callable(member):
27
+ sig = inspect.signature(member)
28
+ methods[name] = (sig, member.__doc__ or "")
29
+
30
+ template = """
31
+ import torch
32
+ from torchabc import TorchABC
33
+ from functools import cached_property
34
+ from typing import Any, Dict
35
+
36
+
37
+ class ClassName(TorchABC):"""
38
+
39
+ if not args.minimal:
40
+ template += """
41
+ \"\"\"A concrete implementation of the TorchABC abstract class.
42
+
43
+ Use this template to implement your own model by following these steps:
44
+ - replace ClassName with the name of your model,
45
+ - replace this docstring with a description of your model,
46
+ - implement the methods below to define the core logic of your model,
47
+ - access the hyperparameters passed during initialization with `self.hparams`.
48
+ \"\"\""""
49
+
50
+ template += """
51
+ """
52
+
53
+ for name in ('dataloaders', 'preprocess', 'network', 'optimizer', 'scheduler', 'loss', 'metrics', 'postprocess'):
54
+ if name in cached_properties:
55
+ doc = cached_properties[name]
56
+ template += f"""
57
+ @cached_property
58
+ def {name}(self):"""
59
+ if not args.minimal:
60
+ template += f"""
61
+ \"\"\"{doc}\"\"\""""
62
+ template += f"""
63
+ {defaults[name] if name in defaults else 'raise NotImplementedError'}
64
+ """
65
+ elif name in methods:
66
+ sig, doc = methods[name]
67
+ template += f"""
68
+ def {name}{sig}:"""
69
+ if not args.minimal:
70
+ template += f"""
71
+ \"\"\"{doc}\"\"\""""
72
+ template += f"""
73
+ {defaults[name] if name in defaults else 'raise NotImplementedError'}
74
+ """
75
+
76
+ if not args.minimal:
77
+ template += f"""
78
+
79
+ if __name__ == "__main__":
80
+ # Example usage
81
+ model = ClassName()
82
+ model.train(epochs=1)
83
+ """
84
+
85
+ with open(args.create, "x") as f:
86
+ f.write(template.lstrip())
87
+
88
+ print(f"Template generated at: {args.create}")
89
+
90
+ if __name__ == "__main__":
91
+ main()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Emanuele Guidotti
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,234 @@
1
+ Metadata-Version: 2.1
2
+ Name: torchabc
3
+ Version: 0.1.0
4
+ Summary: A minimal abstract class for training, evaluation, and inference of pytorch models.
5
+ Home-page: https://github.com/eguidotti/torchabc
6
+ License: MIT
7
+ Author: Emanuele Guidotti
8
+ Author-email: emanuele.guidotti@usi.ch
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Dist: torch (>=2.6.0,<3.0.0)
16
+ Project-URL: Repository, https://github.com/eguidotti/torchabc
17
+ Description-Content-Type: text/markdown
18
+
19
+ # TorchABC
20
+
21
+ [`TorchABC`](https://github.com/eguidotti/torchabc) is a minimal abstract class for training, evaluation, and inference of pytorch models that helps you keep your code organized. It depends on [`torch`](https://pypi.org/project/torch/) only and it consists of a simple self-contained [file](https://github.com/eguidotti/torchabc/blob/main/torchabc/__init__.py).
22
+
23
+ ## Workflow
24
+
25
+ ![diagram](https://github.com/user-attachments/assets/f3eac7aa-6a39-4a93-887c-7b7f8ac5f0f4)
26
+
27
+ `TorchABC` implements the workflow illustrated above. The workflow begins with raw **DATA**, which undergoes a **preprocess** step. This preprocessing step transforms the raw data into **INPUT** samples and their corresponding **TARGET** labels.
28
+
29
+ Next, the individual **INPUT** samples are grouped into batches called **INPUTS** using a **collate** function. Similarly, the **TARGET** labels are batched into **TARGETS**. The **INPUTS** are then fed into the neural **network**, which produces **OUTPUTS**.
30
+
31
+ The **OUTPUTS** of the network are compared to the **TARGETS** using a **LOSS** function. The calculated loss quantifies the error between the model's predictions and the actual targets. This **LOSS** is then used by the **optimizer / scheduler** to update the parameters of the **network** to minimize the loss. The optimizer controls how the parameters are adjusted, while the scheduler can dynamically change the learning rate of the optimizer during training.
32
+
33
+ Finally, the raw **OUTPUTS** from the network undergo a **postprocess** step to generate the final **PREDICTIONS**. This could involve converting probabilities to class labels, applying thresholds, or other task-specific transformations.
34
+
35
+ **The core logic blocks** are abstract. You define their specific behavior with maximum flexibility.
36
+
37
+ ## Quick start
38
+
39
+ Install the package.
40
+
41
+ ```bash
42
+ pip install torchabc
43
+ ```
44
+
45
+ Generate a template using the command line interface.
46
+
47
+ ```bash
48
+ torchabc --create template.py
49
+ ```
50
+
51
+ The template is structured as follows.
52
+
53
+ ```py
54
+ class ClassName(TorchABC):
55
+
56
+ @cached_property
57
+ def dataloaders(self):
58
+ raise NotImplementedError
59
+
60
+ def preprocess(self, data: Any, flag: str = '') -> Any:
61
+ return data
62
+
63
+ @cached_property
64
+ def network(self):
65
+ raise NotImplementedError
66
+
67
+ @cached_property
68
+ def optimizer(self):
69
+ raise NotImplementedError
70
+
71
+ @cached_property
72
+ def scheduler(self):
73
+ return None
74
+
75
+ def loss(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
76
+ raise NotImplementedError
77
+
78
+ def metrics(self, outputs: torch.Tensor, targets: torch.Tensor) -> Dict[str, float]:
79
+ return {}
80
+
81
+ def postprocess(self, outputs: torch.Tensor) -> Any:
82
+ return outputs
83
+ ```
84
+
85
+ Fill out the template with the dataloaders, preprocessing and postprocessing steps, the neural network, optimizer, scheduler, loss, and evaluation metrics.
86
+
87
+ #### `dataloaders`: the dataloaders for training and evaluation
88
+
89
+ This method defines and returns a dictionary containing the [`DataLoader`](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader) instances for the training, validation, and testing datasets. The dictionary's keys should correspond to the names of the datasets (e.g., 'train', 'val', 'test'), and the values should be their respective `DataLoader` objects. Any transformation of the raw input data for each dataset should be implemented within the `preprocess` method of this class. The `preprocess` method should then be passed as the `transform` argument of the [`Dataset`](https://pytorch.org/docs/stable/data.html#torch.utils.data.Dataset) instances.
90
+
91
+ #### `preprocess`: preprocessing
92
+
93
+ This method processes the data differently depending on a `flag`. When `flag` is empty (the default), the data are assumed to represent the model's input used for inference. When `flag` has a specific value, the method may perform different preprocessing steps, such as transforming the target or augmenting the input for training.
94
+
95
+ #### `network`: the neural network
96
+
97
+ Returns a [`Module`](https://pytorch.org/docs/stable/generated/torch.nn.Module.html) whose input and output tensors assume the batch size is the first dimension: (batch_size, ...).
98
+
99
+ #### `optimizer`: the optimizer for training the network
100
+
101
+ Returns an [`Optimizer`](https://pytorch.org/docs/main/optim.html#torch.optim.Optimizer) configured for the `network`.
102
+
103
+ #### `scheduler`: the learning rate scheduler for the optimizer
104
+
105
+ Returns a [`LRScheduler`](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.LRScheduler.html) or [`ReduceLROnPlateau`](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html) configured for the `optimizer`.
106
+
107
+ #### `loss`: loss function
108
+
109
+ This method defines the loss function that quantifies the discrepancy between the neural network `outputs` and the corresponding `targets`. The loss function should be differentiable to enable backpropagation.
110
+
111
+ #### `metrics`: evaluation metrics
112
+
113
+ This method calculates various metrics that quantify the discrepancy between the neural network `outputs` and the corresponding `targets`. Unlike `loss`, which is primarily used for training, these metrics are only used for evaluation and do not need to be differentiable.
114
+
115
+ #### `postprocess`: postprocess
116
+
117
+ This method transforms the neural network outputs to generate the final predictions.
118
+
119
+ ## Usage
120
+
121
+ After filling out the template above, you can use your class as follows.
122
+
123
+ ### Initialization
124
+
125
+ Initialize the class with
126
+
127
+ ```py
128
+ model = ClassName(
129
+ device: Union[str, torch.device] = None,
130
+ logger: Callable = print,
131
+ **hparams
132
+ )
133
+ ```
134
+
135
+ #### Device
136
+
137
+ The `device` is the [`torch.device`](https://pytorch.org/docs/stable/tensor_attributes.html#torch.device) to use. Defaults to `None`, which will try CUDA, then MPS, and finally fall back to CPU.
138
+
139
+ #### Logger
140
+
141
+ A logging function that takes a dictionary in input. The default prints to standard output. You can can easily log with [wandb](https://pypi.org/project/wandb/)
142
+
143
+ ```py
144
+ import wandb
145
+ model = ClassName(logger=wandb.log)
146
+ ```
147
+
148
+ or with any other custom logger.
149
+
150
+ #### Hyperparameters
151
+
152
+ You will typically use several parameters to control the behavior of `ClassName`, such as the learning rate or batch size. These parameters should be passed during the initialization
153
+
154
+ ```py
155
+ model = ClassName(lr=0.001, batch_size=64)
156
+ ```
157
+
158
+ and are stored in the attribute `hparams` of the model. For instance, use `hparams.lr` to access the `lr` value.
159
+
160
+ ### Training
161
+
162
+ Train the model with
163
+
164
+ ```py
165
+ model.train(
166
+ epochs: int,
167
+ on: str = 'train',
168
+ val: str = 'val',
169
+ gas: int = 1,
170
+ callback: Callable = None
171
+ )
172
+ ```
173
+
174
+ where
175
+
176
+ - `epochs` is the number of training epochs to perform.
177
+ - `on` is the name of the training dataloader. Defaults to 'train'.
178
+ - `val` is the name of the validation dataloader. Defaults to 'val'.
179
+ - `gas` is the number of gradient accumulation steps. Defaults to 1 (no gradient accumulation).
180
+ - `callback` is a function that is called after each epoch. It should accept two arguments: the instance itself and a list of dictionaries containing the loss and evaluation metrics. When this function returns `True`, training stops.
181
+
182
+ This method returns a list of dictionaries containing the loss and evaluation metrics.
183
+
184
+ ### Checkpoints
185
+
186
+ Save the model to a checkpoint.
187
+
188
+ ```py
189
+ model.save("checkpoint.pth")
190
+ ```
191
+
192
+ Load the model from a checkpoint.
193
+
194
+ ```py
195
+ model.load("checkpoint.pth")
196
+ ```
197
+
198
+ You can also use the `callback` function to implement a custom checkpointing strategy. For instance, the following example saves a checkpoint after each training epoch.
199
+
200
+ ```py
201
+ callback = lambda self, logs: self.save(f"epoch_{logs[-1]['val/epoch']}.pth")
202
+ model.train(epochs=10, val='val', callback=callback)
203
+ ```
204
+
205
+ ### Evaluation
206
+
207
+ Evaluate the model with
208
+
209
+ ```py
210
+ model.eval(on='test')
211
+ ```
212
+
213
+ where `on` is the name of the dataloader to evaluate on. This should be one of the keys in the `dataloaders`. This method returns a dictionary containing the evaluation metrics.
214
+
215
+ ### Inference
216
+
217
+ Predict raw data with
218
+
219
+ ```py
220
+ model.predict(data)
221
+ ```
222
+
223
+ where `data` is the raw input data. This method returns the postprocessed prediction.
224
+
225
+ ## Examples
226
+
227
+ Get started with simple self-contained examples:
228
+
229
+ - [MNIST classification](https://github.com/eguidotti/torchabc/blob/main/examples/mnist.py)
230
+
231
+ ## Contribute
232
+
233
+ Contributions are welcome! Submit pull requests with new [examples](https://github.com/eguidotti/torchabc/tree/main/examples) or improvements to the core [`TorchABC`](https://github.com/eguidotti/torchabc/blob/main/torchabc/__init__.py) class itself.
234
+
@@ -0,0 +1,7 @@
1
+ torchabc/__init__.py,sha256=bkJ7wt6jKM5nOvAVSE1KKRzOk3HqMBX57G2eBK5Jezk,15994
2
+ torchabc/cli.py,sha256=RRvhivA2JhOHnxy_TG3wAT9W8ar6n32a-OeDokKFcT4,2850
3
+ torchabc-0.1.0.dist-info/LICENSE,sha256=DUdVdCoS4OrUDYtUZHIVx1gX82IqTif3uII-1m5IlIg,1074
4
+ torchabc-0.1.0.dist-info/METADATA,sha256=d530_tiQiYPmsOjlyG4QnqoLhXusWxuoHVFxFxiunlQ,9329
5
+ torchabc-0.1.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
6
+ torchabc-0.1.0.dist-info/entry_points.txt,sha256=57CzcZChCA8OC9RaYv9LEcLakXplqJWLXvxvDKDU0NQ,46
7
+ torchabc-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.6.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ torchabc=torchabc.cli:main
3
+