ARTICLE DETAIL

资讯详情

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

chair_VOCtrainval2012数据集转换YOLO格式训练全流程

chair_VOCtrainval2012数据集转换YOLO格式训练全流程 简介YOLO椅子检测数据集精选自经典的PASCAL VOC2012数据集仅保留含椅子的图像面向使用YOLO系列算法的目标检测任务适合初学及研究者训练、验证模型。压缩包内共2000个文件其中1366张JPEG原图并附带1367个TXT标注文件直接给出椅子边界框的坐标与类别可快速加载用于训练另有1366个XML标注文件按照PASCAL VOC标准记录物体面积、视角、遮挡等信息便于对检测结果做深入分析和可视化。资源总体积为149.4MB图像来自多个年份的VOC数据覆盖室内外各种环境、不同角度和多种造型的椅子样本多样性较明显可方便地划分为训练集和验证集。截至目前已有577人学习浏览适合作为智能家居、室内设计、零售陈列等场景中椅子识别任务的基础数据也有助于开展检测算法的对比实验与调参优化是一份实用且易于上手的计算机视觉资源。1. 拿到 chair_VOCtrainval2012.zip这份椅子检测数据集到底能干什么很多人想训一个 YOLO 椅子检测模型卡在第一步没有自己的标注数据。自己拿 LabelImg 一张张画框画两三百张就烦了而且画框质量还不稳定。chair_VOCtrainval2012.zip 解决的就是这个痛点它是从 Pascal VOC 2012 的 trainval 部分里把 chair 这个类别单独抽出来打包好的数据集压缩包里是标准的 VOC 目录结构——JPEGImages 放原图Annotations 放 XML 标注ImageSets/Main 里放着划分好的训练、验证图片名单。适合谁用刚接触 YOLO 训练自己数据集的新手或者需要在工程项目里快速验证椅子检测方案的工程师。但有个反直觉的结论必须先说这份数据集不能直接喂给 YOLO 训练VOC 的 XML 格式必须转成 YOLO 需要的 txt 格式目录结构也得重排。这篇文章就把从解压到训练跑通的全过程拆开每一步该执行什么命令、改哪个参数、翻车时看哪里都给你落到位。2. 拆开压缩包看内部结构VOC 格式的目录布局与 label 映射关系2.1 解压后的标准 VOC 三段式结构每个目录管什么拿到压缩包解压后你会看到一个典型的 VOC 数据集目录。先执行一条命令把结构看清楚# 解压数据集 unzip chair_VOCtrainval2012.zip -d chair_voc # 查看目录树只显示两层方便快速确认结构 cd chair_voc find . -maxdepth 2 -type d | sort输出应该是这样的三个核心目录. ├── Annotations ├── ImageSets │ └── Main └── JPEGImages这三个目录各管一摊事对应关系必须记牢。JPEGImages 是所有图片原文件没有子目录文件名形如 2011_003271.jpgAnnotations 下是对应的 XML 标注文件一一匹配绝不会出现只有图没有标的情况ImageSets/Main 里是文本列表文件train.txt、val.txt 分别记录训练集和验证集的图片文件名不带 .jpg 后缀。这个设计就是 VOC 原始比赛用的格式图片负责提供视觉信息XML 负责提供坐标和类别txt 负责控制训练与验证的划分。接着快速数一下有多少张图和标注# 统计图片与标注文件数量是否一致 ls JPEGImages | wc -l ls Annotations | wc -l # 看训练/验证划分的比例 wc -l ImageSets/Main/train.txt ImageSets/Main/val.txt正常情况数量应该完全一致如果不一致说明压缩包本身不完整第一步就应该重新找资源。训练集和验证集的比例通常在 4:1 到 5:1 之间这份数据集是从 VOC 2012 trainval 里抽出来的划分比例沿用原版设定先不用动等后面转完格式再自己决定要不要重新划分。2.2 XML 标注的解剖不仅要有框还要知道坐标是绝对像素值打开一张 Annotations 下的 XML 文件里面才是真正有用的东西。用 cat 直接看cat Annotations/2011_003271.xml可以看到里面结构大概是这样的逻辑object 结点里先写 name这里是 chair然后是 bndbox 结点含 xmin、ymin、xmax、ymax 四个整数。这组坐标是绝对像素值单位是像素数值大小跟原图分辨率直接相关。YOLO 训练时不认这种格式它只认归一化后的中心点坐标和宽高。这里有两个新手最容易犯的错一是忽略了同一种物体可能出现多次XML 里有几个 object 结点图片上就有几个目标转换时不能只取第一个二是有的 XML 里可能存在被标记为 difficult 的目标默认情况下训练时应该过滤掉这类样本但在转换脚本里处理不处理都行因为这份 chair 数据集本身量级不算大保留 difficult 反而相当于加了几个难例。看 XML 时顺手验证一下图片尺寸是否一致因为后面归一化要用到真实宽高# 用 Python 检查所有图片尺寸分布 python3 - EOF import os from PIL import Image img_dir JPEGImages sizes {} for f in os.listdir(img_dir)[:50]: # 先抽查 50 张 with Image.open(os.path.join(img_dir, f)) as im: sizes[im.size] sizes.get(im.size, 0) 1 print(sizes) EOF如果尺寸五花八门说明数据集是混合分辨率来源如果比较集中说明来源统一。这个参数直接影响后面配置 imgsz 训练尺寸不要跳过。2.3 从 VOC 转到 YOLO 格式换算公式与一个能直接跑的 python 脚本VOC 的 xmin、ymin、xmax、ymax 是左上角和右下角的绝对坐标YOLO 需要的是归一化到 [0,1] 之间的中心点坐标与宽高。换算公式很固定设图片宽度为 W高度为 H那么center_x (xmin xmax) / 2 / W center_y (ymin ymax) / 2 / H box_w (xmax - xmin) / W box_h (ymax - ymin) / H脚本写完放在项目根目录运行。下面这个转换脚本是实际项目里常用做法的精简版一并处理了类别名、坐标归一并写到 labels 目录import os import xml.etree.ElementTree as ET voc_root chair_voc img_dir os.path.join(voc_root, JPEGImages) xml_dir os.path.join(voc_root, Annotations) txt_dir os.path.join(voc_root, labels) os.makedirs(txt_dir, exist_okTrue) # 类别映射表这份数据集只有 chair 一类设为 0 class_map {chair: 0} def convert(xml_file, img_w, img_h, out_txt): tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: # 防御性检查发现未知类别直接跳过而不是崩溃 continue cls_id class_map[name] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines)) # 遍历每张图片对应的 XML for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue stem os.path.splitext(xml_file)[0] img_path os.path.join(img_dir, stem .jpg) if not os.path.exists(img_path): continue # 用 PIL 获取真实宽高避免后续训练因尺寸不匹配报警 from PIL import Image with Image.open(img_path) as im: w, h im.size convert(os.path.join(xml_dir, xml_file), w, h, os.path.join(txt_dir, stem .txt)) print(转换完成输出目录, txt_dir)脚本的核心逻辑是遍历 Annotations 下所有 XML 文件逐个解析 object 结点将坐标按公式归一化后写出 txt。特别注意两点一是获取图片宽高用的是 PIL 实时读取而不是假设所有图尺寸一致这样最稳妥二是 class_map 只有一个 chair如果你后面想把这个数据集混入其他类别训练必须扩展这里并且 chair 的 ID 也要跟着改不能固定为 0。2.4 验证转换结果打开 txt 看坐标是否在合法区间转换效率很高几百毫秒就完成。但转换完不等于能用必须抽检。最简单的验证方式是查看生成的 txt 文件内容cat chair_voc/labels/2011_003271.txt正常输出应该只有一行0 0.531250 0.562500 0.214000 0.238000这样的格式五个数字依次是类别 ID、中心点 x、中心点 y、宽、高。前两个值必须在 [0,1] 区间内宽高同理且必须大于 0。如果出现 0 开头且后面全是 0 的行大概率是原 XML 里 bndbox 有脏数据如果数字超过 1说明分母用错了检查是否把宽高顺序混淆了。更狠一点的验证方法是自己写个反向渲染脚本把 txt 里的框画回去跟原图对比import cv2 def draw_yolo_box(img_path, txt_path): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: for line in f: _, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img) draw_yolo_box(chair_voc/JPEGImages/2011_003271.jpg, chair_voc/labels/2011_003271.txt)随机抽 5 张画回去看如果框都紧贴椅子边缘没有偏移半个身位的情况说明转换逻辑可靠。这步千万别省我见过坐标分母用错的案例表面看起来 txt 数值正常训练也能跑但 mAP 一直上不去最后画框才发现所有标注都偏到左下角了。3. 重新组织目录并写训练配置YOLOv8 跑通椅子检测的最小方案3.1 目录迁移脚本YOLO 系列要求的 images/labels 同级结构YOLO 官方的训练代码默认按特定目录结构读取数据常见做法是建一个数据集根目录下面分 train 和 val 两个子集每个子集里再分 images 和 labels。刚才转换好的 labels 目录和 JPEGImages 目录都是平铺的需要按训练集、验证集重新分配文件。VOC 的 ImageSets/Main 里已经帮你分好了 train.txt 和 val.txt下面这个脚本就是照着它的名单搬文件# 先建干净的目录骨架 mkdir -p chair_yolo/train/images chair_yolo/train/labels mkdir -p chair_yolo/val/images chair_yolo/val/labels # 按 train.txt 名单把图片和标注搬进 train 子集 while read f; do cp chair_voc/JPEGImages/${f}.jpg chair_yolo/train/images/ cp chair_voc/labels/${f}.txt chair_yolo/train/labels/ done chair_voc/ImageSets/Main/train.txt # 按 val.txt 名单把图片和标注搬进 val 子集 while read f; do cp chair_voc/JPEGImages/${f}.jpg chair_yolo/val/images/ cp chair_voc/labels/${f}.txt chair_yolo/val/labels/ done chair_voc/ImageSets/Main/val.txt这里用 cp 而不是 mv原因在于原始 VOC 目录后面可能还要复用比如你打算同时抽出 person 类别做多类检测原目录别动。这种 while read 逐行读取的做法比for i in $(cat train.txt)更安全不会因为文件名带空格而崩。执行完检查一下 val 目录的文件数量应该跟wc -l val.txt的结果一致。3.2 编写 dataclass 文件类别清单和路径参数的具体写法目录就绪后需要在 YOLO 工程下写一个数据配置文件YOLOv8 用 YAML 格式描述数据集路径、类别列表。这份 chair 数据集只有一个类写起来极简train: /home/your_name/datasets/chair_yolo/train/images val: /home/your_name/datasets/chair_yolo/val/images nc: 1 names: [chair]三个关键点。第一train 和 val 指向图片目录不要指向 labels 目录YOLO 训练时会自动在同级目录下查找 labels 文件夹这是个约定大于配置的设计写错了会报找不到标注。第二路径要用绝对路径别偷懒写相对路径因为训练进程的工作目录可能被命令行参数改变相对路径失效是高频报错。第三names 列表的顺序必须跟标注 txt 里的 class_id 对齐这里只有一个 chairID 就是 0。如果你在第 2 章里把 class_map 扩展成 chair 和 person那么这里 names 必须写成[chair, person]顺序不能乱。3.3 训练命令与预训练模型选择为什么从 yolov8n.pt 开始环境搭建这块不做过多赘述执行pip install ultralytics即可它会自动拉取 PyTorch 相关依赖。训练命令如下cd ultralytics # 进入到你的 YOLO 工程目录 yolo train \ modelyolov8n.pt \ datachair_voc.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ device0modelyolov8n.pt 会自动下载预训练权重下载地址在官网配置的仓库里不用手动干预。为什么选 n 后缀n 是 nano 版模型最小、速度最快显存需求低适合这种单类别小数据集。如果你显存充足且想要更高精度可以换成 yolov8s.pt 或者 yolov8m.pt但起步阶段先用 nano 把流程跑通避免因为显存不足在黑匣子里折腾半天也不知道是模型太大还是配置有误。epochs100 是常见起步值chair 数据量不大100 轮足够收敛再多了反而容易过拟合。imgsz640 是 YOLOv8 默认输入分辨率这份数据集里图片尺寸大部分接近 500×400直接 resize 到 640 会产生一定拉伸但 YOLO 训练本身对拉伸不敏感先不去干预。batch16 是否可行取决于你的显卡显存8GB 左右跑 nano 模型完全没问题。如果你只有 4GB 显存改成 batch8新手不要盲目加大 batch。3.4 训练过程的正常表现如何判断它真的在学训练启动后屏幕上会滚动输出每个 epoch 的 loss 值和各项指标。对一个单类别数据集正常表现是前 10 个 epoch 的 box_loss 从 1.5 左右快速下降到 0.8 附近cls_loss 从 1.0 左右往下掉50 个 epoch 后 box_loss 在 0.3 到 0.5 之间震荡val 集的 mAP50 达到 0.8 以上。如果 mAP50 从第 20 个 epoch 开始就停在 0.5 上下不动先别急着调模型回看数据划分——通道数、类别 ID 对齐、标注框是否过小这三个问题在椅子数据集中最容易出现。训练结束会输出 best.pt 和 last.pt 两个权重文件在 runs/detect/trainX/weights 目录下后面推理都用 best.pt。提示训练时如果日志里出现 NaN loss立刻 CtrlC 停掉。重新检查 YAML 文件里是否有多余空格或换行符以及图片中是否存在全黑或全白样本。这份数据集总体干净但个别曝光过度的图片触发 NaN 也是有的。4. 推理验证与模型能力边界从 mAP 数字到实际图片检测效果4.1 用 best.pt 跑批次推理并输出可视化结果训练完成后先跑一次批量推理把自己看图片时的手感和官方评估数字对应起来。执行yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcechair_voc/JPEGImages \ saveTrue \ conf0.25source 可以指向一个目录ultralytics 会把目录下所有图都跑一遍自动生成标注了检测框和置信度的图片默认输出到 runs/detect/predict。这时花几分钟翻一下生成的结果图目的不是自我安慰而是找两种典型问题一是椅子被漏检尤其当图片里椅子颜色跟背景接近或椅子只在画面里露出一角二是误检比如把一些方形物体误认为椅子。看个 20 张左右就能摸清这个模型的脾气。4.2 混淆矩阵怎么读为什么列的总和不是 100%训练日志里会给出一张混淆矩阵图很多新手看到数值总和不对就开始怀疑模型坏了。这里必须说清楚一个常识YOLO 的混淆矩阵是按类别划分的归一化矩阵纵轴是真实类别横轴是预测类别每一行代表该类别的所有样本被分到各预测类别的比例行和为 100% 才叫归一化列和根本不是一回事。chair 单类别数据集上你看混淆矩阵只需要关注一个格子chair 这一行对应 chair 这一列的值如果小于 0.9说明大约超过 10% 的椅子没有被稳定检出。剩下的比例会流向 background 列那些就是漏检样本。不要被“总和要等于 1”的直觉带偏这也是热词里“yolo混淆矩阵总合不唯一”讨论的本质原因之一。4.3 数据集分出一部分做训练后评估建议多跑一组 0.5 置信度下的精准率通用评估指标里 mAP50 和 mAP50-95 的差异值得单独说一次。用代码做一次细粒度评估from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datachair_voc.yaml, conf0.5) print(metrics.box.map50) # mAP0.5 print(metrics.box.map50_95) # mAP0.5:0.95mAP50 高只说明模型能区分大致位置mAP50-95 高才说明框得准。如果 mAP50 有 0.93、mAP50-95 只有 0.65意味着检测框跟真实框重叠面积不够大常见原因是标注框本身不够紧或训练 imgsz 太低导致小目标特征丢失。椅子在多数图片里算中大型目标mAP50-95 通常能到 0.75 以上低于这个值就要检查转换脚本里坐标有没有算错。5. 避坑与排查chair 数据集训练最常见的几个报错和玄学波动5.1 报错 Found no usable images 但 JPEGImages 里明明有图现象训练命令启动后立刻报错提示图片目录下找不到可用图片。原因dataclass 文件里的 train 路径写错了或指向了 labels 目录。YOLO 只会在指定路径下找*.jpg、*.png等图片格式如果你指向了 labels 目录里面全是 txt自然为空。解决检查 YAML 里 train 字段是否精确指向 images 子目录按下述命令直接验证该目录是否有 jpgls /home/your_name/datasets/chair_yolo/train/images/*.jpg | head这个是路径问题里的头号坑血泪经验永远先 ls 确认路径真实存在再谈其他。5.2 训练正常但 best.pt 什么都检测不到现象loss 正常下降验证集 mAP 也显示 0.5 左右但推理时任何图片都输出 empty。原因大概率是标注 txt 内坐标范围不对最常见是中心点坐标写成了绝对像素没有归一化YOLO 拿到大于 1 的 cx/cy所有预测框都跑到画面外。解决抽检一张训练图片的 txt如果坐标出现 300、450 这类远超 1 的数字回到第 2 章的转换脚本检查归一化分母是否用了 W、H 的真实值。另一个隐蔽原因标注框宽高过小YOLO 默认忽略面积小于一定阈值的框如果椅子在图中特别小先调低模型里的 anchor 阈值再试。5.3 BN 崩溃训练到中途 loss 突然变成 NaN现象一切正常到第 40 个 epoch 左右loss 突然飙升然后报错 NaN整个训练崩掉。原因这是热词里“yolo训练中bn崩溃”的典型场景。BNBatch Normalization层的统计量在 batch 太小时不稳定尤其 chair 数据集很小偶尔一个 batch 里只有一两张图片均值和方差波动巨大数值计算溢出。解决优先把 batch 调大比如从 8 调到 16 或 32如果显存限制无法加大在训练命令里固定 BN 的 momentum 参数把默认的 0.1 改成 0.05让统计量更新更平滑。还要顺带降低学习率把 lr0 从 0.01 降到 0.005经常能救回来。注意训练中断后不要从第 0 个 epoch 重来一遍以图省事直接使用最后保存的 last.pt 加 resumeTrue 续训更稳妥。5.4 椅子训练集太小验证集波动很大每轮结果都不同现象训练了多次每次最终 mAP50 都不一样高的时候 0.93低的时候 0.87且 val_loss 曲线抖得像心电图。原因这不是 bug是数据量太少的正常现象。chair 子集里总共就几百张图验证集可能只有几十张一两个难例的识别成败就能让 mAP 波动几个点。解决换用更好的交叉验证方式或干脆重新划分数据集让验证集占比降到 0.15增加训练样本量。另一个实际做法是把训练集复制三份做数据增强再训练效果提升明显。这里注意不要幼稚地手动改 train.txt 去增加图片数量正确做法是用yolo train内置的增强参数。5.5 加了其他类别后 chair 检测精度反而下降现象扩展数据集加入 person 类别后chair 的 mAP50 下降了三四个点单看椅子反而没以前准了。原因多类别训练时模型会把参数量分散到多个类别单一类别专属特征学得不够充分另外可能是 person 和 chair 两类容易混淆的场景多比如部分椅子上有人坐着前后两物体框重叠。解决如果项目只关心椅子不要做无意义的多类别扩展保持单类别数据集是最高效的。如果必须多类给 chair 类增加难例图片比如椅子上放着衣物、有人坐在上面这类遮挡情况同时把 epochs 从 100 加到 150给模型更多时间收敛多类别特征。6. 进阶用法用图像增强与微调提高泛化能力以及一套完整的验证方案模型在 VOC 原始测试集上表现不错但放到真实场景往往要打个折扣因为办公室、家居环境的椅子形态和 VOC 图片差异很大。常见的进阶做法是两步走。第一步在自己的环境里拍 30 到 50 张椅子照片用这个数据集上训练好的 best.pt 做预标注然后手工修正标注把新图片和标注合并进原数据集重新训练。这个过程相当于把模型当标注工具用省去从零标注的精力但注意要修正而不是直接采信预标注模型输出的框偶尔会偏移。第二步合理使用内置增强参数训练时加上hsv_h0.015 hsv_s0.7 translate0.1 fliplr0.5会让模型对光照变化和左右镜像更鲁棒我之前直接用默认参数与增强了参数的效果对比mAP50-95 能从 0.72 涨到 0.78。最后给一套不用拍新照片也能验证泛化能力的方案从网上找一些会议室、餐厅的公开图片注意版权只做验证不做商用放到一个独立目录里跑推理统计检出的置信度分布。如果大量目标的置信度在 0.6 到 0.8 之间、很少超过 0.9说明模型泛化有余、锐度不足这时调高 conf 阈值到 0.4 减少误报或者回到训练端把 epochs 加大 50 轮。我自己在部署这类单类别模型时习惯把 conf 至少设到 0.35宁可漏掉低置信度的椅子也不愿意让误检干扰后续业务逻辑。训练完成后记住把分类阈值写成配置项而不是硬编码后续换数据只需要改配置。这个数据集不算大但用它把一个 YOLO 训练流程完整跑通、理解从 VOC 到 YOLO 的全链路改造后续换成任何其他单类别检测任务都能直接复用这套方法希望帮到你。本文还有配套的精品资源点击获取
返回列表