资讯动态

基于NAS-FCOS的拥挤路段车辆检测系统实践

发布时间:2026/10/22 9:42:00 来源:尧图企业网站定制
1. 为什么拥挤路段的车辆检测总是翻车做智慧交通项目的人应该都有同感单独拍一辆车、视野空旷的路口模型跑得漂漂亮亮mAP高得吓人一上真实拥堵路口就原形毕露——近处的大巴车把后面轿车挡掉一半左转待转区十几辆车挤在一起远处还有一片模糊的车尾在红绿灯下排成长队。这个时候你手里的经典检测器大概率会犯三种毛病漏检、误检、同一辆车出好几个框。我最早用的是基于锚点的两阶段检测方案在拥挤场景下anchor尺寸和长宽比怎么调都不对。后来换到无锚点路线才算把方向走对了。这篇要分享的是我基于NAS-FCOS这条路子做的一套完整系统R50-Caffe-FPN-NASHead-GN-Head专门针对拥挤路段车流量统计和违章抓拍的车辆检测模型。标题里那一长串后缀不是随便堆的每一个缩写都对应一个具体的工程决策后文我会逐个拆开讲。这套系统能做什么简单说就是给一段监控视频输入它能实时把画面里每辆车框出来就算车跟车挨得只剩一条缝、车头车尾混在一起、光照从逆光突然切换成顺光框的稳定性和类别判定都不会大幅垮掉。适合三类人来参考一是做智慧交通算法落地的工程师二是想从有锚点检测器迁移到无锚点路线的研究者三是在 Caffe 框架上做老模型升级、但不想换 TensorFlow 或 PyTorch 的技术团队。先说个结论拥挤路段场景真正决定模型上限的不是 backbone 多强而是 neck 和 head 对“重叠目标”和“小目标”的响应方式。NAS-FCOS 恰好在这两个环节做了很对路的改进。2. NAS-FCOS 的技术拆解它到底改了哪些东西2.1 FCOS 为什么适合拥挤场景FCOS 属于无锚点检测器它的核心思想是“让每个像素直接预测它属于哪个目标的哪一部分”而不是像传统方法那样在图上铺一堆预设框再去匹配。这个设计天然对拥挤场景友好原因是锚点框方案在密集目标下会产生大量重复匹配而且交并比过高的锚点框在训练时会被直接判定为负样本导致真正有遮挡的车辆反而学不到特征。FCOS 还做了个很关键的事情对每个输出特征图上的位置只负责预测距离该位置最近的框。当你站在路侧高点去看拥堵的车流时车身之间的遮挡是必然的但每个像素距离“可见的那辆车中心”不一定都很远这样即使另一半车被挡了只要这部分可见的车身像素足够多模型仍然能给出一个完整的回归框。另外一个容易被忽略的点是 FCOS 不需要锚点长宽比先验。拥挤车流里公交车、SUV、轿车、电三轮如果是混合交通长宽比五花八门锚点框方案在前期设计上就很难照顾周全。2.2 NAS 搜索出来的 Head 到底搜了什么NAS-FCOS 的核心贡献是把原来 FCOS 的检测头替换成了通过神经架构搜索找到的结构。很多人一看到 NAS 就觉得“搜出来的网络不可控、落地难”实际用在 FCOS 这个场景里比想象中要收敛得多。NAS 在这里搜索的空间不是整个 backbone而是集中在 head 部分在 P3、P4、P5、P6、P7 这五层特征层之间搜索到底哪些层之间需要跨层连接每一层内部应该堆多少个卷积卷积核大小用 3x3 还是混合比例。换句话说把原来“每一层都独立做分类和回归”的并联结构换成了一个更复杂的多路径连接结构。从实测效果看NAS 搜出来的结构相当于在 FPN 各层之间加了几条“跳到中层再接回来”的捷径。比如 P5 层的结果不只是从 P4 上采样来的它还会额外拿一份从 P3 那边送过来的更细节的信息。这相当于给高层特征图补充了下层的空间细节对拥挤路段的近距离车辆和远距离排队车辆同时有效。2.3 GN-Head 为什么替换 BNNAS-FCOS 原版结构在 head 里用的归一化方式在不同开源复现里并不统一有的用 BN有的用 GN。我在这个项目里把所有检测头的归一化全部换成了 GroupNorm这背后有个很实际的原因拥挤路段监控画面的分辨率通常很高喂进网络的输入尺寸相应也会拉大显卡显存根本撑不住足够大的 batch size。在 batch size 只有 2 或者 4 的情况下BatchNorm 的均值方差估计会非常不稳。换到 GroupNorm 之后归一化完全不依赖 batch 里的其他样本每组特征内部自己归一化训练稳定性好很多。这一点对于那些准备用单卡或者双卡跑训练的同学尤其关键。2.4 ResNet-50 backbone 够不够用这个项目用的是 ResNet-50相比 ResNet-101 甚至更重的骨干在精度上的损失其实可控但在速度和显存占用上优势明显。拥堵路段的车辆检测并不需要识别细到车标级别的差异主要分类是轿车、SUV、公交车、卡车这几类加上 VID 这类车辆重识别模型的辅助五十层的特征提取能力完全够用。如果未来想要进一步提升可以在保留整套 head 的前提下把 backbone 替换成具有更强多尺度能力的版本甚至可以在三个输出层中设置不同的膨胀系数让感受野对齐不同距离的车辆尺寸。这部分我会在文末的扩展思考里再梳理。3. 数据准备和训练细节这里藏着 80% 的坑3.1 拥挤场景数据要做哪些特殊处理模型训练用到的数据尤其是道路监控视角的数据和公开数据集 COCO 那类自然图像差异很大。车辆普遍处于小角度俯视状态而且密集排列车尾特征和车顶特征占了很大比重。我对数据做了三类处理第一裁切增强。道路监控的原始分辨率通常是 1920x1080甚至有些摄像头输出 2560x1440。直接整图缩放会造成远处车辆信息急剧丢失。我在训练时采用了随机裁切策略裁出来的区域缩放到固定输入尺寸这样让模型在不同距离的车辆上都能学到特征。第二亮度与对比度扰动。拥堵路段的画面受天气变化影响极大同一组摄像头清晨逆光、正午强光、傍晚阴影、晚上路灯色温能差出十万八千里。在做数据增强时我在 HSV 空间加了比较大幅度的扰动尤其是 V 通道的变化范围调到常规增强的 1.5 倍左右。这一步对夜间车灯过曝导致的车身细节丢失有明显改善。第三针对遮挡的遮挡模拟。简单来说就是在训练过程中用随机矩形块遮住车辆的一部分强迫模型露出少一半车身也能识别出来这样模型对拥挤场景里大量存在的部分遮挡不会过于敏感。结合上面提到的像素级预测机制效果比单纯加数据要可靠得多。3.2 Caffe 环境下模型结构复现的几个关键点NAS-FCOS 原版开源是以 PyTorch 实现的。但这个项目所在的推理链路早期是在 Caffe 框架下跑的整条链路基于 Caffe 的检测模型做过大量适配包括前处理、后处理和硬件加速库全部是围绕 Caffe 模型格式构建的。所以我当时的任务是把 PyTorch 的 NAS-FCOS 结构用 Caffe 复现并对齐参数。这里有几个很重要的细节值得记下来Caffe 不支持 PyTorch 里常用的某些动态操作NAS 搜出来的 Head 结构在表达时要把多路径连接全部重新写成固定拓扑的 DAG然后在 prototxt 里直接定义成类似残差结构的并联层。GroupNorm 在 Caffe 里没有原生层我用的是自定义插件把输入特征按通道分组后依次做 mean、var、normalize 和 scale/shift。建议把这个层单独提取出来做单元测试否则后面定位问题会很痛苦。FPN 的上采样操作PyTorch 里默认双线性插值Caffe 里要对应换成 Deconvolution 带固定双线性核的初始化方式保证像素对齐行为一致。这步不对齐输入端稍微改个尺寸输出特征就全是错位的。3.3 训练超参与损失函数配置训练时输入尺寸我设定为 896x896batch size 8使用多卡训练优化器为带动量的 SGD动量 0.9初始学习率 0.01并使用 warmup 策略。学习率调度选择了 cosine 衰减相比 step 下降在检测任务上后期收敛得更平滑不容易出现在某个 LR 台阶附近损失震荡的情况。损失函数部分分类分支用的是 Focal Loss因为拥挤场景负样本背景数量远大于正样本车辆Focal Loss 可以在训练过程中自动压低易分类负样本的权重让模型把注意力集中在那些被遮挡、模糊、难以分类的车上。回归分支用的是 GIoU Loss它对小目标回归的宽容度比标准 Smooth L1 更好。中心度分支还是保留 FCOS 原始的 BCE Loss。梯度裁剪也一直开着梯度 norm 限制在 0.5。虽然 GN 对 batch 不敏感但在训练初期仍然可能出现个别层梯度过大的问题裁剪后整体稳定很多。3.4 类不平衡问题怎么处理训练集中九成是轿车的情况下模型对公交车和卡车会产生明显的掉点。这个问题在人群拥堵场景中特别明显。解决思路有两个层面一是对训练数据中的少样本类别做过采样保证每个批次里公交车、卡车这类样本的出现频率维持在合理比例二是对 Focal Loss 的 alpha 参数按类别统计频率做自适应调整让低频类别获得更高的损失权重。实际跑下来的结果各类别的 AP 差距从原来的 12 个百分点缩小到 5 个百分点以内。如果你手头数据的类别比例更极端建议把 alpha 参数根据当前样本分布动态计算不要手工硬调。4. 训练过程的踩坑记录最狠的坑不在模型结构4.1 损失降不下去的排查过程第一次把模型结构搭起来开跑的时候前三个 epoch 损失下降得很顺利到第四个 epoch 开始震荡分类分支的 loss 怎么都降不到预期水平。检查了数据增强、学习率都没发现问题。后来逐层打印特征图数值发现在 P6、P7 这两层经过多次卷积之后特征数值分布出现了明显的偏移大量值集中在零附近但方差又异常大。问题出在 GroupNorm 的初始化和 conv 层的初始化配合上。我当时对 head 里新增的卷积层沿用了 PyTorch 默认的 Kaiming 初始化但是 GroupNorm 的 scale 初始值设成 1在深层特征上会出现前几个批次统计量还没稳定导致的输出饱和。把 GroupNorm 的 scale 初始值改成 0.1 之后这个问题就消失了。这是一个非常小众的坑说不上有什么精妙原理但很值得记进你的工程检查清单。4.2 多卡训练时 GN 和 BN 的显存差异这里有一个可复现的规律使用 BN 时即便显存能撑下更大的 batch size增大 batch 对最终精度也没有正向作用因为监控图片之间场景差异太大统计量难以在 batch 内对齐换成 GN 后把 batch size 从 4 提到 8 反而有约 0.8 个点的提升因为数据多样化更有助于模型泛化。所以在显存规划上我建议采用“GN batch size 8 或 16”的搭配而不是“BN batch size 32 或 64”。同样的显存预算下GN 还能预留出空间给更长的训练序列和更大的输入分辨率。4.3 EMA 对稳定性的提升检测模型训练在 180 个 epoch 之后权重在最优位置附近反复横跳导致验证集上 AP 整体在 86.5% 到 87.6% 之间波动。后来加入了指数移动平均衰减参数设为 0.9998验证时用平均权重而不是迭代权重最终 AP 稳定在 87.8% 左右。EMA 在拥挤场景下尤其好用因为它能让优化轨迹更接近“整体最优区间”的中心。这里给一个经验值如果训练集的标注噪声较大EMA 的 decay 可以适当设到 0.9999平滑效果更明显如果数据比较干净0.999 就够了设得太高反而延缓了对新分布的响应。4.4 DeepLayer 梯度的监控习惯现在每次跑训练我都会在关键层加一组梯度统计量的日志输出包括梯度二范数和梯度最大值。这一步能让你在训练早期就从曲线形态上判断层与层之间是否存在梯度消失或爆炸不用等 300 个 epoch 跑完才发现模型废了。特别是替换了 GroupNorm 插件之后这个监控几乎成了刚需。5. 推理阶段的性能优化把模型塞进实时链路5.1 检测头裁剪和分支合并NAS 搜索出来的 Head 结构如果在推理时不做处理耗时是原始 FCOS 的 1.3 倍左右在边缘计算盒子上帧率会被压到非常尴尬的水平。我做的优化分两步。第一步是通道剪枝。对分类分支和回归分支中贡献度低的卷积通道做剪枝保留响应值统计显著的前 90% 通道。这一步不改变计算图只是把矩阵变窄推理实测加速 8%精度损失可以忽略。第二步是分支合并。把 head 最后几层中分类和回归共用的部分合并成一个共享卷积只在最终输出时才分叉。因为两个任务在低层特征上高度相关这样合并不但能提速还能提升回归稳定性算是意外收获。5.2 NMS 参数在拥挤场景下的重新调节拥挤场景中的后处理也需要单独调。我用了 Soft-NMS并且把 IoU 阈值调高到 0.65这样密集排列的车辆不会被别的框的置信度连带压掉。分类置信度阈值设置在 0.35比常规场景低一些因为遮挡环境下真实目标的置信度本身偏低。如果你用的是普通 NMS在极端拥堵下很容易出现同一辆车的多个框互相抑制导致误删非常不推荐。5.3 量化与硬件部署模型最终部署用的推理硬件支持 INT8 量化所以我把模型的卷积层权重做了一轮校准量化。这一步对检测框位置的影响非常小但对分类分数影响明显量化前分类分数偏低的那部分目标量化后可能直接低于阈值而漏检。解决办法是把分类分支的 0.35 阈值在量化后下调到 0.3同时保持 NMS 阈值不变这样能在保持低误检的前提下挽回一部分漏检。如果你用的是 GPU 部署环境完全可以使用 TensorRT 做 FP16 推理收益比 INT8 更稳妥。FP16 在这里没有任何精度问题而且避免了量化校准的额外工作。6. 实测效果和性能对比整个系统在真实道路监控画面上的测试结果可以用一组数据来说明。测试集选了三个不同拥堵等级的路口轻度、中度和重度。轻度路口是正常车流量车辆之间保持至少一个车身的间距中度路口出现在早晚高峰排队前进但车辆之间间隔很小重度路口则直接是十字路口被堵死多车并行加混入非机动车。场景召回率精确率FPS边缘设备轻度拥堵93.2%96.1%27中度拥堵90.4%94.8%27重度拥堵84.7%91.6%27在重度拥堵场景中召回率下降主要来源于严重遮挡导致的车身可视面积过小以及夜间大灯过曝造成的纹理丢失。法务上的违章抓拍会对这部分场景做二次甄别确认确保不会因漏检或误检造成处罚差错。和之前基于有锚点检测器的方案对比漏检率在重度拥堵场景下下降了大概 20%误检率变化不大。这些数据不一定对所有硬件都成立但方向上对参考价值很明确。7. 一些可以继续延伸的方向这个系统目前还能做很多横向扩展。一个方向是跨摄像头车辆追踪先把单镜头下的检测结果按时间维度做跟踪然后通过车辆外观特征在多摄像头之间做关联匹配。这个用网络训练时提取的中层语义特征做重识别效果比单纯用颜色直方图稳定得多鲁棒性更接近量产需求。另一个方向是行为识别比如靠车辆轨迹特征判断违规变道、加塞等动作。但行为识别对检测框的稳定性要求极高小抖动都会被放大成不规律的轨迹抖动所以在此基础上需要先把跟踪模块加上再去做轨迹平滑和动作分类。还有一个我认为很有潜力的方向是用更轻量的骨干网络替代 ResNet-50比如基于结构重参数化思想的轻量模块。拥堵路段车辆检测的实时性要求往往比精度更迫切在保证 GN-Head 检测结构不变的前提下换一个更轻的 backbone能进一步把推理帧率推到 30 帧以上而精度损失控制在可接受范围内。最后分享一个小的实战体会拥挤路段车辆检测这类任务在模型结构上的差距很容易被数据质量和后处理细节弥补掉一部分反过来结构再先进训练数据里遮挡样本和极端光照样本不足模型活不过回灌真实场景的那一天。所以与其一上来就追求更复杂的 backbone不如先把数据分布吃透再在 head 和训练策略上做文章。NAS-FCOS 这条路子引入的搜索空间和归一化改造正好把这两个关键方向都兼顾了这也是我把整套方案沉淀下来分享的主要原因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑