齿轮缺陷检测YOLO数据集详解:标签格式、划分逻辑与训练避坑指南
发布时间:2026/9/24 23:38:14来源:尧图网络
简介面向目标检测与工业质检场景的YOLO格式齿轮缺陷数据集适用于学习YOLOv5训练流程或验证缺陷检测模型尤其适合刚接触目标检测的开发者快速上手。数据严格按YOLOv5文件夹规范保存标签为class、x_centre、y_centre、w、h相对坐标格式共标注孔洞、缺损、齿牙等7个类别具体类别以随包class文件为准数据集已划分训练集、验证集和测试集其中训练集约400张、验证集约100张标签文件与图片按名称对应另含类别说明文本。压缩包共1093个文件、约183.48MB其中546个txt为标签文件、545个jpg为原始图像、1个py脚本用于数据集配置或工具辅助、1个png为类别或结构说明图目录组织清晰便于检索。已有344人浏览学习适合需要快速获得规范YOLO数据集的初学者和进行齿轮表面缺陷检测实验的研究者。拿到后无需额外格式转换即可用于模型训练省去自行采集、清洗与标注文件整理的步骤可直接聚焦模型调参与效果验证。1. 齿轮缺陷检测的数据集为什么值得从一份现成 YOLO 数据开始做齿轮缺陷检测的人绝大多数不是卡在模型选型上而是卡在数据上。自己拿 labelimg 一张张打标几千张图下来手是废的标注质量还不稳定直接从产线拉视频抽帧背景单一但光照、油污、反光全在变缺陷类型又极其不均衡——缺齿、崩齿、裂纹、点蚀、划伤、锈蚀每类样本量差一个数量级是常态。这时候一份划分好的 YOLO 数据集加配套的标签文件和 class 文件就是让你从零到能训练的第一块跳板不用重复造数据先把训练、验证、测试的流程跑通再回来补自己的产线数据。这篇笔记就把这类数据集从头拆到尾目录结构、标签格式、class 文件对应关系、划分逻辑、训练配置以及最容易翻车的几个地方。适合手里刚拿到一份数据集、准备用 YOLOv5 或 YOLOv8 训齿轮缺陷模型的工程师照着做。2. 拆开一份齿轮缺陷 YOLO 数据集目录结构、标签格式与 class 文件的对应关系2.1 先看目录images 和 labels 永远是镜像的拿到任何一份 YOLO 数据集第一件事不是看图片长什么样而是先按目录结构把家底盘清楚。YOLO 系YOLOv5、YOLOv8默认的数据组织形式是双目录镜像图片放 images标签放 labelstrain、val、test 分别在两个目录下保持同名。dataset/ ├── images/ │ ├── train/ │ │ ├── gear_0001.jpg │ │ ├── gear_0002.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ ├── gear_0001.txt │ │ ├── gear_0002.txt │ │ └── ... │ ├── val/ │ └── test/ ├── classes.txt # 或 obj.names / data.yaml └── README.md我一般会先跑一条命令确认每一张图片都有对应的 txt 标签文件多了少了都要当场处理不然后面训练时告警刷屏根本分不清是真问题还是数据集缺文件。# 检查 images/train 与 labels/train 的文件名是否一一对应 for f in images/train/*.jpg; do base$(basename $f .jpg) if [ ! -f labels/train/${base}.txt ]; then echo 缺少标签: ${base} fi done这段脚本的逻辑很简单遍历 train 图片目录下所有 jpg取出不带扩展名的文件名再去 labels 目录找同名 txt。找不到就打印出来。这里有个细节YOLO 标签文件名必须和图片名完全一致包括前缀数字的零gear_01和gear_1会被当成两张不同的图。所以检查配对时用字符串比对不要靠人工目测。2.2 标签不是画框是五个归一化数字打开任意一个 txt 标签文件里面不是坐标点集而是五行以内的归一化数字。每一行对应一个目标框格式是class_id x_center y_center width height注意四点。第一坐标是中心点不是左上角和右下角第二四个数值全部除以图片宽高做了归一化范围在 0 到 1 之间第三class_id 是整数从 0 开始计数第四一个文件里可能有多行代表一张图里有多个缺陷目标。这是 YOLO 格式里最容易被误解的地方。用 labelimg 打标完 yolo 格式的标导出时软件已经帮你把 VOC 的xmin, ymin, xmax, ymax换算成了中心点和宽高。换算关系是# VOC(xmin, ymin, xmax, ymax) 转 YOLO 五个数 x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height反过来把 YOLO 标签画回图上检查时要乘回宽高import cv2 def draw_yolo_label(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) x_c, y_c, box_w, box_h map(float, parts[1:]) # 归一化坐标转像素坐标注意乘的是图像宽或高 x1 int((x_c - box_w / 2) * w) y1 int((y_c - box_h / 2) * h) x2 int((x_c box_w / 2) * w) y2 int((y_c box_h / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(check, img) cv2.waitKey(0)这段代码没什么技术含量但非常实用。拿到数据集后把每张图的标签画出来翻一遍比看任何 JSON 报告都直观框是不是偏了、是不是把背景也框进去了、有没有类别标错的一眼就能看出来。参数上最容易错的是乘宽高的地方x_center乘wy_center乘h框宽乘w框高乘h四个量不能串。2.3 class 文件的真正含义顺序决定 idid 决定一切标题里专门点名了 class 文件这是很多人忽略的。class 文件在 YOLO 生态里有两种常见形态Darknet 体系是obj.names一行一个类名Ultralytics 体系是data.yaml里的names列表。不管哪种形态核心规则只有一条文件里第几行就代表 id 是几。比如classes.txt内容如果是missing_teeth crack rust那标签文件里class_id 0就是缺齿1是裂纹2是锈蚀。如果你训练时自己写的 data.yaml 里names顺序不同后果不是报错而是模型学会了但全部预测错位——检测出裂纹却显示成锈蚀。这种错位在训练时不会报警只有到 val 阶段看混淆矩阵才能发现非常隐蔽。所以拿到数据集先做一件事读取 class 文件确认类别数量再随机抽几个标签文件看 class_id 是否在合法范围内。# 校验标签 class_id 是否越界 with open(classes.txt, r) as f: class_names [line.strip() for line in f.readlines()] import os label_dir labels/train max_id len(class_names) - 1 for name in os.listdir(label_dir): path os.path.join(label_dir, name) with open(path, r) as f: for line in f.readlines(): cls_id int(line.strip().split()[0]) if cls_id max_id: print(f{name} 中的 class_id{cls_id} 越界最大合法值是 {max_id})这里的max_id是类别数减一因为 id 从 0 开始。越界的情况通常发生在数据集的 class 文件被换过、或者合并多个数据集时没统一类别映射。正常的数据集不该出现这种情况但校验脚本跑一遍只要几秒钟值得养成习惯。3. 数据划分是提前做好的但划分逻辑你要能复现3.1 比例怎么定先看缺陷分布再谈 8:1:1标题说数据集已经划分好了但你要明白它为什么这么划分以及这个划分是否合理。常见划分比例是训练集 8、验证集 1、测试集 1但齿轮缺陷场景下有个特殊问题缺陷类别极度不均衡。缺齿可能有两千张点蚀可能只有一百张。如果纯随机划分验证集里大概率没有点蚀样本训练时模型没见过足够点蚀评估时 mAP 还会虚高——因为点蚀样本压根没参与评估。所以在动手训练前先统计每个类别在 train、val、test 中的样本数确认分布一致性。import os from collections import Counter def count_classes(label_dir): counter Counter() for name in os.listdir(label_dir): path os.path.join(label_dir, name) with open(path, r) as f: # 一张图里可能有多个缺陷每个都要计入 for line in f.readlines(): cls_id int(line.strip().split()[0]) counter[cls_id] 1 return counter for split in [train, val, test]: label_dir flabels/{split} print(split, count_classes(label_dir))这段代码统计每个划分下各 class_id 出现的总次数。注意统计单位是目标框的数量不是图片数量——一张图里有三个裂纹计三次。对比三个 split 的输出如果某个类别在 train 中有 500 个框在 val 中是 0这个划分就是有问题的需要重新做分层划分。3.2 分层划分脚本随机抽样不等于科学抽样如果原数据集的划分不满足你的场景比如你要把测试集抽出来单独做产线验证或者你想合并自己的新数据再重新划分就需要自己写划分脚本。我一般用sklearn的train_test_split做分层按图片级标签分布把每一类都均匀分到各个 split 里。import os import random import shutil from collections import Counter, defaultdict from sklearn.model_selection import train_test_split random.seed(42) image_dir all_images label_dir all_labels train_ratio, val_ratio, test_ratio 0.8, 0.1, 0.1 # 给每张图打上“类别指纹”作为分层依据 image_files [f for f in os.listdir(image_dir) if f.endswith(.jpg)] image_to_classes {} for img_name in image_files: label_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) classes set() with open(label_path, r) as f: for line in f.readlines(): classes.add(int(line.strip().split()[0])) # 指纹是类别 id 的排序元组作为分层键 image_to_classes[img_name] tuple(sorted(classes)) # 按指纹分组同一指纹内再随机划分 train_set, temp_set train_test_split( image_files, test_size(val_ratio test_ratio), stratify[image_to_classes[f] for f in image_files], random_state42 ) val_set, test_set train_test_split( temp_set, test_sizetest_ratio / (val_ratio test_ratio), stratify[image_to_classes[f] for f in temp_set], random_state42 ) # 移动文件到目标目录 for split, files in [(train, train_set), (val, val_set), (test, test_set)]: os.makedirs(fimages/{split}, exist_okTrue) os.makedirs(flabels/{split}, exist_okTrue) for img_name in files: shutil.copy(os.path.join(image_dir, img_name), fimages/{split}/{img_name}) label_name img_name.replace(.jpg, .txt) shutil.copy(os.path.join(label_dir, label_name), flabels/{split}/{label_name})这里的关键参数是stratify它接收每个样本的类别标签列表train_test_split会保证划分后每个类别的比例和原始数据一致。对齿轮缺陷这种不平衡数据集分层比纯随机靠谱得多。random_state42固定随机种子保证每次跑出来的划分完全一样——这一点在复现实验时很重要否则每次结果都不同没法对比。实际操作时我会把种子值记在项目 README 里。3.3 划分后必做的三查路径、配对、空标签划分完不是收工而是新一轮检查的开始。我吃过两次亏一次是划分后忘记移动 labels训练时报一堆image ... has no labels另一次是划分脚本只复制了图片标签目录里缺了同名文件。所以每次划分完固定跑三个检查。第一查路径data.yaml 里的路径是相对于训练命令执行位置的不是相对于数据集文件位置的路径不对直接报错。第二查配对之前那段的 bash 循环再跑一遍确认 images 和 labels 数量一致。第三查空标签有些标注文件存在但内容是 0 字节YOLO 训练时会警告WARNING: Ignoring empty label file。# 找出所有空标签文件 find labels -name *.txt -empty -print空标签文件在齿轮数据集里不算罕见原因通常是标注人员画框后又撤销但 labelimg 保存时没删除文件。训练时大量空标签会导致 batch 里有效样本变少训练节奏被打乱。找到后可以直接删掉同时把对应的图片从数据集里移除或者保留图片作为无目标的负样本——YOLO 支持空标签图作为背景样本但官方训练逻辑里空文件会被直接忽略所以要当背景样本得用专门的数据组织方式入门阶段直接删掉最省事。4. 用这份数据集训练自己的 YOLO 模型配置与命令4.1 data.yaml 怎么改从 class 文件到训练配置的一步之遥无论是 YOLOv5 还是 YOLOv8训练前都要写一个数据集配置文件。YOLOv5 叫data.yamlYOLOv8 也沿用这个约定。这个文件就是把目录结构、类别名和类别数量告诉模型的关键也是从 class 文件到训练配置的落点。# data.yaml path: ./dataset # 数据集根目录相对当前工作目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 test: images/test # 测试图片目录可选 nc: 4 # 类别数量必须和 classes.txt 行数一致 names: [missing_teeth, crack, chip, rust] # 类别名顺序即 id写这个文件时最需要注意的是nc和names的取值。nc必须和 class 文件里的行数相等names的顺序必须和 class 文件完全一致否则就回了第 2 章说的错位陷阱。取巧的做法是直接读取 class 文件生成 yaml避免手敲出错。# 从 classes.txt 生成 data.yaml with open(classes.txt, r) as f: names [line.strip() for line in f.readlines()] with open(data.yaml, w) as f: f.write(fnc: {len(names)}\n) f.write(fnames: {names}\n)这段脚本的输入输出都很直观读 class 文件每一行作为名字列表写入 yaml 时nc是列表长度names直接是列表本身。注意 YAML 的names字段用 Python 列表语法写是合法的Ultralytics 能直接解析。手写容易写错引号或漏逗号用脚本生成可以避免这类低级错误。4.2 训练命令与关键超参数不是照着默认值跑就完事配置好 data.yaml 后接下来就是选择训练命令。YOLOv5 和 YOLOv8 命令风格略有差异但核心参数基本对应模型权重、数据配置、图像尺寸、batch size、epochs。对齿轮缺陷检测我建议从轻量模型开始跑通流程再逐步升级。# YOLOv5 训练命令适合新手先跑通流程 python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --project runs/train \ --name gear_defect # YOLOv8 训练命令同一份 data.yaml 可直接使用 yolo detect train \ datadata.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs100 \ projectruns/train \ namegear_defect为什么imgsz齿轮场景尤其要留意因为裂纹、点蚀这类缺陷在整张齿轮图里占比很小。640 的输入尺寸经过模型下采样后小目标可能只剩几个像素特征基本丢失。如果标注框的平均宽度小于整张图宽度的 5%建议直接上 1024 或 1280。代价是显存占用翻倍、训练时间变长但检测效果通常有明显提升。batch的大小取决于显存。16 是 8GB 显存跑 YOLOv5s 的常见值显存不够就降到 8不要硬撑。epochs则建议先跑 100 轮看趋势——如果 val 曲线在 60 轮后还在稳步下降说明数据量撑得住可以加量续跑。4.3 训练后看什么损失函数曲线与混淆矩阵训练结束不是看最后的 mAP 就完事yolo 损失函数曲线能给你更多信息。训练日志里一般有三组损失box_loss定位损失、cls_loss分类损失、dfl_loss分布焦点损失。齿轮缺陷场景里如果cls_loss下降缓慢而box_loss已经收敛说明模型在区分缺齿和崩齿这类形似类别上有困难。# 训练完成后在 runs/train/gear_defect/ 下检查 # results.png —— 损失与指标曲线 # confusion_matrix.png —— 混淆矩阵 # PR_curve.png —— PR 曲线打开confusion_matrix.png重点看对角线以外的值。比如真实缺齿被预测成崩齿的比例很高说明这两个类别在形态上太接近需要回看标注是否有边界模糊或者考虑合并类别。如果某些类别在矩阵里整行都是 0说明验证集里根本没有这个类别的样本回到第 3 章重新审视分层划分。对齿轮缺陷检测PR 曲线的右下角面积比左上角的尖峰更有参考价值。因为缺陷类别不均衡模型可能对多数类过拟合PR 曲线在低召回区域表现良好、但整体曲线向原点塌陷说明漏检严重。这时候先别急着调模型结构把置信度门限调低一点看召回变化往往比改网络更直接。5. 齿轮缺陷数据集的 5 个常见坑现象、原因、解决5.1 标签文件存在但内容为空训练直接炸现象训练日志里大量出现WARNING: Ignoring empty label fileloss 曲线震荡不下降甚至训练中断。原因标注阶段画了框又删除labelimg 保存时生成了空 txt或者划分脚本把 labels 目录建出来了但复制文件时源文件本身是空的。齿轮数据里还存在一种特殊情况图像有 EXIF 旋转信息标注工具显示正常但实际像素旋转后标注框超出图像边界软件导出时过滤掉这些框留下空文件。解决训练前用第 3 章的find labels -name *.txt -empty -print把所有空标签找出来确认对应图片后要么删除图片与标签要么重新标注。不要直接删空标签但保留图片——这样图片会变成无标签样本但不一定按你的预期参与训练。5.2 class 文件顺序和训练 names 不一致全类别错位现象训练完成验证集 mAP 显示 0.9 以上但实际推理时裂纹框标注成锈蚀缺齿框显示成崩齿所有预测都规律性偏移一个类别。原因数据集自带的 classes.txt 与 data.yaml 的 names 顺序不一致。典型场景是合并了两份数据集一份类别顺序是缺齿, 裂纹另一份是裂纹, 缺齿机械拼接后标签的 class_id 含义已经变了。解决以数据集自带的 class 文件为唯一基准重新生成 data.yaml用第 4 章的 Python 片段并把 classes.txt 复制到训练项目的根目录作为参考。改完重新训练一次如果没有时间重训可以用--weights加载原模型配合数据集的类别顺序重新做一次短 epoch 微调。这里没有后悔药可吃只能在数据集层面做统一。5.3 小缺陷在归一化坐标下变成“一个点”模型学不到现象loss 收敛正常但推理时裂纹和点蚀几乎全部漏检缺齿和崩齿这类大目标效果尚可。原因齿轮图分辨率常为 3000×3000 以上而裂纹宽度可能只有 10 个像素。归一化之后标注框的w,h数值只有 0.003 级别在 640×640 输入下缩成 12 个像素特征在下采样后完全消失。解决先跑一个统计脚本看看标注框的尺寸分布确认哪些类别占比最小import os import numpy as np label_dir labels/train boxes [] for name in os.listdir(label_dir): path os.path.join(label_dir, name) with open(path, r) as f: for line in f.readlines(): parts line.strip().split() bw float(parts[3]) bh float(parts[4]) boxes.append((int(parts[0]), bw, bh)) # 按类别统计平均框大小 boxes np.array([(c, bw, bh) for c, bw, bh in boxes], dtypeobject) for cls_id in np.unique(boxes[:, 0]): cls_boxes boxes[boxes[:, 0] cls_id] mean_w np.mean([b[1] for b in cls_boxes]) mean_h np.mean([b[2] for b in cls_boxes]) print(f类别 {cls_id}: 平均宽度 {mean_w:.4f}, 平均高度 {mean_h:.4f})这段代码输出每个类别标注框的平均归一化宽高。如果某个类别平均宽高低于 0.01基本可以判断是典型的小目标问题。对应的解决路径有三条imgsz提高到 1280 甚至更高使用 YOLO 的--multi-scale参数在训练时动态变化输入尺寸增强模型对不同尺度目标的适应性或者对原图做切片切分把大图切成若干小图再训练——后者在齿轮全景图检测里效果很好但需要保证切图时目标不被切断。5.4 划分后类别不均衡验证指标虚高现象训练 loss 正常验证集 map 很高但部署到现场某些缺陷类型的召回率惨不忍睹和离线验证完全对不上。原因划分采用纯随机小类别样本在 val 或 test 里恰好为零模型从没在这类样本上做过评估指标自然好看。产线上真实分布可不会按你的划分来该漏检还是漏检。解决用第 3 章的分层划分逻辑重新切数据让每个类别在所有 split 中的占比接近全局占比。另外在 data.yaml 的同级目录维护一份划分日志记录random_state的参数值方便后续复现。划分完之后用Counter再统一次每个 split 的类别分布打印出来跟全局对比差异超过两倍就要重新切。5.5 齿轮表面反光导致标注框覆盖区域过小或漏标现象部分图片里缺陷明明肉眼可见但标签文件里没有对应框甚至整个文件为空。原因齿轮金属表面存在高光、油污和阴影标注员在强反光区域看不清细小裂纹容易漏标。这类问题在标注质量检查阶段很难发现因为人眼复查同样会被反光干扰。解决第一是在数据集说明文档里明确标注规范比如“裂纹必须延伸到金属表面非反光区域才算缺陷”统一标准第二是用数据增强的亮度扰动模拟不同打光条件降低模型对反光区域的敏感度第三是在训练时给反光严重的图片较低采样权重这需要改造 dataloader工作量较大但效果直接。如果只是想做一期快速验证最简单的方式是把这类图片单独拎出来人工再复查一轮标签。6. 进阶用验证集做错误分析把模型调到能上产线模型训练完不是终点齿轮缺陷检测的难点在漏检率。我的习惯是训练完先跑一次带混淆矩阵输出的验证然后根据错误类型做定向处理。第一步是跑通验证命令输出带轮廓框的预测结果图# YOLOv5 验证并输出预测效果图 python val.py \ --data data.yaml \ --weights runs/train/gear_defect/weights/best.pt \ --img 640 \ --save-json \ --save-conf # YOLOv8 验证 yolo detect val \ datadata.yaml \ modelruns/train/gear_defect/weights/best.pt \ imgsz640 \ save_jsonTrue \ save_confTrue验证结果里最值得翻的是results/*_pred.jpg这类预测图和confusion_matrix.png。我会把漏检的样本按类别汇总看是集中在某一类还是集中在某种光照条件下。齿轮场景里最常见的结果是缺齿、崩齿这类大缺陷检测稳定但裂纹和点蚀漏检率高。因为裂纹的宽高比极大像一条线点蚀则是小而密两者都不适合默认长宽比的锚框。这时候先别急着改网络结构试试调整置信度门限# 推理时把置信度门限从默认 0.25 降到 0.1 yolo detect predict \ modelruns/train/gear_defect/weights/best.pt \ sourcetest_images/ \ conf0.1 \ projectruns/predict \ namelow_conf降门限的好处是召回率立刻上来代价是误检增多。但产线场景里多一次人工复检的成本远低于漏检一个缺陷的赔偿成本。门限降下来后挑出误检类别统计它们集中在什么位置——往往集中在齿轮边缘的倒角高光区那里形状和裂纹有几分神似。针对这类误检我一般会在数据集里补一批倒角高光区域的负样本图片这些图片不带标注框用专门的背景样本来参与训练让模型学会区分倒角和真正的裂纹。再进一步如果降门限后误检依然严重就要检查训练数据的增强参数。Ultralytics 默认开了mosaic增强会把四张图拼接后再训练。齿轮目标大、背景单一mosaic 拼接对缺齿这种大目标没有负面影响但对裂纹这种小目标拼接时目标可能被切到边界外导致训练时模型看到的是残缺目标。遇到小目标检测效果差我一般会在训练后半段关闭 mosaic或者直接把mosaic参数设到 0.5 以下让模型在最后几轮看到完整目标。这套流程跑下来一份数据集的可用性基本摸透了。现在每次拿到新的齿轮缺陷数据我都会先跑一遍标签校验脚本再统计类别分布最后才决定训练参数——这个顺序定下来之后翻车的概率小了很多。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网