-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrain.py
More file actions
170 lines (135 loc) · 6.61 KB
/
Copy pathtrain.py
File metadata and controls
170 lines (135 loc) · 6.61 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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import torch
from torch import cuda, optim, nn, device
from torch.utils.data import DataLoader
from dataset import AnimalImages
from model import get_model
from typing import List, Tuple
from train_config import *
import argparse
@torch.no_grad()
def get_accuracy(arg_model: nn.Module, data_loader: DataLoader, arg_device: device) -> float:
""" Calculate the accuracy of the model given the data loader """
arg_model.eval() # Changing the model to eval mode
correct_predictions, total_predictions = 0, 0
for imgs, labels in data_loader:
imgs, labels = imgs.to(arg_device), labels.to(arg_device)
outputs = arg_model(imgs)
# predicted label is one with largest probability, hence finding max
_, predicted_labels = torch.max(outputs, dim=1)
# increment the number of correct predictions
total_predictions += labels.shape[0]
correct_predictions += int((predicted_labels == labels).sum())
return round((correct_predictions / total_predictions) * 100, 2)
def load_state(
model_name: str, model: nn.Module, optimizer, scheduler) -> None:
""" Load the state of the model and its optimizer or scheduler """
model.load_state_dict(torch.load(f'./data/{model_name}_checkpoint.pth'))
other_checkpoint = torch.load(f'./data/{model_name}_other_checkpoint.pth')
optimizer.load(other_checkpoint["optimizer"])
scheduler.load(other_checkpoint["scheduler"])
def save_state(
model_name: str, model: nn.Module, optimizer, scheduler
) -> None:
""" Save the state to checkpoint"""
torch.save(model.state_dict(), f'/data/{model_name}_checkpoint.pth')
torch.save(
{
"optimizer": optimizer.state_dict(),
"scheduler": scheduler.state_dict()
},
f'./data/{model_name}_other_checkpoint.pth'
)
def mini_batch_training(
num_epochs: int, model: nn.Module, train_data_loader: DataLoader, val_data_loader: DataLoader,
loss_fn, optimizer, scheduler,
) -> Tuple[List[float], List[float]]:
""" Training loop over the minibatches of the dataset """
device = torch.device("cuda") if cuda.is_available() else torch.device("cpu")
train_losses, val_losses = [], []
for epoch in range(1, num_epochs + 1):
# train loss and val loss
avg_train_loss, avg_val_loss = 0.0, 0.0
# compute the train loss
model.train() # switch the model to train mode
for batch_ix, (imgs, labels) in enumerate(train_data_loader):
# make sure the imgs and the labels are converted to be used by right device
imgs, labels = imgs.to(device), labels.to(device)
# forward phase
predicted_train_labels = model(imgs)
train_loss = loss_fn(predicted_train_labels, labels)
# backward phase
optimizer.zero_grad()
train_loss.backward() # calculate and accumulate the current gradient
optimizer.step()
avg_train_loss += train_loss.item()
# The user has an option of not using scheduler to train
if scheduler:
scheduler.step(epoch + batch_ix / len(train_data_loader))
# compute train loss
avg_train_loss = round(avg_train_loss / len(train_data_loader), 4)
train_losses.append(avg_train_loss)
# compute the val loss, turn off the autograd
with torch.no_grad():
model.eval() # switch the model to val mode
for imgs, labels in val_data_loader:
imgs, labels = imgs.to(device), labels.to(device)
# forward pass
predicted_val_labels = model(imgs)
val_loss = loss_fn(predicted_val_labels, labels)
avg_val_loss += val_loss.item()
avg_val_loss = round(avg_val_loss / len(val_data_loader), 4)
val_losses.append(avg_val_loss)
print(f"Epoch {epoch}/{num_epochs}. Train: {avg_train_loss}, val: {avg_val_loss}")
# return different tracks for graphing
return train_losses, val_losses
def main() -> None:
parser = argparse.ArgumentParser(description="Train classification models")
parser.add_argument("model_name", type=str, help="The name of the model")
parser.add_argument("--validate", action="store_true",
help="Get accuracy of the trained model on the dataset")
parser.add_argument("--resume", action="store_true",
help="Resume training the model instead of from scratch")
model_name: str = parser.parse_args().model_name
# initialize the dataset
train_dataset = AnimalImages("./data/animals10/raw-img", 224)
val_dataset = AnimalImages("./data/animals10/raw-img", 224, train=False)
# Data loader of the dataset
# fix the number of workers based on the machine in which this is trained
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=3)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=3)
# Initialize the model
torch.cuda.empty_cache()
# If we only validate the model
if parser.parse_args().validate_model:
# model with the pretrained parameters
animal_classifier = get_model(model_name, load_state=True)
load_state(model_name, animal_classifier)
print(get_accuracy(animal_classifier, train_loader), get_accuracy(animal_classifier, val_loader))
return
# Newly initialized models
animal_classifier = get_model(model_name)
# If we train the model, use ADAMW to auto update the learning rate
this_optimizer = optim.AdamW(
animal_classifier.parameters(), lr=LEARNING_RATE, weight_decay=WEIGTH_DECAY
)
# Scheduler to schedule the global learning rate update periodically
this_scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(
this_optimizer,
T_0=NUM_EPOCHS_EACH_CYCLE, # every cycle is 30 epochs, the scheduler restarts lr
T_mult=1, # cycle length the same
eta_min=1e-5 # min LR
)
# If training is resumed, load the weights as well as state of the optimizer and scheduler
if parser.parse_args().resume:
load_state(model_name, animal_classifier, this_optimizer, this_scheduler)
train_track, val_track = mini_batch_training(
num_epochs=TOTAL_NUM_EPOCHS,
model=animal_classifier,
loss_fn=nn.CrossEntropyLoss(),
optimizer=this_optimizer, scheduler=this_scheduler,
train_data_loader=train_loader, val_data_loader=val_loader
)
# save state to checkpoint file
save_state(model_name, animal_classifier, this_optimizer, this_scheduler)
if __name__ == "__main__":
main()