-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcallbacks.py
More file actions
153 lines (137 loc) · 5.12 KB
/
Copy pathcallbacks.py
File metadata and controls
153 lines (137 loc) · 5.12 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import abc
import os
from difflib import get_close_matches
import torch
import torch.nn as nn
class Callback(abc.ABC):
"""
Abstract Class for defining callbacks in a training loop.
"""
@abc.abstractmethod
def on_epoch_begin(self, **kwargs):
"""
Called at the beginning of each epoch.
Returns: bool: True if training should stop, False otherwise.
"""
return False
@abc.abstractmethod
def on_epoch_end(self, **kwargs):
"""
Called at the end of each epoch.
Returns: bool: True if training should stop, False otherwise.
"""
return False
class EarlyStopping(Callback):
"""
Early stopping utility to stop training when validation loss does not improve.
Args:
patience (int): Number of epochs with no improvement after which training will be stopped.
delta (float): Minimum change in the monitored quantity to qualify as an improvement.
"""
def __init__(self, patience: int = 10, delta: float = 0) -> None:
"""
Early stopping utility to stop training when validation loss does not improve.
Args:
patience (int): Number of epochs with no improvement after which training will be stopped.
delta (float): Minimum change in the monitored quantity to qualify as an improvement.
"""
self.patience = patience
self.delta = delta
self.counter = 0
self.best_loss = float("inf")
self.best_epoch = 0
self.metric_name = "val_loss"
def on_epoch_begin(self, **kwargs) -> bool:
"""
Call this method at the beginning of each epoch.
Returns: bool: True if training should stop, False otherwise.
"""
return False
def on_epoch_end(self, **kwargs) -> bool:
"""
Call this method to check if training should be stopped.
Args:
val_loss (float): Current validation loss.
"""
logs = kwargs.get("logs", {})
val_loss = logs.get(self.metric_name, float("inf"))
if val_loss < self.best_loss - self.delta:
self.best_loss = val_loss
self.counter = 0
return False
elif val_loss > self.best_loss + self.delta:
self.counter += 1
if self.counter >= self.patience:
return True
return False
class ModelCheckpoint(Callback):
"""
Class to save the model at the end of each epoch.
"""
def __init__(
self,
slurm_job_id: str,
save_path: str,
monitor: str = "val_loss",
mode: str = "min",
save_best_only: bool = True,
) -> None:
"""
Args:
save_path (str): Path to save the model.
monitor (str): Metric to monitor for saving the model.
mode (str): One of {'min', 'max'}. In 'min' mode, the model is saved when the monitored metric decreases.
save_best_only (bool): If True, only saves the model when the monitored metric improves.
"""
self.slurm_job_id = slurm_job_id
self.save_path = save_path
self.monitor = monitor
self.mode = mode
self.save_best_only = save_best_only
self.best_metric = float("inf") if mode == "min" else float("-inf")
self.best_epoch = 0
def on_epoch_begin(self, **kwargs) -> bool:
return False
def on_epoch_end(self, **kwargs) -> bool:
"""
Called at the end of each epoch to save the model if the monitored metric improves.
Args:
logs (dict): Dictionary containing the metrics for the epoch.
"""
logs = kwargs.get("logs", {})
epoch = kwargs.get("epoch", 0)
model = kwargs.get("model", nn.Sequential())
if epoch == 1:
# find the closest match to monitor in logs
if self.monitor not in logs:
close_matches = get_close_matches(
self.monitor, logs.keys(), n=1, cutoff=0
)[0]
if close_matches:
self.monitor = close_matches
else:
raise ValueError(
f"Monitor metric '{self.monitor}' not found in logs. Available metrics: {list(logs.keys())}"
)
current_metric = logs.get(self.monitor, float("inf"))
if self.save_best_only:
if (self.mode == "min" and current_metric < self.best_metric) or (
self.mode == "max" and current_metric > self.best_metric
):
self.best_metric = current_metric
self.best_epoch = kwargs.get("epoch", 0)
# Save the model here
torch.save(
model.state_dict(),
os.path.join(self.save_path, f"{self.slurm_job_id}.pth"),
)
else:
# Save the model every epoch
torch.save(
model.state_dict(),
os.path.join(
self.save_path,
f"{self.slurm_job_id}_epoch_{kwargs.get('epoch', 0)}.pth",
),
)
return False