Pytorch 进阶训练技巧
注:本文为阅读开源教程《深入浅出 Pytorch》所做笔记,仅记录部分笔者认为有价值且较少用到的技巧及知识点
一、自定义损失函数
Pytorch 支持实现并调用自定义损失函数,自定义损失函数一般以类的形式定义,继承自 nn.Module,实现构造函数与 forward 函数,在调用时直接实例化并调用计算 loss 即可,例如下文计算 DiceLoss:
class DiceLoss(nn.Module):
# 继承自 nn.Module
def __init__(self,weight=None,size_average=True):
# 构造函数
super(DiceLoss,self).__init__()
def forward(self,inputs,targets,smooth=1):
# 定义损失的计算过程
inputs = F.sigmoid(inputs)
inputs = inputs.view(-1)
targets = targets.view(-1)
intersection = (inputs * targets).sum()
dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)
return 1 - dice
# 使用方法
criterion = DiceLoss()
loss = criterion(input,targets)
二、动态调整学习率
Pytorch 也支持在训练过程中动态调整学习率,通过 torch.optim.lr_scheduler 类实现,该类封装了多种动态学习率调度对象,可以通过以下代码使用:
from torch.optim.lr_scheduler import LambdaLR
net_1 = model()
optimizer_1 = torch.optim.Adam(net_1.parameters(), lr = initial_lr)
scheduler_1 = LambdaLR(optimizer_1, lr_lambda=lambda epoch: 1/(epoch+1))
for epoch in range(epoches):
# train
optimizer_1.zero_grad()
optimizer_1.step()
scheduler_1.step()
常用的动态调整策略包括:
·StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1):
n
e
w
l
r
=
i
n
i
t
i
a
l
l
r
×
γ
e
p
o
c
h
/
s
t
e
p
s
i
z
e
newlr=initiallr × γ^ {epoch/step_{size}}
newlr=initiallr×γepoch/stepsize
·ExponentialLR(optimizer, gamma, last_epoch=-1):
n
e
w
l
r
=
i
n
i
t
i
a
l
l
r
×
γ
e
p
o
c
h
newlr=initiallr×γ^{epoch}
newlr=initiallr×γepoch
·ReduceLROnPlateau(optimizer, mode=‘min’, factor=0.1, patience=10, verbose=False, threshold=0.0001,threshold_mode=‘rel’, cooldown=0, min_lr=0, eps=1e-08):给定一个 metric,当 metric 停止优化时减少学习率
也可以通过类似于以下写法的函数自定义动态学习率:
def adjust_learning_rate(optimizer, epoch):
lr = args.lr * (0.1 ** (epoch // 30))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
# 使用时
for epoch in range(10):
train(...)
validate(...)
adjust_learning_rate(optimizer,epoch)
三、使用预训练模型
预训练模型是目前深度学习极为热门的模型应用,Pytorch 提供了一些经典预训练模型供使用,也可从其他网站下载其他预训练模型使用,Pytorch 提供了修改对接本地任务的接口:① 加载一个保留参数的预训练模型;② 通过函数冻结所有层的梯度更新;③ 将输出层改写对接本地任务。
# 定义冻结参数更新函数
def set_parameter_requires_grad(model, feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
# 修改该模型
import torchvision.models as models
# 冻结参数的梯度
feature_extract = True
model = models.resnet18(pretrained=True)
set_parameter_requires_grad(model, feature_extract)
# 修改模型
num_ftrs = model.fc.in_features
model.fc = nn.Linear(in_features=num_ftrs, out_features=4, bias=True)
四、半精度训练
在显存不足的情况下,可以通过半精度训练来减少显存占用,半精度训练需要修改以下三个地方:
-
导入 autocast:
from torch.cuda.amp import autocast
-
模型 forward 函数使用 autocast 修饰:
@autocast() def forward(self, x): ... return x
-
训练时使用 with autocast():
for x in train_loader: x = x.cuda() with autocast(): output = model(x) ...