十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLO行人检测训练实战:基于2859张人数据集的数据处理与优化

YOLO行人检测训练实战:基于2859张人数据集的数据处理与优化 简介这是一份面向YOLO系列目标检测算法训练与验证的人体数据集资源带标签图像规模为2859张适合需要现成标注数据的计算机视觉学习者和开发者。压缩包约185.64MB共2000个文件文件明细以VOC格式的xml标签为主资源说明中同时提供YOLO格式txt标签两类标签分别保存在不同文件夹中便于按需选择。数据集已经完成划分并附带data.yaml配置文件可适配YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等常见算法版本导入后可较快开展训练、验证与测试。YOLO标签中类别索引从0开始中心点坐标及宽高均以图像尺寸归一化取值在01之间字段含义清晰方便脚本读取和数据检查。目前已有201人学习下载对于需要快速获取现成人像标注数据、节省人工标注时间的学习者或项目团队是一套实用性较强的数据集资源。1. 人数据集 ZIP 不是模型是训练路线的起点yolo算法-人数据集-2859张图像带标签.zip这个压缩包文件名里已经把它最重要的三件事讲清楚了算法框架是YOLO任务是行人检测内容是2859张带标签的图片。很多人下载这类包之后直接解压找best.pt翻遍目录才发现里面只有一个images文件夹加一个labels文件夹——这不是开箱即用的模型权重而是能让你跳过“爬图、清洗、标注”这三个最耗时环节的训练素材包。2859张图在深度学习里属于中小规模数据集它的价值不在于“数据量大”而在于“启动成本低”。如果你正在做安防摄像头人形识别、商场客流统计、或者自动驾驶行人感知的预研验证这份数据够你跑通一整套yolo目标检测流程。代价是精度天花板明显从零训练必欠拟合正确姿势是基于COCO预训练权重做迁移学习。另外“带标签”三个字一般指YOLO格式的TXT标注文件不是COCO的JSON也不是LabelMe的XML如果你的工作流依赖后两者先做好格式转换的心理准备。这篇文章就按“校验整理、格式理解、参数训练、场景优化”这条实际动手顺序往下走。2. 拆包后的三个动作校验、清洗、切分2.1 解压后先核对文件数量和目录对应关系拿到任何带标签数据集第一件事不是训练而是确认文件完整性。用unzip解压后我会先看目录结构再用find命令统计图片和标签的数量差unzip yolo算法-人数据集-2859张图像带标签.zip -d ./people_dataset cd ./people_dataset # 统计图片数量 find ./images -type f \( -name *.jpg -o -name *.jpeg -o -name *.png \) | wc -l # 统计标签数量 find ./labels -type f -name *.txt | wc -l # 找出有图片但没标签的文件名 for img in $(ls ./images); do base${img%.*} if [ ! -f ./labels/${base}.txt ]; then echo missing label: ${img} fi done这段脚本的逻辑很简单图片和标签文件名一一对应如frame_0001.jpg对应frame_0001.txt按 basename 匹配查找缺失项。如果发现缺标签的文件超过几十个建议直接删除对应图片而不是手工补标——手工标注2859张图的工作量对你来说不值当数据集本身的定位就是“够用即可”。IT从业者常犯的一个错误是看到.zip就认为万事大吉忽略了压缩包内部文件权限和编码问题。Windows 下压缩的包在 Linux 上解压偶尔会出现中文文件名乱码、换行符变成\r\n导致解析失败建议解压后用file命令抽查几个标签文件确认编码。2.2 用一段脚本清掉坏图和无法解析的标签文件名齐全不代表数据可用。坏图、空标签、超过边界的目标框这三类问题几乎存在于所有公开数据集中需要主动扫一遍。下面是一段独立的检查脚本import os from PIL import Image IMG_DIR ./images LAB_DIR ./labels bad_images, bad_labels [], [] for fname in os.listdir(IMG_DIR): img_path os.path.join(IMG_DIR, fname) base, ext os.path.splitext(fname) # 检查图片能否正常打开 try: img Image.open(img_path) img.verify() except Exception: bad_images.append(fname) continue # 检查对应标签文件是否存在且非空 lab_path os.path.join(LAB_DIR, base .txt) if not os.path.exists(lab_path) or os.path.getsize(lab_path) 0: bad_labels.append(fname) print(f坏图片数量: {len(bad_images)}) print(f缺失/空标签数量: {len(bad_labels)}) # 清理注意先备份再删 for fname in bad_images: os.remove(os.path.join(IMG_DIR, fname)) for fname in bad_labels: os.remove(os.path.join(IMG_DIR, fname))img.verify()只检查文件头是否完整不负责解码整张图片速度极快。空标签文件则说明这张图没有任何目标在yolo训练中它会作为纯背景样本参与训练影响不大但如果你是按“图数标签数”来验收数据集这种文件会干扰统计。如果你打算把这个数据包作为长期资产复用清完垃圾后顺手打一个新包是值得的。压缩建议用zip -r people_dataset.zip images labels -x *.DS_Store把 macOS 和 Windows 系统文件排除在外的同时保证路径干净。2.3 按 8:1:1 切分数据集yolo官方训练脚本默认从data.yaml读取train和val两个路径test可选——若缺省训练完成后直接用同一批val做评估会得到虚高的指标。我的做法是切三份train/val/test 按 8:1:1 分配import os import random from pathlib import Path random.seed(42) IMG_DIR Path(./images) LAB_DIR Path(./labels) # 收集所有文件名不含扩展名 all_bases [p.stem for p in IMG_DIR.glob(*.jpg)] all_bases [p.stem for p in IMG_DIR.glob(*.png)] all_bases list(set(all_bases)) random.shuffle(all_bases) total len(all_bases) train_split int(total * 0.8) val_split int(total * 0.9) splits { train: all_bases[:train_split], val: all_bases[train_split:val_split], test: all_bases[val_split:], } # 生成目录结构 for split in splits: (Path(./dataset) / split / images).mkdir(parentsTrue, exist_okTrue) (Path(./dataset) / split / labels).mkdir(parentsTrue, exist_okTrue) for base in splits[split]: # 只做软链接避免复制存储开销 src_img IMG_DIR / f{base}.jpg src_lab LAB_DIR / f{base}.txt if not src_img.exists(): src_img IMG_DIR / f{base}.png os.symlink(src_img.resolve(), Path(f./dataset/{split}/images/{src_img.name})) os.symlink(src_lab.resolve(), Path(f./dataset/{split}/labels/{src_lab.name})) print(ftrain: {len(splits[train])} val: {len(splits[val])} test: {len(splits[test])})需要说明两点。第一软链接适合你留在原目录继续修改标注的情况准备发布数据包或放到其他机器上训练时请改成shutil.copy或直接将目录移动过去。第二随机切分不代表分布均衡如果你这批图片包含室内/室外、白天/黑夜多个场景最好按场景分组后再切——人工先看一眼图片内容再决定如何切分是数据工程师的基本素养。脏数据形式检测方法处理建议图片解码失败PIL.Image.verify()直接删除标签文件为空os.path.getsize() 0保留为背景样本或删除标签超过图片边界读图宽高后比对归一化坐标裁剪到边界内或删除重复图片计算感知哈希去重防止训练集/验证集泄露3. YOLO 标签格式解析改错一个坐标就丢一个目标3.1 标签文件中每个数字的含义YOLO格式标签是一个TXT文件每一行描述一个目标格式固定为五个字段0 0.5234 0.4801 0.3492 0.7428 0 0.8120 0.2311 0.1604 0.4355第一个数字是类别ID从0开始计数行人数据通常只有0一类后四个数字依次是x_center、y_center、width、height全部相对于图片宽高做了归一化。假设图片是 1920x1080第一行对应的实际边界框像素坐标为x_center 0.5234 * 1920 ≈ 1004.9 y_center 0.4801 * 1080 ≈ 518.5 width 0.3492 * 1920 ≈ 670.5 height 0.7428 * 1080 ≈ 802.2换算到左上角表示法就是(x_min, y_min) (669.7, 117.4)右下角(x_max, y_max) (1340.2, 919.6)。刚才在2.2里提到的“坐标越界”指的就是这些还原后的像素值跑到图像边界以外比如x_max 1920或y_min 0。由于YOLO坐标全部是比例值0.5意味着目标中心在图片正中央。这个设计让标签不依赖具体图片分辨率同一份标注用于训练和推理时若图片被拉伸目标框依然保持比例正确。3.2 从像素坐标转换到归一化坐标你从其他标注工具导出的数据大概率是x_min, y_min, x_max, y_max或cx, cy, w, h的像素值形式。写一个统一的转换函数能覆盖 kitti标注转yolo 这种常见操作def convert_to_yolo(img_width, img_height, box): 将像素坐标边界框转换为YOLO格式归一化坐标 box 支持两种输入 - [x_min, y_min, x_max, y_max] - [cx, cy, w, h] return: class_id, x_center, y_center, width, height if len(box) 4: x_min, y_min, x_max, y_max box w x_max - x_min h y_max - y_min cx x_min w / 2 cy y_min h / 2 else: cx, cy, w, h box # 防止除零 assert img_width 0 and img_height 0 # 归一化 x_center cx / img_width y_center cy / img_height width w / img_width height h / img_height # 处理越界将超出边界的部分裁剪回 [0, 1] x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) return [x_center, y_center, width, height]值得警惕的是“越界即裁剪”这种处理方式。当边界框一半超出图片时裁剪后目标只剩半截却仍然保留这个标签模型会学到“半个人也算完整目标”的错误信号。我的建议是当裁剪比例超过 30% 时直接舍弃这行标注只有轻微越界比如1到2个像素时才做修正。3.3 类别映射和文件编码中的隐蔽错误带标签数据集常见的问题是类别ID和你预期不一致——0明明该是行人打开发现是一辆车。拿到标签后先别急着训练挑十几张图片把标签按归一化坐标画回图上肉眼核对一下。绘图用cv2.rectangle即可注意在读取标签时用float()而不是int()强转坐标import cv2 def draw_yolo_boxes(img_path, label_path): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue class_id int(parts[0]) x_center, y_center, bw, bh map(float, parts[1:]) # 还原像素坐标 x1 int((x_center - bw / 2) * w) y1 int((y_center - bh / 2) * h) x2 int((x_center bw / 2) * w) y2 int((y_center bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(class_id), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imshow(check, img) cv2.waitKey(0) cv2.destroyAllWindows()问题表现排查方向类别ID偏移画出来的框对应的物体类型和预期不符检查训练数据data.yaml的names顺序标签行数超过目标数同一框被重复标注两次按文件名比对wc -l与图片内容TXT用制表符或空格混用split 后长度不是5统一替换\t为空格标签文件是GBK编码open后报UnicodeDecodeError用codecs或转成UTF-8yolo损失函数对坐标误差非常敏感一个错误的标注会在训练时产生两个方向的梯度一是把目标中心拉向错误位置二是放大缩放因子。单个坏标签的影响比想象中大得多因为模型会认为那张图上的内容“就应该那样”。4. 用这份数据跑通 YOLOv8 训练全流程4.1 配置 dataset.yaml路径写绝对还是相对训练前先准备数据集配置文件这是yolo训练流程中最容易被忽略的环节。官方默认的配置结构如下path: ./people_dataset # 数据集根目录 train: train/images # 相对path的路径 val: val/images test: test/images nc: 1 # 类别数量 names: {0: person} # 类别名称必须与标签文件中的ID一致这里有个常见的路径陷阱path字段如果写成相对路径YOLO解析时会以配置文件所在目录为基准去拼接train/images而不是以当前终端工作目录为基准。为了避免拿到别的机器上就跑不起来的问题我建议path写绝对路径或者写./dataset并把这个yaml文件放到dataset目录的上一级。使用软链接切分时尤其要注意yolo不会解析软链接目标它只按路径读取文件链接断掉会表现为训练到一半报“No such file or directory”。4.2 训练命令与核心参数环境准备跳过安装好 ultralytics 后直接执行当前YOLO主版本用 yolo 命令v5系列用户换成 train.py 参数一致# 基础训练命令 yolo detect train \ data./people_dataset.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ device0 \ workers4 \ project./runs/detect \ nameperson_train_001参数背后的逻辑值得展开。modelyolov8n.pt表示加载COCO预训练权重nano 版本的模型体积小、推理快适合2859张这样的小数据量如果你的显存充足且对精度有更高要求换成yolov8s.pt或yolov8m.pt。imgsz640是输入分辨率目标yolo目标检测流程的标准值增大到1280会明显提升小目标召回但训练时间翻倍。batch取决于显存12GB显存跑yolov8n可以用32跑yolov8m则建议降到16。patience20是个容易被忽略的参数它代表验证集指标连续20轮不提升就提前终止训练。这个值设小了模型容易欠拟合设大了浪费时间。需要注意的是Ultralytics 在训练过程中保存三个权重文件last.pt最后一轮、best.pt验证集mAP最高、以及按轮数命名的检查点。最终部署取best.pt不要拿last.pt去测。4.3 训练日志怎么读训练启动后终端会实时刷新一张指标表。分析重点放在两个Loss列上train/box_loss边界框回归损失和val/box_loss验证集边界框损失。train loss持续下降而val loss止步不前甚至上升说明开始过拟合两个loss同时震荡不降大概率是学习率过大或数据集本身质量问题。epochs100配上patience20实际很少跑满100轮。小数据集在预训练权重的加持下通常30到50轮就能收敛。训练结束后先看验证集指标mAP50和mAP50-95。mAP50表示IoU阈值0.5时的平均精度是人检测场景最常看的指标通常0.8以上算可用mAP50-95是IoU从0.5到0.95的平均阈值更高目标任务做到0.5以上已经是相当好的模型。4.4 用测试集做一次独立验证很多人训练完直接用val集测试忽略了val在训练过程中已经参与了权重选择。用第三步切出来的test集才能说明模型真正的泛化能力yolo detect predict \ model./runs/detect/person_train_001/weights/best.pt \ source./dataset/test/images \ conf0.25 \ save_txttrue \ save_conftrue如果source指向一个文件夹yolo会批量推理文件夹内所有图片指向单张图片路径则只处理该图。conf0.25是置信度阈值低于0.25的预测框会被丢弃。把save_txttrue加上的好处是输出结果也以yolo标签格式写到runs/detect/predict/labels下你可以直接拿它和第二批人工标注做对比计算精确率和召回率。参数推荐值备注imgsz640小目标多可尝试1280显存需求增加约4倍batch16~32训练时观察显存占用OOM就减半lr00.01迁移学习不必太大从COCO权重出发0.01够用epochs100配合patience自动停止workers4或8数据加载线程Windows下不要设05. 行人检测场景里的三个数据优化技巧5.1 小目标行人的增强策略2859张图的样本量决定了模型极易过拟合到“大而清晰的人”。真实摄像头画面里远处的人往往只有几十个像素高增强手段的核心是让模型看到不同尺度的目标。Mosaic增强把四张图拼成一张是yolo官方默认开启的选项能对目标尺度分布起到一定的平滑作用。如果你需要更针对小目标的方案可以手动将训练集中的图片按2x2切块后重新标注等价于变相放大目标或者用SAHISlicing Aided Hyper Inference在推理阶段切块检测这个方案对大图上密集小物体非常有效典型场景是航拍图或整条街道监控的全景截图。5.2 误检回灌把错误帧收集成难例训练集行人检测的误判大多集中在一种情况背景中有人形纹理却无标签的区域被模型当作人输出了。这类问题靠调阈值解决不了因为模型天生不认为“人形树干”和“真人的背影”有什么区别。常规做法是训练完成后再跑一遍测试集把置信度大于0.3但实际是误报的预测框收集起来手动标注成负样本加到训练数据中重新训练一轮。这个思路的真正价值在于形成闭环第一次训练得到baseline用baseline挖掘出模型最脆弱的输入把困难样本喂回去后模型对场景的鲁棒性提升一个台阶。这类图像在二手数据集中往往埋得很深务必自己造。误检样本不需要很多200到500张就足够让模型意识到“这种区域不是人”。5.3 标注边界二次校验脚本最后给一个实战中真正能帮你省时间的技巧训练前跑一遍标签边界检查把所有疑似越界的标注画出来集中人工复核。python - EOF import cv2, os IMG_DIR ./images LAB_DIR ./labels OUT_DIR ./marked_boxes for fname in os.listdir(IMG_DIR): base, ext os.path.splitext(fname) lab_path os.path.join(LAB_DIR, base .txt) if not os.path.exists(lab_path): continue img cv2.imread(os.path.join(IMG_DIR, fname)) h, w img.shape[:2] suspicious False for line in open(lab_path): x, y, bw, bh map(float, line.strip().split()[1:]) x_px, y_px x * w, y * h bw_px, bh_px bw * w, bh * h if x_px - bw_px / 2 0 or y_px - bh_px / 2 0 or \ x_px bw_px / 2 w or y_px bh_px / 2 h: suspicious True # 绘制越界框 cv2.rectangle(img, (int(x_px - bw_px / 2), int(y_px - bh_px / 2)), (int(x_px bw_px / 2), int(y_px bh_px / 2)), (0, 0, 255), 2) if suspicious: os.makedirs(OUT_DIR, exist_okTrue) cv2.imwrite(os.path.join(OUT_DIR, fname), img) EOF脚本运行后marked_boxes目录下就是所有包含越界框的图片红色框代表标注异常。优先复核这些样本而不是全量检查可以将数据清洗时间压缩到原来的十分之一。处理完这一步这个ZIP数据包才算真正变成了你的模型资产。本文还有配套的精品资源点击获取
返回列表