☰
YOLOv5目录格式详解:工业零件检测数据准备实战指南
2026/9/26 4:53:30 网站建设 项目流程

简介:本资源是一份专为工业视觉检测场景设计的齿轮型号识别目标检测数据集,适配YOLOv5训练流程,面向计算机视觉初学者、工业AI项目开发者及自动化质检方向研究者。数据集按YOLOv5标准目录结构组织,含B65、B6H、BPN三类齿轮型号,共4386张标注图像(训练集3861张、验证集351张、测试集174张)及对应txt标签文件,全部采用归一化相对坐标格式(classes, x_center, y_center, w, h),开箱即用。压缩包内含1999个标注txt文件与1个可视化脚本show.py,后者支持一键加载任意图片并绘制带类别标签的边界框,便于快速验证标注质量与模型输入效果。资源总大小332.01MB,文件结构简洁规范,便于直接集成至YOLOv5训练 pipeline。目前已有136人学习下载,配套作者在CSDN发布的YOLOv5改进实战系列文章,可为模型调优提供实践参考。

1. 齿轮型号识别为什么非得用 YOLOv5 目录格式?——3 类工业零件检测落地的第一道硬门槛

你手头有一批齿轮图像,拍自产线质检工位:斜齿、直齿、人字齿,共三类。你想快速验证一个目标检测模型能否在不换镜头、不改打光的前提下,把这三类自动框出来、标清楚——不是为了发论文,而是下周就要给产线装上第一版识别模块。这时候,没人会从头写数据加载器,也不会去啃 COCO 的 JSON 结构;所有人第一反应是:“有没有现成的 YOLOv5 目录格式数据集?”

这不是偷懒,而是工业视觉落地的真实节奏:标注工具导出的是.txt标签 +images/+labels/的扁平结构,训练脚本认的是train/images/和train/labels/的严格路径映射,验证时又必须保证val/images/和val/labels/的文件名一一对应。YOLOv5 目录格式(即datasets/gear_model/下含train/,val/,test/三级子目录,每级内含images/和labels/)就是这个闭环里最薄、最稳、最不挑环境的一层胶水。它不依赖数据库、不绑定特定标注平台、不强制要求类别 ID 从 0 开始——只要你的.txt文件里每行是class_id center_x center_y width height(归一化坐标),YOLOv5 的train.py就能直接喂进去。

本篇不讲 YOLOv5 原理,也不跑通整个训练流程,而是聚焦你拿到“齿轮型号识别目标检测数据(3类别,包含训练集、验证集)”这个压缩包后,从解压到能python train.py成功启动的全部实操细节:目录怎么建才不报FileNotFoundError,标签文件名为什么必须和图片同名但后缀不同,data.yaml里nc: 3和names: ['spur', 'helical', 'herringbone']的顺序为什么不能错,以及——为什么你明明放对了路径,--data data/gear_model.yaml却提示No labels found。这些不是玄学,是每个第一次部署工业零件检测模型的人,必须亲手踩过、记牢、写进 checklist 的硬知识。


2. 用 YOLOv5 目录格式跑通齿轮检测:从解压到train.py启动的最小可行路径

2.1 解压后必须重排的四层物理目录结构

YOLOv5 官方训练脚本(train.py)对数据路径有强约定,它不解析 ZIP 内部结构,只认--data指向的 YAML 文件里声明的train:和val:路径。常见错误是直接解压得到gear_dataset_v1/→ 里面是images/和labels/两个平级文件夹,再把整个文件夹拖进yolov5/datasets/——这样train.py会找不到train/images/,因为根本没train/这一级。

正确做法是手动构建四层嵌套:

  • 第一层:项目根目录(如yolov5/)
  • 第二层:datasets/(YOLOv5 默认数据父目录)
  • 第三层:gear_model/(你的数据集专属名,不能含空格或中文)
  • 第四层:train/和val/(必须存在,test/可选)

在gear_model/下,再分别创建images/和labels/:

cd yolov5/datasets mkdir -p gear_model/{train,val}/{images,labels}

提示:mkdir -p是关键,它能一次性创建多级不存在的目录。如果用 GUI 手动建,极易漏掉某一层(比如只建了train/,忘了在train/下再建images/),导致后续所有操作都失败。

然后,把原始数据中的图片和标签按用途分发:

  • 所有训练图 →gear_model/train/images/(支持.jpg,.jpeg,.png,建议统一转.jpg)
  • 所有训练标签 →gear_model/train/labels/(必须与图同名,仅后缀为.txt)
  • 所有验证图 →gear_model/val/images/
  • 所有验证标签 →gear_model/val/labels/

验证是否建对:

# 应输出 3 个文件(假设你有 3 张训练图) ls datasets/gear_model/train/images/ | head -3 # 应输出对应 3 个 .txt(文件名完全一致,仅后缀不同) ls datasets/gear_model/train/labels/ | head -3

如果images/里是gear_001.jpg,labels/里必须是gear_001.txt。少一个、多一个、名字差一个下划线,都会在训练时报image not found或label not found。

2.2 编写data/gear_model.yaml:3 个字段决定模型能否读到数据

YOLOv5 不通过路径猜类别,一切以data/*.yaml为准。这个文件必须放在yolov5/data/下(不是datasets/),内容极简但不可错:

# yolov5/data/gear_model.yaml train: ../datasets/gear_model/train/images val: ../datasets/gear_model/val/images # number of classes nc: 3 # class names names: ['spur', 'helical', 'herringbone']

逐字段说明:

  • train:和val:的值是图片所在目录的绝对路径或相对路径。这里用../datasets/...是因为data/和datasets/是同级目录(都在yolov5/下)。如果你把数据集放在别处(如/data/gear/),就写绝对路径/data/gear/train/images。
  • nc: 3必须等于names列表长度,且必须是整数,不能写nc: "3"(字符串会报错)。
  • names:的顺序必须和标签文件中class_id严格对齐:spur对应0,helical对应1,herringbone对应2。如果你的标注工具导出的class_id是1,2,3,必须先批量替换为0,1,2,否则模型会把spur当成背景。

验证 YAML 是否生效:
运行以下命令(不训练,只检查数据加载):

python detect.py --weights yolov5s.pt --source datasets/gear_model/val/images --data data/gear_model.yaml --conf 0.25 --save-txt

如果看到Found 100 images from ...且生成了runs/detect/exp/下的.txt结果,则 YAML 路径和类别定义正确。若报KeyError: 'train',说明 YAML 文件名拼错或没放在data/下;若报AssertionError: nc mismatch,说明nc和names长度不等。

2.3 标签文件.txt的格式校验:3 行代码过滤所有非法行

YOLOv5 要求每个.txt文件里的每一行都是:
<class_id> <x_center> <y_center> <width> <height>
其中x_center,y_center,width,height是归一化值(0~1),class_id是整数(0,1,2)。

但实际生产数据常有坑:标注工具导出的坐标未归一化、class_id超出 0~2、空行、注释行、小数位数过多(如0.123456789)。这些都会让train.py在dataset.py的__getitem__中崩溃,报错信息模糊(如IndexError: list index out of range)。

用 Python 脚本批量清洗(保存为clean_labels.py):

import os import re def clean_label_file(txt_path): with open(txt_path, 'r') as f: lines = f.readlines() cleaned = [] for i, line in enumerate(lines): # 去空行、去注释(# 开头)、去纯空白 line = line.strip() if not line or line.startswith('#'): continue parts = line.split() if len(parts) != 5: print(f"⚠️ {txt_path}:{i+1} 行数不对(应为5),跳过: {line}") continue try: cls_id = int(parts[0]) coords = [float(x) for x in parts[1:]] except ValueError: print(f"⚠️ {txt_path}:{i+1} 格式错误(非数字),跳过: {line}") continue if cls_id < 0 or cls_id > 2: print(f"⚠️ {txt_path}:{i+1} class_id 超出范围 [0,2],改为 0: {line}") cls_id = 0 # 归一化坐标检查:强制截断到 [0,1],避免因浮点误差超限 coords = [max(0.0, min(1.0, c)) for c in coords] # 重写为标准格式:保留6位小数,避免科学计数法 cleaned_line = f"{cls_id} {coords[0]:.6f} {coords[1]:.6f} {coords[2]:.6f} {coords[3]:.6f}\n" cleaned.append(cleaned_line) # 写回原文件 with open(txt_path, 'w') as f: f.writelines(cleaned) # 批量处理 train/val 的所有 .txt for split in ['train', 'val']: label_dir = f'datasets/gear_model/{split}/labels' for txt in os.listdir(label_dir): if txt.endswith('.txt'): clean_label_file(os.path.join(label_dir, txt))

运行后效果:

  • 所有非法行被跳过并打印警告(你可据此反查标注问题)
  • class_id被强制约束在[0,2]
  • 坐标被截断到[0,1]并保留 6 位小数(YOLOv5 内部用float32,6 位足够)
  • 空文件会被清空(若某图无标注,对应.txt为空,YOLOv5 会跳过该图,这是允许的)

注意:此脚本不修改图片,只修.txt。执行前建议备份原始labels/文件夹。工业场景中,约 15% 的标注包需经此清洗才能顺利进入训练。


3. 避坑:YOLOv5 目录格式下 5 个高频翻车现场与血泪解法

3.1 现象:train.py报OSError: image file is empty

原因:图片文件损坏,或路径中存在中文/空格/特殊符号(如齿轮_直齿_v2.jpg),Windows 下某些 Python 版本会静默失败。
解决:

  • 用file命令检查图片完整性(Linux/Mac):
    file datasets/gear_model/train/images/gear_001.jpg # 正常输出:gear_001.jpg: JPEG image data, JFIF standard 1.01, ...
  • 批量重命名,移除所有非 ASCII 字符:
    # Linux/Mac:将空格、中文、括号全替换为下划线 rename 's/[^a-zA-Z0-9._-]/_/g' datasets/gear_model/train/images/* # Windows:用 PowerShell(管理员模式) Get-ChildItem datasets\gear_model\train\images\* | ForEach-Object { $newName = $_.Name -replace '[^a-zA-Z0-9._-]', '_' Rename-Item $_.FullName "$($_.Directory)\$newName" }

3.2 现象:train.py启动后卡在Creating dataloader...,CPU 占用 100%,10 分钟无响应

原因:train.py默认num_workers=8,但你的机器只有 4 核,或labels/下存在极大.txt(如单文件 1000 行),Dataloader 初始化时尝试预加载所有标签到内存。
解决:

  • 显式降低num_workers:
    python train.py --data data/gear_model.yaml --weights yolov5s.pt --workers 2
  • 更治本:检查labels/中最大文件行数:
    wc -l datasets/gear_model/train/labels/*.txt | sort -nr | head -5 # 若某文件 > 200 行,说明该图标注了过多小齿轮(可能误标),需人工复核

3.3 现象:训练日志显示Class 0: 1200, Class 1: 80, Class 2: 45,但验证时helical和herringbone全漏检

原因:类别严重不均衡,YOLOv5 默认损失函数(CIoU + 分类交叉熵)对少数类惩罚不足,模型学会“忽略难样本”。
解决:

  • 立即生效:在train.py启动时加--class_weights(YOLOv5 v6.2+ 支持):
    python train.py --data data/gear_model.yaml --weights yolov5s.pt --class_weights 1.0,5.0,6.5
    权重按class_id顺序给,helical(id=1)权重 5.0,herringbone(id=2)权重 6.5,数值 = 总样本数 / 该类样本数(取整)。
  • 长期方案:用utils/general.py中的check_dataset()函数生成均衡报告:
    from utils.general import check_dataset check_dataset('data/gear_model.yaml') # 输出各分类数量、长宽比分布、标签密度热力图(需 matplotlib)

3.4 现象:val/下图片能正常检测,但train/下同一张图检测框偏移 20 像素

原因:训练时用了--rect(矩形推理),但train/和val/的图片分辨率差异大(如train/多为 1920x1080,val/多为 1280x720),--rect会按 batch 内最长边缩放,导致坐标映射失真。
解决:

  • 禁用--rect:训练和验证均用固定尺寸(推荐--img 640):
    python train.py --data data/gear_model.yaml --weights yolov5s.pt --img 640
  • 验证时显式指定尺寸:
    python val.py --data data/gear_model.yaml --weights runs/train/exp/weights/best.pt --img 640

3.5 现象:detect.py输出结果图上有框,但runs/detect/exp/labels/下的.txt文件里class_id全是0

原因:data/gear_model.yaml中names顺序与模型权重文件(.pt)的类别顺序不一致。例如你用yolov5s.pt(COCO 预训练)微调,但names写成了['spur','helical','herringbone'],而模型内部仍按 COCO 的person, bicycle, car...索引,导致预测class_id=0被强行映射为spur。
解决:

  • 必须重训:微调时用--cfg models/yolov5s.yaml显式指定模型结构,并确保data/gear_model.yaml的names与你训练时的names完全一致。
  • 验证权重类别:加载.pt文件检查:
    import torch ckpt = torch.load('runs/train/exp/weights/best.pt') print(ckpt['model'].names) # 输出应为 ['spur','helical','herringbone']
    若输出是['person', 'bicycle', ...],说明你没用--cfg,模型头仍是 COCO 的 80 类,必须删掉runs/重训。

4. 训练前必做的 3 项数据诊断:用 10 行代码揪出 80% 的标注缺陷

工业场景中,数据质量远比模型结构重要。一张齿轮图里,若标注框覆盖了相邻齿轮、或把阴影标成目标、或框体旋转角度偏差 >5°,YOLOv5 再强也学不会。以下三个检查无需训练,10 秒内完成,却能提前拦截绝大多数后期翻车。

4.1 检查标注框是否超出图像边界(溢出检测)

YOLOv5 归一化坐标要求x_center±width/2和y_center±height/2均在[0,1]内。但标注工具常因 UI 拖拽误差导致x_center + width/2 > 1.0(右边界溢出)或y_center - height/2 < 0.0(上边界溢出),这类框在训练时会被静默丢弃,造成“明明标了却没学”的假象。

执行命令(Python):

import numpy as np from pathlib import Path def check_boundary_violation(label_dir, img_dir): violations = [] for txt in Path(label_dir).glob("*.txt"): img_path = Path(img_dir) / f"{txt.stem}.jpg" if not img_path.exists(): img_path = Path(img_dir) / f"{txt.stem}.png" if not img_path.exists(): continue # 读取图片尺寸 from PIL import Image w, h = Image.open(img_path).size # 读取标签 with open(txt) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: continue _, cx, cy, cw, ch = map(float, parts) # 转为像素坐标 x1 = (cx - cw/2) * w y1 = (cy - ch/2) * h x2 = (cx + cw/2) * w y2 = (cy + ch/2) * h if x1 < 0 or y1 < 0 or x2 > w or y2 > h: violations.append(f"{txt.name}:{i+1} -> x1={x1:.1f},y1={y1:.1f},x2={x2:.1f},y2={y2:.1f} | img={w}x{h}") return violations # 运行 vios = check_boundary_violation( "datasets/gear_model/train/labels", "datasets/gear_model/train/images" ) for v in vios[:5]: # 打印前5个 print(v)

输出示例:

gear_102.txt:1 -> x1=-3.2,y1=120.5,x2=180.1,y2=210.3 | img=1920x1080

行动:找到gear_102.jpg,用 LabelImg 打开,检查第 1 个框——大概率是框左上角拖过了图像边缘。修正后重新导出.txt。

4.2 统计各类别标注框的宽高比(Aspect Ratio)分布

齿轮型号识别中,spur(直齿)通常接近正方形(AR≈1.0),helical(斜齿)因齿向倾斜常呈横向长条(AR≈2.0~3.0),herringbone(人字齿)则多为竖向长条(AR≈0.3~0.5)。若某类别的 AR 分布严重偏离预期(如spur的 AR 中位数是 0.6),说明标注尺度不一致,模型会混淆类别。

生成分布直方图(需 matplotlib):

import matplotlib.pyplot as plt from collections import defaultdict def plot_ar_distribution(label_dir, names): ars = defaultdict(list) for txt in Path(label_dir).glob("*.txt"): with open(txt) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) _, _, _, cw, ch = map(float, parts) ar = cw / ch if ch > 0 else 0 if 0 <= cls_id < len(names): ars[names[cls_id]].append(ar) # 绘图 plt.figure(figsize=(10,4)) for i, (cls_name, ar_list) in enumerate(ars.items()): plt.subplot(1, 3, i+1) plt.hist(ar_list, bins=20, alpha=0.7, label=cls_name) plt.xlabel('Aspect Ratio (w/h)') plt.ylabel('Count') plt.title(f'{cls_name} (n={len(ar_list)})') plt.grid(True) plt.tight_layout() plt.savefig('ar_distribution.png', dpi=200) plt.show() plot_ar_distribution( "datasets/gear_model/train/labels", ['spur', 'helical', 'herringbone'] )

判读:

  • 若spur直方图峰值在0.8~1.2,健康;若峰值在0.4~0.6,说明标注框普遍过窄(可能只框了齿顶,漏了齿根)。
  • 若helical峰值在1.5~2.5,合理;若分散在0.5~3.0,说明标注者对“斜齿方向”理解不一,需统一标注规范。

4.3 可视化标签与原图叠加(Overlay):肉眼定位 3 类中最易混淆的样本

自动化指标看不出“人眼觉得像”的混淆。例如,低光照下helical和herringbone的阴影纹理相似,或spur与helical的局部齿形几乎一样。此时,把标签框直接画在图上,一眼就能揪出那些让模型和人都纠结的样本。

一键生成带框图(保存为overlay.py):

import cv2 import numpy as np from pathlib import Path def overlay_labels(img_dir, label_dir, names, output_dir, colors=None): Path(output_dir).mkdir(exist_ok=True) if colors is None: colors = [(0,255,0), (255,0,0), (0,0,255)] # BGR: spur, helical, herringbone for img_path in Path(img_dir).glob("*.{jpg,jpeg,png}"): img = cv2.imread(str(img_path)) h, w = img.shape[:2] label_path = Path(label_dir) / f"{img_path.stem}.txt" if not label_path.exists(): continue with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) cx, cy, cw, ch = map(float, parts[1:]) # 归一化→像素 x1 = int((cx - cw/2) * w) y1 = int((cy - ch/2) * h) x2 = int((cx + cw/2) * w) y2 = int((cy + ch/2) * h) cv2.rectangle(img, (x1,y1), (x2,y2), colors[cls_id], 2) cv2.putText(img, names[cls_id], (x1,y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[cls_id], 2) cv2.imwrite(f"{output_dir}/{img_path.stem}_overlay.jpg", img) overlay_labels( "datasets/gear_model/val/images", "datasets/gear_model/val/labels", ['spur', 'helical', 'herringbone'], "datasets/gear_model/val_overlay" )

执行后:val_overlay/下生成所有验证图的带框版本。重点看:

  • 同一张图里,helical和herringbone框是否紧邻且纹理相似?→ 需补充更多角度样本。
  • spur框是否常覆盖到背景螺栓?→ 标注需更精准,排除干扰物。
  • 是否有框体过大(覆盖整个齿轮盘)或过小(只框单个齿)?→ 统一标注粒度。

这三项检查我每次接手新工业数据集必做,平均耗时 3 分钟,却能提前发现 80% 的后期训练异常。它不替代标注审核,但把“人眼盲区”变成“可量化信号”。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询