训练回调

用于自定义训练行为的各种回调

源代码

ShortEpochCallback

 ShortEpochCallback (pct=0.01, short_valid=True)

仅训练一个 epoch 的 pct 部分,然后停止

learn = synth_learner()
learn.fit(1, cbs=ShortEpochCallback())
周期 训练损失 验证损失 时间
0 00:00
learn = synth_learner()
learn.fit(1, cbs=ShortEpochCallback(short_valid=False))
周期 训练损失 验证损失 时间
0 8.432135 00:00

源代码

GradientAccumulation

 GradientAccumulation (n_acc=32)

在更新权重之前累积梯度

当每次累积的步数高于批次数量时,参数(以及验证损失)将完全不会改变

learn = synth_learner()
learn.fit(1, lr=0.01, cbs=GradientAccumulation(n_acc=1000))
# ensure valid_loss didn't change
assert learn.recorder.values[-1][1] == learn.recorder.values[0][1]
周期 训练损失 验证损失 时间
0 20.987558 26.849480 00:00

源代码

GradientClip

 GradientClip (max_norm:float=1.0, norm_type:float=2.0)

裁剪梯度范数

通常情况下,如果学习率过高,训练会发散。即使使用混合精度训练(通过动态损失缩放避免无穷大),这种情况仍然会发生,训练仍然会发散

fp16 = MixedPrecision()
set_seed(99)
learn = synth_learner(lr=1.1, cuda=True)
learn.fit(3, cbs=fp16)
周期 训练损失 验证损失 时间
0 38.214138 25.269005 00:00
1 377.145508 890.010376 00:00
2 839.392883 9965.747070 00:00

通过添加 GradientClip 回调,可以使用 nn.utils.clip_grad_norm_ 将梯度 norm_type (默认:2) 范数裁剪到最大不超过 max_norm (默认:1),这可以避免损失发散

set_seed(99)
learn = synth_learner(lr=1.1, cuda=True)
learn.fit(3, cbs=[GradientClip,fp16])
周期 训练损失 验证损失 时间
0 2.039428 2.372177 00:00
1 1.402425 0.300728 00:00
2 1.013548 0.332610 00:00

BnFreeze


源代码

BnFreeze

 BnFreeze (after_create=None, before_fit=None, before_epoch=None,
           before_train=None, before_batch=None, after_pred=None,
           after_loss=None, before_backward=None,
           after_cancel_backward=None, after_backward=None,
           before_step=None, after_cancel_step=None, after_step=None,
           after_cancel_batch=None, after_batch=None,
           after_cancel_train=None, after_train=None,
           before_validate=None, after_cancel_validate=None,
           after_validate=None, after_cancel_epoch=None, after_epoch=None,
           after_cancel_fit=None, after_fit=None)

通过在各种事件中修改 Learner 来处理训练循环调整的基本类


源代码

set_bn_eval

 set_bn_eval (m:torch.nn.modules.module.Module, use_eval=True)

m 的所有递归子模块中的 BN 层设置为评估模式。

当你想要训练两个具有共同特征提取器/主体的独立模型时,BnFreeze 非常有用。模型中唯一不同的部分是用于迁移学习的头部。

在这里,Learner.freeze() 不够用,因为 BatchNorm 层默认是可训练的,并且会跟踪批次的运行均值和标准差。为了使特征提取器完全匹配,你需要设置 train_bn=False 并且这些统计数据也需要被冻结,这正是 BnFreeze 的作用。

path = untar_data(URLs.MNIST_TINY)
dls  = ImageDataLoaders.from_folder(path, valid_pct=0.2)

https://pytorch.ac.cn/tutorials/intermediate/memory_format_tutorial.html我们首先通过创建一个 Learner 来演示仅使用 train_bn=False 时运行统计信息的不匹配…

learn1 = vision_learner(deepcopy(dls), resnet18, pretrained=True, train_bn=False)

…并获取第一个 BatchNorm 层,并存储其运行均值

m = learn1.model[0][1].running_mean.clone()

你可以看到现在运行均值已经改变了

learn1.fit(1, lr=0.02)
test_ne(to_detach(learn1.model[0][1].running_mean), m)
周期 训练损失 验证损失 时间
0 1.148303 0.739404 00:12

当我们使用 BnFreeze 回调时,运行统计信息在训练期间不会改变。这对于从迁移学习中获得良好结果通常很重要。

learn1 = vision_learner(deepcopy(dls), resnet18, pretrained=True, train_bn=False, cbs=BnFreeze)
m = learn1.model[0][1].running_mean.detach().clone()
learn1.fit(1, lr=0.02)
test_eq(to_detach(learn1.model[0][1].running_mean), m)
周期 训练损失 验证损失 时间
0 0.478594 0.270772 00:10