1. 自定义损失函数
2. 动态调整学习率
如何实现学习率衰减?
- 使用官方API
PyTorch已经在torch.optim.lr_scheduler
为我们封装好了一些动态调整学习率的方法供我们使用
TORCH.OPTIM
- 自定义scheduler
自定义函数adjust_learning_rate
来改变param_group
中lr
的值,如实现lr
每20epoch
下降为1/10.
def adjust_learning_rate(optimizer, epoch):
lr = args.lr * (0.1 ** (epoch // 30))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
训练过程调用
def adjust_learning_rate(optimizer,...):
...
optimizer = torch.optim.SGD(model.parameters(),lr = args.lr,momentum = 0.9)
for epoch in range(10):
train(...)
validate(...)
adjust_learning_rate(optimizer,epoch)
3. 模型微调-torchvision
迁移学习(transfer learning),将从源数据集学到的知识迁移到目标数据集上。利用在大数据集上训练的模型可以抽取较通用的图像特征,帮助识别边缘、纹理、形状和物体组成等
模型微调(finetune)
- 实例化网络
- 传递
pretrained
参数
import torchvision.models as models
resnet18 = models.resnet18()
- 训练特定层
通过设置requires_grad = False
来冻结部分层
def set_parameter_requires_grad(model, feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
使用resnet18为例的将1000类改为4类,但是仅改变最后一层的模型参数,不改变特征提取的模型参数
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)
训练过程中,model仍会进行梯度回传,但是参数更新则只会发生在fc层。通过设定参数的requires_grad属性。
4. 模型微调-timm
5. 半精度训练
设置
- 导入模块
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)
...
6. 数据增强imgaug
7. 使用argparse进行调参
- 导入包
- 利用包中
ArgumentParser
类生成parse
对象 - 通过对象的
add_argument
函数来增加参数 - 用对象的
parse_args
获取解析的参数
import argparse
parser = argparse.ArgumentParser(description='') # 创建ArgumentParser()对象
parser.add_argument("-A", default = "2", type = ...) # 通过对象的 add_argument 函数来增加参数
args = parser.parse_args() # 获取解析的参数
更高效的使用argparse修改超参数
定义config.py
import argparse
def get_options(parser=argparse.ArgumentParser()):
parser.add_argument('--workers', type=int, default=0,
help='number of data loading workers, you had better put it '
'4 times of your gpu')
parser.add_argument('--batch_size', type=int, default=4, help='input batch size, default=64')
parser.add_argument('--niter', type=int, default=10, help='number of epochs to train for, default=10')
parser.add_argument('--lr', type=float, default=3e-5, help='select the learning rate, default=1e-3')
parser.add_argument('--seed', type=int, default=118, help="random seed")
parser.add_argument('--cuda', action='store_true', default=True, help='enables cuda')
parser.add_argument('--checkpoint_path',type=str,default='',
help='Path to load a previous trained model if not empty (default empty)')
parser.add_argument('--output',action='store_true',default=True,help="shows output")
opt = parser.parse_args()
if opt.output:
print(f'num_workers: {opt.workers}')
print(f'batch_size: {opt.batch_size}')
print(f'epochs (niters) : {opt.niter}')
print(f'learning rate : {opt.lr}')
print(f'manual_seed: {opt.seed}')
print(f'cuda enable: {opt.cuda}')
print(f'checkpoint_path: {opt.checkpoint_path}')
return opt
if __name__ == '__main__':
opt = get_options()
train.py
中调用
# 导入必要库
...
import config
opt = config.get_options()
manual_seed = opt.seed
num_workers = opt.workers
batch_size = opt.batch_size
lr = opt.lr
niters = opt.niters
checkpoint_path = opt.checkpoint_path
# 随机数的设置,保证复现结果
def set_seed(seed):
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
random.seed(seed)
np.random.seed(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
...
if __name__ == '__main__':
set_seed(manual_seed)
for epoch in range(niters):
train(model,lr,batch_size,num_workers,checkpoint_path)
val(model,lr,batch_size,num_workers,checkpoint_path)