基于YOLO的行人检测实战:推理、训练与部署完整指南
发布时间:2026/10/2 14:46:07来源:尧图网络
简介基于YOLO的行人检测项目以压缩包形式提供面向毕业设计、课程设计及计算机视觉入门学习者解决动态场景下行人实时检测与定位问题。压缩包共24个文件容量261KB主要包含6个Python脚本、9个pyc编译文件、模型数据、类别标签文本、图片样本及字体等辅助文件。其中检测执行脚本负责加载预训练模型并输出行人框锚框生成脚本用于计算先验框配置脚本定义网络结构与超参数另有shell脚本便于Linux环境一键运行整体目录结构清晰适合快速上手与二次开发。项目集成YOLO系列算法的改进思路兼顾检测速度与精度可作为智能监控、自动驾驶等场景的算法原型。已有85人学习下载适合用来完成课程作业或理解目标检测实战流程。1. 别急着训练先把zip里的行人检测器跑成最小可行版“基于yolo的行人检测.zip”这个包下载的人多半正卡在两个坎上一是模型出了demo但不知道能不能换到自己的摄像头画面二是拿到的权重在公开视频上表现不错一到自己的路口、楼道、园区就误检频发。我先给一个反直觉结论行人检测项目里决定成败的不是模型精度而是置信度阈值、数据分布和部署端算力这三件事。所以开箱第一步我不是让你看论文而是按“推理最小集 → 场景误检评估 → 训练微调 → 部署”的顺序把它当成一个能改参数、能换数据、能接输出接口的工程件来验收。这套流程对YOLOv5、YOLOv8和后续系列通用适合正要交安防demo、客流统计或毕业设计的开发者。2. 拆包先看什么YOLO行人检测项目的文件结构与模型选型2.1 YOLO系列怎么选从YOLOv5到YOLOv8行人检测场景的取舍不少“基于yolo的行人检测.zip”里同时躺着v5和v8两个版本的推理脚本这是好事但我见过更多人是随手把yolov8n.pt跑一遍就认为“项目通了”这是最典型的起步翻车。行人检测任务的特点是目标尺度变化大两米外的整人和五十米外的半身、头顶可能同时出现在一帧里目标密度不高但遮挡常见实际业务往往要求20FPS以上的实时输出。YOLOv5和YOLOv8在行人这个类别上的mAP差异通常在1到2个点以内真正拉开体验的是推理延迟和部署友好度。模型输入尺寸行人mAP参考部署难度适合场景yolov5n640偏低低树莓派、低性能边缘盒快速验证yolov5s640中等低通用监控、数据试跑性价比最高yolov8n640略高于v5s低中低端设备API上手快yolov8s640较高中有GPU服务器追求低漏检率如果包里还带了实例分割版本我的看法是当它是加分项别当主力。计数、跟踪、告警只需要bbox分割头会让边缘算力白扛一倍的浮点运算。现在确实有YOLO与transformer、多模态特征融合的改进方向但行人检测这种高频、低算力、单类别的任务CNN的YOLO系依然是舒适区没必要用重backbone换那一个点的mAP。2.2 解压后先认文件权重、配置、数据路径三件事拿到压缩包先别双击看图片用命令行把结构摸清楚这一遍能帮你判断这个包是从正经release打的还是从博客二道打包的。# 解压并建立项目根目录 unzip based_on_yolo_person_detect.zip -d yolo_person # 只看两层目录结构快速定位核心文件 find yolo_person -maxdepth 2 -type d | sort # 按大小排序权重文件和数据集占用一眼可见 du -sh yolo_person/* | sort -h这三条命令解决三件事解压路径、目录结构、体积分布。find -maxdepth 2是为了避免误入数据集深层目录浪费时间du按大小排序能看出weights/和datasets/谁占大头也能发现模型权重是不是真的被包进来了。接下来要认三样东西。第一是权重文件常见位置是weights/或runs/train/exp/weights/推荐直接用带best.pt后缀的那是验证集mAP最高的checkpoint。第二是数据配置文件一般叫person.yaml、data.yaml或coco128.yaml里面写死了train、val路径和nc、names跑训练前必须逐行核对。第三是标注格式打开一个Annotations/下的文件看是VOC的xml还是YOLO的txt这直接决定你后面能不能套用train.py。如果压缩包里连requirements.txt都没有你要做好准备环境问题会占掉你半天时间别急着跑模型。2.3 用命令行把推理跑起来第一张图的完整命令与预训练模型下载环境确认是第一步。很多包自带的是老版本依赖和你的Python版本对不上我一般会新建独立环境再跑避免把系统Python搞乱。# 确认torch/cv2版本cuda是否可用 python -c import torch, cv2; print(torch.__version__, torch.cuda.is_available(), cv2.__version__) # 新建环境按项目依赖安装 conda create -n yolo_person python3.9 conda activate yolo_person pip install -r requirements.txttorch在1.10以上、cv2在4.5以上跑v5和v8的主流代码都没问题。如果requirements.txt里给的版本特别老优先装包内指定的版本而不是系统已有的新版很多“torch和torchvision版本不匹配导致加载权重报错”都是从这里开始的。推理命令以YOLOv5为例v8只是把入口换成yolo predict参数含义完全一致。# 单张图片推理只保留person类别 python detect.py --weights weights/yolov5s.pt \ --source data/sample/test01.jpg \ --conf-thres 0.45 \ --iou-thres 0.5 \ --classes 0 \ --project runs/detect \ --name first_run两个参数要注意。--classes 0很关键行人检测只保留COCO类别编号0的person去掉bus、car这些无关框后处理会干净很多。--conf-thres先设0.45而不是默认的0.25目的是看误检率而不只是检出率开局压一点阈值能帮你快速判断权重是否匹配场景。第一次推理常见三个报错权重路径写错、--source里图片名包含空格、缺--project指定输出目录。如果包内没带预训练权重先检查README里给出的下载入口把权重放到weights/目录再跑不要跨目录引用。2.4 用OpenCV读摄像头做连续检测while循环与帧缓冲单图跑通之后很快就要接摄像头或RTSP视频流。这里就是“基于opencv的行人检测”的实际落点YOLO负责检测OpenCV负责取流和显示。# 连续读取摄像头帧并做YOLO推理 import cv2 import torch # 项目内自带yolov5仓库时用sourcelocal避免外网拉取hubconf model torch.hub.load(ultralytics/yolov5, custom, pathweights/yolov5s.pt, sourcelocal) cap cv2.VideoCapture(0) # 0为本地摄像头RTSP流换完整URL cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) while True: ok, frame cap.read() if not ok: break # 摄像头偶发断帧直接跳过这一轮 results model(frame, size640, classes0, conf0.45) rendered results.render()[0] # 在原帧上绘制检测框 cv2.imshow(person_detect, rendered) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()results.render()[0]返回的是绘制好框的BGR图像可以直接给imshow。size640对应训练输入尺寸为了帧率可以降到480但远处小目标会明显变弱安防场景慎用。conf0.45直接传入推理函数和上一条命令里的--conf-thres是同一个值。这里埋一个性能点不要无脑加线程“加速”先看瓶颈在哪。很多“卡顿”不是模型推理慢而是cap.read()和推理串行摄像头采集被阻塞。简单解法是调大cv2.CAP_PROP_BUFFERSIZE让视频帧先缓冲起来推理时取最新帧即可。摄像头偶尔返回空帧是常态代码里if not ok: break要保留不然会拿None帧去推理报奇怪的类型错误。3. 训练自己的行人检测模型数据标注与训练脚本3.1 行人检测数据集从哪来VOC person、COCO person与自采数据行人检测数据集很多新手会直接下载网上打包好的“行人数据集”里面可能混杂了VOC、COCO甚至爬虫抓的图。我的建议是先用公开集跑通流程再考虑自采。VOC2012里带person标签的图片大约两千多张做初版训练够用COCO的person实例更丰富但需要按类别过滤并处理crowd标注。做行人检测时crowd标注我建议保留因为拥挤本来就是真实场景删掉反而让模型在密集人群里表现变差。公开数据之外自采数据才是决定落地效果的部分。监控视角、夜间、雨天、俯仰角度这些是COCO和VOC覆盖不到的长尾。firc-dataset这类电力红外场景数据集也值得关注但要注意红外图是单通道喂给YOLO前先转成三通道否则训练时会报通道数不匹配。自采数据规模参考下表目标场景建议图片数建议实例数备注普通街景2000~50005000用COCO预训练权重初始化足够园区/校园监控5000~1000010000必须包含俯视与逆光样本夜间/雨雾每时段500与原数据按比例混入公开集夜间比例很低数量不是唯一指标同一个机位拍一千张几乎相同的帧价值远低于几十个不同场景各拍几十张。先保证场景多样性再堆数量这是行人检测数据集最容易被忽视的原则。3.2 标注转YOLO格式从VOC的XML到txt的四步脚本包里如果给的是VOC格式训练前必须转成YOLO的txt。这个转换脚本建议自己保留一份后面每次自采数据都要用。注意类别的编号规则VOC的person通常映射成YOLO类别0COCO的person也是类别0保持一致才能直接加载预训练权重。# voc_xml_to_yolo.py # 将VOC的xml标注转为YOLO的txt格式person固定编号0 import os import glob import xml.etree.ElementTree as ET def convert(xml_path, out_dir, class_map{person: 0}): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue # 只保留person其它类别跳过 box obj.find(bndbox) x1, y1 float(box.find(xmin).text), float(box.find(ymin).text) x2, y2 float(box.find(xmax).text), float(box.find(ymax).text) # 防止标注越界导致训练时bbox异常 x1, y1 max(x1, 0), max(y1, 0) x2, y2 min(x2, w), min(y2, h) if x2 x1 or y2 y1: continue # 过滤掉退化框 cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{class_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) if lines: out_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) for xml_file in glob.glob(VOC2007/Annotations/*.xml): convert(xml_file, labels/)转换逻辑分四步解析xml尺寸、遍历object、归一化中心点与宽高、写出txt。脚本只保留person类并把越界框裁回图像边界这是训练时不报warning的关键。归一化精度保留6位小数足够没必要用float64。输出txt必须与jpg同名并放在训练脚本指定的labels目录下YOLO的目录约定通常是images/与labels/同级一张图对应一个txt。转换完一定要抽10张图把txt标签画回图像上人工核对。这一步虽土但能拦住一大半标注问题导致的翻车特别是坐标归一化方向搞反、xywh中心点与左上角混淆这类低级错误。3.3 修改数据配置与超参数anchor、batch、img_size怎么设训练脚本需要一份数据配置yaml这是v5和v8共同的入口。第一遍跑通用别人给的最稳妥不要急着魔改。# person.yaml训练数据的路径配置 train: datasets/person/images/train val: datasets/person/images/val nc: 1 names: [person]train和val路径建议写相对项目根目录的路径避免换机器后绝对路径失效。nc: 1代表只有person一个类别names里的顺序必须与标注txt里的编号0对应顺序写反会在评测阶段出现“类别对不上”的诡异问题。启动训练命令如下参数已按项目常用配置填好python train.py --data person.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 32 \ --epochs 100 \ --multi-scale \ --patience 20 \ --project runs/train \ --name person_v1四个参数值得反复调。--img 640是输入尺寸行人目标小建议从640起步降到416能提速但远距离小目标会漏。--batch 32在V100上约需12GB显存T4降到162080Ti可跑24显存不足时优先减batch不要先减img小图对小目标伤害更大。--multi-scale每10轮随机变化输入尺寸相当于免费的数据增强代价是loss曲线抖动明显属于正常的。--patience 20表示连续20个epoch验证集mAP不涨就早停既省显卡也能防止过拟合设太大会白烧电。anchor这块YOLOv5的默认anchor对行人这种“高瘦目标”已经够用不建议一开始就用k-means重算等训练完发现recall偏低再考虑。真正值得关心的是训练日志里box_loss、cls_loss、obj_loss三个损失项它们分别对应框回归、分类和置信度是下一节要盯的核心信号。3.4 启动训练并盯住loss哪些曲线说明模型没在学训练跑起来后不要盯着黑框看进度条发呆打开results.csv看指标变化# 用results.csv看每一轮的指标比盯终端进度条更直观 column -s, -t runs/train/person_v1/results.csv | tail -n 30results.csv是YOLO训练过程自动落盘的表格包含每个epoch的box_loss、obj_loss、cls_loss、mAP和precision等列。不同版本的列名会有差异比如YOLOv5 6.0之后把metrics列名改成了metrics/mAP_0.5:0.95读的时候按列名定位不要硬套旧版模板。想画图的可以把csv读进pandas画三列loss随epoch的变化曲线。判断训练是否正常的经验有三条。第一box_loss、obj_loss、cls_loss三个loss第一轮冲高是warmup阶段的正常现象但几十轮后obj_loss纹丝不动说明模型可能没拟合到真值优先查数据集的标注是否错位。第二训练集loss持续下降、验证集mAP同时抬升才是真正在学如果loss降了但mAP始终趴着多半是标签噪声大或者类别编号错。第三如果某一轮loss突然跳到nan基本是下一章要说的BN崩溃不要继续等马上停。提示训练中断后想恢复--resume参数可以指定best.pt路径继续跑但如果是loss出现nan后resume建议从nan之前那个正常epoch的权重恢复输出目录里每个epoch都会留权重文件找最近的健康版本。4. 行人检测避坑误检、漏检、BN崩溃与部署翻车4.1 行人稀疏场景的误检先从置信度阈值查起现象园区监控里把路灯杆、树干、墙角阴影框成行人置信度还能到0.5以上告警链路被打爆。原因有两层最直接的是推理时阈值太低YOLO默认conf 0.25是兼顾召回率的值但“行人稀疏、误报会被客户投诉”的场景0.25必然不合适再往深一层公开权重在密集街景上训练对“类人形物”的背景特征没有足够抑制。解决路径第一步把conf从0.25提到0.45观察误检是否明显减少第二步把--iou-thres从默认0.45调到0.5NMS把重叠的冗余框压更狠。调完后用一张相同的测试图跑一遍并保存结果做前后对比别凭感觉说“好像好多了”。如果阈值已经到0.6误检还大量存在说明权重本身与场景不符不要再调参硬扛回第3章补数据重训。另外要提一个很多人没意识到的问题背景里没有单独类别时类人形物会被强行分到person。这类负样本靠调阈值是压不完的需要在训练集里补充包含路灯、树干、塑像的负样本并用6.3的可疑帧回收管线持续收集。4.2 夜间与逆光场景漏检数据增强与曝光补偿现象白天mAP有0.8切到夜间测试视频人几乎全漏偶尔把车灯当成人。原因在于预训练权重的训练分布里白天街景占绝对多数夜间样本占比极低模型学到的行人纹理特征在光照翻转后大面积失配。解决要数据和处理两头走。数据侧给训练集混入夜间自采帧并增强HSV变换中的saturation与value抖动夜间hue抖动意义不大重点是亮度和饱和度。处理侧推理前对输入帧做gamma校正把暗部细节拉出来再送模型。# gamma校正适合逆光与夜间输入帧在cap.read()之后调用 import cv2 import numpy as np def gamma_adjust(frame, gamma1.6): lut np.array([pow(i / 255.0, 1.0 / gamma) * 255 for i in range(256)]).astype(uint8) return cv2.LUT(frame, lut)gamma大于1提亮暗部小于1压暗过曝亮部。这段是逐通道做同一个LUT映射不会造成偏色。如果画面本身太暗gamma拉完再叠加一次CLAHE效果更好。夜间评估还有一个容易踩的坑不要只看mAP要看漏检分布。漏检大多集中在远距离小目标上如果只盯着总体指标调参容易反复试错却找不到方向。4.3 训练中BN崩溃loss爆炸的常见诱因现象训练进行到30到70个epoch时train loss突然冲高甚至变成nan后续指标全部失效。这就是训练日志里“yolo训练中bn崩溃”最经典的现场。原因最常见的有两个。一是batch太小比如batch4BN统计量的方差在后期被个别极端样本带着走统计量乘性放大后梯度爆炸二是学习率太大YOLOv5默认lr00.01是按V100类的训练卡调的换到T4或2080Ti上不降学习率后期容易不稳。解决确认batch不低于8行人数据集建议16起步显存实在不够把图片尺寸降到480而不是把batch压到4。学习率从默认0.01降到0.001~0.005起步观察前20轮loss是否稳定。如果已经出现nan从最近的健康checkpoint恢复而不是从nan那一轮继续同时把数据增强里的mosaic幅度调低或提前关闭它的统计分布和原始图像差异大是BN崩溃的另一个推手。GroupNorm能根除这类问题但需要改网络结构临阵换枪风险太大不建议训练到一半再做这种手术。4.4 边缘设备部署rk3588与树莓派的算力约束现象同一个权重在GPU服务器上跑得很流畅换成rk3588只剩8到15FPS树莓派4B只有2到5FPS视频卡得没法用。原因很直接rk3588的NPU算力约6TOPS树莓派4B更是靠CPU硬扛同样的模型没有做量化和算子适配算力差摆在那。网上那些“一键部署脚本”多数只解决了环境安装没解决推理性能别迷信。解决路径分两步。第一步先量性能瓶颈在推理循环里分别打印解码耗时、模型推理耗时、NMS耗时很多“卡顿”其实是视频解码瓶颈而不是模型本身慢。第二步按设备选方案rk3588用RKNN-Toolkit2把best.pt先导出为ONNX再转RKNN并做INT8量化。量化校准集至少200张行人图片不能随便用200张风景图否则量化误差会把行人目标吃掉。树莓派只建议跑yolov5n输入尺寸降到480或416视频解码交给GPU硬解CPU只跑模型和前处理。# 常见转换链条pt - onnx - rknn python export.py --weights best.pt --include onnx python export_rknn.py --model best.onnx --quantized int8 --dataset quant_list.txt第一条是基于Ultralytics的导出脚本第二条对应RKNN工具包内的转换脚本具体参数以你板子上装的RKNN SDK版本为准。转完后一定要在板端用实际视频流验证不要在PC的模拟器里看结果NPU的算子融合行为和GPU完全不同。边缘盒子落地还有一个容易忽略的点INT8量化后模型输出的置信度分布会整体偏移之前在第2章定的0.45阈值要重新标定常见现象是量化前0.45的框量化后全变成0.3误检率回升。先跑100帧统计置信度分布再定部署阈值。5. 把检测结果接出去跟踪、计数与去重的实战写法5.1 用DeepSORT做跨帧跟踪行人ID与稳定轨迹检测解决“哪里有行人”跟踪解决“同一个行人在连续帧里保持同一个ID”。多人场景不做跟踪计数会重复累加告警会一帧一个样。DeepSORT是目前落地最稳的跟踪方案内置卡尔曼滤波负责位置预测ReID外观特征负责遮挡后重连。项目里一般通过deep_sort_realtime接入这个库封装了检测框到跟踪轨道的全部逻辑接口比原版顺手。# DeepSORT接入示例检测结果 - tracker - 稳定ID from deep_sort_realtime.deepsort_tracker import DeepSort import cv2 tracker DeepSort(max_age30, nn_budget100) cap cv2.VideoCapture(test_video.mp4) while True: ok, frame cap.read() if not ok: break results model(frame, size640, classes0, conf0.45) boxes results.xyxy[0][:, :4].cpu().numpy() scores results.xyxy[0][:, 4].cpu().numpy() cls_ids results.xyxy[0][:, 5].cpu().numpy() dets [] for box, score, cls in zip(boxes, scores, cls_ids): if int(cls) 0 and score 0.45: dets.append((box.tolist(), float(score), 0)) # 第三个参数是类别编号 tracks tracker.update_tracks(dets, frameframe) for t in tracks: if t.is_confirmed(): tid t.track_id x1, y1, x2, y2 map(int, t.to_tlbr()) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, fID{tid}, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(track, frame) if cv2.waitKey(1) 0xFF ord(q): breakmax_age30表示目标消失后状态保留30帧越大越能扛遮挡但也会导致目标离开很久后ID残留。nn_budget100限制外观特征库长度防止长时间运行内存膨胀。to_tlbr()返回整数化的[x1,y1,x2,y2]可以直接画框。如果场景是密集路口、行人互相遮挡严重DeepSORT会频繁切换ID这种场景我会换ByteTrack它把低置信度检测框也纳入关联拥挤场景的ID稳定性明显更好。注意安装了deep-sort-realtime之后轮子里自带的模型文件会自动下载离线环境要先把特征模型放到缓存目录否则tracker初始化会卡在联网。5.2 用计数线与区域判定做人数统计固定机位做进出人数统计最常用的方法是在画面里画一条虚拟线用行人中心点穿越线判断进出方向。这个方法比用脚部点稳定因为检测框底部受遮挡影响大中心点在多数时候是可信的。# 全局状态帧间保持 last_center {} count_in, count_out 0, 0 def cross_count(prev, cur, line_y480, directiondown): if prev is None or cur is None: return 0 if direction down and prev[1] line_y cur[1]: return 1 if direction up and prev[1] line_y cur[1]: return 1 return 0 # 每帧跟踪器更新后执行 for t in tracks: if not t.is_confirmed(): continue x1, y1, x2, y2 map(int, t.to_tlbr()) cx, cy (x1 x2) / 2, (y1 y2) / 2 prev last_center.get(t.track_id) count_in cross_count(prev, (cx, cy), directiondown) count_out cross_count(prev, (cx, cy), directionup) last_center[t.track_id] (cx, cy) if t.time_since_update 5: last_center.pop(t.track_id, None) # 目标消失久了清理状态这里必须依赖上一节的track_id去重否则同一个行人来回走动会被反复计数。time_since_update是跟踪器内置的“目标多久没更新”字段超过阈值就清掉它的历史中心点防止一个消失的行人数据残留在map里干扰后续判定。区域人数统计的逻辑类似只是把穿越线判定换成cv2.pointPolygonTest判断中心点是否在多边形内区别是一个数“进出”一个数“驻留”。计数精度上线位置越低越不容易漏但也越容易受检测框抖动影响建议把线画在画面中下部并保证行人中心点经过时检测框高度至少30像素。5.3 输出结构化JSON把检测结果交给上位机边缘盒子一般需要把结果推给上位机或后端用JSONL逐行写文件比一次写整个JSON数组更抗损坏后端按行读取一条数据坏了不影响后面。import json import time def frame_to_json(frame_id, tracks): persons [] for t in tracks: if not t.is_confirmed(): continue x1, y1, x2, y2 map(int, t.to_tlbr()) det_conf float(t.det_conf) if t.det_conf is not None else None persons.append({ track_id: t.track_id, bbox: [x1, y1, x2, y2], conf: round(det_conf, 4) if det_conf else None, }) return { frame_id: frame_id, timestamp: time.strftime(%Y-%m-%d %H:%M:%S), person_count: len(persons), persons: persons, } # 每5帧写一次落盘降低IO压力 with open(detect_result.jsonl, a, encodingutf-8) as f: for frame_id in range(total_frames): out frame_to_json(frame_id, tracks) if frame_id % 5 0: f.write(json.dumps(out) \n)track_id让上位机可以做联动判断比如同一个ID持续3秒出现在禁区内就触发告警这比裸bbox更有业务价值。conf保留4位小数是省空间t.det_conf在某些跟踪器实现里可能是None为None时不要硬塞进JSON否则上位机拿到null会解析失败。输出时间戳建议统一用本地时间边缘盒子和后端服务器时区不一致时排查日志对不齐很痛苦。这种结构化输出同样适用于MQTT或WebSocket推送字段不用改只换传输层。6. 验证与调优一整套混淆矩阵、PR曲线与误判复盘项目交出去之前我建议花一整天做验证和调优而不是直接交付。这一章不是讲指标概念是讲三个我实际吃过亏的细节。6.1 混淆矩阵总和为什么对不上多类别统计的坑训练或验证后生成的confusion_matrix.png很多人会发现行列之和与总数对不上以为统计有问题。其实这不是bugYOLO的混淆矩阵统计的是预测类别与真值类别的关系一个真值目标可能匹配多个预测框未达置信度阈值的预测又会被归入background格。所以行和列的量纲不同总和自然不唯一。要看真实分类正确率建议看mAP计算过程中生成的tp/fp统计文件那是目标级统计。6.2 用PR曲线挑置信度阈值别再凭感觉设confval结束后会输出precision与recall两条曲线横轴是置信度阈值。挑阈值的逻辑业务不能漏人就在recall曲线开始陡降的拐点右侧取precision较高的位置业务不能误报就取precision曲线走平的位置。我一般会把0.3、0.4、0.5三个阈值下的P、R值打印成表选“误报可接受且recall最高”的那个写进第2.3节的推理命令。注意评估集的场景分布要和最终使用场景一致拿白天街景评估集去定夜间阈值就是刻舟求剑。6.3 落盘推理日志让误判可复盘最后一个习惯与其对着mAP纠结不如让现场系统把可疑帧存下来。做法是当置信度落在0.35到0.5这个模糊区间或者跟踪器刚创建就消失的ID出现时把该帧图像连同检测数据写进一个review目录。每天晚上花十分钟翻一遍把误检帧挑出来回填训练集。我现在每换一个场景第一周都会保留这套“可疑帧回收”管线它帮我挡掉过太多网上数据集给不了的现场数据也帮我省掉了很多次无效重训。数据和场景永远比网络结构更值钱。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网