class RecipeTrainer(d2l.Trainer):
"""A Trainer that sets the learning rate from the model's schedule."""
def fit_epoch(self):
for group in self.optim.param_groups:
group['lr'] = self.model.lr_at(self.epoch, self.max_epochs)
super().fit_epoch()
class ClassicRecipe(ResNet18):
"""Recipe A, ca. 2015: SGD with momentum, step decay, plain loss."""
def configure_optimizers(self):
return torch.optim.SGD(self.parameters(), lr=self.lr,
momentum=0.9, weight_decay=5e-4)
def lr_at(self, epoch, max_epochs):
return step_decay(epoch, max_epochs, self.lr)
class ModernRecipe(ResNet18):
"""Recipe B, ca. 2022: AdamW, cosine warmup, smoothing, Mixup."""
def configure_optimizers(self):
return torch.optim.AdamW(self.parameters(), lr=self.lr,
weight_decay=0.05)
def lr_at(self, epoch, max_epochs):
return cosine_warmup(epoch, max_epochs, self.lr)
def loss(self, y_hat, y):
return F.cross_entropy(y_hat, y, label_smoothing=0.1)
def training_step(self, batch):
X, y_a, y_b, lam = mixup(*batch, alpha=0.2)
y_hat = self(X)
l = lam * self.loss(y_hat, y_a) + (1 - lam) * self.loss(y_hat, y_b)
self.plot('loss', l, train=True)
return l