-
Notifications
You must be signed in to change notification settings - Fork 0
/
mnist_model.py
132 lines (101 loc) · 4.99 KB
/
mnist_model.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from typing import Any, Dict, List, Sequence, Tuple, Union
import hydra
import pytorch_lightning as pl
import torch
import torch.nn.functional as F
from pytorch_lightning.metrics.classification import Accuracy
from torch.optim import Optimizer
from src.architectures.simple_dense_net import SimpleDenseNet
class LitModelMNIST(pl.LightningModule):
"""
Example of LightningModule for MNIST classification.
A LightningModule organizes your PyTorch code into 5 sections:
- Computations (init).
- Train loop (training_step)
- Validation loop (validation_step)
- Test loop (test_step)
- Optimizers (configure_optimizers)
Read the docs:
https://pytorch-lightning.readthedocs.io/en/latest/lightning_module.html
"""
def __init__(self, *args, **kwargs):
super().__init__()
# this line ensures params passed to LightningModule will be saved to ckpt
# it also allows to access params with 'self.hparams' attribute
self.save_hyperparameters()
self.architecture = SimpleDenseNet(hparams=self.hparams)
# loss function
self.criterion = torch.nn.CrossEntropyLoss()
# use separate metric instance for train, val and test step
# to ensure a proper reduction over the epoch
self.train_accuracy = Accuracy()
self.val_accuracy = Accuracy()
self.test_accuracy = Accuracy()
self.metric_hist = {
"train/acc": [],
"val/acc": [],
"train/loss": [],
"val/loss": [],
}
def forward(self, x) -> torch.Tensor:
return self.architecture(x)
def step(self, batch) -> Dict[str, torch.Tensor]:
x, y = batch
logits = self.forward(x)
loss = self.criterion(logits, y)
preds = torch.argmax(logits, dim=1)
return loss, preds, y
def training_step(self, batch: Any, batch_idx: int) -> Dict[str, torch.Tensor]:
loss, preds, targets = self.step(batch)
# log train metrics to your loggers!
acc = self.train_accuracy(preds, targets)
self.log("train/loss", loss, on_step=False, on_epoch=True, prog_bar=False)
self.log("train/acc", acc, on_step=False, on_epoch=True, prog_bar=True)
# we can return here dict with any tensors
# and then read it in some callback or in training_epoch_end() below
# remember to always return loss from training_step, or else backpropagation will fail!
return {"loss": loss, "preds": preds, "targets": targets}
def validation_step(self, batch: Any, batch_idx: int) -> Dict[str, torch.Tensor]:
loss, preds, targets = self.step(batch)
# log val metrics to your loggers!
acc = self.val_accuracy(preds, targets)
self.log("val/loss", loss, on_step=False, on_epoch=True, prog_bar=False)
self.log("val/acc", acc, on_step=False, on_epoch=True, prog_bar=True)
# we can return here dict with any tensors
# and then read it in some callback or in validation_epoch_end() below
return {"loss": loss, "preds": preds, "targets": targets}
def test_step(self, batch: Any, batch_idx: int) -> Dict[str, torch.Tensor]:
loss, preds, targets = self.step(batch)
# log test metrics to your loggers!
acc = self.test_accuracy(preds, targets)
self.log("test/loss", loss, on_step=False, on_epoch=True)
self.log("test/acc", acc, on_step=False, on_epoch=True)
return loss
# [OPTIONAL METHOD]
def training_epoch_end(self, outputs: List[Any]) -> None:
# log best so far train acc and train loss
self.metric_hist["train/acc"].append(self.trainer.callback_metrics["train/acc"])
self.metric_hist["train/loss"].append(
self.trainer.callback_metrics["train/loss"]
)
self.log("train/acc_best", max(self.metric_hist["train/acc"]), prog_bar=False)
self.log("train/loss_best", min(self.metric_hist["train/loss"]), prog_bar=False)
# [OPTIONAL METHOD]
def validation_epoch_end(self, outputs: List[Any]) -> None:
# log best so far val acc and val loss
self.metric_hist["val/acc"].append(self.trainer.callback_metrics["val/acc"])
self.metric_hist["val/loss"].append(self.trainer.callback_metrics["val/loss"])
self.log("val/acc_best", max(self.metric_hist["val/acc"]), prog_bar=False)
self.log("val/loss_best", min(self.metric_hist["val/loss"]), prog_bar=False)
def configure_optimizers(
self,
) -> Union[Optimizer, Tuple[Sequence[Optimizer], Sequence[Any]]]:
"""Choose what optimizers and learning-rate schedulers to use in your optimization.
Normally you'd need one. But in the case of GANs or similar you might have multiple.
See examples here:
https://pytorch-lightning.readthedocs.io/en/latest/common/lightning_module.html#configure-optimizers
"""
optim = hydra.utils.instantiate(
self.hparams.optimizer, params=self.parameters()
)
return optim