
简介这套辣椒植物数据集采用YOLOv格式面向农业科研人员和深度学习开发者用于训练农作物目标检测模型、识别辣椒植株生长状态与病虫害特征可部署于农田智能监测场景。包内共2000个文件核心为874张辣椒植株jpg图像与1116个对应txt标注文件另有6个csv和4个yaml文件用于类别说明与模型配置整体压缩包仅4.78MB轻量易用。数据集涵盖不同生长阶段和光照环境下的植株形态边界框标注了正常植株、病害及虫害等多种类别为YOLO系列模型的训练提供标准化输入。当前已有77人学习浏览适合需要快速开展农业视觉实验或验证目标检测算法的开发者能够帮助其在真实农田场景中实现辣椒健康状态自动识别与预警。1. 辣椒植物数据集为什么要转成 yolo 格式检测任务、训练脚本与落地场景做辣椒植物目标检测时很多人先卡在标注格式上。公开数据集大多是 COCO、VOC 或分割格式但 yolov5、yolov8 的训练脚本默认用 yolo 格式一张图片配一个同名 txt每行是“类别 归一化框坐标”。如果直接把 VOC 的 xml 或 COCO 的 json 塞进去脚本会在数据加载阶段报错或漏掉标签。把辣椒植物数据集整理成 yolo 格式不是为了让数据“更好看”而是为了让训练管线更短、更容易排查问题。从手动标注、已有公共数据转格式、到模型训练和迭代验证都能统一在同一条链路上。这篇文章适合准备用 yolo 系列训练自己的辣椒检测模型的开发者、农业算法工程师和学生。2. 认识 yolo 格式辣椒植物数据集的目录布局、标签 txt 与类别文件2.1 每张图对应一个同名 txtyolo 标签的五个数字意味着什么yolo 标注不把坐标写在 xml 或 json 里而是一个image.jpg对应一个image.txt。txt 每一行代表一个目标五个数字分别是class_id x_center y_center width height其中 x_center、y_center、width、height 都除以图片真实宽高归一化到 0~1 之间。第一次转换时最容易问为什么不是x_min y_min x_max y_max因为模型输出就是归一化的中心点和宽高输入也按这个形式组织可以减少对图片尺寸的依赖。举个例子一张 640x480 的辣椒图片某个辣椒果左上角在 (100, 200)右下角在 (200, 300)类别 id 是 0那么这一行标注写成# 假设图片宽高和左上/右下坐标 img_w, img_h 640, 480 x1, y1, x2, y2 100, 200, 200, 300 x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h box_w (x2 - x1) / img_w box_h (y2 - y1) / img_h print(f0 {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f})归一化的基准必须是图片真实宽高不是 640 或 416。训练脚本读入 txt 后会结合原始图片尺寸换算框的位置再做 resize。如果你除以 640而图片实际是 1920x1080框就会全部偏到左上角。转换时保留 6 位小数足够不需要写成分数或百分数。做辣椒果这类小目标检测时框的四舍五入误差会直接影响 IoU 计算所以尽量用.6f格式输出。2.2 目录怎么摆images、labels、classes.txt 和 train/val 划分常见做法是采用 ultralytics 兼容的目录结构在工程目录下分images/和labels/各自再分train/、val/子目录。完整的辣椒植物数据集目录大致长这样chili/ ├── images/ │ ├── train/ │ │ ├── chili_001.jpg │ │ ├── chili_002.jpg │ │ └── ... │ └── val/ │ ├── chili_val_001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── chili_001.txt │ │ ├── chili_002.txt │ │ └── ... │ └── val/ │ ├── chili_val_001.txt │ └── ... ├── dataset.yaml └── classes.txt对应的dataset.yaml写成path: /home/yourname/datasets/chili train: images/train val: images/val nc: 3 names: [pepper_fruit, pepper_flower, pepper_stem]这里train和val是相对path的路径。训练脚本会在images/train下找图片再自动去labels/train找同名 txt。所以图片目录和标签目录的子结构必须一致否则会报no labels found。有一个排查命令很实用for f in images/train/*.jpg; do base$(basename $f .jpg) [ -f labels/train/$base.txt ] || echo missing label: $f done这个循环会打印所有缺少对应 txt 的图片。实际项目中图片扩展名不统一经常导致同名匹配失败建议统一转成小写.jpg再做入队。2.3 LabelImg 与 Labelme两种主流标注工具转 yolo 的区别与取舍手动标注辣椒数据时最常用的是 LabelImg 和 Labelme。LabelImg 原生支持 yolo 输出保存时直接生成 txt 和 classes.txtLabelme 保存成 json需要二次转换。如果你只做矩形框目标检测优先选 LabelImg能省一步转换。但 LabelImg 对多边形标注支持弱如果后面还要做实例分割不如一开始用 Labelme。两类工具差异可以看这张表工具输出格式原生支持 yolo适合场景LabelImgVOC xml / yolo txt支持矩形框检测快速起步Labelmejson不支持需转换多边形、分割后续转检测框用 LabelImg 时要注意它保存的classes.txt会按标注顺序生成类别 id 依赖这个顺序。如果你标到一半把某个类别删了或者调整了类别顺序所有 txt 第一列的 id 都会错位。只改 classes.txt 不改正 txt模型训练出来会稀烂。所以我的习惯是先固定一份类别清单比如下面的映射再开标class_map { pepper_fruit: 0, pepper_flower: 1, pepper_stem: 2, }中途不要改这个名字和顺序。真需要改动就写脚本批量处理 txt不要手动去改标注文件。3. 把已有辣椒数据转成 yolo 格式VOC/COCO 转换脚本与参数说明3.1 从 VOC xml 转 yolo坐标换算与边界框裁剪很多公开植物数据集是 VOC 格式或者标注工具导出的是 xml。转换思路不复杂遍历Annotations/目录读取每张图对应的 xml把每个object的name和bndbox取出来用归一化公式写入 txt。但有几个细节容易被忽略一张图里同一个目标被标了两次需要去重bndbox越界时不要直接写出越界坐标先裁剪到图片边界再转换。特别是用脚本从旧数据批量转格式时会有少量框超出图片尺寸不管它后面训练损失会变得很大。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, out_txt, class_map): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue box obj.find(bndbox) x1 max(int(float(box.find(xmin).text)), 0) y1 max(int(float(box.find(ymin).text)), 0) x2 min(int(float(box.find(xmax).text)), img_w) y2 min(int(float(box.find(ymax).text)), img_h) if x2 - x1 1 or y2 - y1 1: continue x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{class_map[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))class_map是类别名到 id 的映射比如{pepper_fruit: 0, pepper_flower: 1}。脚本跳过未定义类别同时过滤掉宽或高小于 1 像素的碎框。这个过滤条件很关键有些 xml 里目标被标成一条线不处理就会出现反向框或者面积为 0 的框。3.2 从 COCO json 转 yolo用 pycocotools 读 annotations 并写出 txtCOCO 格式的 JSON 里包含images和annotations两个数组。annotations中的bbox是[x, y, width, height]也就是左上角坐标加宽高category_id是全局编号。转 yolo 时需要先建立image_id到文件名和图片尺寸的映射再遍历每个目标的bbox。注意跳过iscrowd1的标注因为这类目标往往是密集人群或其他复杂区域直接转成普通框会给训练带来很多负样本。import json import os from collections import defaultdict def coco_to_yolo(coco_json, output_dir, class_map): with open(coco_json, r) as f: data json.load(f) img_info {img[id]: img for img in data[images]} anns_by_img defaultdict(list) for ann in data[annotations]: if ann.get(iscrowd, 0) 1: continue anns_by_img[ann[image_id]].append(ann) for img_id, anns in anns_by_img.items(): img img_info[img_id] img_w, img_h img[width], img[height] lines [] for ann in anns: if ann[category_id] not in class_map: continue x, y, w, h ann[bbox] x1, y1 max(x, 0), max(y, 0) x2, y2 min(x w, img_w), min(y h, img_h) if x2 - x1 1 or y2 - y1 1: continue cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h lines.append(f{class_map[ann[category_id]]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join( output_dir, os.path.splitext(img[file_name])[0] .txt ) with open(out_path, w) as f: f.write(\n.join(lines))这个脚本里最容易翻车的地方是直接用ann[bbox]的宽度和高度去计算中心点却以为它是右下角坐标。COCO 的 bbox 本来就是宽高直接xw、yh还原右下角就行不要再减一次。如果还原后的x2小于x1那一定是减错了。3.3 classes.txt 与 category_id类别从 0 编号的对应关系类别映射不能随意设。YOLO 约定类别 id 从 0 开始所以第 0 行是第一个类别第 1 行是第二个类别。如果辣椒植物数据集要检测辣椒果、花、叶、茎和整株 5 类nc就写 5classes.txt写 5 行。yaml 里的names列表顺序必须与 classes.txt 的行顺序一致。多个公开数据集合到一起时最容易出现“同一个目标在两个数据集里类别 id 不同”的问题。比如一个数据集的辣椒果是 class 0另一个数据里是 class 5。合并前必须先做类别归并统一成一份映射。否则模型训练时会把同一个语义类别当成两个不同类别最后指标稀烂。建议把映射表单独放一个 JSON 文件保存import json class_map { pepper_fruit: 0, pepper_flower: 1, pepper_stem: 2, pepper_leaf: 3, whole_plant: 4, } with open(chili_class_map.json, w) as f: json.dump(class_map, f, indent2, ensure_asciiFalse)每次转格式都从这个映射文件读取不要靠记忆手写。3.4 划分 train/val随机种子、图片名单与避免同源图片串集数据划分直接影响评估可信度。常见做法是按 8:2 或 9:1 随机划分但要固定随机种子保证复现。这里有一个农业场景的特殊坑同一个地块、同一株辣椒拍的几十张高度相似如果随机划分验证集里会出现与训练集几乎一样的画面mAP 虚高到 0.95一到现场就崩。所以更好的做法是按“采集批次”或“植株个体”分组划分。比如文件名fieldA_plant01_01.jpg、fieldA_plant01_02.jpg这些同一个植株的多视角图尽量放同一侧不要随机拆散。简单随机划分代码import random from pathlib import Path import shutil random.seed(42) jpg_files sorted(Path(images).glob(*.jpg)) random.shuffle(jpg_files) split_idx int(len(jpg_files) * 0.8) for split_name, files in [(train, jpg_files[:split_idx]), (val, jpg_files[split_idx:])]: for img_path in files: txt_path Path(labels) / (img_path.stem .txt) if not txt_path.exists(): print(fmissing txt: {txt_path}) continue shutil.copy(img_path, fimages/{split_name}/{img_path.name}) shutil.copy(txt_path, flabels/{split_name}/{txt_path.name})按批次分组时可以改成以文件名前缀作为分组依据比如取_分隔后的第一个字段作为 group id再对 group id 做划分。这样能让验证集更接近田间真实场景而不是简单地随机抽样。4. 辣椒植物 yolo 格式数据集的避坑排查从空标签到小目标漏检4.1 训练一启动就报 no labels found标签目录查不到 txt现象数据集在别的格式下能训练转成 yolo 格式后yolov8 刚启动就报no labels found in labels/train/...。原因最常见是目录层级不对。YOLO 脚本要求labels/train与images/train同级且 txt 与图片文件同名。另一个常见原因是图片扩展名是.jpeg或.JPG而脚本按.jpg查找找不到同名 txt。还有人是把标签和图片放在同一个目录以为脚本能自动识别。解决先统一图片扩展名为小写.jpg再用下面命令检查两边文件数find images -name *.jpg | wc -l find labels -name *.txt | wc -l然后随机挑一张图片确认它的 txt 文件确实存在且内容非空。有一个看起来笨但最可靠的方法写个循环把缺失标签的图片路径列出来不要靠眼睛在几千个文件里找。4.2 训练 loss 一直很高预测框全部偏移现象loss 不降验证时预测框落在叶子边缘甚至图片外。原因txt 里的坐标没有归一化或者归一化时用了固定尺寸 640 而不是原图真实宽高。还有一种情况是 VOC 转 yolo 时把xmax、ymax直接当成框的宽高写进去了。解决抽一张图读取图片原始尺寸手动用 txt 里的中心坐标反算回像素坐标。如果还原出来的x_center * img_w不在 0 到 img_w 之间说明坐标换算有严重问题。排查时可以用这段代码import cv2 img cv2.imread(chili_001.jpg) img_h, img_w img.shape[:2] with open(labels/train/chili_001.txt) as f: for line in f: cls, cx, cy, w, h map(float, line.split()) x1 int((cx - w / 2) * img_w) y1 int((cy - h / 2) * img_h) x2 int((cx w / 2) * img_w) y2 int((cy h / 2) * img_h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)把check.jpg打开肉眼对比原图。这一步能过滤掉绝大多数标注转换问题比看训练日志更直接。4.3 类别 id 错位模型把辣椒果全认出花现象PR 曲线开始变得奇怪混淆矩阵里类别之间大量互串比如辣椒果大量被识别成花。原因标注中途改过 classes.txt或者多个标注人员使用的标签版本不同或者合并多个数据集时没有统一类别映射。标到一半删除某个类别所有排在该类别后面的框第一列 id 都会往前挪一格但标注文件不会自动更新。解决写一个批量修正脚本把所有 txt 第一列的旧 id 映射成新 id。但在动手前建议先用可视化脚本把框画到原图上按不同颜色区分类别人工抽检 100 张。如果发现所有框的类别都往后错一位那就是典型的 id 错位。修正后用grep检查每个类别的目标数量是否合理比如辣椒果数量远多于花突然验证集里花是果的 3 倍就要怀疑映射顺序反了。4.4 辣椒果小目标漏检率高mAP50-95 比 mAP50 低太多现象mAP50 还能看mAP50-95 非常低小果在验证图上检不到。原因辣椒果相对整株图片来说偏小标注框平均像素可能只有 20x20而模型下采样倍数大小目标特征在浅层就丢了。也可能标注框太松把果柄和背景都包进去模型学到的是“果子加叶子”的混合特征。解决训练时把imgsz从 640 提高到 1280同时换参数量更大的模型比如用 yolov8m 或 yolov8l。显存不够时优先在大图上用小 batch。但更直接的还是保证标注质量小果的框要紧贴果皮不要为了省事把周围叶子也框进去。如果目标实在太小考虑把原图按区域切块训练而不是整体 resize。4.5 验证集 mAP 高但田间视频里把杂草当辣椒叶现象离线评估数字很好看一到实际视频里误检一堆杂草、土壤、水滴都触发框。原因训练数据里背景太单一所有图片来自同一块地、同一天、同一时间段的光照。yolo 模型会把背景纹理学进特征如果训练集中没有杂草背景的负样本模型就不知道哪个区域不是辣椒叶。解决采集阶段刻意加入不同光照、不同土壤状况、不同生长环境的背景图。可以在数据集里加入不包含任何目标的原图对应 txt 留空。很多人忽略这一点。负样本能显著降低误检率尤其是农业场景背景变化比目标本身还大。5. 用 yolo 格式辣椒植物数据集训练 yolov8data.yaml、训练命令与评估5.1 最小训练命令安装 ultralytics 与 data.yaml 写法数据准备好之后直接用 ultralytics 的 yolo 包就能跑。安装命令很简单pip install ultralytics训练命令yolo detect train datachili.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0datachili.yaml指向之前写好的配置。modelyolov8n.pt表示从预训练权重开始训练不要用yolov8n.yaml从头训辣椒数据量通常不够支撑全随机初始化。epochs100不是固定值数据量少时可以配合早停让模型自己决定何时收敛。chili.yaml范例path: /data/chili train: images/train val: images/val nc: 3 names: - pepper_fruit - pepper_flower - pepper_stem这里nc必须等于names的长度也必须等于所有 txt 中出现的类别总数。如果某个类别在标注里出现了 0 次但names里还留着训练不会报错但那个类别的 AP 会一直为 0。5.2 关键超参imgsz、batch、epochs、patience 和训练提示训练参数不需要一上来就调很多先把几个关键项设对参数建议值说明imgsz640 或 1280辣椒果小目标多时优先 1280显存不够再降 batchbatch8~32显存 OOM 时从 batch 开始降不要先降 imgszepochs100~300数据量小的时候配合早停避免过拟合patience20~50验证集指标连续多少轮不提升就停止device0 或 0,1单卡写 0多卡写 0,1patience很实用。小数据集训练到第 80 轮后验证 mAP 往往开始震荡继续跑 200 轮只会过拟合。设patience30可以让程序在指标不再提升时自动保存最后一次最优权重省时间也省心。还有一个提示如果显存允许开启 AMP 混合精度。yolo 命令默认就是 AMP不需要额外设置。如果训练时 loss 偶尔出现 NaN先关掉 AMP 试一次排除数值精度问题。5.3 评估结果怎么看mAP50、mAP50-95、PR 曲线和验证图训练完成后结果会生成在当前目录下的runs/detect/train/里面有weights/、results.png、confusion_matrix.png和一批验证图片。重点看这几个指标mAP50IoU 阈值取 0.5 时的平均精度反映“能不能检到目标”。mAP50-95IoU 从 0.5 到 0.95 取平均对边界框精度要求更高。辣椒果这种小目标这一项通常偏低。PR 曲线曲线越靠右上方越好。如果曲线尾部掉得很厉害说明召回不足需要补充难例或负样本。混淆矩阵能看出漏检和误检集中在哪两个类别通常能反推标注质量问题。验证集图片也要看。有些人直接看 mAP 数字高就觉得完事了但画出来的预测框如果不贴着果皮松半圈mAP50-95 立刻掉好几个点。先把验证图片上的预测框与原图对比一遍再决定下一步是加数据还是调模型。大部分辣椒植物检测项目的问题出在数据自身比如标注太松、类别错位、背景单一而不是模型结构不好。6. 用验证结果反向修正辣椒植物数据集的三个技巧训练一轮之后不要急着调网络结构先用结果反哺数据。第一个技巧把训练集图片用同一个权重做一次推理筛选出置信度高但和任何标注框都无重叠的预测框。这些框很可能是漏标的辣椒果或花人工确认后补进 labels。用脚本批量画出来比纯靠眼睛翻几百张图快得多。第二个技巧难例挖掘。验证集误检最多的几张图比如把茎当成果、把杂草当成叶收集起来加入训练集。如果某一类误检率特别高可以考虑把类别名称改得更具体比如把pepper_leaf拆成leaf_clean和leaf_diseased有助于模型区分。第三个技巧标注版本管理。每次修改标签后把classes.txt和所有 txt 一起打包存一个版本并在版本号里注明改动点。这个习惯一开始看不出价值等训练到第几十版模型、发现指标倒退时能快速回滚到正确的数据版本不至于把两版标注混在一起。我自己每次标注完都会先花 10 分钟可视化检查再进训练。宁可在这个环节多花时间也不想训练出一个坏模型后回头改数据那个返工成本高得多。希望帮到你。本文还有配套的精品资源点击获取