第一周工作总结
本周参与了三个模型的开发训练工作。其中天气分类模型和交通事故检测模型由我个人完整开发训练,车流量检测模型参与并完成了前期的工作。经过这周的学习,我对于训练模型的全套流程有了更清楚的认识,从寻找数据集,到写训练文件的python代码,再到对模型的量化和剪枝,最后到部署。其中包含了很多学问,比如超参数的设定、神经网络模型的优化和增加模块、将训练好的权重文件转化为onnx格式的文件等等。总的来说,这一周的收获还是很大的。下面对我这一周学习过程中的部分内容做个总结。
一、超参数简介
超参数是算法的高级结构设置,它们在训练阶段之前设定,并在训练阶段保持不变。
YOLOv5 有大约 30 个超参数,用于不同的训练设置。这些参数在 *.yaml 文件中的 /data/hyps 目录。
关于选择哪种文件:
hyp.no-augmentation.yaml:用于指定在训练过程中不使用数据增强的超参数文件。
hyp.Objects365.yaml:专门用于Objects365数据集训练的文件。
hyp.VOC.yaml:专门用于VOC数据集训练的文件。
hyp.scratch-high.yaml:用于重度数据增强模型(v5l,v5x)
hyp.scratch-low.yaml:用于轻度数据增强模型(v5n,v5s)
hyp.scratch-med.yaml:用于中度数据增强模型(v5m)
关于这些超参数,CSDN上都有详细的解释,而且并不是每一个超参数都要进行调整,通常保持其默认值就好了,你只需要针对自己的特殊要求进行相应的调整就好了。接下来我会讲几个我认为比较常用的超参数。
二、常用超参数
1.lr0: 模型训练的初始学习率。学习率决定了模型权重更新的步长。较高的学习率可能导致训练过程中的不稳定性,而较低的学习率可能导致训练速度慢,甚至陷入局部最优解。这个的默认值与后面要讲的优化器有关。
通常使用SGD时为0.01,使用Adam时为0.001。
2.momentum:动量可以理解为参数更新的惯性,类似于小球滚 动的惯性。它通过维护一个动量向量,记录之前梯度方向的加权 平均值,并将其用于参数更新,可以加快训练速度,减小振荡, 提高模型稳定性。
较小的动量可以使更新方向更加平稳。
较大的动量可以加速参数更新。
这个参数也与优化器算法有关,常常会结合使用。
3.obj(置信度损失权重):可以理解为模型预测某个区域包含对象 的置信度。这个值越高,表示模型越确信该区域有对象。这个权 重可以用来平衡样本对于训练的贡献,避免模型偏向于训练样本 数较多的类别,从而提高模型的性能。
4.conf-t(置信度阈值):前面已经介绍了一下。这里再说明一下, 置信度阈值指的是在给定区域包含对象的前提下,模型对于该对 象属于特定类别的置信度。这个值越高,表示模型越确信该对象 属于它所预测的类别。
5.iou_t(交并比阈值):指预测框和真实框之间的交并比。当预测框 和真实框之间的 IoU 大于 iou_t 时,视为检测正确,否则视为检测错误。比如,iuo_t设置为0.5,只有预测框和真实框之间的Iou大 于0.5才会视为正确检测显示出来。
三、超参数实战
本周,主要利用yolo11进行的模型训练开发,故在此总结在这次开发实战中的关于yolo11超参数的总结。注:yolo11没有官方的train.py文件,需要自己去建立,但是这也使我们能够自由地设定超参数。
下面,附上我本次实战的关于超参数的设定的代码部分。
import warnings
from ultralytics import YOLO
warnings.filterwarnings('ignore')
if __name__ == '__main__':
model = YOLO(model=r'yolo11.yaml')
model.load('originalModel/yolo11n.pt') # 加载预训练权重,改进或者做对比实验时候不建议打开,因为用预训练模型整体精度没有很明显的提升
# 训练配置
train_args = {
# 基础配置
'data': r'accident.yaml',
'imgsz': 640,
'epochs': 200,
'batch': 64,
'workers': 8,
'device': '2',
# 优化器设置
'optimizer': 'AdamW', # 保持使用AdamW
'lr0': 0.0005, # 降低初始学习率,提高稳定性
'weight_decay': 0.01, # 降低权重衰减,避免过度正则化
'momentum': 0.937, # 添加动量参数
# 学习率调度相关
'lrf': 0.01, # 最终学习率因子
'warmup_epochs': 3, # 预热轮数
'warmup_momentum': 0.8, # 预热动量
'warmup_bias_lr': 0.1, # 预热偏置学习率
# 损失函数权重
'box': 7.5, # 边界框损失权重
'cls': 0.5, # 分类损失权重(单类别可以设小一些)
'dfl': 1.5, # DFL损失权重
# 数据增强配置
'hsv_h': 0.01,
'hsv_s': 0.4,
'hsv_v': 0.3,
'degrees': 6.0,
'translate': 0.2,
'scale': 0.4,
'shear': 0.1,
'fliplr': 0.5,
'flipud': 0.0,
# 正则化设置
'dropout': 0.05, # Dropout比率
# Mosaic和Mixup设置
'mosaic': 0.8,
'mixup': 0.2,
'close_mosaic': 160,
# 重要:单类别设置
'single_cls': True, # 设置为True,因为只有accident一个类别
# 训练策略
'multi_scale': True, # 开启多尺度训练
'cos_lr': True, # 使用余弦学习率调度
'rect': False, # 矩形训练
# 其他设置
'resume': False,
'project': 'runs/train',
'name': 'exp',
'cache': False,
'amp': True, # 混合精度训练
# 早停相关设置
'patience': 50, # 耐心值:50个epoch
'save_period': 10, # 每10个epoch保存一次
'save': True, # 保存模型
'exist_ok': False, # 不覆盖已有结果
}
# 1. 主训练
model.train(**train_args)
上面的代码,就是我本次对交通事故检测模型的超参数设定部分。
四、优化器
在YOLO系列中,最主要就是两个优化器,分别是SGD和Adam,讲解其优劣势。
SGD(随机梯度下降算法),以其易于实现的简单性和计算成本低而著称。但其最大的缺点是下降速度慢,同时由于其在每次迭代中仅随机使用一个样本计算梯度,这种随机性导致其搜索更新方向不稳定,容易出现局部最优点,同时也容易受到噪声的影响。
Adam优化器,将momentum算法和RMSProp算法(自适应学习率优化算法,通过计算梯度平方的移动平均值,可以动态自适应调节学习率)结合起来使用的一种算法,表现更好,它也是用以解决摆动幅度过大,加快函数的收敛速度;同时可以动态调整每个参数的学习率,实现学习率自适应。但其同时存在着过拟合风险,导致模型泛化能力不强。
关于怎么选择哪个优化器,应根据具体需求来。
如果需要训练比较大规模的数据集,并且希望训练出来的模型泛化能力强,SGD是更合适的选择。虽然其下降速度慢,但其长远表现更佳,其可以有充足的时间收敛到较优解,通常能更好地进行长远优化,其最终结果可以更好地推广,模型泛化能力也更强。
如果需要训练小规模数据集,即希望快速得到结果的场景中,特别是在计算资源有限时,Adam是更合适的选择。因为其收敛速度快,可以在较短的时间内接近最低的损失;同时其自适应学习率避免了手动调参的麻烦;并且其鲁棒性强。但对于大数据集,该算法有可能会有过拟合风险,模型泛化能力弱。
五、模型轻量化
关于模型的轻量化,主要最常用的就是对模型的量化和剪枝。下面我将介绍下关于这两部分的内容。
5.1模型剪枝
5.1.1 剪枝简介
过参数化主要是指在训阶段,在数学上需要进行大量的微分求解,去捕捉数据中的微小的变化信息,一旦完成迭代式的训练之后,网络模型在推理的时候不需要这么多参数,而剪枝算法正是基于过参数化的理论基础提出来的。剪枝算法核心思想就是减少网络模型中的参数量和计算量,同时尽量保证模型的性能不受影响。
5.1.2 剪枝步骤
对模型剪枝有三种常见做法:
1.训练一个模型->对模型进行剪枝->对剪枝后的模型进行微调(最常见);
2.在模型训练过程中进行剪枝->对剪枝后的模型进行微调;
3.进行剪枝->从头训练剪枝后的模型。
剪枝可以进行细粒度剪枝、向量剪枝、核剪枝、滤波器剪枝等不同的剪枝算法。其中很重要的一点是在剪枝之后,对网络模型进行评估,看是否符合要求。剪枝之前需要确定需要剪枝的层,设定一个剪枝阈值或者比例,在具体实现上,通过修改代码,加入一个与参数矩阵尺寸一致的mask矩阵,mask矩阵中只有0和1,它的实际作用是微调网络。
微调是恢复被剪枝操作影响的模型表达能力的必要步骤,结构化剪枝会对原始模型的结构进行调整,因此剪枝后的模型虽然保留了原始模型的参数,但是由于模型结构的改变,模型的表达能力会受到一定程度的影响。具体实现上,在计算的时候先乘以该mask矩阵,mask为1的值将继续训练,并通过反向传播调整梯度,而mask为0的部分因为输出始终为0,则不对后面产生影响。
5.1.3 剪枝分类
2.3.1 结构化剪枝与非结构化剪枝
非结构化剪枝粒度最小,结构化剪枝中的层级、通道级、滤波器级剪枝粒度依次增大。
● 非架构化剪枝主要是对一些独立的权重或者神经元或者神经元的连接进行剪枝,就是随机剪枝,是粒度最小的剪枝。难点是何如确定要剪枝哪些东西,最简单的方法是定义一个阈值,低于这个阈值的权重被减去,高于的被保留。但是有三个主要缺点:1.阈值与稀疏性没有直接联系;2.不同的层应该具有不同的灵敏度;3.这样设置阈值可能会剪掉太多信息,无法恢复原来的精度。还有一种方法是使用一个拼接函数来屏蔽权重,权重值没有剧烈的变化,而且修剪过程可以与再训练过程融合。非结构化剪枝的优点是剪枝算法简单,模型压缩比高。缺点是精度不可控,剪枝后权重矩阵稀疏,没有专用硬件,难以实现压缩和加速效果。
● 结构化剪枝:结构化剪枝是有规律、有顺序的。对神经网络,或者计算图进行剪枝,几个比较经典的就是对layer进行剪枝,对channel进行剪枝、对Filter进行剪枝,剪枝粒度依次增大。优点是大部分算法保留原始卷积结构,不需要专用硬件就可以实现。缺点是剪枝算法比较复杂。
2.3.2 静态剪枝与动态剪枝
静态剪枝在推理之前离线执行所有剪枝步骤,而动态剪枝在运行时执行。
● 静态剪枝在训练后和推理前进行剪枝。在推理过程中,不需要对网络进行额外的剪枝。静态剪枝通过包括三个部分:1.剪枝参数的选择;2.剪枝的方法;3.选择性微调或再训练,提高修剪后的网络的性能,以达到与修剪前网络相当的精度,但可能需要大量的计算时间和能耗。静态剪枝存储成本低,适用于资源有限的边缘设备,但是也存在三个主要问题:1.通道的删除是永久性删除,对于一些较为复杂的输入数据,可能无法达到很好的精度,因为有些通道已经被永久性的剪掉了;2.需要精心设计要剪枝的部分,不然容易造成计算资源的浪费;3.神经元的重要性并不是静态的,而且神经元的重要性很大程度上依赖于输入数据,静态的剪枝很容易降低模型的推理性能。
● 动态剪枝:网络中有一些奇怪的权重,它们在某些迭代中作用不大,但在其他的迭代却很重要。动态剪枝就是通过动态的恢复权重来得到更好的网络性能。动态剪枝在运行时才决定哪些层、通道、过滤器不会参与进一步的活动。动态剪枝可以通过改变输入数据,来克服静态剪枝的限制,从而潜在的减少计算量、宽带和功耗,而且动态剪枝通常不会执行微调或重新训练。与静态剪枝相比动态剪枝能够显著提高卷积神经网络的表达能力,从而在预测精度方面取得更好的性能。同样动态剪枝也存在一些问题:1.之前有方法通过强化学习来实现动态剪枝,但在训练过程中要消耗非常多的运算资源;2.很多动态剪枝的方法都是通过强化学习的方式来实现的,但是“阀门的开关“是不可微的,也就是说,梯度下降法在这里是用不了的;3.存储成本高,不适用于资源有限的边缘设备。
2.3.3 硬剪枝与软剪枝
都是对filter进行剪枝,是结构化剪枝中粒度最大的剪枝。
● 硬剪枝在每个epoch之后会将卷积核直接剪掉,被剪掉的卷积核在下一个epoch中不会出现。这类的剪枝算法通常从模型本身的参数出发,寻找或设计出合适的统计量,来表明连接的重要性。通过对重要性的排序等算法,永久删除部分不重要的连接,保留下来的模型即为剪枝模型。但也存在一些问题:1.模型性能降低;2.依赖预先训练的模型。
● 软剪枝在剪枝后进行训练时,上一个epoch中被剪掉的卷积核在当前epoch训练时仍参与迭代,只是将其参数设置为0,因此那些卷积核不会被直接丢弃。在每轮训练中根据不同的数据,对完整模型进行优化和训练。在每轮之后,为每个加权层计算所有滤波器的L2范数,并将其作为滤波器选择的标准。然后通过将相应的滤波器权重设置为0来修剪所选择的滤波器,随后进行下一轮训练。最后原始的深度神经网络模型被修剪成一个紧凑而有效的模型。软剪枝一般有四个步骤:1.滤波器选择(使用L2范式来评估每个滤波器的重要性,具有较小L2范式的滤波器的卷积结果会导致相对较低的激活值,从而对网络模型的最终预测结果有较小的影响。所以这种具有较小L2范式的滤波器将更容易被剪枝掉);2.滤波器剪枝(将所选滤波器的值设置为0,这可以暂时消除它们对网络输出的影响,然而在接下来的训练阶段这些滤波器仍然可以被更新,保持模型的高性能。在滤波器剪枝的步骤中,可以同时修剪所有加权层。此外,要对所有加权层使用相同的剪枝率);3.重建(在剪枝步骤之后,再训练一轮来重构修剪后的滤波器,修剪滤波器通过反向传播被更新为非零。这样经过软剪枝的模型可以具有与原始模型相当的性能);4.获得紧凑模型(重复以上三个步骤,党模型收敛后可以得到一个包含许多0滤波器的稀疏模型,一个0滤波器对应一个特征图,对应于那些0滤波器的特征图,在推理过程中将总是0,移除这些滤波器以及相应的特征图不会有任何影响)。
5.2 模型量化
5.2.1 量化简介
数字精度(如32位浮点数、16位浮点数或8位浮点数或8位整数),所能表示的范围不同。不同的数字精度会影响模型大小和推理时间,范围越大,精度越高,模型越大,推理时间越长。
卷积神经网络特点:参数量大,计算量大,内存占用多,精度高。
模型量化就是把高位宽(float32)表示的权值或者激活值用较低位宽来近似表示(int8,int4…)在数值上的体现就是将连续的值离散化。模型量化,降低精度以减少模型尺寸,会存在一定的损失,但有时候需要快速的做输出,并不一定要那么精确。
5.2.2 量化方法
早期量化,将input和weight进行量化,再卷积得到输出结果。输出结果在反向传播传到量化部分时,因为量化是一个不可微分的操作,需要使用直通估计器解决。但是每一层的output受量化的weight和input的影响产生误差,这个output作为下一层的input也被量化从浮点域转换到定点域,继续影响下一层的概率分布。如此下去导致整个网络的输出和原先浮点输出相比差距极大。但是反向传播参数更新的时候,依然在浮点权重上更新,对于复杂网络优化难度极大。
Google在2018年在CVPR上提出一种新范式,训练时将input和weight的float值量化为int值,再反量化为float值,再卷积得到输出结果,这样会引入量化误差,在训练时会自动调优这个量化误差,训练效果会很好。推理时将input和weight的float值量化到int,再卷积得到结果output,注意需要对output进行移位定点运算,再与下一层进行计算。
BN折叠量化,BN层折叠到卷积层,折叠进去之后也可以量化。
ReLU折叠量化,也可以折叠,直接进行量化。
Add量化,与训练代码无关,所以可以直接在推理框架里量化。
Concat量化等。
至此,本期内容就到此为止了。由于我大部分内容是照搬CSDN上的内容,故在此一定要附上版权声明,保护版权,人人有责。
———————————————————————————————————————————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/cjnn0710/article/details/141003598