☰
YOLOV5齿轮缺陷检测数据集:从数据预处理到模型部署全流程指南
2026/10/1 13:52:56 网站建设 项目流程

简介:针对目标检测与工业质检场景,已按YOLOV5目录格式整理的齿轮缺陷检测数据集可直接划分训练集与验证集,免去格式转换与标注整理的时间。数据采集自单一背景下的红色齿轮,分辨率为800×600,包含表面擦伤、齿轮掉牙、齿轮不足三个类别,对应break、lack、scratch标签;每张图像均有数个目标框,适合小目标检测与密集检测任务,同时简单背景有助于模型聚焦缺陷特征。压缩包共2000个文件,包括1999个txt格式的标注文件和一个可视化脚本,脚本可随机读取图片并绘制边界框保存到当前目录,便于快速核查标注效果。资源整体大小约41MB,训练集与验证集分别包含2382张和596张图片及对应标签,数量均衡且格式规范。目前已有910人学习,可直接用于YOLOv5等主流框架的实验与工程落地,适合算法工程师、研究人员及竞赛选手使用。

1. 齿轮缺陷检测数据集:用 YOLOV5 格式省掉一半数据预处理时间

做工业视觉质检的人都知道,齿轮这类回转体零件的缺陷检测,难的不是算法选型,而是数据从现场图像到训练格式的这一步。采集回来的照片有反光、有油污、缺陷形态还五花八门,如果不先按 YOLOV5 的目录约定整理成 images 与 labels 配对的结构,后面训练、验证、调参、换模型,每一步都要为数据格式返工。这份目标检测数据集解决的就是这个具体问题:齿轮缺陷检测,3 个类别,训练集和验证集已经按 YOLOV5 目录格式划分好,标注文件是 YOLO 的 txt 格式,下载解压后直接写一个 data.yaml 就能跑train.py。适合正在做表面缺陷检测、刚上手 YOLOv5 训练自己的数据集、或者想拿一份真实工业场景数据跑通检测流程的人。

2. 目录结构与标注规范:先看懂 images 和 labels 的配对规则

2.1 目录结构对照:同名文件一一对应的约定

YOLOV5 对数据集的目录要求并不复杂,但约定很严格。这份数据集解压后,核心目录是images和labels,各自下面再分train和val。也就是说,你想在这份数据上正常训练,目录树应该是这样的:

geardefect/ ├── images/ │ ├── train/ │ │ ├── gear_0001.jpg │ │ ├── gear_0002.jpg │ │ └── ... │ └── val/ │ ├── gear_0101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── gear_0001.txt │ │ ├── gear_0002.txt │ │ └── ... │ └── val/ │ └── gear_0101.txt └── data.yaml

配对的规矩是:images/train/gear_0001.jpg的标注,必须放在labels/train/gear_0001.txt,文件名去掉扩展名后要完全一致,级联目录也不能错。YOLOV5 训练时是拿图片路径去推导标签路径的,靠的就是文件名的这一一对应关系。如果训练时提示WARNING: label file not found,多半就是这里对不上。

常见做法是我拿到任何数据集,第一步都先写一个小脚本遍历一遍,确认每张 jpg 都有对应的 txt,反过来也要检查有没有多余的 txt。这个数据集的目录划分本身已经做好了,你只需要把data.yaml里的路径改成你解压后的实际绝对路径。

2.2 data.yaml 与类别 ID:3 类缺陷的映射关系

YOLOV5 训练时靠data.yaml知道三件事:训练集图片在哪、验证集图片在哪、一共有几个类别、类别名字按什么顺序排。写法如下:

# data.yaml train: /absolute/path/to/geardefect/images/train val: /absolute/path/to/geardefect/images/val nc: 3 names: ['chipped_tooth', 'wear', 'crack']

注意两点。第一,train和val指向的是images下的子目录,不是labels目录,YOLOV5 会自己根据images/train推导出labels/train。第二,names的列表顺序,必须和标注 txt 文件里的第一个数字class_id对应。0 代表chipped_tooth,1 代表wear,2 代表crack。工业场景里不同批次的齿轮缺陷类别侧重点不同,有些项目用的是缺齿、碰伤、锈蚀的组合,所以拿到数据后第一件事是打开任意一个 txt 看一眼前几个文件,确认类别 ID 不超过 2,并且和names对得上。

2.3 标注坐标的归一化规则:看懂 .txt 里的五个数字

每个 txt 文件里的每一行代表一个标注框,格式是五个数字:class_id x_center y_center width height。这是 YOLO 系通用的归一化坐标,不是像素坐标。举个例子:

0 0.5234 0.4871 0.2365 0.3018

这个标签表示:类别是 0(缺齿),框的中心点位于图像宽度方向的 52.34% 处、高度方向的 48.71% 处,框宽占整张图宽的 23.65%,框高占整张图高的 30.18%。想还原成像素坐标,要用图像宽高反算:

# 把归一化坐标转回像素坐标,用于可视化或手工核对 import cv2 img = cv2.imread('gear_0001.jpg') h, w = img.shape[:2] # 归一化坐标 cls_id, x_center, y_center, box_w, box_h = 0, 0.5234, 0.4871, 0.2365, 0.3018 # 还原成像素坐标 x1 = int((x_center - box_w / 2) * w) y1 = int((y_center - box_h / 2) * h) x2 = int((x_center + box_w / 2) * w) y2 = int((y_center + box_h / 2) * h) # 画框 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite('check_gear_0001.jpg', img)

这里有个细节容易搞错:x_center 和 width 都是相对于图像宽度的比例,y_center 和 height 是相对于图像高度的比例,不能混用。有些标注工具导出的格式是x1 y1 x2 y2的左上角右下角像素坐标,如果直接当 YOLO 格式喂给训练器,框的位置会完全错乱,loss 会一直降不下来。遇到这种情况,先确认标注格式再训练,否则后面排查成本很高。

3. 数据集体检:训练前把坏样本揪出来,省得训练到一半翻车

3.1 用脚本检查标签越界与空标注

数据集整理得再规矩,到了自己手里也要做一遍体检。齿轮图像里经常出现一种情况:缺陷正好在图像边缘,标注时框贴边,导出归一化坐标时被工具算成了略大于 1 的值。YOLOV5 训练时遇到这种标签会报label out of bounds错误。我一般会用一个脚本先全局扫一遍:

# check_labels.py 检查标签是否越界、是否为空 import os from pathlib import Path label_dir = Path('labels/train') bad_files = [] for label_file in sorted(label_dir.glob('*.txt')): lines = label_file.read_text().strip().splitlines() if not lines: bad_files.append((str(label_file), 'empty label')) continue for line in lines: parts = line.split() if len(parts) != 5: bad_files.append((str(label_file), 'invalid format')) continue cls_id, x_c, y_c, w, h = map(float, parts) if not (0 <= x_c <= 1 and 0 <= y_c <= 1 and 0 < w <= 1 and 0 < h <= 1): bad_files.append((str(label_file), 'out of bounds')) for f, reason in bad_files: print(f, reason) print(f'total bad files: {len(bad_files)}')

这个脚本的逻辑很直接:遍历 labels 下所有 txt,逐行检查五个数值的合法区间。x_center、y_center必须在 [0,1] 内,width、height必须大于 0 且不超过 1。另外注意一个常被忽略的点:空的 txt 文件。如果某张图明明有缺陷但标注文件是 0 字节,说明标注导出时漏掉了这张图,训练时它会被当作背景图处理,影响不大,但如果你期望它参与正样本学习,就会白白损失一个样本。

3.2 可视化验证:把标注画回原图

数值检查只能证明格式合法,不能证明框得准不准。想确认这份数据集的标注质量,最直接的办法是把标签画回原图看一遍,尤其是边界样本和重叠样本:

# visualize_labels.py 批量画标注回原图,输出到 check_images 目录 import cv2 from pathlib import Path img_dir = Path('images/train') label_dir = Path('labels/train') out_dir = Path('check_images') out_dir.mkdir(exist_ok=True) class_names = ['chipped_tooth', 'wear', 'crack'] colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] for img_file in sorted(img_dir.glob('*.jpg'))[:50]: # 先看前50张 label_file = label_dir / (img_file.stem + '.txt') if not label_file.exists(): continue img = cv2.imread(str(img_file)) h, w = img.shape[:2] for line in label_file.read_text().strip().splitlines(): cls_id, x_c, y_c, bw, bh = map(float, line.split()) x1 = int((x_c - bw / 2) * w) y1 = int((y_c - bh / 2) * h) x2 = int((x_c + bw / 2) * w) y2 = int((y_c + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[int(cls_id)], 2) cv2.putText(img, class_names[int(cls_id)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[int(cls_id)], 1) out_path = out_dir / img_file.name cv2.imwrite(str(out_path), img) print(f'visualized images saved to {out_dir}')

跑完之后去check_images目录里翻一遍,重点看两类:一是框是不是明显偏大或偏小,比如本来是一处细小裂纹,框却把整个齿面都圈进去了;二是两个类别的框是不是经常重叠在同一区域,如果是,说明类别边界在标注时没有统一标准,这个只能靠人工判断,脚本查不出来。可视化这一步花二十分钟,能避免后面训练完才发现数据标注风格不一致,那时候后悔药就不好买了。

3.3 类别分布与目标尺寸统计

工业缺陷数据集几乎都有类别不平衡问题。这份数据集是 3 类,但三类缺陷的出现频率大概率不一样,磨损可能比裂纹多好几倍。训练前统计一下实例数和目标尺寸分布,可以指导你后面怎么设超参数:

# stats.py 统计每个类别的实例数、目标尺寸分布 import numpy as np from pathlib import Path label_dir = Path('labels/train') class_counts = {} widths = [] heights = [] for label_file in sorted(label_dir.glob('*.txt')): for line in label_file.read_text().strip().splitlines(): parts = line.split() if len(parts) != 5: continue cls_id, _, _, w, h = map(float, parts) class_counts[int(cls_id)] = class_counts.get(int(cls_id), 0) + 1 widths.append(w) heights.append(h) for cls_id in sorted(class_counts.keys()): print(f'class {cls_id}: {class_counts[cls_id]} instances') print(f'average box width ratio: {np.mean(widths):.4f}') print(f'average box height ratio: {np.mean(heights):.4f}') print(f'box width ratio > 0.1: {sum(1 for w in widths if w > 0.1) / len(widths) * 100:.1f}%')

这个统计有两个直接用途。如果某个类别的实例数只有另一个类别的十分之一,训练时需要给这个类别更大权重,或者在增强策略里专门针对少数类做复制粘贴增强。如果多数框的宽高比都小于 0.05,也就是目标非常小,那--img参数就不能默认 640,建议提升到 960 或 1280,否则小目标在降采样后可能只剩几个像素,模型根本学不到特征。

4. 用这份数据训练 YOLOV5:环境、参数与训练命令

4.1 环境准备:conda 虚拟环境与依赖版本

跑 YOLOV5 训练,第一步是配环境。我习惯用 conda 建独立虚拟环境,避免不同项目的 PyTorch 版本互相污染:

# 创建虚拟环境并安装依赖 conda create -n yolov5 python=3.8 -y conda activate yolov5 # 拉取官方源码,注意版本标签 git clone https://github.com/ultralytics/yolov5.git cd yolov5 # 安装依赖,包含 torch、torchvision、opencv 等 pip install -r requirements.txt

这里有个容易踩坑的点:requirements.txt里对 PyTorch 版本有默认要求,如果你用的是新版 CUDA 驱动,直接 pip 装到的 PyTorch 版本可能和你的显卡驱动不兼容。常见做法是先单独装 PyTorch 再装其余依赖:

# 先装 PyTorch,按自己机器的 CUDA 版本选命令 conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia # 再装 rest pip install -r requirements.txt

安装完成后验证一下环境,让torch.cuda.is_available()返回 True,然后跑一句python train.py --help,能看到帮助信息说明源码和环境基本没问题。

4.2 训练配置:模型规模、imgsz 与 epoch 的选择

环境就绪后,先把这份数据集的data.yaml复制到 yolov5 源码目录下,或者直接用绝对路径引用。然后准备开始训练。模型规模的选择逻辑很简单:工业场景数据量通常不大,几百到几千张图,没必要一上来就上 YOLOv5x,过拟合风险高,训练时间还长。一般从yolov5s.pt起步最稳妥,如果想更快可以选yolov5n.pt,但 mAP 会有几个点的损失。

imgsz参数的选择要看第 3 章统计出来的目标尺寸。齿轮缺陷里的裂纹和磨损都属于小目标,imgsz=640是默认值,但如果框宽比例普遍小于 0.08,建议调到 960。epochs我一般先给 100,配合 early stopping 机制,它会在 mAP 连续 50 轮不提升时自动停:

# 训练命令:s 模型,640 输入,batch 16,100 轮 python train.py \ --data /path/to/geardefect/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --workers 8 \ --cache \ --project runs/train/geardefect \ --name exp1

--cache参数会把图片提前缓存到内存里,齿轮数据集图片尺寸不大,缓存后训练速度提升明显。--workers 8是数据加载的进程数,Windows 上如果报错可以改成 0 或 2。--project和--name用来指定训练日志和权重输出目录,这个习惯能让你同时跑多组实验时不会把输出混在一起。

4.3 训练启动与日志解读:loss 曲线怎么看

训练开始后,终端会每轮打印一组指标:box_loss、obj_loss、cls_loss、P、R、mAP@0.5、mAP@0.5:0.95。刚开始几轮 loss 下降慢甚至小幅上升是正常的,尤其加载了预训练权重后,前几轮是在适应新数据集的类别分布。真正需要警惕的是两种情况:一是cls_loss前几轮就降到 0.01 以下,这通常说明标注有问题或者类别数配置错误;二是mAP@0.5到 50 轮还在 0.1 以下徘徊,这时候先别急着加 epoch,回去看数据可视化和标签检查的结果。

训练结束后,runs/train/geardefect/exp1目录下的weights/best.pt是验证集上 mAP 最高的权重,last.pt是最后一轮的权重。部署时用best.pt,继续训练时用last.pt配合--resume:

# 中断后从上次状态继续训练 python train.py --resume runs/train/geardefect/exp1

手动中断训练后想接着跑,--resume会自动读取last.pt和之前的训练状态,不需要重新设置--data和--epochs。这个机制在我实际项目里用得很频繁,白天调参跑一半,晚上关机,第二天接着跑,省不少时间。

5. 避坑与常见问题:训练翻车的高频原因排查

5.1 类别名不一致导致 mAP 为 0

现象:训练能正常启动,loss 也有下降,但训练完检验验证集,mAP 始终是 0,或者个别类别 AP 是 0。

原因:data.yaml里的names顺序和标注 txt 里的class_id数字对应错了。比如names: ['wear', 'crack', 'chipped_tooth'],但标注文件里 0 代表的是 chipped_tooth,模型从一开始就学反了。这个问题在 loss 曲线上看不出来,loss 会照常下降,只是类别语义全错。

解决:训练前用脚本遍历所有 txt,统计 class_id 的实际取值,只有几种染色体的实际类别数跟names长度相等且 ID 从 0 连续编号才算对。标注工具的类别顺序和最终数据的类别顺序不一定一致,导出后必须人工核对一次。

5.2 标签越界导致训练中途崩溃

现象:训练进行到某个 epoch 时突然报错Assertion failed: w > 0或者label out of bounds,训练进程直接退出,重启后每次都在同一位置炸。

原因:某个 txt 文件里的坐标算出来不在 [0,1] 区间,或者是框宽高算出来是负数。这通常来自标注工具导出的四舍五入误差,或者标注的时候把框拖出了图像边界。YOLOV5 源码里有一行检查逻辑,遇到这类标签会直接断言失败。

解决:训练前一定要跑第 3 章的越界检查脚本,把不合法的标签修正或删除。我一般直接在检查脚本里加一个修复逻辑:对大于 1 的值钳位到 0.999,小于 0 的钳位到 0.001。但要小心,如果大量标签都越界,说明标注工具导出格式有系统性问题,不能靠钳位糊弄。

5.3 类别不平衡:缺齿类样本多,裂纹类只有几十个

现象:训练后每个类别的 AP 拉开巨大差距,样本多的缺齿类 AP 0.85,样本少的裂纹类 AP 只有 0.3。

原因:YOLOV5 默认对所有类别平等看待,少样本类别在每轮训练中参与更新的次数太少,特征没学充分。齿轮缺陷里裂纹本来就少见,如果标注还漏掉一部分,问题会更严重。

解决:优先在损失函数里加类别权重,或者对少样本类别做离线复制粘贴增强,把裂纹目标贴到没有缺陷的齿轮图上,合成新样本。注意增强时背景要选同场景的图,贴合齿轮纹理特征,否则模型学到的是纹理拼接痕迹,而不是缺陷特征。另外一个做法是下调--cls_pw参数,但这个参数调节不好容易让 loss 波动变大,先从数据层面解决更稳。

5.4 验证集与训练集存在同源样本

现象:训练时 mAP 一路涨到 0.9 以上,但部署到现场新采集的图像上,检测效果大幅缩水。

原因:验证集和训练集划分不干净。齿轮缺陷数据通常是连续采图,同一工件的多张不同角度照片可能被同时分进 train 和 val,验证集的"高精度"其实是模型对同一工件记忆的结果,不是泛化能力。

解决:这种问题的排查在于划分方式。拿到数据先按图像文件名里的工件编号分组,保证同一编号的图全部进 train 或全部进 val,再做随机划分。如果这份数据集没按编号分组,你可以用文件名做个二次划分,最后哪怕只是把 val 换成一批完全没见过的图,mAP 掉到 0.7 也是正常的,至少这个指标是可信的。

5.5 微调预训练权重时 loss 爆炸

现象:加载yolov5s.pt训练自己的数据,前几轮box_loss冲到 0.3 以上,之后一直下不去,甚至出现 NaN。

原因:最常见的是学习率不匹配。YOLOV5 默认的学习率 0.01 是针对 COCO 这种大规模数据集调出来的,齿轮数据集只有几百张图,0.01 太大了。另外也不排除标签里有坏数据,导致个别 batch 的梯度异常。

解决:先把学习率调到 0.001 跑一轮看趋势,loss 能降就继续用。同时再跑一遍标签检查脚本,去掉坏标签后清空cache缓存重新开始。这两步做完还炸,才考虑是模型结构或 anchor 的问题,但概率很低,锚框是自动计算的,不需要手动调。

6. 进阶:把训练好的模型部署进检测逻辑:输出解析、阈值调节与快速验证

训练只是第一步,产线上用起来还得把模型输出变成可用的检测结果。detect.py的命令行很直接,但有两个参数在工业场景下值得反复调。第一个是置信度阈值--conf-thres,默认 0.25。齿轮缺陷检测里漏检的代价比误检高得多,因为缺陷流到下游装配会出安全事故,所以这个阈值在产线上往往降到 0.15 甚至 0.1。第二个是 NMS 的 IoU 阈值--iou-thres,默认 0.45,它决定两个重叠框是否被合并。齿轮表面缺陷密集时,同一个齿面上可能同时有磨损和裂纹两个相邻框,IoU 阈值调太高会把它们合并成一个,调太低又会在一个缺陷上出多个框。

deploy 前用best.pt跑一批验证集图片,先看检测结果的整体感觉:

python detect.py \ --weights runs/train/geardefect/exp1/weights/best.pt \ --source /path/to/geardefect/images/val \ --conf-thres 0.2 \ --iou-thres 0.45 \ --save-txt \ --save-conf

--save-txt会把每个检测框按 YOLO 格式写进runs/detect/exp/labels,--save-conf会在 txt 里附带置信度分数。这样做的价值在于:你可以用脚本统计每个类别的平均置信度,如果裂纹类的平均置信度明显低于磨损类,说明裂纹特征没学透,优先回数据层面补样本或调类别权重,而不是继续堆 epoch。

再进一步,如果你要把这个模型嵌进自己的 C++ 或 Python 检测服务里,需要从模型输出里还原坐标。YOLOV5 的模型输出是归一化坐标,和标注格式一致,还原到图像坐标系时需要乘以图像宽高。下面这段逻辑我每次写部署代码都会用到:

import torch import cv2 import numpy as np # 加载模型 model = torch.hub.load('yolov5', 'custom', path='runs/train/geardefect/exp1/weights/best.pt', source='local') img = cv2.imread('gear_test.jpg') results = model(img, size=640) # results.pandas().xyxy[0] 返回的是像素坐标,直接解析 df = results.pandas().xyxy[0] for _, row in df.iterrows(): x1, y1, x2, y2 = int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax']) conf = row['confidence'] cls = int(row['class']) label = row['name'] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, f'{label} {conf:.2f}', (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 1) cv2.imwrite('gear_test_result.jpg', img)

这一段看起来简单,但有两个坑值得说。第一个是torch.hub.load传source='local',它会强制从本地源码加载模型,不然默认从 GitHub 拉最新版,YOLOV5 版本迭代快,可能加载出和训练时结构不一致的权重文件,推理结果全乱。第二个是size=640必须和训练时的--img一致,如果你训练时用了 960,推理时用 640,小目标的检测率会明显下降,因为输入分辨率缩小后特征细节丢了。

我自己在产线部署时还养成一个习惯:每次训练完,固定抽 30 张验证集图片跑一遍 detect,把结果图按类别归档,用眼睛过一遍。模型 mAP 高不代表现场好使,只有人眼看过每张图的检测框才敢往产线上推。从那以后我每次换数据集训练,都强制走一遍这个流程,包括标签检查、可视化核对、置信度分布分析,最后再拿一组完全没参与训练的照片验证,靠这个流程还真堵住过好几次数据划分不干净的问题。希望这套流程对你也帮得上忙。

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

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

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

立即咨询