完整的内容见Numpy, Torch,cv近日小结。
1.重要超参数的设定
1.1 batch size的设定
如果用了batch norm, 那么batch size不宜太小,最好要大于等于32.
一开始时可以设置为32, 之后x2逐步调整。
1.2 学习率设置与更新
1.2.0 warmup
warmup作用:由于刚开始训练时,模型的权重(weights)是随机初始化的,此时若选择一个较大的学习率,可能带来模型的不稳定(振荡),选择Warmup预热学习率的方式,可以使得开始训练的几个epoches或者一些steps内学习率较小,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。
在选择lr初值和更新方式的阶段,不要使用warmup。否则warmup可能会掩盖lr初值设置过大的问题。应该等lr, batch size, epoch等参数选定好后,再尝试使用warmup看效果。
1.2.1 学习率初值选定
当batachsize固定(如32),可以把学习率初值设为1e-3, 然后10x调整,最后再x2或者x5微调。
如果训练过程中准确率始终维持在一个很低的水平或者出现NAN, 那么准确率应该调小; 如果
1.2.2 学习率与batchsize的关系
对于收敛精度,由于增大了batch size使梯度估计相较于badeline的梯度更加准确,噪音减少,更容易收敛到附近的local minima,类似于GD的效果。batch size增大K倍,相当于将梯度的方差减少K倍,因此梯度更加准确。
如果要保持方差和原来SGD一样,相当于给定了这么大的方差带宽容量,那么就可以增大lr,充分利用这个方差容量。
因此可将lr增加sqrt(K)倍,以提高训练速度,这也是在linear scaling rule之前很多人常用的增大lr的方式。但在实际中,发现直接将lr增大K倍的效果较好。
1.2.3 学习率调整策略
常见的方式包括Linear, step, multistep, cosine等。
def get_scheduler(optimizer, opt) :
"""Return a learning rate scheduler
Parameters:
optimizer -- 网络优化器
opt.lr_policy -- 学习率scheduler的名称: linear | step | plateau | cosine
"""
def linear_rule(epoch) :
lr_l = 1.0 - epoch / opt.epochs
return lr_l
def warmup_rule(epoch) :
if epoch < opt.warmup_epoch :
lr_l = epoch / opt.warmup_epoch
else:
T = epoch - opt.warmup_epoch
total_epoch = opt.epochs - opt.warmup_epoch
if opt.adjust_lr == 'cosine':
lr_l = 0.5 * (1 + math.cos(T / total_epoch * math.pi))
elif opt.adjust_lr == 'step':
gamma = opt.step_gamma
step_size = opt.step_size
lr_l = gamma ** (T//step_size)
return lr_l
if opt.adjust_lr == 'linear' :
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=linear_rule)
elif opt.adjust_lr == 'step' :
# scheduler = lr_scheduler.StepLR(optimizer, step_size=100, gamma=0.5)
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=warmup_rule)
# elif opt.adjust_lr == 'multistep' :
# epochs = opt.epochs
# milestones = [epochs // 2, epochs * 3 // 4]
# scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.1)
elif opt.adjust_lr == 'plateau':
scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, threshold=0.01, patience=5)
elif opt.adjust_lr == 'cosine':
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=warmup_rule)
else :
raise NotImplementedError('learning rate policy [%s] is not implemented', opt.adjust_lr)
return scheduler
1.3 epoch的设置
lr的decay策略与epoch有关,因此要严格分析起来会比较复杂。
大体的原则是先设置一个比较大的epoch如100。然后观察准确率和train loss的变化.
如果刚开始准确率就上升比较快,但后面的绝大部分epoch准确率没有明显变化或者后面的绝大部分epoch上train Loss基本没有下降,就可以考虑减少epoch; 反之,如果epoch快结束时这两个还在变化,就考虑增加epoch。
1.4 optimizer选择
一般来说Adam是最优选择,其他的以后有机会再了解。
2. train network
2.1 如何防止过拟合
过拟合主要有两个原因造成的,数据太少和模型太复杂。对于模型复杂,最简单暴力的做法就是减少网络的层数和神经元的个数,但是一般不是很提倡这种做法,是因为人为并不能很好掌控删减的程度,因此下面介绍几种高效的方法。
- 正则化与权重衰减。 L2正则化也就是权重衰减。
一般,Weight Decay=0.001。可以根据实际训练效果,做x10倍的上下调整。
另外,pytorch里通过设置优化器的weight_decay来进行权重衰减,貌似没有分母的n(因为优化器是没有办法得知训练集的样本大小的)。
- 数据增强(crop, resize, noise, rotate, flip and etc). 需要注意的是标签可能也需要随之变化.
- 提前终止。 当testset上的准确率(或loss等)有下降的趋势,立刻停止。
- dropout与BatchNormalization。 根据一些最新的研究论文,相比于dropout,更推荐使用BN。
需要注意的是,使用BN时batch size不能太小。一般可以选择32,再根据实际训练效果x2上调下调。
注意: PyTorch设置 Dropout 时,torch.nn.Dropout(keepProb = 0.5), 这里的 keepProb是指该层(layer)的神经元在每次迭代训练时会被丢弃(失活)的可能性。其他的如TF等参数的意义到底是失活概率还是不失活概率,还需查阅文档。
PS:根据本人目前浅薄经验, 数据增强是最有效的(因为无需参数的设定,只要增强了数据,基本上都会起到一定的防止过拟合效果)。
2.2 train loss与test loss结果分析
train loss 不断下降,test loss不断下降,说明网络仍在学习;
train loss 不断下降,test loss趋于不变,说明网络过拟合;
train loss 趋于不变,test loss不断下降,说明数据集100%有问题;
train loss 趋于不变,test loss趋于不变,说明学习遇到瓶颈,需要减小学习率或批量数目;
train loss 不断上升,test loss不断上升,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题。
2.3 解决神经网络训练时train loss不下降的问题
训练集Loss不下降,说明欠拟合。
总结的如下解决方案:
- 正则化过度,**开始训练时可以考虑一个小的正则化系数。**过拟合后,再根据训练情况进行调整。
- 选择合适的损失函数。损失函数相当于模型拟合程度的一个评价指标,如果损失函数选取不当,导致学习难度过大,就容易欠拟合。考虑降低损失函数的学习难度。
- 选择合适的优化器和学习速率。我们需要手动调整学习率,首先选择一个合适的初始学习率,当训练不动之后,稍微降低学习率,然后再训练一段时间,这时候基本上就完全收敛了。
- 训练时间不足
- 神经网络的拟合能力不够。增加神经网络的层数。
具体参考原文链接:
如何解决神经网络训练时loss不下降的问题
深度学习:欠拟合问题的几种解决方案
2.4 L1 Loss与L2 Loss的对比与选取
L1 Loss:
- 以稳定速率收敛
- 对输入数据中的离群点不敏感
- 不光滑,存在不可导点
L2 Loss:
- 随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。
- 对离群点敏感
- 光滑,处处可导
smooth L1 Loss:
- 当与 ground truth 差别过大(存在离群点)时,梯度值不至于过大而跑飞;
- 当与 ground truth 差别很小时,梯度值足够小,较快收敛到最小值。
L1与L2 Loss的选择:
对于大多数CNN网络,我们一般是使用L2-loss而不是L1-loss,因为L2-loss的收敛速度要比L1-loss要快得多。
如果数据离群点较多,可以考虑使用L1 Loss.
2.5 网络结构和参数量估计
2.5.1 Vgg16
参考https://blog.csdn.net/hzhj2007/article/details/80164909
最后是三个全连接层
(7x7x512) x 4096=102M
4096x4096=17M
4096x1000=4M
最后全连接层共有123M parameters. 而整个Vgg16一共有138M parameters.
如果是用float32, 那么大小为138M x 4 = 500~MB.
2.6 同时使用多个数据库训练
2.6.1 使用一个dataloader
第一个方法是把所有的数据库拼接在一起。
参考:
https://zhuanlan.zhihu.com/p/222772996
https://pytorch.org/docs/stable/data.html?highlight=concatdataset#torch.utils.data.ConcatDataset
2.6.2 zip多个dataloader
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象。
我们可以使用 list() 转换来输出列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
如果两个dataloader的长度不一样, 使用cycle,我们将再次重复最小的数据集,直到迭代器查看最大数据集中的所有样本。
# len(dataloader1) == len(dataloader2)
for i,data in enumerate(zip(dataloader1, dataloader2)):
(x1,y1),(x2,y2) = data
# len(dataloader1) < len(dataloader2)
from itertools import cycle
for i,data in enumerate(zip(cycle(dataloader1), dataloader2)):
(x1,y1),(x2,y2) = data
2.7 训练加速
2.7.1 读取数据加速
- 服务器内存一般都很大(100GB级别),可以在训练之前把所有数据全部读入内存。
- 把num_worker设置为8或者16