以前会用mmdetection做些微调模型结构的实验,配置文件的方式用不习惯~。
v8出来后提供了另一种模型结构改进方法,个人觉得比mmdetection要好用些,从模型层面讲,yolov8本身已经是非常优秀的模型,但yolov8提供了一种任意修改模型结构的“方法论”,这才是最有价值的地方。
最近想以华为的gold-yolo(主要是对neck层的修改)替换yolov8原版neck层,看效果是否会有更好的提升。趁此几乎,分享下yolov8如何改进模型,以及gold-yolo替换neck层的方法。
一 配置文件说明
配置文件与网络结构对应图,有好多文章解读了yolov8网络结构,这里不再多说了。
二 模块类型分析
所有模块改进都发生在task.py的函数parse_model中。
所有的模块可分为以下几类:
① __init__模块有参数,且该模块是卷积类型模块,那么参数为输入通道数,输出通道数+其他,这种类型的模块可归为一类,可用同一个模式处理。
② __init__模块没参数,或没有__init__模块,所有处理都在forward函数中进行,可归为一类,也可用同一的模式处理。
③ 有类似resnet结构的残差模块重复数量,需要在①的基础上再额外添加重复数量。
④ 跳出三界外,不再五行中的奇葩模块,多为定制化的模块,要单独处理,比如我要进行的gold改进就有这样情况。Inject是注入模块,需要索引值global_index指定使用哪个分发特征。表面看属于第①类, 但这个模块要在使用时才根据global_index指定输入指定输入通道数, 明显不符合上面说的三种情况。
ps: 这个模块的入参处理其实也可以写在Inject内部, 用①的统一方法处理,做法是输入通道可以接受列表, 用global_index去索引使用输入通道列表的哪一个, 如本文第三部分代码中,有对输入通道列表的改写方法. 这里演示特殊模块的处理流程, 没有做进一步优化!
⑤ head层,是模型的最后一部分,合并neck层输出结果,因此backbone与neck的改动也会影响head层,因为这部分输出要对齐neck层的输出索引位置。对head层的替换会涉及代码中很多位置的修改。
已有的,与改进的模块都可归为以上5类中,下面从代码中解析
三 parse_model模块解析
根据以上模块类型分析,我构建以下4个列表存储不同类型模块,将它们添加到不同处理分支中。
add_block: 对应模块类型①
forbid_insert: 加入这个列表的模块,不在参数中插入单元重复数量的参数,例如,gold-yolo中neck部分定制化的模块只使用一次。
add_concat:类似concat没有参数的模块
add_detect: 对检测头的魔改模块全都加入这里。
# 对C1,C3,C2f这样的模块封装, 输入输出+其他参数
add_block = [C2f_ODConv, CSPStage, C2f_DLKA, ASCPA,
# gold-yolo
Low_IFM, Low_LAF, SimConv, RepBlock,
]
# 禁止插入重复次数的参数, 如gold的各个模块
forbid_insert = [Low_IFM, Low_LAF, SimConv]
# 与concat类似,初始化不需要参数, forward接受tensor然后通道维度合并在一起
add_concat = [Low_FAM, High_FAM, High_LAF]
# 最后的检测头魔改
add_detect = [Detect_AFPN3, Detect_FASFF, RepHead]
以上模块的添加位置如下,其中AIFI就是不能用统一方法处理的定制模块,对模块的输入参数args处理与前面都不同:
对head层的替换:
还有以下地方要修改:
- 把当前模块注册为检测任务,该函数有两处需要修改。注意这里会把模块名全部转为小写再判断:
- 在task.py的BaseModel类中:
- 在task.py的DetectionModel中
四 总结
根据模块类型,将其添加到对应位置,清晰明了。通过此方法可以快速搭建新模型架构。接下来只需填充配置文件及对应参数就好。
具体实施过程,参考下一篇将gold引入yolov8的文章。
https://editor.csdn.net/md?articleId=137601611