1 学习率
1.1 余弦退火
训练时当越来越接近Loss值的全局最小值时,学习率应该变得更小来使得模型尽可能接近这一点,而余弦退火(Cosine annealing)可以通过余弦函数来降低学习率。余弦函数中随着x的增加余弦值首先缓慢下降,然后加速下降,再次缓慢下降。这种下降模式能和学习率配合,以一种十分有效的计算方式来产生很好的效果
简单的单步长余弦退火,pytorch中cawb_steps 为 []
from torch.optim.lr_scheduler import _LRScheduler
class WarmUpLR(_LRScheduler):
"""warmup_training learning rate scheduler
Args:
optimizer: optimzier(e.g. SGD)
total_iters: totoal_iters of warmup phase
"""
def __init__(self, optimizer, total_iters, last_epoch=-1):
self.total_iters = total_iters
super().__init__(optimizer, last_epoch)
def get_lr(self):
"""we will use the first m batches, and set the learning
rate to base_lr * m / total_iters
"""
return [base_lr * self.last_epoch / (self.total_iters + 1e-8) for base_lr in self.base_lrs]
# return [base_lr for base_lr in self.base_lrs] # 这一句 可以固定学习率
# 使用时:
warmup_scheduler = WarmUpLR(optimizer, iter_per_epoch * config.WARM)
多 step 重启动,训练时陷入局部最小之后,如果忽然加大学习率,有助于跳出局部最优解,因此可以使用多step重启策略,其实就是将step和余弦退火结合起来
1.2 warm up
神经网络参数是随机初始化的,如果一开始采用较大的学习率可能会使模型震荡甚至进入自由区(loss=nan),因此尝试阶段逐步增大学习率,平稳后再逐步减小学习率。下面是warm up和余弦退火的结合使用。
2 训练加速
2.1分布式训练ddp
分布式并行训练的方式包括两种方式:
数据并行:每个GPU或服务器分别训练一部分数据,并行处理,相当于增加batch
模型并行:有些模型比较大,如医疗场景的3D数据需要放在多张卡上,如前五层在GPU1,后五层在GPU2,并行训练
目前深度学习框架主要还是以支持数据并行为主,如pytorch的DP和DDP
DistributedDataParallel和 DataParallel之间的区别是:DistributedDataParallel 使用multiprocessing,即为每个GPU创建一个进程,而 DataParallel使用多线程。通过使用multiprocessing,每个GPU都有其专用的进程,这避免了Python解释器的GIL导致的性能开销。
DDP多GPU训练的关键之处在于如何同步梯度,以达到增加batch的效果,使用了all reduce操作,所有进程在进行Allreduce之前,会进行一次同步,然后才开始做通信,同步多卡的梯度,然后进行反响传播。
DDP支持多卡并行以及多机并行,实现代码可参考DDP代码介绍
相对普通训练代码,DDP主要变动的位置包括:
- 启动的方式引入了一个多进程机制;
- 引入了几个环境变量;
- DataLoader多了一个sampler参数;
- 网络被一个DistributedDataParallel(net)又包裹了一层;
- ckpt的保存方式发生了变化。
2.2 自动混合精度训练AMP
待总结
参考链接
3 优化器选择
选择一个正确的优化器。有许多流行的自适应优化器,如Adam, Adagrad, Adadelta,或RMSprop等。SGD+动量被广泛应用于各种问题领域。有两件事需要考虑:第一,如果你关心快速收敛,使用自适应优化器,如Adam,但它可能会陷入局部极小,提供了糟糕的泛化(下图)。第二,SGD+momentum可以实现找到全局最小值,但它依赖于鲁棒初始化,而且可能比其他自适应优化器需要更长的时间来收敛(下图)。我建议你使用SGD+动量,因为它能达到更好的最佳效果。(YOLO V5的作者建议是,如果需要训练较小的自定义数据集,Adam是更合适的选择,尽管Adam的学习率通常比SGD低。但是如果训练大型数据集,对于YOLOV5来说SGD效果比Adam好)
SGD最简单的梯度下降,没有动量概念,下降慢,容易在沟壑旁边震荡,陷入局部最小值
SGD with momentum 有动量的概念,下降快慢依赖于之前积累的惯性(一阶动量),想象高速公路上汽车转弯,在高速向前的同时略微偏向,急转弯可是要出事的
Adam和Nadam的出现就很自然而然了——它们是前述方法的集大成者。我们看到,SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum