视觉智能体要识别“画面里有什么”时,很多人会直接问:YOLO 该选哪个版本?这个问题太早了。真正影响结果的,往往是目标有多小、类别是否稳定、画面是否拥挤、每秒必须处理多少帧,以及模型最终运行在 GPU、CPU 还是边缘设备。模型名称只是最后一步。
本文把常见选择分为三条路径:传统视觉规则、YOLO 一类单阶段检测器、RT-DETR 一类实时 Detection Transformer。它们不是互相替代的产品清单,而是针对不同输入和约束的技术路线。示例使用脱敏的三段 1080P 视频帧和一组人工标注帧,只用于说明选型、评测和交付方法,不公开真实业务视频、模型权重或部署参数。
环境边界:Python 3.11、PyTorch 推理环境、可替换的 ONNX/TensorRT/OpenVINO 运行时、Java 17、Spring Boot 风格服务层、MySQL 8.x。文中样本数据为教学示意,任何速度、精度结论都必须在自己的类别、设备和输入尺寸上重测。
目录
- 先区分检测、规则与后续理解
- 三条技术路线各自适合什么
- 固定评测案例:别拿不同条件下的数字比较
- 选型表:从场景约束倒推方案
- Python 实现:把评测口径写成可复用代码
- 服务层:保存选择依据而不是只保存模型名
- 预期输出与自动测试
- SQL 验证:模型版本、样本集和结果能否对上
- 误区、边界与上线验收
- 小结和延伸阅读
一、先区分检测、规则与后续理解
目标检测的输出通常是类别、边界框和置信度:
frame=00241, class=person, box=(812, 260, 1006, 914), confidence=0.91这只回答“这一帧看见了什么、在什么位置”。它不能直接回答同一个人是否持续出现、是否完成一个肢体动作、是否进入某个区域。那些问题还需要跟踪、关键点、时序模型或规则。先划清边界,才能避免把后续能力缺失误判为检测模型不够强。
图1:检测给出类别和位置;跨帧身份、姿态和业务事件是不同层次的问题。
二、三条技术路线各自适合什么
传统视觉规则适合目标外观、背景、机位和光照高度受控的任务,例如固定机位下的高对比标记或明确颜色区域。它快、可解释、无需训练,但环境改变时边界很窄。YOLO 一类单阶段检测器适合多数需要较低延迟、类别明确、部署链路成熟的目标检测任务。RT-DETR 一类实时 Transformer 提供另一种端到端检测路线,适合希望在同一评测口径下比较精度、模型规模和推理代价的场景。
| 路线 | 适合的输入条件 | 主要优势 | 主要代价 | 不应把它当成什么 |
|---|---|---|---|---|
| 传统规则 | 固定机位、颜色/形状稳定、背景简单 | 快、透明、可离线调参 | 对光照、反光、遮挡和镜头变化敏感 | 通用物体识别器 |
| YOLO 类单阶段检测 | 类别清晰、需要较低延迟、需方便导出部署 | 生态与部署选项丰富,适合从原型到工程验证 | 小目标、遮挡、领域差异仍依赖数据与评测 | 轨迹、动作或业务结论 |
| RT-DETR 类实时 Transformer | 需要把精度与推理成本按同一数据集比较 | 端到端实时检测路线,可作为不同架构的对照 | 运行时、模型规格、导出与设备适配要单独验证 | “精度必然更高”的结论 |
Ultralytics 的公开文档将验证指标、推理时间和导出格式放在同一套 Benchmark 流程中;RT-DETR 官方实现也给出了不同骨干网络在指定数据集与硬件上的模型规模和吞吐参考。这些公开数字可以帮助建立评测方法,却不能直接代替本地结论,因为类别分布、输入分辨率、阈值、运行时和硬件都不同。
图2:没有脱离场景的“最佳模型”;必须同时比较环境稳定性、精度需求、时延和部署条件。
三、固定评测案例:别拿不同条件下的数字比较
本例建立一个小型、可复查的评测集,而不是直接引用公开排行榜。每段素材都抽取人工复核帧,并分别标注person、hand_object、vehicle等与任务相关的通用类别:
SET-A:固定机位,光照稳定,目标明显,80 帧。 SET-B:多人遮挡与小目标,80 帧。 SET-C:逆光、运动模糊和镜头轻微抖动,80 帧。三条路线都使用相同的帧、类别映射、IoU 阈值、置信度阈值、输入尺寸和设备。至少记录四项结果:precision、recall、mAP50-95、单帧端到端耗时。只报一个 FPS 没有意义:不同输入尺寸、预处理、后处理、批次、运行时甚至是否包含解码,都会让数字失去可比性。
图3:同一批帧、同一类别和同一设备是横向比较的前提;困难样本必须单独观察。
四、选型表:从场景约束倒推方案
| 场景约束 | 优先尝试 | 选择原因 | 必做验证 | 不满足时的转向 |
|---|---|---|---|---|
| 机位固定、目标外观单一、要求极低资源 | 规则方案,再保留检测兜底 | 规则可解释且成本低 | 光照变化、背景扰动、误触发率 | 画面变化增大时改用检测器 |
| 多类别通用目标、需要较低延迟 | YOLO 类模型作为基线 | 易建立基线并比较不同导出运行时 | 各类别 recall、小目标漏检、端到端时延 | 领域差异大时补标或微调 |
| 拥挤画面或需要比较不同检测架构 | RT-DETR 与单阶段模型同集评测 | 不凭架构宣传做决定 | 困难子集、显存、导出后速度 | 若成本超预算,回到更小模型或缩小任务范围 |
| 后续要判断动作 | 检测作为前置,再加关键点/时序模型 | 检测框不包含关节和动作顺序 | 关键点可见率、动作窗口误判 | 不要无限调检测阈值来代替动作模型 |
| CPU 或边缘设备部署 | 先确定运行时,再反选模型规格 | 部署格式和量化影响实际速度 | 同设备、同输入、同精度下的耗时和内存 | GPU 不可用时不应沿用 GPU 结论 |
此表的产物不是“模型胜负”,而是一份可审查的选择理由。例如,某个任务可以接受 200ms 单帧延迟但不能接受小目标漏检,那么更大模型是否值得,必须由困难样本 recall 和设备实测共同决定。
五、Python 实现:把评测口径写成可复用代码
下面的最小实现不绑定具体推理框架。它强制不同候选方案返回同一种结果,并按相同字段汇总,从而避免把模型 A 的推理时间与模型 B 的“解码加推理加绘制”混在一起。
fromdataclassesimportdataclass@dataclass(frozen=True)classDetectionMetrics:candidate:strsample_set:strprecision:floatrecall:floatmap50_95:floatinference_ms:floatend_to_end_ms:floatdefcan_promote(metrics:DetectionMetrics,*,min_recall:float,max_end_to_end_ms:float)->bool:return(metrics.recall>=min_recallandmetrics.end_to_end_ms<=max_end_to_end_msandmetrics.map50_95>0)defcompare(candidates:list[DetectionMetrics],min_recall:float,max_end_to_end_ms:float)->list[DetectionMetrics]:returnsorted((itemforitemincandidatesifcan_promote(item,min_recall=min_recall,max_end_to_end_ms=max_end_to_end_ms)),key=lambdaitem:(-item.recall,item.end_to_end_ms),)它没有把mAP当成唯一门槛。对安全或漏检敏感任务,先设最低recall;对实时任务,再限制端到端时延;最终在达标候选中选择更稳定的方案。类别级明细和困难子集必须与总分一起保存,否则高平均值会掩盖关键类别失败。
图4:选型不是按单一分数排序,而是先过滤不满足业务阈值的候选方案。
六、服务层:保存选择依据而不是只保存模型名
服务层不推理模型,但必须让一次选择可追溯:用过哪些样本、什么输入尺寸、运行在哪类设备、阈值是什么,以及为何允许它进入试运行。
@Transactional(rollbackFor=Exception.class)publicCandidateDecisiondecide(DetectionCandidateCommandcommand){EvaluationSetsampleSet=evaluationSetRepository.requireReady(command.sampleSetCode());if(!sampleSet.categoryVersion().equals(command.categoryVersion())){thrownewBizException("样本集类别版本不匹配");}DetectionMetricsmetrics=evaluator.evaluate(command.candidate(),sampleSet,command.runtime(),command.inputSize(),command.threshold());booleanapproved=metrics.recall()>=command.minRecall()&&metrics.endToEndMs()<=command.maxLatencyMs()&&metrics.map5095()>0;decisionRepository.save(command,metrics,approved?"PILOT":"REJECTED");returnCandidateDecision.of(approved,metrics);}这里保存的是“评测事实”,不是产品核心策略。模型可以替换,阈值也可以变化,但每一次试运行都必须能回答:为什么选择它、在哪个样本集上测过、它没有覆盖哪些边界。
七、预期输出与自动测试
规则方案:给出规则版本、困难样本误触发率和适用机位说明。 检测候选:给出类别级 precision/recall、mAP50-95、推理与端到端耗时。 试运行决定:给出通过/拒绝状态及其阈值依据,而不是只有一个模型文件名。deftest_candidate_with_high_map_but_low_recall_is_rejected():metrics=DetectionMetrics("candidate-a","SET-B",0.95,0.72,0.70,20,35)assertnotcan_promote(metrics,min_recall=0.85,max_end_to_end_ms=80)deftest_candidates_are_sorted_by_recall_after_threshold_filter():low=DetectionMetrics("low","SET-A",0.9,0.70,0.6,10,20)high=DetectionMetrics("high","SET-A",0.9,0.90,0.6,20,40)assert[x.candidateforxincompare([low,high],0.80,80)]==["high"]@TestvoidshouldRejectCandidateWhenCategoryVersionDiffers(){fixture.readySet("SET-A","category-v2");DetectionCandidateCommandcommand=command("SET-A","category-v1");assertThrows(BizException.class,()->service.decide(command));verifyNoInteractions(evaluator);}八、SQL 验证:模型版本、样本集和结果能否对上
CREATETABLEdetection_candidate_evaluation(idBIGINTPRIMARYKEYAUTO_INCREMENT,evaluation_noVARCHAR(64)NOTNULL,candidate_nameVARCHAR(128)NOTNULL,candidate_versionVARCHAR(128)NOTNULL,sample_set_codeVARCHAR(64)NOTNULL,category_versionVARCHAR(64)NOTNULL,runtime_kindVARCHAR(32)NOTNULL,input_sizeINTNOTNULL,precision_valueDECIMAL(8,5)NOTNULL,recall_valueDECIMAL(8,5)NOTNULL,map50_95DECIMAL(8,5)NOTNULL,inference_msDECIMAL(10,3)NOTNULL,end_to_end_msDECIMAL(10,3)NOTNULL,decisionVARCHAR(16)NOTNULL,create_timeDATETIMENOTNULL,UNIQUEKEYuk_detection_evaluation_no(evaluation_no),CHECK(decisionIN('PILOT','REJECTED')));-- 允许试运行的候选必须有正的评测指标,预期为空。SELECTevaluation_noFROMdetection_candidate_evaluationWHEREdecision='PILOT'AND(recall_value<=0ORmap50_95<=0ORend_to_end_ms<=0);-- 同一候选若用了不同类别版本,不能混合比较,预期为人工复核清单。SELECTcandidate_name,candidate_version,COUNT(DISTINCTcategory_version)AScategory_versionsFROMdetection_candidate_evaluationGROUPBYcandidate_name,candidate_versionHAVINGCOUNT(DISTINCTcategory_version)>1;-- 端到端耗时不应小于纯推理耗时,预期为空。SELECTevaluation_noFROMdetection_candidate_evaluationWHEREend_to_end_ms<inference_ms;图5:可以被复核的选型结论,必须回溯到同一批样本、同一类别定义和同一运行条件。
九、误区、边界与上线验收
| 常见误区 | 为什么不成立 | 应如何验证 |
|---|---|---|
| 公开 mAP 高就适合本项目 | 公开类别、数据分布、设备与阈值不同 | 用自己的困难帧和类别映射重测 |
| FPS 高就是实时 | 可能没有包含解码、后处理、绘制和编码 | 同时报推理耗时与端到端耗时 |
| 检测框不稳就无限调阈值 | 遮挡、模糊、类别定义或训练数据也可能是原因 | 单独统计漏检、误检、小目标与遮挡样本 |
| 换大模型一定能解决 | 输入质量、目标尺寸和部署预算可能才是瓶颈 | 先做输入质量与任务边界检查 |
| 检测通过就能判断动作 | 动作依赖关节关系与时间顺序 | 进入关键点或时序动作模型评测 |
上线前应冻结评测集版本、明确关键类别的最低 recall、记录运行时与输入尺寸、保留困难样本的抽检结果,并在目标设备做一次端到端测量。模型更新后必须重新评测;不能因为文件名相同或平均分相近,就沿用旧结论。
十、小结和延伸阅读
目标检测选型不是在 YOLO 和 Transformer 名称之间站队,而是先明确需要识别什么、漏检能否接受、目标设备能承受多少时延,再用固定样本集和统一口径比较。传统规则、单阶段检测器和实时 Transformer 都有清晰位置;真正可复用的能力,是把它们放进可验证的选择过程。
- Ultralytics Model Benchmarking
- Ultralytics RT-DETR Documentation
- RT-DETR Official Implementation
- Ultralytics Model Export
- MySQL 8.0 Reference Manual: CREATE TABLE