前言
常用tricks:Rect、优化器和学习率设置、warmup、多尺度训练、混合精度训练。
一、Rect
过程:计算所有训练图像的高宽比,再进行排序,目的是让有着类似高宽比的图像挨在一起,后续分配batch时,就能将有相似高宽比的图像放在同一个batch中。
部分代码如下:
# 记录每张图像的原始尺寸
self.shapes = np.array(s, dtype=np.float64)
# Rectangular Training https://github.com/ultralytics/yolov3/issues/232
# 如果为ture,训练网络时,会使用类似原图像比例的矩形(让最长边为img_size),而不是img_size x img_size
# 注意: 开启rect后,mosaic就默认关闭
if self.rect: # 使得相同batch的图像有类似的高宽比
# Sort by aspect ratio
s = self.shapes # wh
# 计算每个图片的高/宽比
ar = s[:, 1] / s[:, 0] # aspect ratio
# argsort函数返回的是数组值从小到大的索引值
# 按照高宽比例进行排序,这样后面划分的每个batch中的图像就拥有类似的高宽比
irect = ar.argsort() # 得到的是索引
# 根据排序后的顺序重新设置图像顺序、标签顺序以及shape顺序
self.img_files = [self.img_files[i] for i in irect] # 根据索引排序,从而使得图像具有类似的高宽比
self.label_files = [self.label_files[i] for i in irect]
self.shapes = s[irect] # wh
ar = ar[irect]
# set training image shapes
# 计算每个batch采用的统一尺度
shapes = [[1, 1]] * nb # nb: number of batches
for i in range(nb):
ari = ar[bi == i] # bi: batch index bi = [00001111...], i:0,1,2... ari = [0.222 0.224 0.292 0.294]
# 获取第i个batch中,最小和最大高宽比
mini, maxi = ari.min(), ari.max()
# 如果高/宽小于1(w > h),将w设为img_size
if maxi < 1:
shapes[i] = [maxi, 1]
# 如果高/宽大于1(w < h),将h设置为img_size
elif mini > 1:
shapes[i] = [1, 1 / mini]
# 计算每个batch输入网络的shape值(向上设置为32的整数倍)
self.batch_shapes = np.ceil(np.array(shapes) * img_size / 32. + pad).astype(np.int) * 32 # 每个batch都有自己的shape值, getitem时,获取图像的index和其对应的batchsize尺寸
二、优化器和学习率设置
# Optimizer
nbs = 64 # nominal batch size
accumulate = max(round(nbs / batch_size), 1) # accumulate loss before optimizing
hyp['weight_decay'] *= batch_size * accumulate / nbs # scale weight_decay
optimizer = smart_optimizer(model, opt.optimizer, hyp['lr0'], hyp['momentum'], hyp['weight_decay'])
# Scheduler
if opt.cos_lr:
lf = one_cycle(1, hyp['lrf'], epochs) # cosine 1->hyp['lrf']
else:
lf = lambda x: (1 - x / epochs) * (1.0 - hyp['lrf']) + hyp['lrf'] # linear
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
accumulate 是自己设置的batchsize要多少轮才能达到名义的batchsize(nbs),例如,batchsize设为4,那么accumulate就为4,即需要4轮才能达到batch_size=64。
当给模型喂了4批图片数据后,将四批图片数据得到的梯度值,做累积。当每累积到4批数据时,才会对参数做更新,这样就实现了与batch_size=64时相同的效果。
由于batch_size发生了变化,hyp[‘weight_decay’]也要做相应的缩放。
学习率的设置常用一下两种方式:
(1)、线性学习率,通过线性插值的方式调整学习率;
(2)、余弦退火学习率,即采用余弦曲线衰减学习率。
三、Warmup
训练时,learning rate如果一开始就设置得比较大,容易出现梯度爆炸,模型难以收敛,一般都是先以较小的学习率进行训练,再慢慢增大到正常学习率。
# Warmup
if ni <= nw:
xi = [0, nw] # x interp
# compute_loss.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
accumulate = max(1, np.interp(ni, xi, [1, nbs / batch_size]).round())
for j, x in enumerate(optimizer.param_groups):
# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 0 else 0.0, x['initial_lr'] * lf(epoch)])
if 'momentum' in x:
x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])
四、多尺度训练
#Multi-scale
if opt.multi_scale:
sz = random.randrange(int(imgsz * 0.5), int(imgsz * 1.5) + gs) // gs * gs # size
sf = sz / max(imgs.shape[2:]) # scale factor
if sf != 1:
ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
imgs = nn.functional.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)
多尺度训练是在默认训练尺寸的基础上(0.5~1.5)之间随机选取一个值,作为下一批次训练用的数据的数据大小。
五、混合精度训练
深度学习框架默认的精度一般为32位单精度浮点数(FP32),混合精度训练是使用16位浮点数(FP16)进行训练,从而减少训练深度学习模型所需的内存,同时FP16比FP32运算速度更快,进一步提高硬件效率。
使用pytorch框架通过:import torch.cuda.amp 导入amp模块,可以方便的进行混合精度训练。
#Forward
with torch.cuda.amp.autocast(True):
pred = model(imgs) # forward
loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
总结
本文主要总结目标检测训练常用的tricks。