恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
白萝卜检测数据集处理与YOLO训练实战全流程
首页
资讯中心
/
白萝卜检测数据集处理与YOLO训练实战全流程
白萝卜检测数据集处理与YOLO训练实战全流程
发布时间:2026/9/15 5:50:07
简介面向YOLO系列目标检测任务的白萝卜检测数据集包含1000张带标注图像适合需要快速上手YOLO模型训练的学生、算法工程师以及农业智能化项目开发者。数据集已划分好训练集与验证集并附带data.yaml配置文件可直接在YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10及YOLO11等版本中调用训练与测试。压缩包内文件共2000个包含999个YOLO格式的txt标签、1000个VOC格式的xml标签以及1个yaml配置txt标签采用class x_center y_center width height的相对坐标格式xml则保留VOC通用标注结构方便按项目习惯选用。资源整体大小约32.8MB目前已59人学习下载。对农业检测场景而言这份数据省去了大量采集与标注成本直接支撑模型快速迭代与效果验证是目标检测入门及项目落地的实用素材。1. 白萝卜检测数据集拿到 1000 张带标签图之后的第一件事把「白萝卜检测数据集」这个压缩包解压出来你面对的不是模型而是 1000 张等着被验证的图片和一堆 txt 文件。这类农业视觉数据集在 YOLO 项目里出现频率很高——白萝卜分级、收获机械手定位、田间表型统计都要用到检测模型而数据集质量往往比网络结构更影响最终精度。常见的情况是标注工具导出的格式不一致、类别编号错位、训练验证集划分不干净直接拿去跑 YOLO 大概率在验证阶段翻车。这篇文章把「拿到数据集之后做什么」讲透包括 YOLO 标签格式解析、标签合法性校验、可视化检查、训练配置和数据增强边界适合正在用 YOLO 训练自定义检测模型的工程师也适合刚接手数据集预处理任务的研究者。1000 张图不算多但用对了方法足以训练出一个可用的白萝卜检测器。2. 数据集结构与 YOLO 标签格式先搞懂 1000 张图该怎么组织2.1 白萝卜数据集的标准目录划分一个合格的检测数据集压缩包解开之后目录结构通常是这样的white_radish_dataset/ ├── images/ │ ├── train/ # 约 800 张 │ ├── val/ # 约 150 张 │ └── test/ # 约 50 张 ├── labels/ │ ├── train/ # 与 images/train 同名 .txt │ ├── val/ │ └── test/ └── dataset.yaml # 类别与路径定义拿到数据集第一步不是急着训练而是确认目录是否满足 YOLO 的硬性约定图片文件名与标签文件名必须完全一致只有扩展名不同例如radish_001.jpg对应radish_001.txt。很多标注工具导出时会把标签文件命名为radish_001.txt.txt或混入中文名这类问题在加载时会直接报错或静默丢弃样本。我一般先跑一个脚本把 images 和 labels 两个目录下的文件名做差集比对找出缺失项。另一个需要确认的是图片是否全部为 RGB 三通道个别数据集里混入灰度图或 PNG 带透明通道虽然 YOLO 训练时能读但会影响归一化后的分布一致性。如果压缩包里没有现成的 train/val/test 划分需要自己按比例切分常见做法是 8:1:1。切分时注意按照「图片-标签成对」移动文件避免只移动了图片而留下孤儿标签。还有一种容易被忽略的情况数据集作者可能把图片放在一个目录、标签放在另一个目录但文件名前缀并不一致例如图片是IMG_20231001_01.jpg标签却是label_001.txt这种情况下需要靠标注工具自带的映射关系或图像内容重新匹配属于脏数据里的麻烦情形。2.2 YOLO txt 标签的五个数字到底代表什么YOLO 格式的标签不是 VOC 的 XML也不是 COCO 的 JSON而是最简单的纯文本。每行代表一个目标框格式固定为class_id x_center y_center width height比如0 0.512345 0.678901 0.213456 0.187654含义是类别编号为 0目标框中心点的 x 坐标在图片宽度的 51.23% 处y 坐标在图片高度的 67.89% 处框宽度占图片宽度的 21.35%框高度占图片高度的 18.77%。这五个数字全部是相对坐标数值范围应该在 0 到 1 之间除了 width 和 height 理论上可以略大于 1如果目标超出图片边界。归一化的好处是标签与图片绝对尺寸无关无论是 640x640 还是 1280x960 的输入标签不需要重新计算。这里有一个常见的误读坐标不是左上角加宽高而是中心点加宽高。如果要用 OpenCV 画框查看必须先换算import cv2 def yolo_to_xyxy(x_center, y_center, w, h, img_w, img_h): x1 int((x_center - w / 2) * img_w) y1 int((y_center - h / 2) * img_h) x2 int((x_center w / 2) * img_w) y2 int((y_center h / 2) * img_h) return x1, y1, x2, y2 # 读取图片尺寸 img cv2.imread(white_radish_dataset/images/train/radish_001.jpg) h, w img.shape[:2] # 假设从标签文件读到了一行 x_c, y_c, bw, bh 0.512345, 0.678901, 0.213456, 0.187654 x1, y1, x2, y2 yolo_to_xyxy(x_c, y_c, bw, bh, w, h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_radish.jpg, img)这段代码的关键在于坐标系的换算逻辑先减半宽得到左上角 x再乘图片宽度还原为像素值。很多人在这一步直接拿归一化坐标去画框画出来的框位置完全错乱。yolo_to_xyxy函数是数据可视化和标签校验的地基后续所有检查脚本都从这里展开。2.3 dataset.yaml 的类别名顺序不能乱YOLO 训练依赖一个 YAML 文件来声明类别列表和数据集路径这就要求标签里的 class_id 必须与 YAML 里的类别顺序严格对应。白萝卜检测数据集如果只有一个类别写法很简单path: ./white_radish_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: white_radishnc是类别总数names是类别名列表索引序号就是标签里第一列的编号。如果数据集里包含多个类别比如white_radish、soil、weed顺序错一个数字整个模型的预测结果就会全部串位训练过程甚至不会报错。检查方法很简单统计所有标签文件中出现的最大类别编号再与nc对比。如果最大编号是 2 但nc配的是 2对应类别名只有 0 和 1就会在训练时触发索引越界错误。我一般会在跑训练前用一条命令把标签里的编号分布拉出来看cat labels/train/*.txt | awk {print $1} | sort | uniq -cawk 取每行第一列sort 加 uniq -c 统计每个类别编号出现的次数一眼就能看出类别分布是否均衡以及是否存在超出预期的编号。这个命令不依赖任何 Python 环境在任何 Linux 服务器上都能直接执行。3. 标签合法性校验与可视化训练前必须做掉的三类坑3.1 用 Python 脚本做标签规则检查拿到数据集直接开训的做法风险很高因为标签文件里可能有坐标越界、宽高为 0、空文件、重复标注等情况。YOLO 训练时如果遇到完全为空的标签文件会输出 warning 但不会中断遇到坐标越界则可能在计算 loss 时产生 NaN。因此写一个独立的检查脚本是必要的我一般这样实现import os def validate_labels(label_dir, img_dir): issues [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue img_name fname.replace(.txt, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): issues.append(f[missing image] {fname} - {img_path}) continue label_path os.path.join(label_dir, fname) with open(label_path, r) as f: lines f.readlines() if len(lines) 0: issues.append(f[empty label] {fname}) for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: issues.append(f[bad format] {fname} line {i}: {line.strip()}) continue try: cid, x_c, y_c, w, h map(float, parts) except ValueError: issues.append(f[non-numeric] {fname} line {i}: {line.strip()}) continue if cid 0 or int(cid) ! cid: issues.append(f[bad class] {fname} line {i}: cid{cid}) if not (0 x_c 1 and 0 y_c 1): issues.append(f[center out of range] {fname} line {i}) if w 0 or h 0: issues.append(f[non-positive size] {fname} line {i}: w{w}, h{h}) if w 1 or h 1: issues.append(f[size exceeds image] {fname} line {i}: w{w}, h{h}) return issues issues validate_labels(white_radish_dataset/labels/train, white_radish_dataset/images/train) for msg in issues[:30]: print(msg) print(ftotal issues: {len(issues)})脚本逐行检查五类问题核对图片是否存在、内容是否为空、字段个数是否为 5、坐标是否在 0 到 1 区间、宽高是否为正数。其中「中心点越界」是最常见的标注错误通常发生在目标紧贴图片边缘时标注工具没有做好裁剪。这类数据会让 YOLO 学习到错误的定位偏移因为网络输出的中心点理论上必须在图片内部。「size exceeds image」检查的是目标框比图片还大的情况这在物理上不合理通常意味着标注时边界框选取逻辑出错。建议把检查结果分成「可自动修复」和「需要人工处理」两类中心点越界但框体仍有较多部分在图片内的可以裁剪后重新归一化宽高为 0 或负数的直接删除该行。3.2 OpenCV 画框确认标注是否贴合目标规则检查只能过滤格式错误无法判断标注内容是否正确。要实现这一点需要把标注框绘制到原图上人眼快速扫一遍。1000 张图全看不太现实抽 50 到 100 张即可发现系统性错误。我通常把画框后的图片拼接成网格图一次看 25 张import cv2 import os import math def draw_grid(image_dir, label_dir, output_path, grid_size5, sample25): all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] selected all_images[:sample] thumb_w, thumb_h 320, 240 grid_img None for idx, img_name in enumerate(selected): img cv2.imread(os.path.join(image_dir, img_name)) if img is None: continue ih, iw img.shape[:2] label_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) if os.path.exists(label_path): with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue _, x_c, y_c, w, h map(float, parts) x1, y1, x2, y2 yolo_to_xyxy(x_c, y_c, w, h, iw, ih) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, f{x2 - x1}x{y2 - y1}, (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) thumb cv2.resize(img, (thumb_w, thumb_h)) if grid_img is None: grid_img thumb.copy() else: pos idx % grid_size if pos 0 and idx 0: grid_img cv2.vconcat([grid_img, thumb]) else: grid_img cv2.hconcat([grid_img, thumb]) cv2.imwrite(output_path, grid_img) draw_grid(white_radish_dataset/images/val, white_radish_dataset/labels/val, label_grid.jpg)这个拼接脚本使用了 OpenCV 的hconcat和vconcat作用是把缩略图按行拼接成一张大图方便快速查看。这里的yolo_to_xyxy复用第 2 章的换算函数保证坐标逻辑一致。画框时把框的像素宽高也标注在框上方这样能直观发现「标注框比人眼判断明显偏大或偏小」的问题。白萝卜的特点是长条状如果大量标注框接近正方形说明标注时用矩形包络没有贴合物体主干方向这时候需要考虑旋转框OBB标注但 YOLO 原生版本不支持 OBB需要改用 YOLOv8-OBB 分支。3.3 类别分布与样本难度分析单类别数据集不需要担心类别不均衡但需要关注「每张图片的目标数量分布」。如果大部分图片只有 1 个目标偶尔出现一两张有 20 个目标的图片训练时 loss 会被大目标数的样本主导。用awk统计每张图片的框数分布for f in labels/train/*.txt; do echo -n $(basename $f): wc -l $f done | awk {sum[$2]} END {for (k in sum) print k, sum[k]} | sort -n | tail -20这个命令先循环每个标签文件输出文件名和行数再由 awk 按行数做频次统计。白萝卜检测场景中田间拍摄的图片往往密集排列单图目标数可能超过 30 个而收获机视角的图片可能每张只有 2 到 3 根萝卜。如果分布跨度大训练时需要通过rect模式YOLO 的矩形训练减少背景填充带来的计算浪费或者手动按密度对数据集做加权采样。还有一种需要注意的情况是数据集中存在一些图片没有任何标签负样本这些图片对降低误检有正面作用但如果数量过多会拉低正样本的学习效率。检查一下 labels 目录中是否有 0 字节文件就知道负样本占比有多少。4. YOLO 训练白萝卜检测模型从 data.yaml 到 mAP 指标4.1 选 YOLOv8n 还是 YOLOv8s1000 张图的最优解1000 张图属于小规模数据集模型选择上不能盲目追求大模型。YOLOv8 系列中 nnano和 ssmall是两个常见选择计算量和精度差异如下表模型参数量GFLOPs训练显存占用batch16, 640px适合场景YOLOv8n3.2M8.7约 6 GB1000 张图、边缘部署、实时性优先YOLOv8s11.2M28.6约 12 GB1000~5000 张图、精度优先、服务器推理我倾向于在 1000 张图规模下优先尝试 YOLOv8n。原因是目标检测的精度上限受数据量约束小模型在这种规模下更容易收敛且过拟合风险更低。如果迁移学习使用 COCO 预训练权重YOLOv8n 的特征提取能力对白萝卜这类纹理简单的目标已经足够。只有当你发现 val 集上的 mAP50 超过 0.95 且训练 loss 还没到底时才考虑换成 s 或 m 模型来榨取更多精度。DECI 的结论是模型大小应该和数据集规模匹配数据量翻倍再考虑模型升档。4.2 data.yaml 配置与训练超参数设置训练前需要准备 data.yaml第 2.3 节已有基础版本这里是完整版加路径修正path: /home/user/white_radish_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: white_radish需要特别注意的是path字段建议写绝对路径YOLO 在解析相对路径时以运行目录为基准如果后续在别的目录下启动训练样本加载会全部失败。train和val字段不要写成./images/train因为 YOLO 源码中Path(path) / train会拼接出./开头的相对路径不同版本处理方式略有差异。启动训练的命令yolo detect train \ modelyolov8n.pt \ datawhite_radish_dataset/dataset.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ optimizerAdamW \ patience20 \ projectwhite_radish_exp \ namerun1核心参数配置逻辑epochs设为 100但配合patience20做早停即连续 20 个 epoch 在验证集上 mAP 没有提升就自动停止防止过拟合浪费算力。imgsz选 640这是精度和速度的平衡点白萝卜目标不算小不需要 1280 的高分辨率。batch取决于显卡显存先按 16 试跑如果报 CUDA out of memory 就降到 8。optimizer推荐 AdamW它在小数据集上收敛比 SGD 更稳定虽然最终精度可能略低一点但不容易出现 loss 震荡。lr00.01适合迁移学习场景如果从头训练不加载预训练权重需要调到 0.001。训练过程中重点关注三个指标train/box_loss 是否持续下降、val/box_loss 是否在某个 epoch 后回升、metrics/mAP50 的变化曲线。val loss 回升而 train loss 继续下降就是过拟合的明确信号此时应该回退到之前最佳 epoch 的权重而非继续训练到 100 轮。4.3 训练后的权重评估与可视化推理训练完成后white_radish_exp/run1/weights/目录下会生成best.pt和last.pt。best.pt是验证集上指标最优的权重last.pt是最后一轮权重。评估验证集指标yolo detect val \ modelwhite_radish_exp/run1/weights/best.pt \ datawhite_radish_dataset/dataset.yaml \ imgsz640 \ batch16输出结果里需要看的核心指标mAP50IoU 阈值取 0.5 时的平均精度和mAP50-95阈值 0.5 到 0.95 步进 0.05 的平均。对于白萝卜这种目标边界清晰的场景mAP50 达到 0.9 以上是合理预期mAP50-95 在 0.6 到 0.7 之间已经不错。评估完成后可以挑几张图可视化模型的检测结果from ultralytics import YOLO model YOLO(white_radish_exp/run1/weights/best.pt) results model.predict( sourcewhite_radish_dataset/images/val, conf0.25, iou0.45, saveTrue, projectinference_output, nameval_pred )conf0.25是置信度阈值低于这个值的框会被丢弃。iou0.45是 NMS 的 IoU 阈值这个值调高会保留更多重叠框调低则抑制得更狠。白萝卜目标一般不会重叠太厉害0.45 是稳妥默认值。如果预测结果里出现大量同一根萝卜被框两次的情况把iou调到 0.5 到 0.6 消除冗余框。5. 用 1000 张图把精度再往上顶一档的四个技巧5.1 数据增强参数别照搬默认值YOLOv8 默认开启 Mosaic-4 增强把 4 张图拼成一张训练。这对于小目标是好事但对白萝卜这种长条形目标会产生副作用拼接边界会切断目标导致模型学到残缺的萝卜形态。常见做法是保留 Mosaic 但把mosaic0.5降到0.3同时开启close_mosaic10即最后 10 个 epoch 关闭 Mosaic让模型在正常的完整目标上做微调。另外hsv_h、hsv_s、hsv_v这些颜色扰动参数在农业场景下可以适当加大因为田间光线变化大颜色增强有助于泛化。建议的改动mosaic: 0.3 close_mosaic: 10 hsv_h: 0.02 hsv_s: 0.7 hsv_v: 0.5 degrees: 5.0这里degrees5.0表示随机旋转不超过 5 度白萝卜在土壤里的姿态基本是竖直或微倾的过大的旋转角度会生成不真实的训练样本。对比默认的degrees0.0增加 5 度的旋转可以让模型对轻微倾斜更鲁棒但没必要设成 90 度。5.2 K-Fold 交叉验证弥补数据量不足1000 张图做一次随机划分可能恰好把最难样本全分到 val 集导致训练结果偏低。K-Fold 交叉验证在小数据集上价值很高常见做法是 5-Fold把数据切成 5 份每次用其中 4 份训练、1 份验证训练 5 次取 mAP 均值。Ultralytics 官方提供了一个 kfold 脚本但也可以自己用sklearn实现目录级别的切分。切分时需要保证每个 fold 中图片不重叠最简单的方式是先把所有图片名打乱按序号取模分配到 5 个 foldimport os import shutil from sklearn.model_selection import KFold files sorted(os.listdir(white_radish_dataset/images)) kf KFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(kf.split(files)): os.makedirs(ffold{fold}/images/train, exist_okTrue) os.makedirs(ffold{fold}/images/val, exist_okTrue) os.makedirs(ffold{fold}/labels/train, exist_okTrue) os.makedirs(ffold{fold}/labels/val, exist_okTrue) for i in train_idx: src_img os.path.join(white_radish_dataset/images, files[i]) src_lbl os.path.join(white_radish_dataset/labels, files[i].replace(.jpg, .txt)) shutil.copy(src_img, ffold{fold}/images/train/) shutil.copy(src_lbl, ffold{fold}/labels/train/) for i in val_idx: src_img os.path.join(white_radish_dataset/images, files[i]) src_lbl os.path.join(white_radish_dataset/labels, files[i].replace(.jpg, .txt)) shutil.copy(src_img, ffold{fold}/images/val/) shutil.copy(src_lbl, ffold{fold}/labels/val/)5 次训练之后把每个 fold 的 mAP50 求平均得到的指标比单次划分更接近真实泛化精度。如果你的时间只够跑一次训练至少要把随机种子固定保证实验可复现。5.3 观察混淆矩阵与错误样本训练生成的confusion_matrix.png在white_radish_exp/run1/目录下不要只看 mAP 数值就结束。白萝卜检测场景中最容易混淆的是萝卜与土壤背景中的石块、杂草根茎。如果混淆矩阵里 background 被预测为 white_radish 的比例偏高说明模型的误检集中在纹理相似目标上。这时不需要加数据而是用训练后的模型去跑所有训练集图片把预测置信度最低的样本挑出来人工查看看是标注错误还是目标本身模糊。Ultralytics 的val模式会保存预测结果到runs/segment/val可以用第 3 章的网格图脚本快速浏览这些低置信度样本找出模型「学不会」的模式。最后一招是针对性挖掘难例把验证集里预测失败的图片单独复制到一个新目录在下一次训练时把它们重复采样加入训练集即给难例更高的采样权重这种做法虽然没有修改模型结构但往往比调参带来的提升更明显。1000 张图的数据集足够支撑这四步优化跑完之后再考虑是否继续采集数据扩大规模。本文还有配套的精品资源点击获取