首页 / 资讯中心 / 文章详情

YOLO腰果缺陷检测实战:数据集格式、可视化与训练避坑指南

YOLO腰果缺陷检测实战:数据集格式、可视化与训练避坑指南 ★ FEATURED ARTICLE
简介这套腰果缺陷检测数据集面向YOLO目标检测实战覆盖Broken、Defect、SplitDown、SplitUp、Whole五类常见外观缺陷适用于工业质检、农产品分选、缺陷检测算法验证等场景。数据整体按YOLOV5标准目录组织训练集3186张、验证集304张、测试集150张每张图像都配有同名txt标签文件标注采用yolo相对坐标格式类别、中心点x、y、宽高无需格式转换即可直接训练。压缩包共2000个文件以标签txt和类别文本文件为主体另附show.py可视化脚本随机传入一张图片即可自动绘制边界框并保存结果方便快速核查标签质量对数据检查和入门调试都很友好。资源包大小约101.94MB整体结构清晰直观类别文件明确五类名称便于直接修改模型配置适合刚接触YOLO的初学者也适合需要补充缺陷样本的工程人员。已有63人学习下载可作为腰果缺陷检测项目的可靠数据基础。1. 腰果缺陷检测为什么难5类问题与一份“拎包入住”的YOLO数据集做工业质检的朋友对腰果这种目标应该不陌生外形不规则、表面反光、缺陷尺度差异极大一个开裂可能占半个果仁而一处霉斑只有几个像素。YOLO腰果缺陷检测5类这类数据集的价值在于它帮你把“找缺陷”这件事从拍脑袋变成了可量化的目标检测任务。我拿到这类带划分好的数据集时首先确认的不是模型能跑多准而是标注文件、类别文件和可视化脚本是否齐全——这三样决定了你当天能开始训练还是先花两小时去补数据工程的债。这篇笔记就围绕这套东西展开数据格式怎么理解、可视化脚本怎么用、训练参数怎么设、坑在哪里。2. YOLO格式的底细class文件、txt标注与训练集划分2.1 YOLO标注文件里那五个数字代表什么YOLO系列的标注文件是纯文本每一行对应一个目标格式固定为class_id x_center y_center width height注意这里所有坐标都是归一化后的相对值范围在0到1之间不是像素值。比如一张640×640的图里有一个bbox左上角在(160, 160)右下角在(480, 480)那么x_center (160 480) / 2 / 640 0.5 y_center (160 480) / 2 / 640 0.5 width (480 - 160) / 640 0.5 height (480 - 160) / 640 0.5这一行就是class_id 0.5 0.5 0.5 0.5。很多新手第一次打开标注txt看到一堆0.4、0.6的小数会误以为标注坏了其实是归一化坐标。做一个自动检查脚本会省心很多# -*- coding: utf-8 -*- # 检查标注坐标是否越界及格式是否合法 import os def check_label_folder(label_dir, img_w640, img_h640): bad_files [] for root, _, files in os.walk(label_dir): for f in files: if not f.endswith(.txt): continue path os.path.join(root, f) with open(path, r, encodingutf-8) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_files.append((path, 字段数不为5)) continue try: nums [float(p) for p in parts] except ValueError: bad_files.append((path, 存在非数字字符)) continue cx, cy, w, h nums[1:] if cx 0 or cx 1 or cy 0 or cy 1: bad_files.append((path, 中心点越界)) if w 0 or h 0 or cx w / 2 1 or cx - w / 2 0: bad_files.append((path, 宽度越界)) if cy h / 2 1 or cy - h / 2 0: bad_files.append((path, 高度越界)) return bad_files if __name__ __main__: issues check_label_folder(datasets/cashew/labels/train) if issues: for item in issues[:10]: print(item) else: print(标注格式全部合法)这段代码的逻辑是把每个txt的每行拆成5个字段先看字段数再转float最后逐项检查归一化坐标是否在合理范围内。这里特别注意cx w / 2和cx - w / 2的检查bbox可以中心点合法但超出画布边界这类错误在图形标注工具里不常见但脚本批量处理时很容易出现必须查。检查完格式合法性再看类别id有没有越界。5类数据集的class_id范围是0到4如果出现5或更大训练时YOLO会直接报错或者把框忽略掉属于很阴性的故障。2.2 划分好的数据集目录到底应该长什么样“划分好的数据集”这句话实际落地就是三个文件夹和三个txt路径。标准做法是datasets/cashew/ ├── images/ │ ├── train/ # 训练图片 │ ├── val/ # 验证图片 │ └── test/ # 测试图片可选 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── classes.txt # 类别文件一行一个类名 ├── train.txt ├── val.txt └── test.txtimages和labels必须一一对应同一个样本在两张表里名字一致只是扩展名不同。注意一个常见翻车点如果图片是jpg但标注是png同名或者路径大小写不一致YOLO训练时会显示“0 labels found”这种提示排查起来很费时间。我自己习惯写一个脚本来校验对应关系防止漏标注的文件悄悄溜进训练集# -*- coding: utf-8 -*- # 校验图片与标签是否一一对应 import os def verify_pairs(image_dir, label_dir): img_names {os.path.splitext(f)[0] for f in os.listdir(image_dir)} lbl_names {os.path.splitext(f)[0] for f in os.listdir(label_dir)} img_only img_names - lbl_names lbl_only lbl_names - img_names if img_only: print(以下图片没有对应标注文件) for name in sorted(img_only)[:20]: print( , name) if lbl_only: print(以下标注文件没有对应图片) for name in sorted(lbl_only)[:20]: print( , name) if not img_only and not lbl_only: print(图片与标注一一对应共%d对 % len(img_names)) verify_pairs(datasets/cashew/images/train, datasets/cashew/labels/train)这种脚本不是训练流程的一部分但在换电脑、拷数据、扩容数据集时能救你一把。数据集的“划分好”不等于“检查过”不要因为别人给了划分就默认它是干净的。划分比例方面见过工业场景里用8:1:1的也见过7:2:1的。我的建议是训练:验证:测试 7:2:1测试集不要动。腰果缺陷检测里如果数据总量不到3000张可以考虑去掉test集或把test当作val用因为硬分三份会让每份都太小验证集不稳定指标跳动很大。2.3 class文件顺序就是你的损失函数里那个idclasses.txt有时叫classes.yaml或data.yaml的内容极其简单但顺序非常关键。以腰果5类缺陷为例常见命名是broken black_spot moldy white_spots normal这里的第0行对应标注文件里class_id0第1行对应class_id1依次类推。也就是说标注文件和class文件之间的关联靠的是行号不是靠名称。这个设计很反直觉但YOLO就是这么工作的。某个做腰果的朋友跟我说过他的翻车经历直接把别人给的labels拷进新项目忘了检查classes.txt的顺序结果代码里标注的“moldy”发霉在训练数据里被当成了“black_spot”黑斑模型loss一直不降看了两天才明白是命名和行号错位了。写一个快速抽查脚本能止血# -*- coding: utf-8 -*- # 统计标注中出现的类别id并与class文件行号对照 from collections import Counter def class_id_stats(label_dir, classes_path): with open(classes_path, r, encodingutf-8) as fp: class_names [line.strip() for line in fp if line.strip()] print(class文件共有%d类 % len(class_names)) for i, name in enumerate(class_names): print( id%d: %s % (i, name)) counter Counter() for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), r, encodingutf-8) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: continue counter[int(parts[0])] 1 print(\n标注里的类别id分布) for cid in sorted(counter): if cid len(class_names): print( 警告id%d超出class文件范围 % cid) else: print( id%d (%s): %d个标注框 % (cid, class_names[cid], counter[cid])) import os class_id_stats(datasets/cashew/labels/train, datasets/cashew/classes.txt)这段统计虽然简单但能一眼看出两件事有没有越界id以及每个类别大约有多少框。后者直接关系到训练的类别均衡性后面避坑章节会展开。3. 用数据可视化脚本把标注“画出来”别急着训练3.1 可视化脚本的价值一次看100张图比看100次单张强标题里特别提到“数据可视化脚本”我理解这是指把标注框画回图片上的工具以及统计类别分布、标注框大小的脚本。很多工业场景的数据集是外包标注员做的标注员理解“腰果开裂”和算法工程师理解的边界不一样这种不一致肉眼可辨但单张看效率太低——1000张图看一遍要一小时还容易疲劳漏掉。数据可视化脚本就是来解决这个效率问题的。常见思路是把图片和它的标注文件读取进来用OpenCV或PIL把bbox画上去同时显示类别名称和置信度如果有的话批量输出到一张拼图或一个文件夹。能用PIL就尽量别用matplotlib。matplotlib画边框可以但处理几百张图时速度慢而且坐标轴、图像插值这些默认行为经常跟你不合拍你会花时间在“为什么图这么小”“为什么坐标轴还在”这种问题上。OpenCV的rectangle函数一次画一个框速度很快批量处理体验好得多。3.2 画标注框脚本一张A4纸上放9个图下面这个脚本可以作为你的可视化模块直接使用它会把每个batch的图片加上标注框拼成一张大图# -*- coding: utf-8 -*- # 将标注框绘制到图片上并批量拼图展示 import os import cv2 import numpy as np CLASS_NAMES [broken, black_spot, moldy, white_spots, normal] COLORS [(0, 0, 255), (0, 255, 255), (255, 0, 255), (255, 255, 0), (0, 255, 0)] IMG_DIR datasets/cashew/images/train LBL_DIR datasets/cashew/labels/train OUT_DIR visual_check def draw_single(path_img, path_lbl): img cv2.imread(path_img) h, w img.shape[:2] with open(path_lbl, r, encodingutf-8) as fp: lines fp.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: continue cid int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) 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), COLORS[cid], 2) cv2.putText(img, CLASS_NAMES[cid], (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, COLORS[cid], 2) return img def main(): os.makedirs(OUT_DIR, exist_okTrue) files [f for f in os.listdir(IMG_DIR) if f.lower().endswith((.jpg, .png, .jpeg))] files.sort() for idx in range(0, min(len(files), 200), 9): batch files[idx:idx9] canvas np.zeros((3 * 400, 3 * 600, 3), dtypenp.uint8) canvas.fill(255) for j, fname in enumerate(batch): img_path os.path.join(IMG_DIR, fname) lbl_path os.path.join(LBL_DIR, os.path.splitext(fname)[0] .txt) if not os.path.exists(lbl_path): continue img draw_single(img_path, lbl_path) img cv2.resize(img, (600, 400)) row, col divmod(j, 3) canvas[row*400:(row1)*400, col*600:(col1)*600] img out_name os.path.join(OUT_DIR, batch_%04d.jpg % (idx // 9)) cv2.imwrite(out_name, canvas) print(已生成, out_name) if __name__ __main__: main()这个脚本有四个参数值得说明。第一batch 9按3×3拼图每张缩放到600×400看小目标够用看微小霉斑会吃力需要再放大单独看。第二range(0, 200, 9)限制只处理前200张因为全量拼图会生成几百张图你根本看不过来工业化流程是先抽查再定向排查。第三COLORS[cid]的取值顺序跟classes.txt完全对应方便你按颜色识别类别。第四画框时对y1 - 8做了下限保护防止文字画到图像外面报错。3.3 从可视化里能看出什么东西可视化抽查不是“看一眼有没有框”就完了。我一般会重点找三类问题框是否贴着缺陷边缘贴太松说明标注框习惯差同一个腰果上是否有多重标注一个缺陷分别标成了开裂和黑斑以及大量“normal”图的分布是否合理——很多数据集里的正常品是纯背景画出来就是一张图上没有框这没问题但如果正常品里混着暗色斑点但没标框那就是标注漏检。可以再看一个维度同一张图里缺陷尺度的差异。腰果的开裂缺陷bbox面积可能占图的30%而霉斑只有3%甚至更小。如果你发现标注框尺寸普遍大于你预期的小目标范围后面训练时要把imgsz调大或者把anchor调整得更贴近数据分布。有一个调参经验当你发现可视化里某个类别的框特别多或特别少先不要急着改loss函数第一步是看标注分布是否合理。比如black_spot黑斑占了所有标注的60%以上那模型训练出来对黑斑的mAP虚高很正常因为样本多、学习充分不代表模型“擅长”这个类。这种情况下你需要在训练时调整类别权重或做数据增强而这些都是可视化脚本统计出的结论直接引导出来的。4. 开始训练最小命令、三个必调参数与YOLO版本差异4.1 从一份yaml开始数据配置文件的写法YOLO系列训练的第一步不是敲命令行而是写数据配置文件。用YOLOv8举例子它的data yaml长这样# cashew.yaml path: D:/datasets/cashew # 数据集根目录用绝对路径最稳 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 test: images/test # 测试图片可省略 nc: 5 # 类别数量 names: [broken, black_spot, moldy, white_spots, normal]这里有几个细节容易踩。第一path字段用绝对路径还是相对路径取决于你的启动目录如果从项目根目录启动相对路径datasets/cashew也能用一旦用IDE或者换了工作目录相对路径可能解析失败我建议写成绝对路径省得排查“训练时找不到图片”这种尴尬问题。第二train和val指向的是文件夹而不是txt文件这是YOLOv5之后的新约定老项目里常见的train.txt方式在v8里已经不再必需。第三names的顺序必须和classes.txt一致这与你之前检查的类别顺序是同一条链路错一次就白训一次。训练命令的最小形态yolo detect train datacashew.yaml modelyolov8n.pt epochs100 batch16 imgsz640 projectruns/cashew nameexp1这行命令的意思是用yolov8n的预训练权重初始化在cashew.yaml指定的数据上训练100轮每批16张图输入分辨率640。project和name用来指定输出目录默认是runs/detect/exp1之类的。4.2 三个必调参数第一是imgz输入分辨率。YOLO默认640但腰果这种对象比较吃分辨率——霉斑可能小到只有10×10像素。如果你有标注先算一下所有标注框的宽高分布如果大量小框的像素宽度在20以下建议把imgsz提高到960或1280。代价是训练时间约平方级增长显存也上涨所以要在“看得清小缺陷”和“训练跑得动”之间取平衡。我一般先看可视化脚本拼接的全景图判断最小目标在640下大概多大再决定是否调高。第二是batch size。它受显存限制A100能跑32甚至64普通消费级显卡16基本到头。调batch的时候要注意贴一下你实际的GPU型号和显存容量不要照抄别人的配置。显存不足时报错信息常出现 “CUDA out of memory”这种提示是显存不够的标准信号。别把batch调太小——batch4时BN层的统计噪声很大loss曲线会明显震荡。第三是epochs训练轮数。工业场景里100轮是个比稳妥的起点配合早停机制patience20能让它在loss不降时自动停止。yolo detect train datacashew.yaml modelyolov8n.pt epochs100 batch16 imgsz640 patience20 projectruns/cashew nameexp1这里断言一下模型选型YOLOv5s和YOLOv8n对腰果这种中等数量目标的数据集差距不明显真正影响精度的是数据质量和标注一致性。不要盲目上YOLO11或换backbone先把5类数据喂进一个标准模型跑通拿到baseline再谈优化。很多团队翻车是因为一开始想用最好的模型结果bug藏在数据里最后换谁都不好使。关于v100 yolo这种搜索词其实V100上YOLO训练完全没问题8卡V100跑640分辨率batch64很常见。需要注意的反而是在V100这种老显卡上如果YOLO版本过新v9/v11部分新算子可能不兼容当前用v8稳定版踩坑最少。4.3 预训练权重的选择不要迷信“yolo预训练模型下载”热搜词里yolo预训练模型下载热度很高但这里我建议冷静。对于腰果缺陷检测这种跟COCO类别完全不同的任务预训练权重的作用是提供backbone对通用视觉特征的学习边缘、纹理、形状而不是直接提供腰果知识。用yolov8n.pt或yolov8s.pt就够没必要非找专门为腰果训练过的权重——那基本不存在。如果数据集规模足够大超过5000张带标注的腰果图你可以尝试从头训练不给.pt文件但你会发现收敛速度慢几倍最终精度未必有预训练的好。用手头有通用权重做迁移是这个场景的标准做法省时间且效果好。在下载预训练权重时注意别把YOLOv5的模型文件和v8的混用不同大版本的权重结构不通用会直接报错。5. 腰果缺陷检测避坑手记5条血泪经验5.1 显存爆掉真正的原因是imgsz和batch的乘积超标现象训练刚开始终端直接抛 “CUDA out of memory”程序退出。原因显卡显存能容纳的数据量 batch × imgsz² × 模型通道数。很多人只调batch不调imgsz或者反过来忽略了二者是乘积关系。我见过有人把imgsz从640提到1280batch没动显存需求直接翻4倍瞬间爆掉。解决先用无训练模式测显存或按梯度下调。调试顺序是先把batch降到4如果还爆再降imgsz如果batch4、imgsz640还爆说明显卡需要换了或者考虑混合精度训练。在YOLOv8中有个办法yolo detect train datacashew.yaml modelyolov8s.pt epochs100 batch-1 imgsz640batch-1是让程序根据显存自动选择最大batch省去试错时间。自动batch找到的值不保证最优但保证能跑起来。后续你可以在它给出的数值基础上上调或下调。5.2 分类别评估发现“normal”类mAP为0现象训练结束验证集上mAP整体有0.85但看每类AP的时候normal类是0。原因normal类的缺陷是“没有缺陷”所以它的训练数据里根本没有标注框。YOLO是目标检测模型它只能检测“有东西”的区域无法检测“不存在”。让模型学会判断正常品你需要的不是给它标注normal框而是靠低置信度后处理策略——检测不到任何缺陷就归为normal。建模思路错了指标必然难看。解决训练阶段不要去追求normal类的AP把关注点放在broken、black_spot、moldy、white_spots这4类缺陷的召回率上。预测阶段如果一张图里没有任何框的置信度超过阈值就判定为normal。这个方案最简单实际生产里也最稳定。不要尝试训练一个“无目标检测”网络那会把简单问题复杂化。5.3 验证集mAP高但现场翻车分布不一致现象val集上mAP有0.93模型放到生产线上对实拍的腰果gif提不出来召回率掉了一半。原因数据集是实验室里拍的背景干净、光照恒定、腰果规整现场是传送带上的有阴影、有遮挡、有震动模糊。模型没见过这些领域差异验证集只能证明它在你划定的小世界里不错。解决把一部分现场图补充进训练集这是治本。注意现场图不要直接压缩到原数据集里要过一遍可视化脚本确认标注清晰可用。另外训练时的mosaic增强和随机HSV调整在一定程度上缓解光照差异但没法替代真实数据。建议划分时不要把“现场图”全塞进val却不进train——那会把模型本来很可爱的泛化能力无情暴露。应该做的是按比例混入train和val让分布的差异在训练时就被模型看到。5.4 数据增强开太大小目标越增强越看不见现象训练loss一直在降但val的mAP在30轮后开始缓慢下坡。原因YOLO默认开启的增强策略里mosaic会把4张图拼成一张然后随机缩放。当目标本身比较小时经过mosaic的缩放它可能从16×16缩到8×8甚至4×4在特征图上只占一个点模型根本学不到有效特征。此外rotate和scale如果开大框和目标的贴合可能被破坏。解决在训练配置里缩小增强强度。# augment.yaml mosaic: 0.5 # 默认是1.0 mixup: 0.1 scale: 0.3 fliplr: 0.5 hsv_h: 0.01 hsv_s: 0.5 hsv_v: 0.4在YOLOv8里这些值可以直接当作--mosaic 0.5等参数传。腰果这类以中小目标为主的数据mosaic降到0.3到0.5、scale降到0.2到0.3是常见动作。增强不是越猛越好它是给模型找麻烦的麻烦太大会把问题也带偏。5.5 类间混淆集中在“white_spots”和“black_spot”之间现象混淆矩阵里white_spots被预测成black_spot的比例达到20%以上肉眼也能注意到这两种缺陷长得像。原因在灰度图像中白斑和黑斑本质上都是“与周边腰果表面像素差异大的区域”如果原图光线暗、曝光不足模型能分辨的纹理特征太少分类器只能依赖颜色。解决数据增强里加亮度、对比度扰动刻意让模型不依赖绝对颜色同时回看可视化脚本看这类样本是不是本身标注就有误——标注员对“白而不黑”还是“黑中带白”的理解不同会直接污染类别边界。如果标注本身噪声大再怎么调参也没用。6. 训练完别急着收工验证脚本与迭代的“最后一公里”训练完成后标准流程是看三张图混淆矩阵、PR曲线、以及若干张预测结果的可视化样例。YOLO训练完会自动生成混淆矩阵和PR曲线但很多人只看一眼总体mAP就结束了这很浪费。我通常的做法是再做一次交付级验证挑10张“典型缺陷图”和10张“边界难例”单独跑一遍预测把结果和真实标注并排输出。# -*- coding: utf-8 -*- # 对指定图片跑推理并保存预测结果 from ultralytics import YOLO model YOLO(runs/cashew/exp1/weights/best.pt) test_imgs [ samples/case_hard.jpg, samples/case_light.jpg, samples/case_large_mold.jpg, samples/case_tiny_stain.jpg, ] for path in test_imgs: results model.predict( sourcepath, conf0.25, saveTrue, projectruns/verify, namefinal_check, line_width2, ) # results[0].boxes 里可以读取每个框的xyxy、conf和cls这里的conf0.25是置信度阈值低于0.25的框会被滤掉。工业现场部署时这个值的设定需要调速阈值设太高漏检会漏缺陷设太低误报会把正常品全部当缺陷产线根本没法用。没有一个万能阈值需要在你的实际测试集上扫描0.1到0.5画一条置信度-召回率曲线再选点。关于迭代我有一个固定习惯每次实验只改一个变量分辨率、增强强度、模型大小三选一用同一个测试集去对比。很多失败的训练不是模型不行是变量混在一起出问题根本不知道是哪个参数引起的。我会在runs目录下建一个对比表格记录每轮实验的imgsz、batch、增强参数、mAP50、mAP50-95这四列几轮下来趋势自然浮现。还有一个值得做的验证是检查错误预测里哪些是标注本身的错——把预测框和真实标注框的IoU算出来超过0.5就算正确命中低于0.3的要单独看是漏检还是标注漏标。工业数据集的标注质量参差不齐出现“模型学对了、标注标错了”的情况很常见这时候需要回到数据侧修正这比调任何参数都有效。最后说回数据可视化脚本这个点。训练完再跑一遍可视化看的是预测结果里的置信度分布如果所有正确预测的置信度都刚好擦着0.25的边缘说明模型学得勉强如果大部分集中在0.7以上说明类别可分性不错可以放心下线。视觉直觉和量化指标配合使用比单纯看mAP更容易定位到那个“看不见的坑”。我自己吃过大亏有一次因为省事没有重跑可视化直接把训练好的模型部署到产线结果white_spots类识别崩溃了。后来查原因是那一批新补充的训练图标注只画了框没有更新classes.txt的顺序id错位导致整个类别混乱。从那以后不管从哪拿到的数据集先跑一遍标注检查脚本再跑一遍可视化拼图最后才碰训练命令。这套流水线看着慢但比起“训了三天才发现数据是错的”这种事故便宜太多了。希望这篇笔记能帮你在腰果缺陷检测这条路上少走几个来回。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站