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

资讯详情

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

YOLO仓库工人数据集实战:555张带标签图像的训练与标签转换指南

YOLO仓库工人数据集实战:555张带标签图像的训练与标签转换指南

简介:面向目标检测与YOLO系列算法实践者的仓库工人场景数据集,是训练、验证与测试YOLOv5、v8、v7、v9、v10、v11等检测模型的直接素材,适合算法学习者、企业安防研发或毕业设计使用。资源包共含1666个文件,以555张JPG图像为核心,配套555个YOLO格式TXT标注和555个VOC格式XML标注,另含1个YAML配置文件,整体压缩包约28.82MB,目录结构清晰、开箱即用。数据集已预先划分好训练集与验证集,无需自行拆分;TXT标注完整记录类别索引、归一化中心点坐标及宽高比例,XML标注遵循Pascal VOC标准,两种格式分文件夹存放,既可直接被YOLO系列原生读取,也便于在不同标注工具与训练框架间迁移。yaml文件内置类别定义与路径配置,可帮助快速搭建训练环境、节省格式转换时间。该资源尤其适用于仓储物流中的人员检测、安全帽佩戴识别等场景,也可作为“检测任务与格式适配”教学案例,或用于复现论文实验。目前已有57人学习下载。

1. 仓库工人数据集:YOLO算法落地前最该先拿到的555张带标签底料

在YOLO算法项目里,数据永远是第一个瓶颈。仓库工人检测这个场景很典型:背景复杂、货架遮挡多、光线不均匀、人员姿态变化大,拿通用数据集去训练,模型拿到真实仓库里基本是废的。这个仓库工人数据集有555张已标注图像,标签同时提供YOLO格式(txt)和VOC格式(xml),train/val/test已经分好,yolov5、v7、v8、v9、v10、v11都能直接训练和验证。适合两类人:一是刚接触YOLO训练,想用一份结构完整的数据集跑通全流程的新手;二是做智慧仓储、人员检测与行为分析项目的工程师,拿它当基线数据校验自己的训练配置和标签脚本。整包解压后不用改目录结构就能开跑,这一点对赶时间的人来说很关键。

2. 数据集的目录与标签解剖:先搞清楚555张图和两种标签怎么共存

2.1 目录结构:train/val/test划分与两个标签文件夹的真实关系

拿到zip解压后,第一件事不是急着训练,而是先把目录结构看清。常见做法是把images和labels分成两个顶层目录,各自再挂train、val、test三个子目录。这套数据集也是这种风格,但有个容易忽略的细节:标签本身拆成两个子文件夹,yolo格式的txt放一个,voc格式的xml放另一个,文件名和图像保持同名前缀。

用tree命令扫一遍确认布局:

unzip yolo算法-仓库工人数据集-555张图像带标签-warehouse-skj9z.zip -d warehouse_dataset find warehouse_dataset -maxdepth 4 -type d | sort

正常的目录树是这样:

warehouse_skj9z/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── yolo_format/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── voc_format/ │ ├── train/ │ ├── val/ │ └── test/ └── dataset.yaml

这里藏着第一个坑:yolo训练时,默认去images同级的labels目录找标签,也就是images/train对应labels/train。这套数据把txt放在labels/yolo_format/train,等于多隔了一层。训练前要么把txt拷到顶层labels/train,要么在配置里把路径写对,否则直接报找不到标签。我第一次拿到这种结构时没细看就跑,训练丢了一堆标签,后来才意识到是子目录层级的问题。数据集划分好指的是图像层面已经分好train/val/test,标签层面需要你按框架规则对齐。

2.2 YOLO格式:归一化坐标为什么用0到1的比例值

YOLO的txt标签每行一个目标,格式是五个空格分隔的值:

<class> <x_center> <y_center> <width> <height>

关键是x_center、y_center、width、height全部是相对于图像宽度和高度的比例,范围0到1,不是像素绝对值。这样设计的好处是:不管输入图像缩放到416×416还是640×640,标签都不用改,模型在letterbox或resize之后把比例值乘上当前宽高就能还原框位置。

具体换算举个例子。某张640×480的图像里,一个工人框的像素坐标是x=160、y=120、宽320、高240,归一化后是:

x_center = 160 / 640 = 0.25 y_center = 120 / 480 = 0.25 width = 320 / 640 = 0.50 height = 240 / 480 = 0.50

所以txt里记录的是0 0.25 0.25 0.5 0.5。yolov8、v9、v10这些新版本训练时会先对图像做letterbox,保持长宽比填充灰边到目标尺寸。如果直接拿像素坐标当标签,模型在resize后的图上做回归,框的位置就是错的。这是所有YOLO官方数据集强制归一化的根本原因,也是新手最容易犯的错误。

2.3 VOC格式:XML标签结构细节

VOC格式的表达完全不同,它记录的是绝对像素坐标和图像元信息。典型的xml标签:

<annotation> <folder>train</folder> <filename>img_0807_59.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>worker</name> <bndbox> <xmin>160</xmin> <ymin>120</ymin> <xmax>480</xmax> <ymax>360</ymax> </bndbox> </object> </annotation>

同一张图在txt里写0 0.25 0.25 0.5 0.5,在xml里写160 120 480 360,表达的是同一个目标框。格式不同,各有用途。VOC格式的优势是能直接读出坐标范围,便于人工核查和后续做图像裁剪;YOLO格式的好处是省去了解析成本,直接进训练管线。不少标注工具默认输出xml,而YOLO训练要txt,最终都要过一道转换。这份数据集两种都给,等于把最容易出错的环节提前解决了。

需要注意xml里的xmin、ymin、xmax、ymax通常用整数像素值,如果标注工具导出的是浮点坐标,做目标检测训练前最好先取整,避免后续坐标换算不一致。另外解析xml时object节点不保证按类别分组,用iter('object')遍历比findall更稳,因为findall只找直接子节点,而iter会递归查完整棵对象树。

2.4 训练前的标签完整性校验

无论哪份数据集,训练前都要过一遍完整性检查。我的习惯是写个小脚本统计每个图对应的标签是否存在、坐标是否越界、类别索引是否合理:

import os from collections import Counter image_dir = 'warehouse_skj9z/images/train' label_dir = 'warehouse_skj9z/labels/yolo_format/train' imgs = [f for f in os.listdir(image_dir) if f.endswith('.jpg')] missing, invalids, cls_counter = [], [], Counter() for img in imgs: base = os.path.splitext(img)[0] txt = os.path.join(label_dir, base + '.txt') if not os.path.exists(txt): missing.append(img) continue for line in open(txt, 'r').read().strip().splitlines(): parts = line.split() if len(parts) != 5: invalids.append((img, line)) continue cls, cx, cy, w, h = map(float, parts) if not (0 <= cx <= 1 and 0 <= cy <= 1 and 0 < w <= 1 and 0 < h <= 1): invalids.append((img, line)) cls_counter[int(cls)] += 1 print('缺失标签图片数:', len(missing)) print('非法标签行数:', len(invalids)) print('类别分布:', cls_counter)

脚本逐行检查五个值是否完整、归一化坐标是否落在0到1区间、类别索引是否集中在0。对这个仓库工人数据集,只有一个worker类,类别索引固定是0。如果统计结果里出现1或2,说明标签配置和训练配置没对上,训练前必须修。标签合法性检查看起来是小事,实际上大部分训练翻车都源于脏数据没提前清洗,这个步骤值得每次训练前都跑一遍,换机器换数据也一样。

3. 把数据集跑进YOLO:从dataset.yaml到训练参数全解

3.1 dataset.yaml的正确写法

YOLO系列训练前要一份数据集配置。yolov5叫data.yaml,yolov8/9/10/11叫dataset.yaml,写法几乎一致。新建warehouse.yaml:

# warehouse.yaml path: warehouse_skj9z train: images/train val: images/val test: images/test nc: 1 names: 0: worker

几句配置解决三个核心映射:path指定数据集根目录,train和val告诉框架去哪找图和标签,nc和names声明类别信息。path最容易出问题,很多人图省事写绝对路径,换机器或移动项目目录后直接报错找不到图像。我一般用相对路径,只要训练命令在warehouse_skj9z的上一级目录执行,yaml就能正确解析。

yolov8的yaml里test字段可选,但如果你要跑最终测试集评估,最好还是写出来。还有一个隐含约定:YOLO找标签的基准路径是images路径,自动把images替换为labels去找同名txt。这意味着images/train对应的标签在labels/train。这套数据的yolo标签在labels/yolo_format/train,和默认路径对不上。我的建议是训练前做一次扁平化复制:

mkdir -p warehouse_skj9z/labels/train cp warehouse_skj9z/labels/yolo_format/train/*.txt warehouse_skj9z/labels/train/ mkdir -p warehouse_skj9z/labels/val cp warehouse_skj9z/labels/yolo_format/val/*.txt warehouse_skj9z/labels/val/ mkdir -p warehouse_skj9z/labels/test cp warehouse_skj9z/labels/yolo_format/test/*.txt warehouse_skj9z/labels/test/

复制是成本最低的解决方案。不想复制就得改框架源码里的label路径逻辑,但那样等于给后续每个版本升级埋雷,没必要为省一次拷贝去动框架。

3.2 训练命令与关键参数

以yolov8为例:

yolo detect train model=yolov8s.pt data=warehouse.yaml epochs=100 imgsz=640 batch=16 device=0

参数逐个拆开讲。epochs=100表示训练100轮,对555张图来说,100轮够模型收敛,再往上容易过拟合。imgsz=640是输入图像边长,YOLO会自动等比缩放并填充到640×640。batch=16是批量大小,取决于显存,8G显存从16开始试,报OOM就降8。device=0指定第一块GPU,只有CPU就改device=cpu,但训练速度会慢很多,555张图也可能要跑几个小时。

yolov5的命令风格不一样:

python train.py --data warehouse.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100

v7也是类似train.py加参数,v9、v10、v11走ultralytics风格,和v8命令一致。这份数据集覆盖了两种命令行体系,掌握v8的写法,切到v5/v7只是换个参数名的事。根据这个数据规模,我一般先跑30轮看loss曲线是否稳定下降,再决定要不要加长。555张图不算多,模型可能30轮左右就收敛,硬跑100轮反而见过拟合。

3.3 训练监控:loss曲线与验证指标

训练开始后,终端里会输出每轮指标,重点看这几项:

val/box_loss 目标框回归损失 val/cls_loss 分类损失 metrics/precision 精确率 metrics/recall 召回率 metrics/mAP50 IoU=0.5的平均精度 metrics/mAP50-95 IoU阈值0.5到0.95综合平均精度

单类别检测场景里,mAP50能到0.9是合理目标,mAP50-95一般在0.6到0.75之间。如果PR曲线右上角不贴坐标轴,说明置信度分布不够干净,要回去查标签质量或调整推理阈值。训练过程中一旦发现验证loss开始回升,就要准备早停,不要只看训练loss。

训练结束默认生成best.pt和last.pt两个权重,best.pt按验证集表现保存,部署用这个;last.pt是最后一轮的,继续训练用这个。yolov8训练完在runs/detect/train下生成混淆矩阵、PR曲线和验证样例图,值得逐个看一眼,能直观发现哪类场景容易漏检。

3.4 验证与测试

训练完成,用detect模式跑测试集:

yolo detect predict model=runs/detect/train/weights/best.pt source=warehouse_skj9z/images/test save_dir=runs/detect/test

这条命令用best.pt逐张推理test目录图像,保存带预测框的结果图。跑完打开图检查:框是否完整包住人体、有没有把货架或叉车误判成工人。这一步是模型能不能用的最终判断。yolov5对应的是python detect.py --weights ... --source ...,逻辑一样。

有个高发问题:训练imgsz=640,推理时为了提速改成320,结果框的位置明显偏移。推理imgsz必须和训练一致,要提速只能在训练阶段就选小图尺寸,不能训练和推理各定各的。

4. 标签格式互转实战:YOLO格式与VOC格式的转换脚本与边界处理

4.1 坐标换算公式

YOLO txt和VOC xml互转,核心是两套表达式换算。设图像宽W、高H。

YOLO到VOC:

xmin = (cx - w / 2) * W ymin = (cy - h / 2) * H xmax = (cx + w / 2) * W ymax = (cy + h / 2) * H

VOC到YOLO:

cx = (xmin + xmax) / 2 / W cy = (ymin + ymax) / 2 / H w = (xmax - xmin) / W h = (ymax - ymin) / H

所有转换脚本都建立在这两组公式上。写的时候要注意两点:除法必须用浮点,图像的宽高必须从xml的size节点或实际图片读取,不能硬编码。如果图像带EXIF旋转信息,要先修正方向再读宽高,否则横竖直接看反。这套数据两种格式都给,意味着你可以随时拿一份txt和同图xml交叉验证,检查自己的转换逻辑有没有偏差。

4.2 双向转换脚本

下面给一个双向转换实现,只依赖Python标准库:

import os import xml.etree.ElementTree as ET def yolo_to_voc(txt_path, xml_path, image_width, image_height, class_names): root = ET.Element('annotation') ET.SubElement(root, 'filename').text = os.path.basename(txt_path).replace('.txt', '.jpg') size = ET.SubElement(root, 'size') ET.SubElement(size, 'width').text = str(image_width) ET.SubElement(size, 'height').text = str(image_height) ET.SubElement(size, 'depth').text = '3' with open(txt_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls, cx, cy, w, h = map(float, parts) if not (0 <= cx <= 1 and 0 <= cy <= 1): continue obj = ET.SubElement(root, 'object') ET.SubElement(obj, 'name').text = class_names[int(cls)] bndbox = ET.SubElement(obj, 'bndbox') xmin = int((cx - w / 2) * image_width) ymin = int((cy - h / 2) * image_height) xmax = int((cx + w / 2) * image_width) ymax = int((cy + h / 2) * image_height) ET.SubElement(bndbox, 'xmin').text = str(xmin) ET.SubElement(bndbox, 'ymin').text = str(ymin) ET.SubElement(bndbox, 'xmax').text = str(xmax) ET.SubElement(bndbox, 'ymax').text = str(ymax) ET.ElementTree(root).write(xml_path, encoding='utf-8', xml_declaration=True) # 调用示例 # yolo_to_voc('img_0807_59.txt', 'img_0807_59.xml', 640, 480, ['worker'])

脚本把每行五段值解析后反归一化,写成VOC的object节点。两处防护值得注意:跳过字段不完整的行,跳过归一化越界的行。标注工具偶尔会产出cx=1.2这类非法值,直接写进xml等于把框顶出图像边界,训练时干扰极大,宁可丢弃也不要带病入训。

反向转换:

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, txt_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() width = int(root.find('size/width').text) height = int(root.find('size/height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue cls = class_names.index(name) bndbox = obj.find('bndbox') xmin = int(float(bndbox.find('xmin').text)) ymin = int(float(bndbox.find('ymin').text)) xmax = int(float(bndbox.find('xmax').text)) ymax = int(float(bndbox.find('ymax').text)) cx = (xmin + xmax) / 2 / width cy = (ymin + ymax) / 2 / height w = (xmax - xmin) / width h = (ymax - ymin) / height lines.append(f'{cls} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}') with open(txt_path, 'w') as f: f.write('\n'.join(lines)) # 调用示例 # voc_to_yolo('img_0807_59.xml', 'img_0807_59.txt', ['worker'])

反向转换里类别映射通过class_names列表的index实现。VOC存的name是字符串,转YOLO时靠列表顺序对应整数索引。这个列表一旦调整顺序,所有txt的类别索引都要跟着变。当前数据集只有worker一个类,顺序简单;以后如果要加forklift、box等新类,务必用脚本批量更新全部txt,不要手改。

4.3 转换中的边界坑

先说小数精度。VOC的xmin、ymin是整数像素,YOLO转换回来的cx、cy、w、h至少保留6位小数。只保留2位,框位置误差会放大到几个像素,大目标问题不大,小目标直接偏出目标体。格式化统一用{:.6f}。

然后是空标签文件。有些图里没有任何工人,txt内容为空,行数零。脚本要兼容这种情况,xml转yolo时输出空字符串文件就行。框架能接受空标签图作为负样本参与训练,这也是合理的。

最后是类别索引偏移,这是最隐蔽的问题。类别索引从0开始是YOLO的约定,但不少人写脚本时从1开始,结果所有框变成相邻的错误类别。最坑的是整体指标还正常——loss低、mAP也不差,推理出来类别名永远是错的。血泪经验:每次拿到新数据集,先统计标签里的最大类别索引,一旦大于等于类别数,立即全量排查,别等训练完才看。

5. 避坑与排查:555张图训练中常见的5个翻车现场

5.1 报错“No labels found in …”

现象:yolov8启动训练后,控制台提示找不到标签文件,有些版本直接不加载任何样本,loss从第一轮起就是零或者异常。

原因:YOLO框架默认从images同级的labels目录读取标签,这份数据的txt在labels/yolo_format子目录里,目录层级对不上。

解决:训练前把txt复制到与images匹配的扁平labels目录,复制后用脚本核对每张图有没有同名txt。这一条基本能解决九成的标签读取问题,复制完再跑一遍2.4的校验脚本,双保险。

5.2 类别数不匹配导致识别错乱

现象:训练能跑通,验证loss很低,但部署推理时框出的类别名和实际目标完全对不上,或者类别名显示成奇怪的东西。

原因:warehouse.yaml的nc或者names与训练框架默认配置冲突。如果没指定配置文件,框架用了内置coco80类定义,模型会按coco的类别索引解释输出,worker被当成其他类别对象。

解决:检查yaml里nc=1、names只含worker且索引从0开始。用命令行训练时显式data=warehouse.yaml,不要依赖框架内置的coco.yaml。如果跑出来的结果里出现多个类别名,优先怀疑这里。

5.3 置信度阈值过高导致漏检

现象:推理时真实工人在画面里却没框出来,只留下置信度最高的少数几个框,检查源图确认目标明明存在。

原因:仓库场景中部分人员被货架遮挡或距离镜头较远,模型对这类目标的置信度偏低,只有0.3到0.5。默认conf=0.25或0.5把这些都过滤掉了。

解决:推理参数加conf=0.1,先看全量输出,再按业务需求找合适阈值。检测框宁可多出几个假阳性,也不要漏掉真实人员,尤其安防场景下漏检代价远高于误检。确认阈值后再做最终部署配置。

5.4 letterbox尺寸不一致导致框偏移

现象:训练用640,推理临时改320,框能出来但位置明显偏移,看起来像整体向左上或右下漂移。

原因:训练时letterbox填充灰边,框坐标是在补边图上预测的。推理用不同输入尺寸,缩放比例和补边不同,坐标映射全部偏移。

解决:推理imgsz强制等于训练imgsz。如果推理速度是硬指标,只能在训练阶段就使用更小尺寸,不能训练和推理分别定。改完imgsz后重新训练或至少重新验证一轮,不要只改推理端。

5.5 数据量不足过拟合

现象:训练到30轮后训练loss还在降,验证loss开始回升。训练集上模型几乎完美,验证集上错误增加。

原因:555张图相对yolov8s参数量来说偏少,后期模型在记忆训练集细节,没在学工人的一般特征。仓库场景里同类背景反复出现,更容易加速过拟合。

解决:调大YOLO内置数据增强,如hsv_h、flipud、mosaic等参数适当增加。或者启用预训练权重做迁移学习,模型先有通用视觉特征再适配仓库场景,收敛路径完全不同。若要进一步扩数据,采集不同时间段和光照条件下的图像,但至少在现有资源下迁移学习是性价比最高的手段。

6. 进阶:用迁移学习压缩训练时间,从预训练权重一小时收敛

6.1 先冻结骨干做头部适配,再解冻微调

对555张图这种规模的数据,最不值得做的就是从随机权重开始。我拿到这份仓库工人数据集后的标准做法是迁移学习两段式。

第一步,用预训练权重冻结前半段网络训练:

yolo detect train model=yolov8s.pt data=warehouse.yaml epochs=30 imgsz=640 batch=16 freeze=10

freeze=10表示冻结yolov8s的前10层,也就是骨干网络里负责边缘、纹理、轮廓等底层特征提取的卷积层。这些特征在coco上已经学得很扎实,仓库工人和通用目标在这些底层特征上差别不大,没必要重复训练。冻结后只有检测头和部分颈部在更新,对数据量的要求低很多,30轮能看出清晰的收敛趋势。

第二步解冻全部层,用更小学习率微调:

yolo detect train model=runs/detect/train/weights/last.pt data=warehouse.yaml epochs=30 imgsz=640 batch=16 lr0=0.001 freeze=0

这里用last.pt而不是best.pt,因为best.pt可能保存的是第一阶段的某个局部最优,接续训练容易把后续更新带偏。解冻后所有层都参与更新,但学习率压到0.001以下,防止破坏预训练出来的通用特征。两步合计在一小时稍多点跑完,效果与从零训练100轮持平。

从那以后,我拿任何小规模数据集做YOLO目标检测,都强制先跑标签校验脚本,再写yaml,然后冻结训练、最后解冻微调,整套流程下来翻车率低了很多。这也是这份数据集最值得照着复现的工作流。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表