机械零件检测数据集实战:VOC+YOLO双格式5900张5类从训练到部署

发布时间:2026/10/9 20:16:57
机械零件检测数据集实战:VOC+YOLO双格式5900张5类从训练到部署 简介这份资源面向从事工业视觉检测、智能制造与深度学习目标检测的开发者与学习者提供一套机械零件识别数据集可用于训练与验证轴承、螺栓、法兰、齿轮、螺母、弹簧等常见零件的检测模型适合课程设计、算法对比实验及产线质检原型开发。压缩包共约2000个文件以1999个VOC格式xml标注文件和1个说明txt为主图片与YOLO格式txt标注配套齐全整体约224.71MBxml与txt分别对应Pascal VOC和YOLO两种主流训练框架方便直接接入不同检测网络。数据集共5913张jpg图片标注类别6类总框数达24049个其中nut、bolt、gear、bearing等类别样本较充足flange与spring相对偏少便于开展类别不均衡实验。目前已有924人学习下载可帮助读者省去从零采集与标注的成本快速搭建机械零件检测基线并复现训练流程。1. 机械零件检测数据集5900 张 5 类 VOCYOLO 双格式到底能跑出什么产线上掉了一个螺栓、装配时混进一个垫片这类问题靠人眼抽检迟早要翻车。我最近拆了一份机械零件目标检测数据集5900 张图、5 个类别、同时给了 VOC 和 YOLO 两套标注格式拿到手就能直接喂给检测网络。它解决的不是“从零标注”的问题而是“标注完还要转格式、还要清洗、还要划分”的重复劳动。适合谁做工业质检 Demo 的算法同学、带学生做课程设计的导师、想验证自己模型在零件场景下泛化能力的从业者。如果你手头正缺一份能直接跑通的机械零件数据这份资源值得花十分钟看完怎么用。2. 拆开压缩包VOC 与 YOLO 双格式的目录结构与类别映射拿到一个数据集我第一件事不是急着训练而是把目录结构摸清楚。因为格式不对、类别对不上后面训练再花哨都是白搭。这份资源同时提供 VOC 和 YOLO 两套标注意味着你可以跳过格式转换这一步但前提是你得知道两套格式各自长什么样、类别索引怎么对应。2.1 VOC 格式Annotations 与 JPEGImages 的配对逻辑VOC 格式的核心是两张表要对上图片在JPEGImages/下标注在Annotations/下文件名不含扩展名必须一一对应。标注文件是 XML里面记录了图片尺寸、每个目标的类别名和边界框坐标xmin、ymin、xmax、ymax左上角加右下角。常见做法是先用脚本扫一遍配对情况确认没有孤儿文件。我一般会跑这么一段import os import xml.etree.ElementTree as ET img_dir VOCdevkit/VOC2007/JPEGImages ann_dir VOCdevkit/VOC2007/Annotations # 收集图片和标注的文件名不含扩展名 img_names {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith((.jpg, .png))} ann_names {os.path.splitext(f)[0] for f in os.listdir(ann_dir) if f.endswith(.xml)} # 找出只有图片没标注、或只有标注没图片的情况 only_img img_names - ann_names only_ann ann_names - img_names print(f图片总数: {len(img_names)}, 标注总数: {len(ann_names)}) print(f缺标注的图片: {len(only_img)} 个 - {list(only_img)[:5]}) print(f缺图片的标注: {len(only_ann)} 个 - {list(only_ann)[:5]}) # 统计每个类别的目标数量 cls_count {} for ann_file in os.listdir(ann_dir): if not ann_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, ann_file)) for obj in tree.getroot().findall(object): name obj.find(name).text cls_count[name] cls_count.get(name, 0) 1 print(类别分布:, cls_count)这段脚本干三件事检查图片和标注是否一一对应、统计缺失情况、统计每个类别的目标框数量。参数上没什么需要改的把img_dir和ann_dir换成你解压后的实际路径就行。跑完你会对数据集的完整性心里有数——如果某个类别只有几十个框那训练时就得考虑过采样或者类别权重了。2.2 YOLO 格式labels 目录下的归一化坐标YOLO 格式就简洁得多每张图对应一个.txt文件每行是一个目标格式为类别索引 x_center y_center width height全部是归一化到 0~1 的值。类别索引从 0 开始对应一个classes.txt或data.yaml里的类别列表。这里最容易踩的坑是类别索引的顺序。VOC 里用的是类别名比如 “bolt”、“nut”YOLO 里用的是数字索引。如果两套格式的类别顺序不一致你混着用就会把螺栓标成垫片。我一般会写个脚本把两边的类别映射关系打印出来核对import os # 读取 YOLO 的类别列表 with open(classes.txt, r, encodingutf-8) as f: yolo_classes [line.strip() for line in f.readlines() if line.strip()] print(YOLO 类别索引映射:) for idx, name in enumerate(yolo_classes): print(f {idx} - {name}) # 从 VOC 标注里提取所有出现过的类别名 import xml.etree.ElementTree as ET voc_classes set() ann_dir VOCdevkit/VOC2007/Annotations for ann_file in os.listdir(ann_dir): if ann_file.endswith(.xml): tree ET.parse(os.path.join(ann_dir, ann_file)) for obj in tree.getroot().findall(object): voc_classes.add(obj.find(name).text) print(\nVOC 中出现的类别:, sorted(voc_classes)) print(两边类别是否一致:, set(yolo_classes) voc_classes)逻辑说明先读 YOLO 的类别文件再扫一遍 VOC 标注提取实际出现的类别名最后做集合比较。如果输出False说明两套格式的类别定义有出入必须手动对齐后再用。参数上注意classes.txt的路径有些打包方式会把它放在labels/同级或者data.yaml里按实际情况改。2.3 5 类机械零件的典型分布与划分建议5 个类别在机械零件场景下常见的是螺栓、螺母、垫片、轴承、齿轮这类组合但具体是哪 5 类得看你解压后的classes.txt。不管具体类别是什么5900 张图分到 5 个类平均每类 1180 张但实际分布往往不均匀——大零件比如齿轮出现频率高小零件比如垫片可能偏少。我一般按 7:2:1 划分训练集、验证集、测试集。如果某个类别样本特别少会在训练集里做过采样但验证集和测试集保持原始分布这样才能真实反映模型在产线数据上的表现。划分脚本可以用sklearn的train_test_split但要注意按类别分层抽样否则某个类别可能在验证集里一个都没有。提示划分前先把所有图片文件名和对应的标注路径整理成一个列表打乱后再切分避免因为文件名排序导致某个类别的图片集中在某一批。3. 从零跑通 YOLO 训练data.yaml 配置与超参设置格式核对完之后就可以进入训练环节了。这份数据集给了 YOLO 格式最省事的路径就是直接用 Ultralytics 的 YOLO 系列来跑。但“能跑”和“跑得好”之间差着一堆参数这一章把配置和调参的细节拆开讲。3.1 data.yaml 的五个关键字段YOLO 训练依赖一个data.yaml文件里面至少要有这几个字段path: /home/user/dataset # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 test: images/test # 测试集图片相对路径可选 nc: 5 # 类别数量 names: # 类别名称列表顺序必须和 labels 里的索引一致 - bolt - nut - washer - bearing - gear这里最容易翻车的是names的顺序。YOLO 的标签文件里存的是数字索引训练时按names列表去映射类别名。如果顺序错了模型学到的就是错的类别。我一般会拿一张图做可视化验证用cv2把标注框画出来看看框的位置和类别名是否对得上。import cv2 import os img_path images/train/001.jpg label_path labels/train/001.txt names [bolt, nut, washer, bearing, gear] img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: cls_id, xc, yc, bw, bh map(float, line.strip().split()) # 归一化坐标转像素坐标 x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, names[int(cls_id)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(vis_check.jpg, img) print(可视化结果已保存打开 vis_check.jpg 核对类别和框位置)这段代码把 YOLO 格式的归一化坐标还原成像素坐标画框并标注类别名。跑完打开图片看一眼如果框的位置明显偏了或者类别名对不上说明names顺序或者坐标解析有问题。参数上注意names列表要和data.yaml里完全一致。3.2 训练命令与 batch size、imgsz 的取舍配置好data.yaml之后训练命令本身不复杂yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/train \ namemech_parts逐项说明model选yolov8n.pt是最小的预训练权重适合先跑通流程如果显存够可以换yolov8s.pt或yolov8m.pt。imgsz640是默认输入尺寸机械零件如果普遍偏小可以提到 800 或 1024但显存占用会明显上升。batch16在 8GB 显存上比较稳如果 OOM 就降到 8。lr00.01是初始学习率配合patience20做早停——20 个 epoch 验证指标不提升就停省时间。我一般会先跑 10 个 epoch 看看 loss 曲线和 mAP 走势确认没有明显异常再拉长到 100 或 200。如果一开始 loss 就炸了多半是学习率太大或者数据有问题别硬跑。3.3 训练过程中的指标解读与早停策略YOLO 训练输出里几个关键指标box_loss框回归损失、cls_loss分类损失、mAP50IoU 阈值 0.5 时的平均精度、mAP50-95IoU 从 0.5 到 0.95 的平均。机械零件检测里如果零件尺寸差异大mAP50-95往往比mAP50低不少这是正常的。早停策略上patience20是个保守值。如果数据集只有 5900 张训练很快可以设patience30给模型更多机会。但要注意如果验证集 mAP 一直在震荡不上升可能是学习率没降下来可以加cos_lrTrue让学习率按余弦退火。注意训练日志里如果cls_loss一直不降优先检查类别标签是否越界比如索引从 1 开始而不是 0这是 YOLO 格式最常见的翻车点。4. 避坑与排查机械零件数据集训练中的五类常见问题这一章是我自己踩过的坑也是周围人问得最多的。每条按“现象 → 原因 → 解决”写你对照着排查能省不少时间。4.1 现象训练 loss 正常但 mAP 始终为 0原因类别索引越界或者names列表长度和nc不一致。YOLO 在计算分类损失时如果标签里的类别索引超过了nc-1会直接忽略该目标导致模型学不到任何东西。解决跑一遍标签检查脚本统计所有标签文件里出现的最大类别索引确认它小于nc。同时核对data.yaml里names的长度是否等于nc。4.2 现象验证集 mAP 很高但实际推理时框全偏了原因训练时用了rectTrue矩形推理或者数据增强里的mosaic导致模型对边界敏感而推理时用了默认的 letterbox 填充方式两者坐标映射不一致。解决推理时显式指定imgsz和训练时一致并且用augmentFalse关闭测试时增强。如果还是偏检查验证集的标注是否和训练集用了同一套坐标定义VOC 的 xyxy 和 YOLO 的 xywh 别混。4.3 现象某个类别几乎检测不到其他类别正常原因类别样本极度不平衡。5900 张图里如果某个类只有几十个实例模型会倾向于把它预测成背景或其他大类。解决在data.yaml里给这个类别加权重或者用copy_paste增强把该类别实例复制到其他图上。更直接的办法是过采样包含该类别的图片但要注意验证集不能过采样否则指标虚高。4.4 现象训练到一半突然 OOM 或者 loss 变 NaN原因学习率太大或者 batch size 太大导致梯度爆炸。机械零件数据集里如果有些图分辨率特别高imgsz设大了也会在后期触发 OOM。解决把lr0降到 0.001 或 0.005batch减半加ampTrue开启混合精度。如果 loss 已经 NaN从上一个保存的权重继续训练别从头再来。4.5 现象VOC 和 YOLO 两套格式混用后类别错乱原因VOC 的类别名和 YOLO 的索引没有做显式映射直接拿 VOC 的 XML 去生成 YOLO 标签时类别顺序按字母排序了和classes.txt不一致。解决写一个固定的映射字典比如{bolt: 0, nut: 1, washer: 2, bearing: 3, gear: 4}生成标签时严格按这个字典取索引不要依赖sorted()或set()的默认顺序。5. 进阶技巧用 VOC 格式做交叉验证与模型导出跑通 YOLO 训练之后这份数据集的价值还没榨干。VOC 格式的标注可以拿来做交叉验证也可以导出成 ONNX 或 TensorRT 部署到边缘设备。这一章讲两个我常用的进阶操作。5.1 用 VOC 标注做 5 折交叉验证YOLO 官方没有直接提供交叉验证的命令但可以用 VOC 格式的标注文件自己划分 5 折。思路是把所有图片按类别分层分成 5 份每次取 1 份做验证、其余 4 份做训练跑 5 次取平均 mAP。import os import xml.etree.ElementTree as ET from sklearn.model_selection import StratifiedKFold import numpy as np ann_dir VOCdevkit/VOC2007/Annotations img_dir VOCdevkit/VOC2007/JPEGImages # 构建样本列表和主类别标签取每张图里出现次数最多的类别作为分层依据 samples [] labels [] for ann_file in os.listdir(ann_dir): if not ann_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, ann_file)) classes [obj.find(name).text for obj in tree.getroot().findall(object)] if not classes: continue # 取众数作为该图的主类别 main_cls max(set(classes), keyclasses.count) samples.append(os.path.splitext(ann_file)[0]) labels.append(main_cls) samples np.array(samples) labels np.array(labels) skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(skf.split(samples, labels)): print(fFold {fold}: train{len(train_idx)}, val{len(val_idx)}) # 这里把 train_idx 和 val_idx 对应的文件名写入各自的 train.txt / val.txt with open(ffold{fold}_train.txt, w) as f: for i in train_idx: f.write(samples[i] \n) with open(ffold{fold}_val.txt, w) as f: for i in val_idx: f.write(samples[i] \n)逻辑说明用StratifiedKFold按主类别分层保证每折里各类别比例接近。参数n_splits5是折数random_state42固定随机种子方便复现。跑完会生成 5 组 train/val 文件列表分别训练后取平均 mAP比单次划分更能反映模型稳定性。5.2 导出 ONNX 并验证推理一致性训练完的 YOLO 权重可以导出成 ONNX方便在 C 或 TensorRT 环境里部署yolo export modelruns/train/mech_parts/weights/best.pt formatonnx imgsz640 opset12 simplifyTrue参数说明formatonnx指定导出格式imgsz640要和训练时一致opset12是 ONNX 算子集版本simplifyTrue会做图优化。导出后一定要用同一张图分别跑 PyTorch 和 ONNX 推理对比输出框的差异。如果差异超过 1e-3检查预处理归一化、letterbox是否一致。我一般会写个小脚本做一致性验证import onnxruntime as ort import numpy as np import cv2 # 预处理letterbox 到 640x640 def preprocess(img_path, imgsz640): img cv2.imread(img_path) h, w img.shape[:2] scale min(imgsz / h, imgsz / w) nh, nw int(h * scale), int(w * scale) resized cv2.resize(img, (nw, nh)) canvas np.full((imgsz, imgsz, 3), 114, dtypenp.uint8) canvas[:nh, :nw] resized blob canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 return np.expand_dims(blob, 0) session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name blob preprocess(test.jpg) outputs session.run(None, {input_name: blob}) print(ONNX 输出形状:, outputs[0].shape)这段代码只做到推理输出后续还要做 NMS 和坐标还原。重点是确认 ONNX 输出和 PyTorch 输出在相同输入下的数值差异。如果差异大优先检查opset版本和预处理是否对齐。从那以后我每次导出模型都强制走一遍一致性验证哪怕只差一个小数点也要查清楚。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询