ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLO鸡蛋品质分级数据集实战:五类标签与训练避坑指南

YOLO鸡蛋品质分级数据集实战:五类标签与训练避坑指南 简介一套面向YOLO系列算法训练与验证的鸡蛋品质分级目标检测数据集包含615张带标签图像标注覆盖血染鸡蛋、棕色鸡蛋、脏污鸡蛋、白鸡蛋和钙沉积蛋五类典型对象适合食品分拣、农畜产品质检、智能养殖等场景也可作为目标检测课程设计或算法对比实验的数据基础。压缩包内总计1846个文件含615张jpg原图、615个txt标注YOLO格式和615个xml标注VOC格式另有1个data.yaml配置文件两套标签分别放在独立文件夹中文件名末尾带有类别标识整体目录清晰。txt标注采用类别 中心点x 中心点y 宽 高的归一化坐标格式可被yolov5、yolov7、yolov8、yolov9、yolov10、yolov11直接读取xml则适合在VOC流程或标注工具中查看。数据集已划分好训练/验证集配合data.yaml即可直接训练和测试无需额外整理或转换标签。整包大小仅25.41MB轻量易获取目前已有104人学习可帮助快速构建鸡蛋品质分级原型并把更多精力放在模型调参与效果验证上。1. 鸡蛋品质检测不是玄学这套 YOLO 数据集到底解决了什么YOLO算法做鸡蛋品质分级听起来有点大材小用但真到了产线上你才发现缺的不是模型而是带标签的数据。这批615张图像带标签的鸡蛋品质分级数据集正是为了解决这个问题准备的。在禽蛋加工厂传送带上的鸡蛋每一颗都要经过人工肉眼检查。血染、脏污、钙沉积这类缺陷不同工人判断口径还不一样。我把目标检测模型引入现场后发现算法本身不是瓶颈真正难得的是一份干净、可复现的带标注样例。这套资源覆盖血染、棕色、脏污、白色、钙沉积五类同时提供YOLO的txt和VOC的xml两种标签已经划分好train/valid/test内置data.yaml可以直接喂给YOLOv5/v7/v8/v9/v10/yolo11。不管你是做食品质检自动化、农业视觉落地还是想找一份“处理数据集用于yolov8训练”的标准练习样本都能直接拿它起步。2. 鸡蛋数据集的底细五类标签、双格式坐标和目录划分拿到一个数据集第一件事不是急着训练而是把它拆开看。这个数据集的标签组织和常规目标检测数据集不太一样它同时给了你YOLO的txt和VOC的xml。理解这两套格式的差异以及它们和data.yaml的配合方式是后面所有操作不掉链子的前提。2.1 五类鸡蛋的视觉特征与标注难点数据集里的五类分别是血染鸡蛋blood_stained、棕色鸡蛋brown、脏兮兮的鸡蛋dirty、白鸡蛋white、钙沉积蛋calcium_deposit。血染蛋表面有红色或暗红色斑块轮廓不规则有时和深棕色蛋壳很难区分棕色蛋的颜色跨度很大从浅褐到深褐都有光照变化下容易和血染蛋混脏污蛋表面有泥土、粪便等遮挡了蛋壳本身的纹理白鸡蛋比较干净但恰恰是这种“干净”让它和钙沉积蛋成了最难分的一对。钙沉积蛋的蛋壳上有凸起的白色钙斑常见于气室周围小尺度下和白鸡蛋的蛋壳表面非常相似。标注难点就在这里白色和钙沉积的边界是模糊的。同一个标注者过几天再标同一张图都可能给出不同标签。所以我一直强调这份数据集能不能用出效果关键在标签质量。615张图不算多你完全有条件在训练前做一轮人工抽检把明显标错的框修正掉。具体怎么做我放在后面的避坑章节里。2.2 双标签格式YOLO txt 与 VOC xml 的读取与转换YOLO格式的txt文件每一行是class x_center y_center width height其中中心坐标和宽高都是相对原图的归一化值范围0~1。VOC格式的xml文件则是标准的PASCAL VOC结构object节点里包含name和bndbox给出的是绝对像素坐标。为什么同时给两种就我接触的项目来看老一些的质检流程还在用VOC格式喂SSD或Faster R-CNN新项目基本都直接在ultralytics框架上走YOLO。你拿到手后绝大多数场景用txt就够了但偶尔需要统一格式。比如你有一个现成的xml标注工具想把这份数据并进去那就得把txt转成xml。反过来如果你要跑YOLO但手头只有xml也一样能转。先演示读取YOLO txt并绘制框的方法import cv2 def draw_yolo_boxes(img_path, label_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) box_w float(parts[3]) box_h float(parts[4]) 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.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) return img names [blood_stained, brown, dirty, white, calcium_deposit] img draw_yolo_boxes(img_0642_150.jpg, img_0642_150.txt, names) cv2.imshow(check, img)这段代码把归一化坐标还原成像素坐标再画框显示。注意乘以图像宽w和高h时一定要用原始图像尺寸而不是resize后的尺寸。如果你发现画出来的框错位先检查图像是不是被旋转了再检查txt里的坐标有没有超过0~1范围。读取VOC xml要简单一些用标准库即可import xml.etree.ElementTree as ET def parse_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() boxes [] for obj in root.iter(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) boxes.append((name, xmin, ymin, xmax, ymax)) return boxes这里返回的是绝对像素坐标和类别名。转YOLO格式时记得用图像宽高归一化。转换脚本我会在第4章给出但核心逻辑是x_center (xmin xmax) / 2 / img_w宽度w (xmax - xmin) / img_w并且保留6位小数不要四舍五入到整数。2.3 data.yaml 与 train/valid/test 目录结构摘要提到数据集已经划分好并且包含data.yaml。我猜它的目录结构大致是这样的egg_data/ ├── data.yaml ├── train/ │ ├── images/ │ └── labels/ ├── valid/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/data.yaml是YOLO训练的关键配置常见内容如下train: train/images val: valid/images test: test/images nc: 5 names: 0: blood_stained 1: brown 2: dirty 3: white 4: calcium_deposit注意names的索引顺序必须和txt里的class索引一致。很多朋友问“为什么我mAP一直是0”十有八九是这里顺序搞反了。这份数据集既然内置了data.yaml本来可以直接用但我仍然建议你打开检查一遍。尤其是train和val的路径写法有些版本喜欢相对路径有些版本必须用绝对路径不然会报错找不到图片。另外划分好的目录虽然方便但615张图做五类检测验证集可能只有60~80张。如果验证集太小模型的mAP波动会很大。如果你有足够时间可以考虑合并train和valid然后做K折交叉验证。不过对于快速验证数据集能不能用直接采用默认划分就够了。2.4 标签体检统计类别分布与坐标越界检查训练前我习惯先跑一段“体检”脚本看看各类别目标数量是否均衡标签坐标是否都在图像范围内。这个步骤能帮你提前发现很多坑。from pathlib import Path import cv2 import collections labels_dir Path(train/labels) images_dir Path(train/images) counter collections.Counter() out_of_range 0 for txt_path in labels_dir.glob(*.txt): img_path images_dir / (txt_path.stem .jpg) if not img_path.exists(): img_path images_dir / (txt_path.stem .png) img cv2.imread(str(img_path)) h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) counter[cls_id] 1 x_center float(parts[1]) y_center float(parts[2]) box_w float(parts[3]) box_h float(parts[4]) if not (0 x_center 1 and 0 y_center 1 and 0 box_w 1 and 0 box_h 1): out_of_range 1 print(类别分布:, counter) print(越界标签数量:, out_of_range)这段代码统计每个类别的目标数量并检查归一化坐标是否越界。如果某类只有个位数目标比如血染蛋只有20个那么模型的该类AP大概率会很低。你可以考虑针对该类做简单的数据增强或者收集更多样本。越界标签则需要回退到原始标注重新转换。3. 把数据集跑起来YOLOv8 训练鸡蛋分级模型的完整步骤训练这一步网上教程很多但很多都是搬官方文档缺少实际参数调整的经验。这里我按自己的实际操作顺序来写尽量让新手也能一次把模型跑起来。3.1 环境准备ultralytics 的安装与版本选择训练YOLOv8只需要ultralytics这个包官方pipeline已经把所有细节封装好了。安装命令pip install ultralytics如果你之前装过建议卸了重装否则可能与PyTorch版本产生冲突。我一般用ultralytics 8.x搭配Python 3.9。训练用的硬件有N卡最好没有N卡用CPU也能跑只是时间会拉长。615张图100个epochCPU大概要跑三四个小时GPU通常十几分钟。安装完成后验证一下yolo --help能打印出帮助信息就说明环境没问题。3.2 准备数据集目录并修改data.yaml解压下载的压缩包后把整个文件夹放到一个没有中文和空格的路径下比如/data/。这一点很多人忽略Windows下路径带中文ultralytics偶尔会读取失败。下一步是确认data.yaml里的路径。如果你和我一样把项目放在/data/egg_data/下建议把data.yaml改成绝对路径省得后面启动训练时还要切换工作目录。改完后的内容类似这样train: /data/egg_data/train/images val: /data/egg_data/valid/images test: /data/egg_data/test/images nc: 5 names: 0: blood_stained 1: brown 2: dirty 3: white 4: calcium_deposit改好后用一小段python代码验证yaml能否正常加载import yaml cfg yaml.safe_load(open(/data/egg_data/data.yaml)) print(cfg[nc], len(cfg[names])) assert cfg[nc] len(cfg[names]), nc和names数量不匹配如果能打印出5 5并通过断言说明配置没问题。3.3 启动训练预训练权重、batch、epochs 怎么设训练命令yolo train data/data/egg_data/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0解释一下关键参数modelyolov8n.pt使用nano预训练权重显存占用小训练快。如果显存大于6G可以换成yolov8s.pt精度会更好。鸡蛋检测这种任务s通常够用。imgsz640输入图像缩放尺寸。这份数据集的原始图像尺寸不一定统一ultralytics会自动resize。鸡蛋不算极小目标640够了。batch16根据显存调整。显存不够就降到8或4显存充裕可以加到32。device0单卡GPU训练。CPU训练就改成devicecpu。训练开始后控制台会打印每个epoch的box_loss、cls_loss、dfl_loss以及验证集的mAP50、mAP50-95。如果你看到前几个epoch的mAP50直接跳到0.9以上先别高兴很可能是类别数错误或验证集太小。我一般会再等20个epoch看cls_loss是否平稳下降如果一直震荡优先回去查标签。3.4 训练输出目录与权重选择训练完成后结果默认保存在runs/detect/train/下。里面有两个权重文件best.pt和last.pt。best.pt是在验证集上mAP50最高的权重推理时用这个。last.pt是最后一个epoch的权重用于断点续训。你还会看到results.png和confusion_matrix.png。results.png包含loss曲线、精确率、召回率、mAP曲线以及所有类的PR曲线。confusion_matrix.png能直观看出哪两类容易混淆。对于这份数据重点看calcium_deposit和white之间的混淆率如果互相认错的百分比高后面要专门处理。如果训练中途断了想接着跑yolo train resume modelruns/detect/train/weights/last.pt这样会沿用之前的优化器状态和训练进度。4. 验证与推理把模型丢去测鸡蛋还要会改标签格式训练好模型只是第一步。你真正要回答的问题是这个模型在实际场景里能不能用这一章讲怎么评估、怎么推理以及怎么把两种标签格式互相转换顺便解决一些常见的格式问题。4.1 用验证集评估模型性能用训练好的权重跑验证集yolo val modelruns/detect/train/weights/best.pt data/data/egg_data/data.yaml输出会包含mAP50、mAP50-95、每类的precision和recall。重点关注calcium_deposit和white这两类的AP。如果它们明显低于其他类那么模型在产线上会把钙沉积蛋漏检或误报成白鸡蛋。这时候打开confusion_matrix.png确认是哪种错误占主导。如果只有个别类别差可以尝试提高该类的样本权重或者做针对性数据增强。但如果所有类都一般优先回头检查标签质量。4.2 对单张图像和视频做推理用命令行推理最简单yolo predict modelruns/detect/train/weights/best.pt source/data/egg_data/test/images/img_0642_150.jpg saveTruesaveTrue会在runs/detect/predict下生成带标注框的结果图。如果要批量处理把source改成整个目录路径即可。如果要跑视频或摄像头source指向视频路径或摄像头ID。产线场景建议把conf阈值调高一点比如conf0.4减少误检。命令行里加conf0.4即可。如果你要在自己的python脚本里调用参考下面这段from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(/data/egg_data/test/images, conf0.25, saveTrue) for r in results: boxes r.boxes.xyxy.cpu().numpy() clss r.boxes.cls.cpu().numpy() scores r.boxes.conf.cpu().numpy() print(boxes.shape, clss, scores)返回的boxes是像素坐标的xyxy格式clss是类别索引scores是置信度。记得在部署脚本里要把clss映射回类别名称。4.3 标签格式互转xml转txt 与 txt转xml虽然这份资源同时提供了两种格式但你可能会遇到只有一个xml或只有一个txt的场景这时候需要自己写转换脚本。下面是xml批量转txt的脚本import os import xml.etree.ElementTree as ET from pathlib import Path def convert_xml_to_yolo(xml_path, out_txt_path, class_names, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) dw 1.0 / img_w dh 1.0 / img_h x_center (xmin xmax) / 2.0 * dw y_center (ymin ymax) / 2.0 * dh w (xmax - xmin) * dw h (ymax - ymin) * dh lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) class_names [blood_stained, brown, dirty, white, calcium_deposit] convert_xml_to_yolo(sample.xml, sample.txt, class_names, 1920, 1080)注意几个要点class_names.index(name)要求xml里的类别名和列表完全一致大小写、空格都不能差。转换时用的img_w和img_h必须是xml对应的原始图像尺寸如果你用了resize后的尺寸坐标就会偏。另外保留6位小数千万不要用整数。反过来txt转xml的思路类似但更少见。如果你的标注工具要求VOC格式可以用labelImg直接打开txt生成xml但我个人觉得没必要手动写除非你要批量处理。4.4 混淆矩阵与F1曲线定位类间混淆ultralytics训练结束后会在输出目录生成confusion_matrix.png和results.png。confusion_matrix.png是一个5x5的矩阵背景类也算的话是6x6每一行代表真实类别每一列代表预测类别对角线上的值越高越好。我见过有人只看mAP不看混淆矩阵结果上线模型把钙沉积蛋全部误判成白鸡蛋漏检率直接爆表。F1曲线在results.png的右下角它反映了不同置信度阈值下的F1分数。你可以从图中找到峰值对应的置信度阈值用它作为推理时的conf。对于鸡蛋分级这种场景高精确率比高召回率更重要因为误检造成的成本比漏检大。所以我通常会把阈值定在F1曲线峰值偏右一点也就是稍高的置信度。5. 避坑指南训练鸡蛋数据集最常踩的5个坑这个数据集虽然结构干净但如果你不提前注意几个细节还是会把大量时间耗在排错上。下面是我自己实际踩过的坑也是群里朋友问得最多的问题。5.1 类别索引错位导致mAP一直为0现象训练20个epoch验证集mAP始终是0cls_loss不下降甚至往上走。原因最常见的是类别索引错位。比如data.yaml里names顺序是0: blood_stained, 1: brown...但txt文件里的class索引却是从1开始或者某些xml转换工具把索引加了一次。ultralytics不会报错因为它只按索引读结果就是模型把所有目标当成另一个类别loss自然不会收敛。解决用前面2.4的统计脚本得到所有txt的类别索引分布。再检查data.yaml里的names顺序确保两者一一对应。一个简单技巧是把所有txt里出现的最大索引打印出来如果等于nc-1就一般没问题如果超过nc-1就肯定错了。转换脚本里用class_names.index(name)而不是手动写数字索引。5.2 EXIF方向导致推理框偏移现象训练时loss正常mAP也还行但推理画框时框整体偏移尤其是小目标歪得明显。原因部分图像来自手机或相机带了EXIF方向信息。cv2.imread默认不处理这条信息读出来的图像是旋转前的样子但标签是在旋转后的图像上标注的坐标自然对不上。解决在数据准备阶段统一处理。我一般会写一个预处理脚本把所有图像用cv2.imread后读取EXIF方向如果不是标准方向就旋转然后覆盖保存。或者用PIL.Image的ImageOps.exif_transpose转正后再保存。这样保证所有图像的像素排列和标签严格一致。检测的时候也一样先transpose再喂给模型。5.3 数据划分泄漏导致验证集虚高现象验证集mAP50有0.96但一拿到产线上测漏检一堆尤其脏污蛋。原因数据集划分时没有按图像来源分组。这套数据集的图像文件名有前缀比如img_0642_150.jpg和img_0642_151.jpg很可能来自同一个拍摄序列。如果随机划分同一个拍摄序列的前后帧可能同时进入train和valid验证集就有了“记忆”导致指标虚高。解决检查文件名前缀按前缀分组后再划分。如果没有分组信息就用连续帧之间的间隔做划分比如每隔10帧抽1帧作为验证集其余训练。或者干脆手动把相似背景的图分开。这一步可以通过统计每张图的感知哈希来做但615张图手动分也没问题。5.4 标签噪声背景阴影被标成目标现象模型把托盘边缘识别成白鸡蛋或把传送带上的水渍识别成脏污。原因标注时不够严格把蛋壳上的反光、阴影、甚至蛋托边缘也标成了目标。这种噪声在小目标检测里非常致命因为模型会把背景特征当成正样本。解决做一轮标签人工清洗。写一个脚本把所有训练图像和标注框输出到一个小视频或拼接图片人眼快速扫一遍。615张图半小时足够。我检查时重点看两类white和calcium_deposit因为它们最容易标错。发现有问题的框直接用labelImg修正或删除。5.5 VOC转YOLO时数值精度丢失现象转换后的txt文件里坐标看起来正常但训练时小目标的框一直在抖loss在后期不降。原因转换脚本用了四舍五入到百分位比如round(x_center, 2)。YOLO格式的归一化坐标要用高精度尤其是小目标误差会被放大。解决转换时保留6位小数使用格式化字符串f{x_center:.6f}。同时确保图片宽度和高度是原始数据不是训练时的resize尺寸。转换后随机抽10张图画框和原图重叠检查一遍确认坐标没有偏移再训练。6. 进阶技巧用预训练权重和数据增强把mAP再拉高几个点如果你已经用默认参数训练完感觉指标卡在0.8过不去可以试试这几个技巧。它们不复杂但能让你的模型从“能跑”变成“好用”。第一把预训练权重从yolov8n.pt换成yolov8m.pt。模型容量增大对特征能力的拟合会更好。显存不够就减小batch比如从16降到8或者把imgsz调成480再训练之后推理再回到640。这个方法一般能带来12个点的mAP提升。第二调整数据增强参数。ultralytics默认开了大量增强但有些针对小目标不友好。比如mosaic会把多张图缩放拼接小目标缩得更小。你可以试着把mosaic关掉或者降低mixup权重在data.yaml里加一行augment: mosaic: 0.5 mixup: 0.2 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4具体数值需要实验。对于鸡蛋这种蛋壳颜色敏感的任务色调扰动不宜过大否则会把棕色改成红色破坏语义。我个人习惯把hsv_h压到0.01以下。第三针对易混淆类别做困难样本挖掘。训练完第一个模型后用它在验证集上预测把所有预测错误或置信度低于0.5的图像挑出来。你可能会发现这些图像基本都是白色和钙沉积混标签的错误样本。把它们拎出来人工修正标签后再加入训练集重新训练。虽然615张图不大但这个操作能显著降低混淆率。最后一个不常被提但很实用的技巧合并易混淆类别。如果产线不强制要求区分白鸡蛋和钙沉积蛋你可以把calcium_deposit合并进white把五类任务变成四类。类别少了模型学习的负担小了整体AP往往能上涨。我见过不少项目用这招后mAP直接提高4个点以上。当然如果产线确实需要把钙沉积单独分出来还有另一个思路在YOLO后面加一个专门的二分类细分头但这已经超出这份数据集的应用范围了。从那以后我每拿到一份数据集都会强制走一遍“标签体检”——统计类别分布、画框检查、确认data.yaml顺序完整然后才允许自己点下yolo train。这个习惯看起来琐碎但确实帮我省掉了无数个深夜调bug的时间。希望这套鸡蛋品质分级数据集的完整演示也能帮你在自己的目标检测项目里少走弯路祝你顺利。本文还有配套的精品资源点击获取
返回列表