torchabc 0.2.4__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: torchabc
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: A simple abstract class for training and inference in PyTorch.
5
5
  License: MIT
6
6
  Author: Emanuele Guidotti
@@ -50,73 +50,141 @@ Generate a template using the command line interface.
50
50
  torchabc --create template.py
51
51
  ```
52
52
 
53
- The template is structured as follows.
53
+ Fill out the template.
54
54
 
55
55
  ```py
56
+ from torchabc import TorchABC
57
+ from functools import cached_property
58
+
59
+
56
60
  class ClassName(TorchABC):
61
+ """A concrete subclass of the TorchABC abstract class.
62
+
63
+ Use this template to implement your own model by following these steps:
64
+ - replace ClassName with the name of your model,
65
+ - replace this docstring with a description of your model,
66
+ - implement the methods below to define the core logic of your model,
67
+ - access the hyperparameters passed during initialization with `self.hparams`.
68
+ """
57
69
 
58
70
  @cached_property
59
71
  def dataloaders(self):
72
+ """The dataloaders.
73
+
74
+ Returns a dictionary containing multiple `DataLoader` instances. The keys of
75
+ the dictionary are the names of the dataloaders (e.g., 'train', 'val', 'test'),
76
+ and the values are the corresponding `torch.utils.data.DataLoader` objects.
77
+ """
60
78
  raise NotImplementedError
61
79
 
62
- def preprocess(self, data: Any, flag: str = '') -> Any:
80
+ @staticmethod
81
+ def preprocess(data, flag='predict'):
82
+ """The preprocessing step.
83
+
84
+ Transforms the raw data of an individual sample into the corresponding tensor(s).
85
+
86
+ Parameters
87
+ ----------
88
+ data : Any
89
+ The raw data.
90
+ flag : str, optional
91
+ A flag indicating how to transform the data. The default transforms the
92
+ input data for inference. You can use additional flags, for instance,
93
+ to perform data augmentation or transform the targets during training or validation.
94
+
95
+ Returns
96
+ -------
97
+ Union[Tensor, Iterable[Tensor]]
98
+ The preprocessed data.
99
+ """
63
100
  return data
64
-
101
+
65
102
  @cached_property
66
103
  def network(self):
104
+ """The neural network.
105
+
106
+ Returns a `torch.nn.Module` whose input and output tensors assume the
107
+ batch size is the first dimension: (batch_size, ...).
108
+ """
67
109
  raise NotImplementedError
68
110
 
69
111
  @cached_property
70
112
  def optimizer(self):
113
+ """The optimizer for training the network.
114
+
115
+ Returns a `torch.optim.Optimizer` configured for `self.network.parameters()`.
116
+ """
71
117
  raise NotImplementedError
72
118
 
73
119
  @cached_property
74
120
  def scheduler(self):
121
+ """The learning rate scheduler for the optimizer.
122
+
123
+ Returns a `torch.optim.lr_scheduler.LRScheduler` or `torch.optim.lr_scheduler.ReduceLROnPlateau`
124
+ configured for `self.optimizer`.
125
+ """
75
126
  return None
76
127
 
77
- def loss(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
128
+ @staticmethod
129
+ def loss(outputs, targets):
130
+ """The loss function.
131
+
132
+ Compute the loss to train the neural network.
133
+
134
+ Parameters
135
+ ----------
136
+ outputs : Union[Tensor, Iterable[Tensor]]
137
+ The tensor(s) returned by the forward pass of `self.network`.
138
+ targets : Union[Tensor, Iterable[Tensor]]
139
+ The tensor(s) giving the target values.
140
+
141
+ Returns
142
+ -------
143
+ Tensor
144
+ A scalar tensor giving the loss value.
145
+ """
78
146
  raise NotImplementedError
79
-
80
- def metrics(self, outputs: torch.Tensor, targets: torch.Tensor) -> Dict[str, float]:
81
- return {}
82
-
83
- def postprocess(self, outputs: torch.Tensor) -> Any:
84
- return outputs
85
- ```
86
-
87
- Fill out the template with the dataloaders, preprocessing and postprocessing steps, the neural network, optimizer, scheduler, loss, and optional evaluation metrics.
88
-
89
- #### `dataloaders`: the dataloaders for training and evaluation
90
-
91
- 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.
92
-
93
- #### `preprocess`: preprocessing
94
-
95
- 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.
96
-
97
- #### `network`: the neural network
98
147
 
99
- 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, ...).
100
-
101
- #### `optimizer`: the optimizer for training the network
102
-
103
- Returns an [`Optimizer`](https://pytorch.org/docs/main/optim.html#torch.optim.Optimizer) configured for the `network`.
104
-
105
- #### `scheduler`: the learning rate scheduler for the optimizer
106
-
107
- 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`.
108
-
109
- #### `loss`: loss function
148
+ @staticmethod
149
+ def metrics(outputs, targets):
150
+ """The evaluation metrics.
151
+
152
+ Compute additional evaluation metrics.
153
+
154
+ Parameters
155
+ ----------
156
+ outputs : Union[Tensor, Iterable[Tensor]]
157
+ The tensor(s) returned by the forward pass of `self.network`.
158
+ targets : Union[Tensor, Iterable[Tensor]]
159
+ The tensor(s) giving the target values.
160
+
161
+ Returns
162
+ -------
163
+ Dict[str, float]
164
+ A dictionary where the keys are the names of the metrics and the
165
+ values are the corresponding scores.
166
+ """
167
+ return {}
110
168
 
111
- 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.
169
+ @staticmethod
170
+ def postprocess(outputs):
171
+ """The postprocessing step.
112
172
 
113
- #### `metrics`: evaluation metrics
173
+ Transforms the neural network outputs into the final predictions.
114
174
 
115
- 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.
175
+ Parameters
176
+ ----------
177
+ outputs : Union[Tensor, Iterable[Tensor]]
178
+ The tensor(s) returned by the forward pass of `self.network`.
116
179
 
117
- #### `postprocess`: postprocess
180
+ Returns
181
+ -------
182
+ Any
183
+ The postprocessed outputs.
184
+ """
185
+ return outputs
118
186
 
119
- This method transforms the neural network outputs to generate the final predictions.
187
+ ```
120
188
 
121
189
  ## Usage
122
190
 
@@ -140,15 +208,13 @@ The `device` is the [`torch.device`](https://pytorch.org/docs/stable/tensor_attr
140
208
 
141
209
  #### Logger
142
210
 
143
- 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/)
211
+ 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/) or with any other custom logger.
144
212
 
145
213
  ```py
146
214
  import wandb
147
215
  model = ClassName(logger=wandb.log)
148
216
  ```
149
217
 
150
- or with any other custom logger.
151
-
152
218
  #### Hyperparameters
153
219
 
154
220
  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
@@ -179,9 +245,9 @@ where
179
245
  - `on` is the name of the training dataloader. Defaults to 'train'.
180
246
  - `val` is the name of the validation dataloader. Defaults to 'val'.
181
247
  - `gas` is the number of gradient accumulation steps. Defaults to 1 (no gradient accumulation).
182
- - `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.
248
+ - `callback` is a function that is called after each epoch. It should accept two arguments: the instance itself and a list of dictionaries containing logging info up to the current epoch. When this function returns `True`, training stops.
183
249
 
184
- This method returns a list of dictionaries containing the loss and evaluation metrics.
250
+ This method returns a list of dictionaries containing logging info.
185
251
 
186
252
  ### Checkpoints
187
253
 
@@ -212,7 +278,7 @@ Evaluate the model with
212
278
  model.eval(on='test')
213
279
  ```
214
280
 
215
- 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.
281
+ 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 loss and evaluation metrics.
216
282
 
217
283
  ### Inference
218
284
 
@@ -230,6 +296,20 @@ Get started with simple self-contained examples:
230
296
 
231
297
  - [MNIST classification](https://github.com/eguidotti/torchabc/blob/main/examples/mnist.py)
232
298
 
299
+ ### Run the examples
300
+
301
+ Install the dependencies
302
+
303
+ ```
304
+ poetry install --with examples
305
+ ```
306
+
307
+ Run the examples by replacing `<name>` with one of the filenames in the [examples](https://github.com/eguidotti/torchabc/tree/main/examples) folder
308
+
309
+ ```
310
+ poetry run python examples/<name>.py
311
+ ```
312
+
233
313
  ## Contribute
234
314
 
235
315
  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.
@@ -30,73 +30,141 @@ Generate a template using the command line interface.
30
30
  torchabc --create template.py
31
31
  ```
32
32
 
33
- The template is structured as follows.
33
+ Fill out the template.
34
34
 
35
35
  ```py
36
+ from torchabc import TorchABC
37
+ from functools import cached_property
38
+
39
+
36
40
  class ClassName(TorchABC):
41
+ """A concrete subclass 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
+ """
37
49
 
38
50
  @cached_property
39
51
  def dataloaders(self):
52
+ """The dataloaders.
53
+
54
+ Returns a dictionary containing multiple `DataLoader` instances. The keys of
55
+ the dictionary are the names of the dataloaders (e.g., 'train', 'val', 'test'),
56
+ and the values are the corresponding `torch.utils.data.DataLoader` objects.
57
+ """
40
58
  raise NotImplementedError
41
59
 
42
- def preprocess(self, data: Any, flag: str = '') -> Any:
60
+ @staticmethod
61
+ def preprocess(data, flag='predict'):
62
+ """The preprocessing step.
63
+
64
+ Transforms the raw data of an individual sample into the corresponding tensor(s).
65
+
66
+ Parameters
67
+ ----------
68
+ data : Any
69
+ The raw data.
70
+ flag : str, optional
71
+ A flag indicating how to transform the data. The default transforms the
72
+ input data for inference. You can use additional flags, for instance,
73
+ to perform data augmentation or transform the targets during training or validation.
74
+
75
+ Returns
76
+ -------
77
+ Union[Tensor, Iterable[Tensor]]
78
+ The preprocessed data.
79
+ """
43
80
  return data
44
-
81
+
45
82
  @cached_property
46
83
  def network(self):
84
+ """The neural network.
85
+
86
+ Returns a `torch.nn.Module` whose input and output tensors assume the
87
+ batch size is the first dimension: (batch_size, ...).
88
+ """
47
89
  raise NotImplementedError
48
90
 
49
91
  @cached_property
50
92
  def optimizer(self):
93
+ """The optimizer for training the network.
94
+
95
+ Returns a `torch.optim.Optimizer` configured for `self.network.parameters()`.
96
+ """
51
97
  raise NotImplementedError
52
98
 
53
99
  @cached_property
54
100
  def scheduler(self):
101
+ """The learning rate scheduler for the optimizer.
102
+
103
+ Returns a `torch.optim.lr_scheduler.LRScheduler` or `torch.optim.lr_scheduler.ReduceLROnPlateau`
104
+ configured for `self.optimizer`.
105
+ """
55
106
  return None
56
107
 
57
- def loss(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
108
+ @staticmethod
109
+ def loss(outputs, targets):
110
+ """The loss function.
111
+
112
+ Compute the loss to train the neural network.
113
+
114
+ Parameters
115
+ ----------
116
+ outputs : Union[Tensor, Iterable[Tensor]]
117
+ The tensor(s) returned by the forward pass of `self.network`.
118
+ targets : Union[Tensor, Iterable[Tensor]]
119
+ The tensor(s) giving the target values.
120
+
121
+ Returns
122
+ -------
123
+ Tensor
124
+ A scalar tensor giving the loss value.
125
+ """
58
126
  raise NotImplementedError
59
-
60
- def metrics(self, outputs: torch.Tensor, targets: torch.Tensor) -> Dict[str, float]:
61
- return {}
62
-
63
- def postprocess(self, outputs: torch.Tensor) -> Any:
64
- return outputs
65
- ```
66
-
67
- Fill out the template with the dataloaders, preprocessing and postprocessing steps, the neural network, optimizer, scheduler, loss, and optional evaluation metrics.
68
-
69
- #### `dataloaders`: the dataloaders for training and evaluation
70
-
71
- 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.
72
-
73
- #### `preprocess`: preprocessing
74
-
75
- 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.
76
-
77
- #### `network`: the neural network
78
127
 
79
- 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, ...).
80
-
81
- #### `optimizer`: the optimizer for training the network
82
-
83
- Returns an [`Optimizer`](https://pytorch.org/docs/main/optim.html#torch.optim.Optimizer) configured for the `network`.
84
-
85
- #### `scheduler`: the learning rate scheduler for the optimizer
86
-
87
- 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`.
88
-
89
- #### `loss`: loss function
128
+ @staticmethod
129
+ def metrics(outputs, targets):
130
+ """The evaluation metrics.
131
+
132
+ Compute additional evaluation metrics.
133
+
134
+ Parameters
135
+ ----------
136
+ outputs : Union[Tensor, Iterable[Tensor]]
137
+ The tensor(s) returned by the forward pass of `self.network`.
138
+ targets : Union[Tensor, Iterable[Tensor]]
139
+ The tensor(s) giving the target values.
140
+
141
+ Returns
142
+ -------
143
+ Dict[str, float]
144
+ A dictionary where the keys are the names of the metrics and the
145
+ values are the corresponding scores.
146
+ """
147
+ return {}
90
148
 
91
- 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.
149
+ @staticmethod
150
+ def postprocess(outputs):
151
+ """The postprocessing step.
92
152
 
93
- #### `metrics`: evaluation metrics
153
+ Transforms the neural network outputs into the final predictions.
94
154
 
95
- 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.
155
+ Parameters
156
+ ----------
157
+ outputs : Union[Tensor, Iterable[Tensor]]
158
+ The tensor(s) returned by the forward pass of `self.network`.
96
159
 
97
- #### `postprocess`: postprocess
160
+ Returns
161
+ -------
162
+ Any
163
+ The postprocessed outputs.
164
+ """
165
+ return outputs
98
166
 
99
- This method transforms the neural network outputs to generate the final predictions.
167
+ ```
100
168
 
101
169
  ## Usage
102
170
 
@@ -120,15 +188,13 @@ The `device` is the [`torch.device`](https://pytorch.org/docs/stable/tensor_attr
120
188
 
121
189
  #### Logger
122
190
 
123
- 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/)
191
+ 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/) or with any other custom logger.
124
192
 
125
193
  ```py
126
194
  import wandb
127
195
  model = ClassName(logger=wandb.log)
128
196
  ```
129
197
 
130
- or with any other custom logger.
131
-
132
198
  #### Hyperparameters
133
199
 
134
200
  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
@@ -159,9 +225,9 @@ where
159
225
  - `on` is the name of the training dataloader. Defaults to 'train'.
160
226
  - `val` is the name of the validation dataloader. Defaults to 'val'.
161
227
  - `gas` is the number of gradient accumulation steps. Defaults to 1 (no gradient accumulation).
162
- - `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.
228
+ - `callback` is a function that is called after each epoch. It should accept two arguments: the instance itself and a list of dictionaries containing logging info up to the current epoch. When this function returns `True`, training stops.
163
229
 
164
- This method returns a list of dictionaries containing the loss and evaluation metrics.
230
+ This method returns a list of dictionaries containing logging info.
165
231
 
166
232
  ### Checkpoints
167
233
 
@@ -192,7 +258,7 @@ Evaluate the model with
192
258
  model.eval(on='test')
193
259
  ```
194
260
 
195
- 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.
261
+ 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 loss and evaluation metrics.
196
262
 
197
263
  ### Inference
198
264
 
@@ -210,6 +276,20 @@ Get started with simple self-contained examples:
210
276
 
211
277
  - [MNIST classification](https://github.com/eguidotti/torchabc/blob/main/examples/mnist.py)
212
278
 
279
+ ### Run the examples
280
+
281
+ Install the dependencies
282
+
283
+ ```
284
+ poetry install --with examples
285
+ ```
286
+
287
+ Run the examples by replacing `<name>` with one of the filenames in the [examples](https://github.com/eguidotti/torchabc/tree/main/examples) folder
288
+
289
+ ```
290
+ poetry run python examples/<name>.py
291
+ ```
292
+
213
293
  ## Contribute
214
294
 
215
295
  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.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "torchabc"
3
- version = "0.2.4"
3
+ version = "0.3.0"
4
4
  description = "A simple abstract class for training and inference in PyTorch."
5
5
  authors = [
6
6
  {name = "Emanuele Guidotti",email = "emanuele.guidotti@usi.ch"}
@@ -15,6 +15,12 @@ dependencies = [
15
15
  [tool.poetry.scripts]
16
16
  torchabc = "torchabc.cli:main"
17
17
 
18
+ [tool.poetry.group.examples]
19
+ optional = true
20
+
21
+ [tool.poetry.group.examples.dependencies]
22
+ torchvision = "^0.22.0"
23
+
18
24
  [project.urls]
19
25
  homepage = "https://github.com/eguidotti/torchabc"
20
26
  repository = "https://github.com/eguidotti/torchabc"
@@ -1,8 +1,10 @@
1
1
  import abc
2
2
  import torch
3
+ from torch import Tensor
4
+ from torch.utils.data import default_collate
3
5
  from functools import cached_property
4
6
  from types import SimpleNamespace
5
- from typing import Any, Union, Dict, List, Callable
7
+ from typing import Any, Iterable, Union, Dict, List, Callable
6
8
 
7
9
 
8
10
  class TorchABC(abc.ABC):
@@ -31,8 +33,6 @@ class TorchABC(abc.ABC):
31
33
  The function used for logging.
32
34
  hparams : SimpleNamespace
33
35
  A namespace containing the hyperparameters.
34
- epoch : int
35
- The last epoch seen during training.
36
36
  """
37
37
  super().__init__()
38
38
  if device is not None:
@@ -45,205 +45,139 @@ class TorchABC(abc.ABC):
45
45
  self.device = torch.device("cpu")
46
46
  self.logger = logger
47
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
48
 
79
49
  @abc.abstractmethod
80
50
  @cached_property
81
51
  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.
52
+ """The dataloaders.
88
53
 
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.
54
+ Returns a dictionary containing multiple `DataLoader` instances. The keys of
55
+ the dictionary are the names of the dataloaders (e.g., 'train', 'val', 'test'),
56
+ and the values are the corresponding `torch.utils.data.DataLoader` objects.
97
57
  """
98
58
  pass
99
59
 
60
+ @staticmethod
100
61
  @abc.abstractmethod
101
- def preprocess(self, data: Any, flag: str = '') -> Any:
102
- """Prepare the raw data for the network.
62
+ def preprocess(data: Any, flag: str = 'predict') -> Union[Tensor, Iterable[Tensor]]:
63
+ """The preprocessing step.
103
64
 
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.
65
+ Transforms the raw data of an individual sample into the corresponding tensor(s).
109
66
 
110
67
  Parameters
111
68
  ----------
112
69
  data : Any
113
- The raw input data to be processed.
70
+ The raw data.
114
71
  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.
72
+ A flag indicating how to transform the data. The default transforms the
73
+ input data for inference. You can use additional flags, for instance,
74
+ to perform data augmentation or transform the targets during training or validation.
117
75
 
118
76
  Returns
119
77
  -------
120
- Any
78
+ Union[Tensor, Iterable[Tensor]]
121
79
  The preprocessed data.
122
80
  """
123
81
  pass
124
82
 
125
83
  @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`.
84
+ @cached_property
85
+ def network(self) -> torch.nn.Module:
86
+ """The neural network.
136
87
 
137
- Returns
138
- -------
139
- Any
140
- The postprocessed outputs.
88
+ Returns a `torch.nn.Module` whose input and output tensors assume the
89
+ batch size is the first dimension: (batch_size, ...).
141
90
  """
142
91
  pass
143
92
 
144
93
  @abc.abstractmethod
145
- def loss(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
146
- """Loss function.
94
+ @cached_property
95
+ def optimizer(self) -> torch.optim.Optimizer:
96
+ """The optimizer for training the network.
147
97
 
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.
98
+ Returns a `torch.optim.Optimizer` configured for `self.network.parameters()`.
99
+ """
100
+ pass
151
101
 
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.
102
+ @abc.abstractmethod
103
+ @cached_property
104
+ def scheduler(self) -> Union[None, torch.optim.lr_scheduler.LRScheduler, torch.optim.lr_scheduler.ReduceLROnPlateau]:
105
+ """The learning rate scheduler for the optimizer.
158
106
 
159
- Returns
160
- -------
161
- torch.Tensor
162
- A scalar tensor representing the computed loss value.
107
+ Returns a `torch.optim.lr_scheduler.LRScheduler` or `torch.optim.lr_scheduler.ReduceLROnPlateau`
108
+ configured for `self.optimizer`.
163
109
  """
164
110
  pass
165
111
 
112
+ @staticmethod
166
113
  @abc.abstractmethod
167
- def metrics(self, outputs: torch.Tensor, targets: torch.Tensor) -> Dict[str, float]:
168
- """Evaluation metrics.
114
+ def loss(outputs: Union[Tensor, Iterable[Tensor]], targets: Union[Tensor, Iterable[Tensor]]) -> Tensor:
115
+ """The loss function.
169
116
 
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.
117
+ Compute the loss to train the neural network.
174
118
 
175
119
  Parameters
176
120
  ----------
177
- outputs : torch.Tensor
178
- The tensor containing the network's output.
179
- targets : torch.Tensor
180
- The targets corresponding to the outputs.
121
+ outputs : Union[Tensor, Iterable[Tensor]]
122
+ The tensor(s) returned by the forward pass of `self.network`.
123
+ targets : Union[Tensor, Iterable[Tensor]]
124
+ The tensor(s) giving the target values.
181
125
 
182
126
  Returns
183
127
  -------
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.
128
+ Tensor
129
+ A scalar tensor giving the loss value.
187
130
  """
188
131
  pass
189
132
 
190
- def collate(self, batch: Any) -> Any:
191
- """Collate a batch of data.
133
+ @staticmethod
134
+ @abc.abstractmethod
135
+ def metrics(outputs: Union[Tensor, Iterable[Tensor]], targets: Union[Tensor, Iterable[Tensor]]) -> Dict[str, float]:
136
+ """The evaluation metrics.
192
137
 
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.
138
+ Compute additional evaluation metrics.
196
139
 
197
140
  Parameters
198
141
  ----------
199
- batch : Any
200
- The batch of data to collate.
142
+ outputs : Union[Tensor, Iterable[Tensor]]
143
+ The tensor(s) returned by the forward pass of `self.network`.
144
+ targets : Union[Tensor, Iterable[Tensor]]
145
+ The tensor(s) giving the target values.
201
146
 
202
147
  Returns
203
148
  -------
204
- Any
205
- The collated batch of data.
149
+ Dict[str, float]
150
+ A dictionary where the keys are the names of the metrics and the
151
+ values are the corresponding scores.
206
152
  """
207
- return torch.utils.data.default_collate(batch)
153
+ pass
208
154
 
209
- def move(self, data: Any) -> Any:
210
- """Move data to the current device.
155
+ @staticmethod
156
+ @abc.abstractmethod
157
+ def postprocess(outputs: Union[Tensor, Iterable[Tensor]]) -> Any:
158
+ """The postprocessing step.
211
159
 
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.
160
+ Transforms the neural network outputs into the final predictions.
215
161
 
216
162
  Parameters
217
163
  ----------
218
- data : Any
219
- The data to move to the current device.
164
+ outputs : Union[Tensor, Iterable[Tensor]]
165
+ The tensor(s) returned by the forward pass of `self.network`.
220
166
 
221
167
  Returns
222
168
  -------
223
169
  Any
224
- The data moved to the current device.
170
+ The postprocessed outputs.
225
171
  """
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
- )
172
+ pass
239
173
 
240
174
  def train(self, epochs: int, on: str = 'train', val: str = 'val', gas: int = 1, callback: Callable = None) -> List[dict]:
241
175
  """Train the model.
242
176
 
243
177
  This method sets the network to training mode, iterates through the training dataloader
244
178
  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.
179
+ model parameters, and logs the training loss and metrics. It optionally performs
180
+ validation after each epoch.
247
181
 
248
182
  Parameters
249
183
  ----------
@@ -257,22 +191,21 @@ class TorchABC(abc.ABC):
257
191
  The number of gradient accumulation steps. Defaults to 1 (no gradient accumulation).
258
192
  callback : Callable, optional
259
193
  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.
194
+ the instance itself and a list of dictionaries containing logging info up to the
195
+ current epoch. When this function returns True, training stops.
262
196
 
263
197
  Returns
264
198
  -------
265
199
  list
266
- A list of dictionaries containing the loss and evaluation metrics.
200
+ A list of dictionaries containing logging info.
267
201
  """
268
202
  logs, log_batch, log_epoch = [], {}, {}
269
- for epoch in range(self.epoch + 1, self.epoch + 1 + epochs):
270
- self.epoch = epoch
203
+ for epoch in range(1, 1 + epochs):
204
+ loss_gas = 0
271
205
  self.network.train()
272
- self.network.to(self.device)
273
206
  self.optimizer.zero_grad()
274
- loss_gas = 0
275
207
  for batch, (inputs, targets) in enumerate(self.dataloaders[on], start=1):
208
+ self.network.to(self.device)
276
209
  inputs, targets = self.move((inputs, targets))
277
210
  outputs = self.network(inputs)
278
211
  loss = self.loss(outputs, targets)
@@ -318,68 +251,104 @@ class TorchABC(abc.ABC):
318
251
  break
319
252
  return logs
320
253
 
321
- def eval(self, on: str) -> Dict[str, float]:
254
+ def eval(self, on: str, reduction: Union[str, Callable] = 'mean') -> Dict[str, float]:
322
255
  """Evaluate the model.
323
256
 
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.
257
+ This method sets the network to evaluation mode, iterates through the given
258
+ dataloader, calculates the loss and metrics, and returns the results.
327
259
 
328
260
  Parameters
329
261
  ----------
330
262
  on : str
331
- The name of the dataloader to evaluate on. This should be one of
332
- the keys in `self.dataloaders`.
263
+ The name of the dataloader to evaluate on. This is one of the keys
264
+ in `self.dataloaders`.
265
+ reduction : Union[str, Callable]
266
+ Specifies the reduction to apply to batch statistics. Possible values
267
+ are 'mean' to compute the average of the evaluation metrics across batches,
268
+ 'sum' to compute their sum, or a callable function that takes as input a
269
+ list of floats and a metric name and returns a scalar.
333
270
 
334
271
  Returns
335
272
  -------
336
273
  dict
337
274
  A dictionary containing the loss and evaluation metrics.
338
275
  """
276
+ metrics_lst = []
339
277
  self.network.eval()
340
- tot_loss, num_batches = 0, 0
341
- all_outputs, all_targets = [], []
342
278
  with torch.no_grad():
343
- self.network.to(self.device)
344
279
  for inputs, targets in self.dataloaders[on]:
280
+ self.network.to(self.device)
345
281
  inputs, targets = self.move((inputs, targets))
346
282
  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:
283
+ metrics = self.metrics(outputs, targets)
284
+ metrics['loss'] = self.loss(outputs, targets).item()
285
+ metrics_lst.append(metrics)
286
+ if isinstance(reduction, str):
287
+ reduce = lambda x, _: getattr(torch, reduction)(torch.tensor(x)).item()
288
+ else:
289
+ reduce = reduction
290
+ return {
291
+ name: reduce([metrics[name] for metrics in metrics_lst], name)
292
+ for name in metrics_lst[0]
293
+ }
294
+
295
+ def predict(self, data: Iterable[Any]) -> Any:
356
296
  """Predict the raw data.
357
297
 
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.
298
+ This method sets the network to evaluation mode, preprocesses and collates
299
+ the raw input data, performs a forward pass, postprocesses the outputs,
300
+ and returns the final predictions.
362
301
 
363
302
  Parameters
364
303
  ----------
365
- data : Any
366
- The raw input data to predict.
304
+ data : Iterable[Any]
305
+ The raw input data.
367
306
 
368
307
  Returns
369
308
  -------
370
309
  Any
371
- The postprocessed prediction.
310
+ The predictions.
372
311
  """
373
312
  self.network.eval()
374
- self.network.to(self.device)
375
313
  with torch.no_grad():
376
- preprocessed = self.preprocess(data)
377
- batch = [preprocessed]
378
- collated = self.collate(batch)
379
- inputs = self.move(collated)
314
+ data = [self.preprocess(d, flag='predict') for d in data]
315
+ batch = default_collate(data)
316
+ self.network.to(self.device)
317
+ inputs = self.move(batch)
380
318
  outputs = self.network(inputs)
381
- predictions = self.postprocess(outputs)
382
- return predictions[0]
319
+ return self.postprocess(outputs)
320
+
321
+ def move(self, data: Union[Tensor, Iterable[Tensor]]) -> Union[Tensor, Iterable[Tensor]]:
322
+ """Move data to the current device.
323
+
324
+ This method moves the data to the device specified by `self.device`. It supports
325
+ moving tensors, or lists, tuples, and dictionaries of tensors. For custom data
326
+ structures, overwrite this function to implement the necessary logic for moving
327
+ the data to the device.
328
+
329
+ Parameters
330
+ ----------
331
+ data : Union[Tensor, Iterable[Tensor]]
332
+ The data to move to the current device.
333
+
334
+ Returns
335
+ -------
336
+ Union[Tensor, Iterable[Tensor]]
337
+ The data moved to the current device.
338
+ """
339
+ if isinstance(data, Tensor):
340
+ return data.to(self.device)
341
+ elif isinstance(data, list):
342
+ return [self.move(item) for item in data]
343
+ elif isinstance(data, tuple):
344
+ return tuple(self.move(item) for item in data)
345
+ elif isinstance(data, dict):
346
+ return {key: self.move(value) for key, value in data.items()}
347
+ else:
348
+ raise TypeError(
349
+ f"Unsupported data type: {type(data)}. "
350
+ "Please implement the method `move` for custom data types."
351
+ )
383
352
 
384
353
  def save(self, checkpoint: str) -> None:
385
354
  """Save checkpoint.
@@ -391,7 +360,6 @@ class TorchABC(abc.ABC):
391
360
  """
392
361
  torch.save({
393
362
  'hparams': self.hparams.__dict__,
394
- 'epoch': self.epoch,
395
363
  'network_state_dict': self.network.state_dict(),
396
364
  'optimizer_state_dict': self.optimizer.state_dict(),
397
365
  'scheduler_state_dict': self.scheduler.state_dict() if self.scheduler else None,
@@ -408,7 +376,6 @@ class TorchABC(abc.ABC):
408
376
  """
409
377
  checkpoint = torch.load(checkpoint, map_location=self.device)
410
378
  self.hparams = SimpleNamespace(**checkpoint['hparams'])
411
- self.epoch = checkpoint['epoch']
412
379
  self.network.load_state_dict(checkpoint['network_state_dict'])
413
380
  self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
414
381
  if checkpoint['scheduler_state_dict']:
@@ -11,7 +11,7 @@ def main():
11
11
  args = parser.parse_args()
12
12
 
13
13
  cached_properties = {}
14
- methods = {}
14
+ static_methods = {}
15
15
  defaults = {
16
16
  'scheduler': 'return None',
17
17
  'metrics': 'return {}',
@@ -23,15 +23,20 @@ def main():
23
23
  if hasattr(member, '__isabstractmethod__'):
24
24
  if isinstance(TorchABC.__dict__.get(name), cached_property):
25
25
  cached_properties[name] = member.__doc__ or ""
26
- elif callable(member):
26
+ elif isinstance(TorchABC.__dict__.get(name), staticmethod):
27
27
  sig = inspect.signature(member)
28
- methods[name] = (sig, member.__doc__ or "")
28
+ sig = sig.replace(
29
+ parameters=[
30
+ param.replace(annotation=inspect.Parameter.empty)
31
+ for param in sig.parameters.values()
32
+ ],
33
+ return_annotation=inspect.Signature.empty
34
+ )
35
+ static_methods[name] = (sig, member.__doc__ or "")
29
36
 
30
37
  template = """
31
- import torch
32
38
  from torchabc import TorchABC
33
39
  from functools import cached_property
34
- from typing import Any, Dict
35
40
 
36
41
 
37
42
  class ClassName(TorchABC):"""
@@ -41,10 +46,10 @@ class ClassName(TorchABC):"""
41
46
  \"\"\"A concrete subclass of the TorchABC abstract class.
42
47
 
43
48
  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`.
49
+ - replace ClassName with the name of your model,
50
+ - replace this docstring with a description of your model,
51
+ - implement the methods below to define the core logic of your model,
52
+ - access the hyperparameters passed during initialization with `self.hparams`.
48
53
  \"\"\""""
49
54
 
50
55
  template += """
@@ -62,26 +67,18 @@ class ClassName(TorchABC):"""
62
67
  template += f"""
63
68
  {defaults[name] if name in defaults else 'raise NotImplementedError'}
64
69
  """
65
- elif name in methods:
66
- sig, doc = methods[name]
70
+ elif name in static_methods:
71
+ sig, doc = static_methods[name]
67
72
  template += f"""
73
+ @staticmethod
68
74
  def {name}{sig}:"""
69
75
  if not args.min:
70
76
  template += f"""
71
77
  \"\"\"{doc}\"\"\""""
72
78
  template += f"""
73
79
  {defaults[name] if name in defaults else 'raise NotImplementedError'}
74
- """
75
-
76
- if not args.min:
77
- template += f"""
78
-
79
- if __name__ == "__main__":
80
- # Example usage
81
- model = ClassName()
82
- model.train(epochs=1)
83
80
  """
84
-
81
+
85
82
  with open(args.create, "x") as f:
86
83
  f.write(template.lstrip())
87
84
 
File without changes