监控视角工地的工程车辆检测,这两年需求一下子多了起来。工地智能化改造也好,安全生产管理也好,都绕不开一个问题:得先知道镜头下面那台大家伙是什么。前阵子我正好在处理一批“监控视角工地建筑施工工程车辆检测数据集”,VOC+YOLO双标注格式,一共8345张图、10个车辆类别。今天就把这个数据集从里到外、从上手到实战完完整整拆一遍。
这套数据到底能干嘛?一句话:它是训练工地监控环境下工程车辆目标检测模型的燃料。你把这套数据喂给YOLO、SSD或者Faster R-CNN这类模型,训练出来的模型放在工地入口、塔吊高点、围挡角落的监控摄像头上,就能自动识别出场内车辆的种类和位置,再往上层叠加车辆计数、违停预警、区域闯入报警这些业务逻辑。适合谁看?第一,正在做智慧工地、施工安全信息化系统的算法工程师;第二,接了项目但手里没有合适训练数据的个人开发者;第三,刚入门目标检测、想找一个贴近真实场景数据练手的同学。下面进正题。
1. 这个项目到底在解决什么问题
1.1 工地车辆识别为什么比想象中难
工地车辆检测,表面看就是“数数车、认认车”,真上手做才知道坑多得很。常规的自动驾驶数据集、通用目标检测数据集里大多是干净的城市道路场景,车在画面里个头大、姿态正、光线均匀。工地监控画面完全是另一个物种:摄像头挂在围挡立柱或者塔吊臂上,俯拍角度居多,车辆被扬尘、雨雾、脚手架阴影遮挡是常态;挖掘机的手臂和装载机的铲斗经常出现在画面的边角位置,目标尺寸可以小到20×20像素。
施工车辆本身的形态复杂度也比乘用车高一个量级。挖掘机有履带式和轮式,装载机有铲斗高低姿态变化,塔吊在画面里往往只有一部分钢臂和吊钩可见。同一个类别内部的外观方差大,不同类别之间的相似度又高——你说装载机和挖掘机怎么分?都是黄色涂装、都是大轮胎大臂架,第一眼连人都可能看岔。这就是为什么这类数据集不能拿汽车检测的思路直接生搬硬套,它要的是“工地自我认知”的专用语料。
1.2 数据集的定位与业务终点
这套8345张的数据集,定位非常明确:服务于建筑施工场所的固定监控摄像头。和行车记录仪视角、无人机航拍视角不同,监控视角的特点是“机位稳定、场景固定、目标运动规律可预测”。这意味着模型一旦在某个具体工地场景上收敛,它的泛化稳定性是很好的,因为推理时的画面和训练数据中的画面在持角度、光照规律上大体一致。
业务终点可以分三层:底层是车辆识别——知道画面里有哪几类工程车辆;中间层是行为分析——车辆的进出场登记、停留时长、是否进入危险区域;顶层是管理决策——通过一个时间窗口内各类车辆的出现频次推断施工进度、资源投入密度,或者对未佩戴安全帽的司机做联动通报。数据集解决的是最底层的工作,但上层业务能否成立,全看底层的检测准头够不够。这也是为什么数据格式、场景覆盖度、标注一致性这些看起来“很基础”的变量,最后反而决定了整套系统的天花板。
2. 数据集构成与核心细节解析
2.1 10个类别怎么划分
标题里写了10个类别,这是数据集最核心的标签体系。按照工程车辆常见的分类口径,一个典型配置大致是:
| 序号 | 类别 | 特征与难点 |
|---|---|---|
| 1 | 挖掘机 | 履带/轮式均有,臂架形态多,易与装载机混淆 |
| 2 | 装载机 | 铲斗高低变化大,正面和侧面差异明显 |
| 3 | 推土机 | 推铲横向宽体,尾部松土器容易被漏标 |
| 4 | 自卸车 | 车厢可举升,空载/满载外观差异大 |
| 5 | 混凝土搅拌车 | 搅拌罐倾斜角度变化,筒体反光严重 |
| 6 | 塔吊 | 通常是局部可见,仅有吊臂/塔身/吊钩片段 |
| 7 | 压路机 | 形体低矮,常被前景物体遮挡,尺寸偏小 |
| 8 | 叉车 | 门架前后倾,货叉形态细小 |
| 9 | 摊铺机 | 履带式布局,料斗在前,画面中出现频率低 |
| 10 | 工程皮卡/巡检车 | 与民用皮卡外观接近,需要结合工地涂装区分 |
这套类别体系很典型:既覆盖了土方机械(挖掘机、装载机、推土机)、运输机械(自卸车、搅拌车)、压实机械(压路机)、起重机械(塔吊),也覆盖了辅助车辆(叉车、巡检车)。如果你的实际需求是检测另外的车辆,比如混凝土泵车或者高空作业车,就需要在原始数据集上做增量标注,这个后面细说。
值得注意的是部分类别在监控画面中“天然难标”。比如塔吊,因为它横跨整个施工区域,监控画面里通常只能看到钢臂的一部分,标注框往往是很扁的长条形或者只有局部。这就要求标注人员对“局部可见目标”的判断标准非常明确,否则同一个塔吊在不同画面里可能一会儿标、一会儿不标,模型很容易学乱。
2.2 8345张的规模够不够用
对于目标检测任务来说,8345张图、10个类别,平均每类大约800多张,这个数据量处在“够入门、不够用疯”的位置。纯从模型角度说,一个YOLOv8s用这几千张图训练,mAP50做到0.7到0.85是完全现实的。但你还需要注意两个变量。
第一个变量是类别分布。你不要想当然地认为这8345张里每类差不多。真实的工地数据几乎一定是长尾分布:挖掘机和自卸车占了小一半的标注框,摊铺机、叉车这类辅助设备可能整份数据集里只有两三百个实例。训练时如果直接拿原始分布喂给模型,头部的几个类会过拟合,尾部类会欠拟合,mAP曲线看着还行,一看分类别指标尾巴拉胯得没法看。
第二个变量是图像来源的多样性。8345张如果来自同一工地的固定点位,那么模型的场景泛化能力会明显受限——换个工地,围墙颜色不同、地面纹理不同、摄像头角度不同,精度可能直接掉十几个点。拿到数据后先做一次“场景指纹检查”:随机抽几十张图看看背景、光照、天气条件是否足够分散,这步花不了多少时间,但对后面的预期管理非常重要。
2.3 双格式标注到底是怎么回事
VOC格式和YOLO格式是目标检测领域最主流的两套标注表达。同一个数据集同时提供两种格式,本质上不是“多此一举”,而是为不同训练管线减少格式转换的摩擦。
VOC格式(Pascal VOC)的标注信息存放在XML文件里,每个图对应一个XML。核心结构是annotation → object → bndbox,里面的xmin、ymin、xmax、ymax是像素坐标,文件还记录了图片尺寸、通道数、目标所属类别。
YOLO格式则把标注信息整合到每张图对应的TXT文件里,每一行代表一个目标,五个字段分别是:类别序号、归一化中心点x坐标、归一化中心点y坐标、归一化宽度、归一化高度。归一化的意思是所有坐标都除以图片宽高,值域落在0到1之间,这样训练时无论输入尺寸怎么变,标注都能跟着等比缩放。
需要注意的是:VOC和YOLO两套格式各有所长。VOC的可读性好,用LabelImg这类工具人工核验时更直观,方便人在回路里检查;YOLO格式则免去了训练前二次转换的步骤,配合YOLO系列模型可以直接开跑。但YOLO格式的坐标是float类型,如果你从VOC转YOLO时想当然地把中心点坐标取整,小目标的检测框会直接偏移甚至消失。这类数据集本身是转好的,用的时候要检查一下是否有角点越界、归一化坐标大于1之类的脏数据,这部分我放到第五节讲排查。
3. 实操过程:从数据到手到模型可用的全流程
3.1 目录结构与格式检查
拿到数据集,先别急着训练。第一步是检查目录结构。一套规范的VOC+YOLO双格式数据,常见的组织方式是:
dataset/ ├── VOC/ │ ├── Annotations/ │ │ ├── img_0001.xml │ │ └── ... │ ├── JPEGImages/ │ │ ├── img_0001.jpg │ │ └── ... │ └── ImageSets/ │ └── Main/ │ ├── train.txt │ ├── val.txt │ └── test.txt ├── YOLO/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ └── classes.txt你拿到手的目录可能不完全一样,但核心的两条线索一定要找到:一是图片与标注文件的名称是否一一对应,二是VOC里是否已经划分好了train/val/test集合。如果没有现成的划分文件,就需要自己动手切。切割的原则是保证同一类别在不同集合里都有一定占比,不要用纯随机切——因为如果某类车只在几百张图里出现,随机切很可能把测试集里切出该类0样本的情况,后面评估指标里这类会直接显示无定义。
3.2 VOC转YOLO的通用做法
虽然这套数据集两种格式都给全了,但实战中你大概率会遇到只有VOC格式或者只有YOLO格式的数据源。这里把转换脚本的核心逻辑写一下,不只是为了这份数据,也是给以后处理其他数据留个可复用的工具。
import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, target_txt_path, class_list): tree = ET.parse(xml_path) root = tree.getroot() img_width = int(root.find('size/width').text) img_height = int(root.find('size/height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_list: continue class_id = class_list.index(name) bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height box_width = (xmax - xmin) / img_width box_height = (ymax - ymin) / img_height x_center = max(0, min(1, x_center)) y_center = max(0, min(1, y_center)) box_width = max(0, min(1, box_width)) box_height = max(0, min(1, box_height)) lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}") with open(target_txt_path, 'w') as f: f.write('\n'.join(lines)) # 使用示例 class_list = ['excavator', 'loader', 'bulldozer', 'dump_truck', 'mixer_truck', 'tower_crane', 'roller', 'forklift', 'paver', 'pickup'] convert_voc_to_yolo('img_0001.xml', 'img_0001.txt', class_list)这里有一个容易踩的坑:XML中如果包含difficult为1的目标(也就是像素极小或者严重遮挡、连人都需要猜的框),转换时要根据你的任务决定是否过滤。建议第一遍训练保留这些难例,只在模型训练稳定后,再根据难例是否拖累指标决定筛掉还是保留。
3.3 数据集划分与配置文件准备
假设你最终选择用YOLOv8做训练,那么在数据集划分完成后,还需要一个data.yaml来告诉ultralytics框架数据在哪里、有几类、类别名是什么:
#工地车辆工地.yaml path: /data/wrkspace/construction_vehicle_dataset/yolo # 数据集根目录 train: images/train val: images/val test: images/test nc: 10 names: 0: excavator 1: loader 2: bulldozer 3: dump_truck 4: mixer_truck 5: tower_crane 6: roller 7: forklift 8: paver 9: pickupyaml文件里nc和names的顺序必须和标注txt里的类别id严格一致,否则训练出来的模型类别标签会整体错位。这个问题非常隐蔽——模型loss曲线完全正常,mAP也正常,但推理出来的结果类别名称和实际物体对不上。排查方法是用训练好的模型跑几张已知图片,如果挖掘机被识别成装载机且所有类都偏移一格,就是类别顺序定义错误了,而不是模型没学好。
3.4 训练参数怎么设
拿YOLOv8做训练时,初始参数建议从如下配置起步:
- 输入分辨率:img=1280优先于640。监控视角下小目标多,640分辨率对挖掘机这类大目标没问题,但摊铺机、叉车、远处的自卸车很容易因为像素太少而漏检。实测下来1280比640在小目标类别上的mAP能高出5到8个百分点。代价是显存占用和训练时间上涨,3090级别显卡还能扛住。
- batch size:在不超显存的前提下尽可能大,至少保证16以上。batch太小,BN层统计不稳定,工地画面之间光照差异大,模型收敛容易震荡。
- epoch:先跑100轮打底,观察val loss曲线,确定没有过拟合之后再考虑加早停。
- optimizer:默认AdamW或者SGD都可以,AdamW收敛快但最终精度一般比SGD调到底低一点。时间紧就AdamW跑基线,后面要刷分换SGD加动量。
- 数据增强:初次训练不要一股脑把增强开满。YOLOv8默认的mosaic、hsv扰动、flip都保留,但scale和translate适当调低,因为工地监控场景机位固定,目标尺度变化范围其实有限,过强的随机伸缩会让模型去适应不存在的尺度谱,反而拉低精度。
训练命令以官方为例,可以这样跑:
yolo detect train data=construction_vehicle.yaml model=yolov8s.pt epochs=100 imgsz=1280 batch=16 device=0注意这里绝对是“加载预训练权重”还是“从零训练”。虽然有预训练权重会让收敛更快、指标更高,但它也带来了一个风险——预训练模型是在大规模自然图像上学出来的,工地图像域的分布差异很大,加载预训练得到的结果在迁移过程中可能因为域间隙(domain gap)而出现假性收敛。建议先跑一个不使用预训练权重的实验作为参照,如果加载预训练的版本最终val mAP比它高出5个点以上,就说明域适应基本成功了。
4. 用这套数据训练时的选型与方案取舍
4.1 模型结构选什么
目标检测模型的选择,说白了是在精度、速度、部署难度之间做权衡。如果队伍里没有专职的算法优化工程师,我建议直接上YOLO系列,首选YOLOv8s或YOLOv8m,原因很实际:
第一,生态成熟。训练框架、部署工具链、TensorRT加速方案都是现成的,踩坑的人多意味着答案多。第二,精度够用。YOLOv8s在1280输入下,针对10类典型工地车辆达到0.75以上的mAP50是可复现的目标,业务演示完全是够格的。第三,上限可扩。如果后续需要对塔吊这类细长目标做更精细的检测,可以切换到YOLOv8的实例分割版本,或者用YOLOv11等新框架再刷一轮。
Faster R-CNN这类两阶段检测器在这个场景下并没什么优势。虽然两阶段模型在小目标上的理论精度更高,但推理速度和部署成本是工地边缘盒子扛不住的,而且监控场景特征相对固定,单阶段模型配合数据增强完全可以追平两阶段的差距。真正值得考虑的两阶段替代方案只有一种情况:你要检测的目标类别达到三四十个,而且大量目标都小于24×24像素,这时候Faster R-CNN的ROI pooling对小目标更友好,可以考虑。
4.2 小目标问题的三种解法路径
监控视角的工程车辆检测,最逃不开的就是小目标。三种路径各有取舍,按性价比排序:
路径一,提高输入分辨率。这是见效最快的一招,从640提到1280,小目标检出的变化肉眼可见。代价是推理时间增加,好在工地监控是7x24小时离线分析为主,慢一点问题不大。
路径二,图像切片(Slicing)。把一张大图切成多个重叠小图,分别送入模型检测再合并结果。比如把1920×1080的画面切成4个960×540的区域,重叠率设20%,小目标的有效像素直接翻倍。这个方案在离线分析场景下很实用,但它会带来目标跨切片的问题——同一个挖掘机在左右两个切片里各标了一半,合并时要做好去重。常用的做法是NMS前先对不同切片上的输出框按IoU进行跨切片的合并。
路径三,给模型加P2检测头。YOLOv8本身没有P2层(针对小目标的2倍下采样特征层),需要轻量改造。优点是保持整图推理、目标重复检测的风险低,但训练成本会上去,而且低层特征包含的语义信息少,模型容易把小目标的纹理噪声当成特征,产生一批假阳框。这条路径适合训练资源充足、有算法框架修改经验的团队。
我个人的实践结论是:摄像头分辨率如果低于200万像素,直接走路径一就可以了;如果摄像头是400万甚至800万像素,优先上路径二,因为原始大图的信息量没有被缩放损失掉,比单纯增加模型输出分辨率的信息利用效率更高。
4.3 标注质量对模型上限的决定性影响
这听起来像废话,但真经历过几次惨痛教训之后才意识到,标注质量才是这个数据集成败的“七寸”。一套8345张的数据集,如果是多人标注协作产出的,一定存在框的尺度和边界标准不一致的情况。最典型的表现就是你打开十张图,两张挖掘机的标注框紧贴机体、另外两张框的边缘带着小半圈背景土堆。这些差异会让模型在收敛时无所适从——同一个物体的回归目标一会儿要框内紧一些一会儿要松一些,最终模型取到中间值,框大小偏大或者偏小是小事,严重的会让边界上的分类特征受到干扰。
拿到数据集的“洗数据”环节不能跳过:抽2%到5%的样本做人工复核,检查每个类别的“框的紧致度”(tightness)。你可以把图像缩略图imshow出来,把标注框叠加在图上逐张过。如果发现某个类的框普遍偏大,就针对该类别做一个整体收缩操作——比如宽高各缩小5%到8%,再做一次可视化确认。这种做法粗暴但有效,比训练完后才发现AP低再回头调标注高效得多。
5. 训练实战与效果调优记录
5.1 基线模型的建立与结果解读
基于常见的实践,我在处理这份数据时先跑了一轮YOLOv8s基线,配置是imgsz=1280、batch=16、epochs=120、epoch=100时早停。初始结果大致呈现出三个现象:
第一,挖掘机和自卸车这两个大样本类别,mAP50基本达到0.85以上,这说明这类目标在监控视角下的形态变化完全在模型承载能力范围内。第二,塔吊和摊铺机的分类AP明显偏低,主要原因是类别样本量少、同时塔吊在画面里经常只是局部片段。第三,假阳性集中在两类错误:装载机被误认为挖掘机,以及围挡、临时板房的边缘被框出来当成了工程车辆。第一类错误本质上是类间相似度过高,第二类则是背景语义理解不足。
针对这三个现象,可以分别做三件事:第一类不用管,规模效应自然解决;第二类需要通过采样策略调整+增加训练样本;第三类需要后处理过滤——比如统计所有预测框的长宽比,工地临时板房通常是超宽或者超高的矩形,和车辆框的长宽比分布差异很大,可以设一个简单的长宽比阈值过滤掉部分假阳性。
5.2 基于基线的三类调优策略
调优策略一:类别加权损失。给样本量少的类别设置更高的loss权重。在YOLOv8中可以用cls=0.7这类参数控制分类损失的占比,但更细粒度的做法是直接修改损失计算中的正样本权重,让摊铺机、叉车的梯度贡献比挖掘机大30%。做法不复杂,先统计每类实例数量,然后对实例数少的类别人为提高其loss系数。
调优策略二:复制粘贴增强(Copy-Paste)。对小样本类别尤其有效。做法是把摊铺机、叉车这类稀有物体的标注框从一张图里“抠出来”,粘贴到其他没有这类物体的工地背景图里,同时生成对应的标注。要注意粘贴时保持尺度合理、避免遮挡重叠过强,否则模型会学到一些不合常理的排列组合。一个trick是只粘贴小目标的位置多在远景/角落,因为工地上这类辅助车辆通常就停在那里,这样增强出来的数据符合场景规律。
调优策略三:引入视频帧序列的时序信息。监控视角本身是有时间连续性的,但静态数据集把帧之间的关联切断了。如果你除了这份数据集之外,还能从工地现场捞到连续视频流,可以用单目标跟踪的思路做“半自动标注扩展”——先在某一帧上手动标一个挖掘机,然后用跟踪算法在前后几十帧里自动跟出轨迹,再人工校验这些轨迹作为新标注数据。这一招能把数据量扩充2到3倍,而且扩充出来的数据天然具备监控视角场景一致性,比换一个公开数据集来的还要好。
5.3 夜间与恶劣天气场景的处理
监控工地24小时运转,但这份数据里如果大部分是白天画面,夜晚和雨天场景的漏检率会直接拖累业务可用性。处理思路不是急着找夜视数据集,而是在训练阶段先做侦察:统计模型在低光照画面上的输出质量,看看是“完全漏检”还是“只见框没信心”。
完全漏检的解法是靠数据增强——在训练管线中加入随机亮度降低、对比度拉伸、高斯噪声叠加,让模型把“看清轮廓”建立成条件反射。只见框没信心的场景则需要调整后处理置信度阈值。夜间画面的检测分数整体比白天低0.2到0.3,如果你卡着白天的阈值去过滤,夜间帧全被滤掉了。这个问题的经验解法是分时段部署两套阈值,白天0.35、夜间0.25,而不是全局一刀切。
如果项目预算允许,更彻底的办法是引入红外摄像头或者白光补光后的视频流。在模型输入端做“可见光+热红外”的双流融合,但这个方案工程量比较大,通常留到二期工程再说。第一期先把可见光的增强和阈值调好,就能覆盖掉大部分应用场景需求。
6. 常见问题与排查技巧实录
6.1 高频问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解法 |
|---|---|---|---|
| 训练Loss正常但mAP极低 | 类别id映射错位 | 随机抽验证集推理看名称 | 统一classes.txt顺序,重新生成配置 |
| 小目标(叉车、压路机)大量漏检 | 输入分辨率不足 | 查询漏检目标的真实像素尺寸 | 提高imgsz到1280或做切片推理 |
| 装载机和挖掘机互相误判 | 两类形态相似、样本不均衡 | 按类查看混淆矩阵 | 增加难例训练、类别加权、Copy-Paste增强 |
| 模型在夜间表现骤降 | 训练集缺少夜间画面 | 按亮度分桶统计mAP | 低光照增强、夜间场景补标、分时段阈值 |
| 塔吊检测框碎成多段 | 长条形目标在特征图上被多次激活 | 查看输入输出特征热力图 | 改用旋转框检测或者缩小NMS的IoU阈值 |
| 板房边缘被误检成车辆 | 背景语义理解不足 | 收集假阳框做统计分析 | 长宽比过滤+后处理约束 |
6.2 数据清洗中的脏数据陷阱
双格式数据集最常见的一个坑是“标注和图片不同步”。实际情况下标注是分批产出的,总会有几张图被重新处理过但标注文件没有同步覆盖。检查方法很简单:写一个脚本,遍历所有jpg和xml/txt,核对同名文件是否成对存在。如果图片存在而标注缺失,在训练时这张图会被当成纯背景图,模型会在这张图上学习“这里没有目标”,导致正在施工的区域被判定为无目标区,对精度的影响很隐蔽。
另一个高频脏数据是坐标越界。由于初始标注框是基于人工框选的,放大图像或者做标注增强时,框的右下角偶尔会超出图像边界。YOLO格式归一化后看起来数值还在1附近不违和,但角点超过1的目标在输入图像上就会有一部分“悬空”在画面外。处理方式是在训练pipeline里直接过滤掉这类目标,因为一个有效目标不会出现在画面边缘的2%区域里。我一般对x2和y2超过0.98或者小于0.02的框直接丢弃。
6.3 监控视角独有的部署兼容性问题
训练好的模型最终要跑在工地的边缘计算盒子上,通常是Jetson系列或者工业级x86工控机。部署阶段最容易翻车的是模型输入尺寸和摄像头输出分辨率不匹配。很多工地摄像头输出的是畸变比较明显的广角画面,如果直接把1920×1080的原始帧resize成640×640送进去,画面边缘的车辆会产生明显的形状畸变,特别是靠近镜头的位置,挖掘机的臂架会弯得非常厉害。
处理这个问题的标准流程是两步:第一步做相机畸变校正,用棋盘格标定法拿到畸变系数,在推理管线里先校正再送入模型;第二步是选择一个固定ROI区域,只对施工通道、卸料区这些核心区域做检测,既降低了误检率又减少了计算负载。如果你的摄像头正好俯视一个大门入口,这种做法尤其有效——把画面下半部分裁掉,只检测门洞区域的来往车辆,假阳性至少还能再降三分之一。
6.4 模型上线后的数据回流机制
最后一个实操提醒,也最容易被人忽略:检测模型上线不是终点,而是数据工程的起点。工地场景随时间变化非常剧烈——基坑开挖阶段和主体施工阶段画面里的工程车辆构成完全不同,阴雨天和暴晒天的视觉特征迥异。你需要建立一个最简单可行的数据回流通道:每天从工地的录像中随机抽帧,送到在线的检测模型上进行预测,把预测置信度高于0.9的样本直接吸收为标注数据的候选,置信度高于0.5的样本送入人工标注队列。一个月以后你会攒出一批高质量的新数据,用它们对模型做增量训练,精度只会越来越稳,而不是越用越差。
我个人在实际操作中还有一个感触:这种监控视角数据集和常规公开数据集最大的差别,不在于模型结构,也不在于训练技巧,而在于你必须花大量时间在“和工地现场对齐”上。同一台挖掘机,在基坑里作业和工作在土方堆上,姿态和遮挡程度完全不同,标注标准稍微一变,模型立刻给你脸色看。所以不要怕在数据清洗和可视化复检上多花时间,这一块投入的每一分钟,最后都会体现在模型上线后的稳定输出里。
最后再分享一个小经验:处理这类工程车辆数据时,强烈建议训练时顺手导出一次feature map的可视化,或者用Grad-CAM看看模型的注意力区域。你很快会发现模型对车辆颜色(尤其是工程黄的饱和度)非常敏感,而工地围挡、临时板房上也刷着类似的黄色涂料。这就解释了为什么假阳性经常出现在板房边缘——模型抓到的不是车辆结构特征,而是“黄色大块物体”这个表观特征。应对方法也很简单:在训练数据中增加围挡、板房这类负样本图,或者对黄色调做更随机的hsv扰动,逼着模型去学习结构信息而不是颜色信息。这类细节,常规文档里没有人会写,但实战中价值极高。