ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RetinaNet自定义数据训练模型准备指南:从backbone到权重加载

RetinaNet自定义数据训练模型准备指南:从backbone到权重加载 如果把系列第1篇比作备菜那模型准备这一步就是开火前确认锅具、调料和火候。数据已经按照规范格式归置好了接下来要做的不是把训练命令一股脑丢进终端而是先把模型层面的变量逐个捋清楚。用RetinaNet训练自己的数据真正能稳定跑起来之前backbone选型、预训练权重、环境依赖、类别和anchor配置这几样东西都得先对齐。下面这套流程是我在几个实际项目里反复用过的适合已经跑通基础环境、正准备调整模型适配自定义数据的同学。不管你做的是工业缺陷检测、遥感目标识别还是交通场景理解模型准备阶段的思路基本都是通用的。我按实际操作顺序来讲尽量把每个选择背后的理由也交代清楚这样你照着做的时候就不只知其然还能知道为什么。1. 先想清楚模型从哪来backbone选型与预训练权重的取舍1.1 backbone选型的实际考量不是越大越好RetinaNet的网络骨架backbone决定了它从图像中提取特征的能力上限。绝大多数开源实现里默认用的是ResNet50这也是COCO上最常见的基线配置。我第一次训练自己的数据时用的就是ResNet50加FPN的组合因为手里的显卡不算顶级ResNet101虽然也能跑但每个epoch的耗时明显变长整个试错节奏一下子被拖慢了。关于ResNet50和ResNet101的取舍用一张小表对比最直观对比项ResNet50ResNet101特征表达能力中等更强显存占用较低更高训练耗时基准通常多30%以上适合场景数据量数万以内、追求快速迭代数据量大、算力充足这里想记住一句话在自有私有数据规模没那么大的情况下大backbone带来的精度增益往往没有想象中那么高。我前后在几个项目里验证过两万张以下的数据集ResNet50和ResNet101的最终mAP差距常常在一个百分点以内但训练时间却差了三分之一。backbone选型本质上是数据量、算力和迭代效率之间的平衡别被越大越强带偏。1.2 预训练权重下载与文件校验模型准备最核心的动作是把预训练权重拉到本地。这里有两类权重来源一是backbone在ImageNet上做图像分类任务训练出的通用特征权重二是RetinaNet整个模型在COCO数据集上训练好的完整权重。这两种我都用过。如果你的目标数据集和COCO的类别分布差异比较大比如工业场景里的划痕、脏污检测我倾向于只加载ImageNet的backbone权重让模型从通用特征开始学习而不携带COCO里面的类别偏好。如果你的数据属于常规物体范畴和COCO类别有部分重叠那直接用RetinaNet在COCO上训练好的完整权重做微调收敛速度会快很多。无论从哪个渠道下载拿到权重文件后的第一件事都是校验完整性。权重体积动辄上百兆下载过程中损坏的概率并不低。很多人忽略这点结果训练到某个step时loss突然变成NaN翻遍代码也找不到原因最后才发现是权重文件本身就有问题。校验方法不复杂比对一下官方给出的sha256值几秒钟的事一定要做。1.3 权重与代码版本的匹配问题权重和代码版本不匹配是模型准备阶段最常见也最隐蔽的一个坑。keras-retinanet这个库从TF1时代过渡到TF2时代时权重保存格式和层命名有一些变化。老版本代码加载新权重报错信息一般是无法匹配层名称这种还算明显但有时候新版代码加载老权重也能加载上一部分层只是某些位置的参数被随机初始化了这种情况下训练不会直接报错但效果会很差。我在实践中习惯这么做项目一开始就把代码仓库固定到一个具体的release版本然后按对应版本去下载权重。不要图省事随便拿一个权重文件就开跑。版本对齐这件事多花十分钟确认能避免训练到一半才发现问题。模型准备阶段的工作量其实很大程度不在配置本身而在对齐这些容易忽略的细节。2. 搭建训练环境代码库版本、依赖库与显卡三者的配合2.1 选择keras-retinanet还是mmdetection搜RetinaNet训练自己的数据会找到两个比较主流的方案fizyr/keras-retinanet和OpenMMLab的mmdetection。两者都是成熟的实现都能完成训练但对第一次跑自定义数据的用户来说上手路径差别还挺大的。keras-retinanet的做法是把训练封装成命令行工具参数基本都写在启动命令里配置直接对新手友好。缺点是工程化程度相对低实验管理、结果对比这些都要自己来。mmdetection则把所有配置集中在配置文件里目录结构标准换backbone、调anchor都是在配置里改几行的事但初学时要理解这一整套配置体系还是有一定门槛的。我的建议是第一次跑自定义数据优先用keras-retinanet把流程走通先理解RetinaNet的输入输出和训练机制等需要做大量对比实验的时候再迁移到mmdetection去享受更成熟的工程化能力。不要一上来就在框架选型上纠结太久两个都能完成同一件事差别更多在哪种组织方式对你更顺手。2.2 环境安装里最容易忽略的细节keras-retinanet的TF2分支依赖TensorFlow系列库通常把官方requirements.txt里的依赖装好就够用了。但有一个步骤很关键代码clone下来后需要执行一下仓库里的setup.py build_ext --inplace。这一步会编译一些C扩展组件如果跳过了后面在解析标注数据的时候可能会遇到一些奇怪的报错排查起来很费劲。另外依赖库版本不要随手升级。TensorFlow、NumPy这类库的版本耦合很紧升级一个经常会牵连出一串兼容性问题。我的经验是严格按照项目要求的版本范围来装最好用conda或virtualenv隔出一个独立环境。环境一旦乱掉排查所花的时间远远超过重新建一个环境这个账一定要算清楚。2.3 GPU和显存对模型准备的影响RetinaNet本身带着FPN和两个预测子网络显存占用比普通单阶段网络要高不少。模型准备阶段你很可能遇到训练脚本一启动就被OOM杀掉的场景。这时候第一反应不应该是换显卡而是先降低训练配置。把batch size从默认值降到2或者把图像resize的尺寸调小一档往往就能跑起来。输入尺寸这块变化很敏感把长边从1333缩到1000左右显存占用可能会下降超过一半。等到整个流程稳定之后再逐步把输入尺寸调回去用这个办法去试探当前硬件条件下精度和速度的平衡点。显存这件事与其在训练途中反复被坑不如在模型准备阶段就想明白。3. 把模型改成能看懂你的数据核心配置项逐一修改3.1 类别文件与num_classes的对应关系RetinaNet不会自动知道你的数据集有哪些类别需要显式告诉它。用keras-retinanet的csv数据格式时要准备一个classes.csv文件每一行写一个类别名行号就对应类别索引。这里特别提醒一下类别ID必须从0开始连续编号中间不能有空缺。如果从1开始编号或者中间跳了序号训练时大概率会出现类别id越界这类问题。另外类别名尽量用英文小写避免中文和特殊字符。我踩过中文类别名的坑标注软件里看着没问题生成标签文件时编码就乱了。后来的做法是模型文件里统一用英文类别名单独再建一个映射表去做中文展示两边分开互不干扰。3.2 anchor参数先统计你的数据分布再调整RetinaNet的anchor默认配置是针对COCO这类常规检测目标设计的包含多种尺度以及三种长宽比。如果训练的目标尺寸分布比较常规默认配置完全可以直接用。但我建议在模型准备阶段顺手统计一下数据集中所有标注框的宽高分布。做法很简单把标注文件读进来计算每个框的宽高比画一张分布图看看。如果发现大量目标的长宽比集中在1:3或3:1这种比较极端的比例而默认anchor只有1:1、1:2、2:1三种比例就要考虑扩展anchor的比例范围。这个问题我在一个条形码检测项目里遇到过目标全是细长条默认anchor怎么train都检测不到后来对着分布图调整了比例参数效果立竿见影。要不要动anchor判断依据必须来自你的数据本身而不是拍脑袋。3.3 训练超参数的初始值建议模型准备阶段不需要把超参数调到最优重点是让训练能够稳定启动。一个稳妥的初始方案可以是batch size按显存选2或4学习率从1e-5这个量级起步epoch先设50然后拿一个小数据子集跑几个epoch看loss曲线是否正常下降。用预训练权重做微调的时候学习率一定不要从大值开始。很多人会把其他项目里的1e-3直接抄过来结果前几个epoch的loss直接爆炸还误以为是代码写得有问题。微调场景我从1e-4以下起步如果loss不降再适当往上调。超参数本来就该跟着loss曲线一点点试出来模型准备阶段先把调试的流程跑顺后面优化起来才有效率。3.4 验证集与评估回调的设置训练不能没有验证集。模型准备阶段就要把数据划分好并配置验证评估回调。划分验证集有个容易被忽略的原则图片分布应该和训练集相似但图片内容不能有重叠。如果训练集和验证集来自同一段视频的连续帧验证结果会出现精度虚高的情况看起来模型很厉害实际泛化能力很差。评估回调会在训练过程中周期性计算验证集上的mAP但完整评估很耗时。我的做法是每隔几个epoch做一次完整评估不要每个epoch都算不然训练会被拖得很慢。模型准备阶段不用太关心验证指标具体是多少先确认训练链路稳定跑通指标好不好是后面调参阶段的事。4. 让模型真正跑起来的自检环节从加载权重到跑通一个step4.1 权重加载的异常排查模型准备阶段反复出现的报错大多集中在加载权重的时候。你改了类别数但预训练权重的分类输出层还是按COCO的80类构建的加载时最后一层的维度对不上。这种报错是正常的不用慌张。keras-retinanet的train.py会为新的类别数自动重建预测头加载权重时跳过那些维度不匹配的层只加载backbone和FPN部分的参数。这里需要注意不要手动尝试让最后一层强制加载旧权重那样会破坏新预测头的随机初始化状态。真正需要留意的是除了最后一层之外其他层有没有维度对不上的情况如果有多半是代码版本和权重版本不匹配造成的。4.2 通过单batch前向推理验证模型可用性训练正式启动之前我习惯先写一段简单的脚本读入一张训练图片做预处理后送进模型做一次单batch前向推理。这个过程很快如果几十秒内没有报错基本就能确认数据加载、模型结构和权重这三部分已经对齐了。检查的时候看两个关键输出维度分类分支的输出应该是(batch, anchor总数, 类别数)回归分支的输出应该是(batch, anchor总数, 4)。只要这两个数字符合预期模型结构上就没有大问题。这个验证步骤虽然简单但能帮你把训练脚本启动后第一分钟才可能暴露的错误提前消化掉。我每次新建项目都会做这一步相当于正式开工前的试运行成本极低收益却很直接。4.3 从头训练与微调的启动策略差异模型准备阶段还要想清楚一个问题你是从零开始训练整个网络还是在预训练权重的基础上做微调。这两种策略下后续训练的很多决策都会不一样。从零训练时backbone没有预训练特征收敛速度会很慢学习率可以设得相对高一些。微调场景正好相反学习率要低必要时还要考虑冻结部分层。如果数据集规模不大我一般只让FPN以上的层参与微调backbone保持预训练阶段已经学到的通用特征提取能力。这样既节省显存也能有效降低过拟合风险。这个策略最好在模型准备阶段就定下来因为它决定了模型结构里哪些层要冻结或者训练脚本里该传哪些参数临到训练了再改就容易被各种问题缠住。5. 预训练权重之外模型备份、日志记录与输入尺寸的预留5.1 模型权重与类别文件统一归档模型准备阶段生成的所有东西包括预训练权重、类别文件、训练脚本和配置文件都应该放进一个结构清晰的项目目录里统一管理。我习惯按weights、configs、logs三个目录来划分分别放权重、配置文件和训练日志。训练脚本里所有路径都用相对路径保证整个项目文件夹拷贝到另一台机器上就能直接运行不用改来改去。这个习惯是被一次踩坑经历逼出来的。那次训练到第40个epoch时发现某个配置参数写错了需要回退到第10个epoch的检查点重新训练结果因为之前的权重和配置文件放得乱七八糟花了很长时间才把对应的检查点找齐。从那以后每个训练实验启动前我都会把目录建好实验名带上日期检查点命名也带上epoch数。这些事情单独看都很小但在关键时刻能省下大把时间。5.2 日志记录与训练过程可视化训练日志是模型准备阶段容易被忽略但后期价值很大的东西。从第一个实验开始就应该记录每个epoch的loss、验证mAP和训练耗时。有了这些数据后面判断一个新改动到底是好是坏才有据可依。keras-retinanet训练时会持续输出loss信息也可以把TensorBoard接进来把loss曲线和验证指标可视化。关键一条日志输出路径要独立不要多个实验共用同一个路径否则日志和检查点会互相覆盖。我给每次实验起一个带日期的名字不同实验之间路径严格分开这样回看历史实验时一目了然。这些配置放在模型准备阶段顺手就做了后面对比实验时的收益远比投入大。5.3 为后续迭代预留扩展空间最后想聊一个偏软的问题模型准备阶段最好提前想一想后续迭代里你可能会改哪些参数。比如你预测自己之后会测试不同的输入分辨率那么图像resize的尺寸参数就应该单独抽出来放到配置里而不是散落在代码的好几个地方。再比如数据集后续可能还会新增类别那么classes.csv的生成尽量做成自动扫描标注文件的脚本而不是每次手工维护一份。这种预留不是过度设计而是让可能变化的参数统一收敛在一个地方。训练时只改配置、不动代码会让整个迭代过程轻松很多。等真正开始跑大批量实验的时候你会感谢当初在模型准备阶段做的那些不起眼的安排它们不知不觉就把后面容易出错的路都提前填平了。
返回列表