2. 数据准备与增强别一上来就莽模型进入正题前先说说数据的事。车道线检测挑战赛给的数据集虽然标注得比较规整但跟真实路上跑的场景还是有差距的。我拿到数据后第一件事不是看模型而是先把数据翻了个底朝天——类别平衡、光照分布、遮挡情况、Cul-de-sac死胡同占比这些直接决定了后面训练策略怎么定。2.1 监督标签如何处理Apollo挑战赛的车道线标注格式跟TuSimple不同不是简单给一条折线而是标注了车道线的语义类别。比如实线、虚线、双黄线、路沿等类别每个类别在后续后处理里的重要性完全不一样。我之前第一版模型把所有类别笼统一视同仁地做多分类训练结果虚线跟实线互相吃两类在分割图边缘都有点糊。后来把标签拆开看统计才发现数据集中实线的像素量远大于虚线和路沿如果不做类别重加权小类别的IoU基本就是被压着打。所以标签预处理环节我干了两件事按类别统计像素占比计算权重后写入损失函数而不是用默认的CrossEntropyLoss。对原始标注做了形态学后处理把单像素宽度的线先膨胀到5到7像素降低分割头对正样本的拟合难度。这一步不是可选项是刚需。2.2 在线增强策略比想象中重要车道线检测对几何变换非常敏感。我的做法是训练时做随机旋转范围±5度、随机水平翻转、随机亮度和对比度抖动以及轻度透视变换。这里有个关键细节——所有几何变化的参数必须作用在图像和标注的掩膜上而且要保证两者用同一套变换矩阵否则数据集内部就出现了“图像与标签对不上”的脏数据。我调了很久发现一个规律翻转数据的比例不能太高车道线本身有左右语义比如实线和虚线在左右车道相对位置不同过度翻转反而让模型学到“对称的错”在验证集上左右IoU偏差会变大。实测翻转概率设在0.3到0.4之间效果比较稳。2.3 关于数据量焦虑挑战赛给的数据量说多不多说少也不少横向对比同类型的车道线公开数据集大概处在一个中等偏上的量级。但注意这个赛题的数据分布跟公开数据集明显不同直接拿预训练权重然后微调往往比从头训练效果更差。原因在于特征分布偏移——公开数据集里单目相机装的位置、焦距和视角跟赛题数据不一样直接迁移等于让模型戴了一副度数不对的眼镜。我在这个踩了坑之后采用了分段解冻的方式前10个epoch冻结backbone的一部分只训练分割头和解码器让模型先适配数据集的特征分布之后再全部解冻小学习率全局微调。这样既保住了预训练权重里低层特征的泛化能力又让高层特征充分拟合当前数据domain。3. 模型选型与结构设计主打分割还是检测车道线检测在Apollo挑战赛里最常见的做法有两派一种是基于语义分割的端到端方案代表作就是SCNN、UFLD这类另一种是传统视觉处理加曲线拟合类似颜色分割加滑动窗口。以我多次参赛的实际体感来说挑战赛这种数据规模下基于分割的方案优势非常明显稳定性和精度全面占优。纯传统视觉方案受光照、阴影、路面纹理干扰太大一旦遇到树荫、车影、裂缝路面曲线拟合直接崩盘。3.1 为什么选分割路线车道线本质上是细长结构在图像里只占很小一部分像素。基于目标检测的bbox思路天生不适合车道线——一辆车可以框出来但一条从左下蜿蜒到右上的车道线没法用一个四边形包住。分割路线的核心优势在于每个像素点都参与分类对线形结构天然友好而且后处理时能直接拿到逐像素置信度方便做车道线实例聚类和曲线拟合。3.2 网络结构怎么搭我最后采用的方案是一个编码器-解码器结构编码器部分用了ResNet变体作骨干网络下采样倍数控制得比较保守避免特征分辨率损失过多导致细线断裂。解码器部分带了一个轻量级的ASPP模块用来扩大感受野的同时保持边缘细节。训练输入分辨率设置为288×800这个分辨率在精度和显存占用之间达成了平衡。太高了显存吃紧且训练速度慢太低了远端车道线直接糊成一团看不出类别。输出方面我让模型输出了三个head一个负责车道线语义分割即每个像素属于车道线的概率一个负责实例embedding每个像素的实例编码向量一个分支直接回归车道线起始点的关键点坐标3.3 损失函数组合分割分支我用了加权CrossEntropy加Dice Loss的组合。Dice Loss对前景占比严重不平衡的场景非常有效它直接优化“区域重叠度”避免模型把所有像素都预测成背景。实例embedding分支用判别损失同类像素拉近、异类像素推远。关键点回归分支用平滑L1损失它比MSE对离群点更鲁棒远端车道线的关键点偶尔有噪声标注平滑L1能把影响压制住。三路损失的权重我分别设成1.0、0.8、0.5这个比例不是拍脑袋定的而是做了一轮小规模消融实验比出来的效果分割主任务的权重必须最大辅助任务只是帮忙学特征。4. 训练策略与调参实录从翻车到上分如果你以为把模型架构定好了就能直接开训那就太天真了。这套模型我第一次训的时候前20个epoch损失下降非常快正当我心里暗喜的时候验证集的分割IoU反而开始往下走了——典型的过拟合信号。数据量不够的时候模型的容量越大越容易背题而不是真正学会表达。4.1 训练超参数如何定我经历了三轮完整的调参循环最终稳定的方案是这样的优化器用AdamW初始学习率5e-4权重衰减1e-4batch size在单卡上压到12。学习率用余弦退火策略从5e-4降到1e-5周期设30个epoch。前5个epoch做线性warmup把学习率从1e-5慢慢抬到5e-4防止模型一上来就被不稳定的梯度冲乱预训练特征。为什么选AdamW而不是SGD车道线这样的稠密预测任务里AdamW对稀疏梯度和噪声标注更宽容而且权重衰减跟Adam的解耦方式让正则化的调节更直接。但从收敛精度的上限来看后期切换到SGD往往还能再捞一点提升我在第25个epoch左右从AdamW切换成SGD学习率对应降到1e-4最后验证集IoU果然又涨了约1.2个百分点。4.2 评估指标导向的调参法则挑战赛的排名指标通常是mIoU偶尔还会综合考虑车道线点的距离误差。我在训练过程中持续监控验证集的IoU还不够必须同时监控按类别拆分的IoU曲线。很典型的例子就是路沿类别的IoU早早到达60%之后就开始徘徊但虚线类别的IoU一直在40%以下爬不动。这个差距说明模型对虚线类别的特征表达不够可能需要在损失函数里把虚线类别的权重再抬高或者单独给虚线类别的分支加一个辅助监督。我的做法是把虚线类别的损失权重调到其他类别的2倍同时额外在decode阶段的浅层特征上加了一个辅助分割监督相当于让低层特征早一点接触到线形结构的信息。这一步搞完虚线类别的IoU又涨了5个点左右直接把整体分数带飞。4.3 训练过程中的隐藏坑有一点很低调但影响很大——Batch Normalization在batch size偏小的时候误差会被放大。我的单卡batch才12BN的统计量估算很不稳定直接导致验证集结果像心电图一样上下猛跳。解决办法是把BN换成GroupNorm或者保持BN但增加梯度累积步骤让等效batch size涨到24以上。实测把batch size改成梯度累积到24后验证集曲线立刻平滑了很多最终精度也明显提升。此外我还在训练过程中每个epoch保存了checkpoint取验证集最优模型的权重做最终评测而不是用最后一个epoch的权重。后来复盘发现最后一个epoch的模型因为学习率太低权重陷入局部盆地指标不仅没有提升反而降了0.8个百分点。最优checkpoint在早于最后epoch约4个step的位置出现这就纯属经验了。5. 推理优化与后处理把模型的最后一丝力气榨干挑战赛的推理阶段同样需要精细操作很多人以为模型训完就完事了其实后处理直接决定了你最终的Leaderboard排名能不能再上一个档位。5.1 两段式推理法我的做法是先把测试图像等比缩放至网络输入大小完成一次前向推理得到三个分支各自的分割概率图。然后关键点来了——把概率图上采样回原始尺寸后再对下半部分的感兴趣区域做一次小范围的二次推理用拼接信息把远端车道线的细节补回来。虽然这个操作相当于做了两遍前向但推理时间只增加了约25%精度提升接近1个百分点性价比非常高。5.2 车道线实例聚类分割分支的输出是一张概率图每个像素只知道“是车道线”还是“不是车道线”但不知道具体属于哪一条线。这个实例聚类我用的是基于embedding向量的MeanShift聚类方法好处是不用预设车道线数量靠曼哈顿距离和带宽参数自然聚类。聚类完成后用最小二乘法对每条线做三次多项式拟合得到平滑的车道线方程。拟合时权重按像素置信度来定置信度越高的像素点对拟合结果影响越大。实测下来这个加权拟合比纯最小二乘更能抵抗远处噪点的影响拟合出的曲线不会出现明显甩尾现象。5.3 推理期的一点点防御性技巧测试集里偶尔会有极端照度或者过曝现象单次推理的分割概率图会在大片区域出现“幻影车道线”——本来没有线的地方被模型脑补出了线。考虑到这种情况我加了一个全局背景抑制策略把分割概率图里低于阈值0.35的区域直接清零同时只保留面积大于某个最小像素数的连通域面积太小的连通域直接删除。这样可以滤掉大多数由于纹理杂乱引起的小面积误判。6. 常见问题与踩坑记录含泪总结这部分记录了我在训练和推理过程中真实遇到的问题每个问题都配了最终有效的解决思路没有废话。6.1 训练Loss下降但验证集指标不动这是最折磨人的问题。我印象最深的一次训练Loss从0.4降到了0.15但验证IoU始终徘徊在52%上下。命令行显示一切都正常没有任何NaN或者异常波动但就是上不去。最后排查下来是解码器部分使用了BatchNorm输出特征被强制按通道归一化导致大量语义相关的结构信息被压缩。换成GroupNorm后验证集指标马上有了反应。经验是当训练和验证严重脱节时先检查归一化层再检查dropout最后检查数据增强的强度。6.2 车道线断线问题分割结果里经常出现某个车道线的中间段落消失短的两段线甚至五六段线。这主要是因为感受野太小模型只关注局部上下文没有把线看在“眼里”。我采用了一个技巧在训练时随机擦除部分车道线区域迫使模型学会利用周围上下文补全被遮挡的线。这在语义分割里叫CutOut或Random Erasing作用在标签掩膜上相当于给模型做了一个“线中断抗性训练”。第二个原因是下采样倍数太高。把Encoder的下采样从32倍调整到8倍后断线率明显下降了。代价是计算量上升但质量换速度在比赛中完全值得。6.3 提交格式错误很多次成绩直接作废都是因为提交格式没对上。比赛要求按GeoJSON格式提交车道线的坐标点序列而且每条线要有唯一的类别属性。我第一次提交的时候点顺序和实际行驶方向是反的被系统判定为方向错误所有的线全部判错。后来我写了一个离线检查脚本在提交前先可视化一遍GeoJSON的重叠效果确保每条线的点是从左到右或者从下到上的一致性这个问题就彻底解决了。6.4 多尺度推理争议见过有些选手分享说测试时把图像缩放到多个尺度分别推理然后取平均结果能提升IoU。我的实测结论是这个方法对提升大目标检测有帮助但对车道线这种细长结构几乎没有正收益甚至可能因为不同尺度下线的宽度不一致导致融合后的概率图直接“糊”掉。还是老老实实两次推理原始尺寸下半部分二次推理最稳。6.5 GPU显存溢出的应急方案训练中经常遇到显存溢出特别是当我把输入分辨率抬到512×1280时。这种时候别急着换卡先把batch size降到4同时打开混合精度训练。混合精度在1000系列之后的显卡上都会有显著的显存节约效果而且对分割任务的精度影响微乎其微。我实测下来精度的损失大概在0.2个百分点以内完全可以接受。6.6 数据标注错误的容错赛题数据集里偶发存在标注错位的情况比如虚线标成了实线或者某条车道线的局部到标签突然断开后再接上。我的处理办法是在训练时把标签矩阵做一个概率化的噪声注入也就是以很小的概率把某个像素的标签随机改为其他类别。这样可以模拟标注噪声提升模型的抗错能力。看似不优雅但确实有效。7. 最终效果与我的个人体会这套方案最终跑下来的成绩在我的参赛时间段里排在挑战赛榜单的头部区域比纯基线方案大概高出了接近7个百分点的mIoU。整体来看车道线检测挑战赛的决胜点根本不在模型结构有多大创新而在于数据处理的细致程度、训练策略的时序安排以及后处理阶段对细长结构的专门优化。我个人的体会是这条赛道上真正拉开差距的不是谁的网络更深而是谁更能抠细节。比如多训练几轮、调整一次损失权重、改一个后处理参数、加一次二次推理每一分提升都不起眼但叠加起来就是质的飞跃。如果后面再扩展值得尝试的方向包括把模型输出直接连到下游规划模块里去验证闭环效果以及在embedding分支上再接一个轻量级的图结构建模车道线之间的平行关系。但这些都算锦上添花先把前面这些实打实的训练和后处理经验吃透你的榜单排名绝对不会差。