OpenPCDet到目前版本(v 0.3.0)为止集成的优化器有3种:adam,sgd和adam_onecyel。这个可见代码build_optimizer(model,optim_cfg)函数。
def build_optimizer(model, optim_cfg):
if optim_cfg.OPTIMIZER == 'adam':
optimizer = optim.Adam(model.parameters(), lr=optim_cfg.LR, weight_decay=optim_cfg.WEIGHT_DECAY)
elif optim_cfg.OPTIMIZER == 'sgd':
optimizer = optim.SGD(
model.parameters(), lr=optim_cfg.LR, weight_decay=optim_cfg.WEIGHT_DECAY,
momentum=optim_cfg.MOMENTUM
)
elif optim_cfg.OPTIMIZER == 'adam_onecycle':
def children(m: nn.Module):
return list(m.children())
def num_children(m: nn.Module) -> int:
return len(children(m))
flatten_model = lambda m: sum(map(flatten_model, m.children()), []) if num_children(m) else [m]
get_layer_groups = lambda m: [nn.Sequential(*flatten_model(m))]
optimizer_func = partial(optim.Adam, betas=(0.9, 0.99)) #Adam超参数
optimizer = OptimWrapper.create(
optimizer_func, 3e-3, get_layer_groups(model), wd=optim_cfg.WEIGHT_DECAY, true_wd=True, bn_wd=True
)
else:
raise NotImplementedError
return optimizer
平常训练用得较多的是adam_onecycle。它本质上是adam优化器再加上onecycle学习率调整策略。我比较关心的是这个onecycle的学习率调整策略,在这里使用一个少量样本的训练集来认识一下关于onecycle学习率调整中涉及到的若干参数。
OPTIMIZATION:
BATCH_SIZE_PER_GPU: 5
NUM_EPOCHS: 100
OPTIMIZER: adam_onecycle
LR: 0.003
WEIGHT_DECAY: 0.01
MOMENTUM: 0.9
MOMS: [0.95, 0.85]
PCT_START: 0.3
DIV_FACTOR: 10
DECAY_STEP_LIST: [35, 45]
LR_DECAY: 0.1
LR_CLIP: 0.0000001
LR_WARMUP: False
WARMUP_EPOCH: 1
GRAD_NORM_CLIP: 10
我设置的训练集样本为100,BATCH_SIZE为5,总的训练epochs数num_epochs为100。按此计算的话,总的训练迭代次数为200。训练过后得到的lr曲线如下:
我把要关注的几个点在图中以红色圆圈标示了出来。从左至右,第一个红圈为学习率的起点low_lr,它的值是由配置文件中给定的两个参数LR和DIV_FACTOR计算得到,low_lr = LR / DIR_FACTOR,即0.0003。第二个红圈是学习率最大的地方lr_max,它正是配置文件中设置的LR,值为0.003。第三个红圈为训练结束时学习率lr_end。
class oneCycle(LRSchedulerStep):
def __init__(self, fai_optimizer, total_step, lr_max, moms, div_factor,
pct_start):
self.lr_max = lr_max
self.moms = moms #e.g. [0.95, 0.85]
self.div_factor = div_factor #e.g. 10
self.pct_start = pct_start #学习率上升的部分step数量的占比, e.g. 0.4
a1 = int(total_step * self.pct_start)
a2 = total_step - a1
low_lr = self.lr_max / self.div_factor #初始学习率
lr_phases = (
(0, partial(annealing_cos, low_lr, self.lr_max)), #上升期,low_lr-->lr_max
(self.pct_start, partial(annealing_cos, self.lr_max, low_lr / 1e4)) #下降期,lr_max->low_lr/1e4
)
mom_phases = (
(0, partial(annealing_cos, *self.moms)),
(self.pct_start, partial(annealing_cos,*self.moms[::-1]))
)
fai_optimizer.lr, fai_optimizer.mom = low_lr, self.moms[0]
super().__init__(fai_optimizer, total_step, lr_phases, mom_phases)
总的说的,adam_oncycle中学习率曲线的变化分成两个部分。第一个部分是由low_lr上升到lr_max,这一部分由PCT_START这个系数控制上升阶段的迭代次数(PCT_START*num_total_iters),由cos函数控制调整步长。第二部分是学习率由lr_max下降到lr_end,剩余迭代次数当然就是(1-PCT_START)*num_total_iters,同样是由cos函数控制调整步长。 在具体的训练任务中,通过合理地调整LR、PCT_START等参数来使训练任务达到最优。至于配置文件中的其它参数,如:DECAY_STEP_LIST、LR_DECAY、LR_DECAY_STEP等,在adam_onecycle优化器中并未使用到。



