ARTICLE DETAIL

资讯详情

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

用大豆叶病数据集入门YOLOv8:从环境搭建到模型训练避坑指南

用大豆叶病数据集入门YOLOv8:从环境搭建到模型训练避坑指南 简介基于YOLOv8的大豆叶病目标检测项目面向目标检测初学者与农业领域开发者帮助读者通过大豆叶病识别这一具体场景系统掌握YOLO框架的构建思路与训练流程。项目基于PyTorch实现压缩包共7个文件包含6个Python脚本和1个说明文档总大小仅9KB结构非常紧凑。6个Python脚本分别承担数据集加载、模型搭建、损失函数定义、训练与推理等核心功能README则提供基本使用说明便于按模块解读代码。已有47人学习适合想了解YOLOv8代码组织方式、快速入门目标检测的开发者。通过研读这些文件可以直观看到从数据预处理到模型输出的完整链路对理解YOLO整体框架构建和深度学习模型训练优化都有参考价值。1. 为什么我推荐用大豆叶病来入门yolov8目标检测框架很多人学 YOLOv8 目标检测第一步就是拿开源的猫狗 demo 跑一遍跑通之后仍然说不清数据集、标注、训练、评估、部署之间到底怎么串。我的建议是换一个“小而真”的场景大豆叶病。它的类别不多通常只关心灰斑、锈病、霜霉等几种叶片病害区域有大有小有密集也有稀疏能逼你把目标检测里最难啃的边界框回归和阈值选择都走一遍。这篇内容按“环境搭建 → 数据集制作 → 模型训练 → 避坑 → 模型验证”的顺序展开目标只有一个你在完成大豆叶病检测模型的同时把 yolov8 如何从数据到权重、再到推理结果的完整框架记在脑子里而不只是会敲两行命令。2. Ubuntu 20.04搭建yolov8环境cpu版本miniconda到ultralytics服务器和开发机上 Ubuntu 20.04 的存量很大用 CPU 版本先跑通环境是最稳妥的第一步。CPU 环境不是拿来训大模型的而是用来验证代码路径、数据加载、前后向能否正常走通真正大量训练时再切 GPU成本更低。这里带上 conda 做环境隔离避免把系统的 Python 搅乱这也是深度学习项目最常见的翻车点。2.1 用Miniconda隔离Python环境为什么不用系统Python系统自带的 Python 3.8 通常被 apt 和系统脚本依赖直接 pip 安装 PyTorch 和 ultralytics很容易出现包冲突严重时系统命令都被破坏。用 Miniconda 创建独立虚拟环境相当于给 yolov8 单独开一间屋子里面装什么都不会影响整个系统的 Python。# 下载 Miniconda 安装脚本并静默安装到用户目录 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda # 把 conda 加入当前 shell 的 PATH export PATH$HOME/miniconda/bin:$PATH # 创建 Python 3.10 虚拟环境 conda create -n yolo python3.10 -y # 激活虚拟环境 conda activate yolo代码里的-b是静默安装-p指定安装目录到$HOME/miniconda这样不用 sudo也不会污染/usr。Python 3.10 是 YOLOv8 生态下比较稳妥的版本3.8 也能跑但部分新依赖在 3.10 上更少出现奇怪的 wheel 编译报错。每次打开新终端都要重新执行export PATH$HOME/miniconda/bin:$PATH如果嫌麻烦可以把这一行追加到~/.bashrc结尾。之后不要再用python3直接跑先在终端确认提示符前面出现了(yolo)。2.2 安装PyTorch CPU版和ultralytics最小命令CPU 版 PyTorch 的安装命令和 GPU 版不同一定要加--index-url指定 cpu 源否则默认装的是 CUDA 版本虽然没有 GPU 也能用但包体积大不少而且在没有 Nvidia 驱动的机器上可能报 CUDA 初始化错误。# 激活虚拟环境后执行 conda activate yolo # 安装 CPU 版 PyTorch pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics会自动拉取依赖 pip install ultralyticsPyTorch CPU 版安装完成后用python -c import torch; print(torch.__version__)检查。这里不要省略 CPU 源很多初学者在这一步直接pip install torch结果装了一个好几个 GB 的 CUDA 包跑起来还不停弹 CUDA 警告。ultralytics这个包就是 YOLOv8 的开源实现主包它把训练、验证、导出、推理都封装成了yolo命令行工具。安装它之后不需要再单独装 opencv-python、pandas、matplotlib依赖会自动带上来。2.3 用预训练模型验证环境yolov8n.pt的第一次前向推理环境装没装对最快的方法是加载一个预训练模型并做一次假图推理。这里不使用网络图片避免因为请求超时造成误判直接生成一个零数组图片让模型走一遍前向。import numpy as np from ultralytics import YOLO # 第一次运行会自动下载 yolov8n.pt 预训练权重 model YOLO(yolov8n.pt) # 打印模型结构信息确认依赖加载正常 model.info() # 生成一张纯黑图仅用于验证前向传播能否跑通 fake_img np.zeros((640, 640, 3), dtypenp.uint8) # predict 会把 numpy 数组转成模型输入 results model.predict(fake_img, verboseFalse) print(检测结果数量, len(results))这段代码里YOLO(yolov8n.pt)如果不写路径包会从官方权重地址下载下载位置在当前目录。model.info()能看到模型的层数和参数量如果 CPU 也能顺利打印出来说明 torch 和 ultralytics 的版本基本匹配。model.predict是推理接口verboseFalse关掉大量日志否则第一次跑会刷屏。在选择模型尺度时很多人纠结 n、s、m、l、x这里有一个大致关系可以参考模型尺度相对参数量推理速度适用场景YOLOv8n最小最快CPU 可训练适合学习框架和快速验证YOLOv8s小快小数据集微调显存 4G 可尝试YOLOv8m中较慢需要更高精度且 GPU 显存 6G 以上YOLOv8l / x大慢接近上线场景训练成本明显高第一次学 YOLO 框架构建建议固定用yolov8n.pt。不是因为精度最好而是它参数最少训练一轮时间短方便反复试错。等你把整个流程跑通再换大模型也不迟。3. 制作大豆叶病数据集并转成yolo标注格式从拍摄到data.yamlyolov8 训练自己的数据集核心不是算法而是数据。很多人在这一步翻车标注工具用了图片也拍了不少最后训练时报 “All labels empty” 或 mAP 一直为 0。这一章把数据集的目录结构、labelme 转 YOLO 格式脚本、数据集切分讲清楚。3.1 大豆叶病类别设计和数据集目录结构先定义类别。大豆叶病常见的有大豆锈病、灰斑病、霜霉病、细菌性斑点病等实际做项目时不需要一开始就追求十几个类别选 2 到 4 类最典型的病害即可。每一类至少准备 200 到 500 个实例如果图片数量不够宁可减少类别数也不要做成极端不平衡的多分类。推荐的数据集目录结构如下datasets/ images/ train/ val/ labels/ train/ val/ data.yamlimages/train下放训练图片images/val下放验证图片两个目录里的图片数量按 8:2 左右划分。labels/train和images/train下的文件名必须一一对应比如soybean_leaf_001.jpg对应soybean_leaf_001.txt。YOLO 的标签文件格式是每一行一个目标class_id x_center y_center width height注意这里不是左上角坐标加宽高而是中心点坐标且全部除以图片宽高做了归一化。也就是说坐标值都在 0 到 1 之间。写标签文件时坐标小数位数建议保留 6 位过少的位数会让小目标框出现抖动。3.2 用labelme标注并转换成YOLO txt转换脚本与五个边界条件labelme 是常见标注工具导出的是 json 文件里面记录了图片尺寸和多边形点坐标。YOLO 不认 json需要转换成 txt。下面是我常用的转换脚本基于 labelme 的矩形标注和多边形标注都能处理。import json import os # 类别列表顺序就是 YOLO 类别 ID class_names [soybean_rust, soybean_frog_eye] def convert_labelme_to_yolo(json_path, output_dir): # 读取 labelme 生成的 json with open(json_path, encodingutf-8) as f: data json.load(f) img_w data.get(imageWidth) img_h data.get(imageHeight) if not img_w or not img_h: print(f[跳过] {json_path} 缺少 imageWidth/imageHeight) return False basename os.path.splitext(os.path.basename(json_path))[0] lines [] for shape in data[shapes]: label shape.get(label) if label not in class_names: print(f[跳过] {basename} 中出现未定义类别{label}) continue class_id class_names.index(label) points shape[points] # 兼容矩形和多边形取外接矩形 xs [point[0] for point in points] ys [point[1] for point in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 边界检查坐标不能超出图片范围 x_min max(0, x_min) x_max min(img_w, x_max) y_min max(0, y_min) y_max min(img_h, y_max) # 转为 YOLO 归一化坐标 x_center ((x_min x_max) / 2) / img_w y_center ((y_min y_max) / 2) / img_h box_w (x_max - x_min) / img_w box_h (y_max - y_min) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if len(lines) 0: print(f[跳过] {basename} 没有有效标注) return False output_path os.path.join(output_dir, f{basename}.txt) with open(output_path, w, encodingutf-8) as f: f.write(\n.join(lines)) return True这个脚本里最重要的逻辑是labelme 标注的多边形可能因为手抖不是水平矩形YOLO 要求目标框只能是与图片轴对齐的矩形所以必须取所有点的最小外接矩形也就是代码里的min/max。这在小目标较多的叶片病害上特别关键不要用旋转框。转换时有几个边界条件需要留意。第一json 里的imageWidth/imageHeight必须是图片原始分辨率不能是 labelme 界面里缩放后的尺寸。第二类别顺序一旦定下来后续 data.yaml 里的 names 顺序必须和脚本里的 class_names 完全一致否则训练时的标签会错位。第三坐标值除以的是原图宽高不是训练时的输入尺寸这一点搞混会让检测框全部偏移。第四如果一张图只有一个很小的病斑归一化后的 width 和 height 可能只有 0.01 左右这种目标本身很难学建议缩略图时先裁剪出病害区域再标注。第五txt 文件最后一行不需要强制加换行但每一行必须是完整的六段内容少一个空格都会让数据加载报错。3.3 分割train/val和编写data.yaml类别顺序的一票否决转换完成之后把原始图片和 txt 标签按照比例复制到训练集和验证集。为了避免同一种叶片的不同照片被分到两边建议按叶片编号分桶而不是直接对单个文件随机切分。import os import random import shutil from glob import glob random.seed(42) # 假设 raw/images 下是原图raw/labels 下是转换后的 txt image_paths glob(raw/images/*.jpg) glob(raw/images/*.png) random.shuffle(image_paths) train_ratio 0.8 split_index int(len(image_paths) * train_ratio) train_paths image_paths[:split_index] val_paths image_paths[split_index:] for split_name, paths in [(train, train_paths), (val, val_paths)]: os.makedirs(fdatasets/images/{split_name}, exist_okTrue) os.makedirs(fdatasets/labels/{split_name}, exist_okTrue) for image_path in paths: base os.path.splitext(os.path.basename(image_path))[0] label_path fraw/labels/{base}.txt if not os.path.exists(label_path): print(f[警告] {image_path} 缺少对应标签跳过) continue shutil.copy(image_path, fdatasets/images/{split_name}/{os.path.basename(image_path)}) shutil.copy(label_path, fdatasets/labels/{split_name}/{base}.txt)random.seed(42)是为了保证每次运行划分结果一致。这里shutil.copy是复制而不是移动方便原始标注文件保留下来后续如果发现标签错误可以直接回到 raw 目录修改。划分完成后在 datasets 目录下创建data.yamlpath: datasets train: images/train val: images/val names: 0: soybean_rust 1: soybean_frog_eyepath是相对 data.yaml 文件的根目录。YOLOv8 会自动根据images/train推导出标签目录为labels/train所以不需要在 yaml 里写 labels 路径。names的索引从 0 开始实际文件里第一列如果是 1就表示soybean_frog_eye。新手最常犯的错误是把 names 写成了 list但把类别 ID 从 1 开始标这样训练出来的模型和推理时的类别名会对不上。4. 训练自己的大豆叶病检测模型最小命令与参数调优数据集就绪后进入正式训练。这一章讲怎么用yolo train启动训练以及 yolov8 的模型训练参数含义。很多人把训练当成黑匣子只知道敲命令遇到 loss 不降就慌。其实损失函数、学习率、batch 这些变量都有明确作用理解了它们框架就会变得透明。4.1 用yolo train启动训练最小命令到完整命令训练最核心的命令只有一行# 在 conda yolo 环境中执行 yolo train datadatasets/data.yaml modelyolov8n.pt epochs50 imgsz640 batch16这行命令会从yolov8n.pt加载预训练权重然后在大豆叶病数据集上继续训练。modelyolov8n.pt的意思是加载预训练模型而不是从零初始化。对学习者来说这是一个非常好的习惯用 ImageNet 和 COCO 上学到的特征做热启动能在小数据集上得到明显更好的结果。如果要在服务器后台跑通常我会把输出目录和训练细节写全yolo train \ datadatasets/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ projectruns/detect \ namesoybean_leaf \ device0 \ workers4这里project和name控制训练日志和权重保存位置最终权重会在runs/detect/soybean_leaf/weights/best.pt中。device0表示用第一张 GPU如果没有 GPU可以改成devicecpu但训练速度会慢很多建议先用下一小节的参数减负方案。4.2 yolov8模型训练参数含义epochs、batch、imgsz、patience训练参数里影响最大的是epochs、batch、imgsz、patience和lr0。epochs是完整遍历数据集的次数。大豆叶病这样的小数据集200 张图片训练 50 到 100 轮已经能看到明显结果再增大容易过拟合。batch是每次送入 GPU 的图片张数它由显存决定如果显存报 OOM优先把 batch 从 16 降到 8 或 4。imgsz是输入图片的边长YOLOv8 会等比缩放图片到 640x640对叶片上的细小病斑imgsz640是底线降到 416 会丢失太多细节精度下降明显。patience是早停耐心值验证集 mAP 连续多少轮不提升就停止训练设 20 能省时间。lr0是初始学习率默认 0.01如果 loss 剧烈震荡可以降到 0.001但不要一开始就调优学习率先把前面参数跑通再说。训练过程中会打印三类损失box_loss、cls_loss、dfl_loss。这就是 yolov8 的损失函数组成box_loss 负责回归框的位置、宽度和高度cls_loss 负责分类是否正确dfl_loss 是 distribution focal loss用于优化边框质量的分布。看到这三个 loss 都在缓慢下降说明训练在正常收敛。训练结束之后建议把 results.csv 画成曲线直观判断是否过拟合或欠拟合import pandas as pd import matplotlib.pyplot as plt # 路径根据实际训练结果修改 df pd.read_csv(runs/detect/soybean_leaf/results.csv) fig, axes plt.subplots(1, 2, figsize(12, 4)) # 画 box_loss 曲线train 和 val 对比 axes[0].plot(df[epoch], df[train/box_loss], labeltrain box_loss) axes[0].plot(df[epoch], df[val/box_loss], labelval box_loss) axes[0].set_xlabel(epoch) axes[0].set_ylabel(box_loss) axes[0].legend() # 画 precision 和 recall 曲线 axes[1].plot(df[epoch], df[metrics/precision(B)], labelprecision) axes[1].plot(df[epoch], df[metrics/recall(B)], labelrecall) axes[1].set_xlabel(epoch) axes[1].set_ylabel(metrics) axes[1].legend() plt.savefig(soybean_training_curve.png, dpi150)这段代码的核心是将results.csv里的历史指标读出来。如果train/box_loss持续下降而val/box_loss在第 30 轮开始反弹就说明过拟合了应该减少 epochs 或增加数据增强。如果两条 loss 都在高位不动先检查数据集标签是否为空、学习率是否太大。4.3 显存不够时的处理gtx1660ti跑yolov8的可行方案很多人在 6GB 显存的老显卡比如 GTX 1660 Ti 上跑 YOLOv8。1650 之前我还见过用 4GB 显存硬跑 640x640 的结果自然是 OOM。显存不足时按下面顺序调整# 方案一减小 batch yolo train datadatasets/data.yaml modelyolov8n.pt epochs50 imgsz640 batch4 # 方案二减小输入尺寸但精度会下降 yolo train datadatasets/data.yaml modelyolov8n.pt epochs50 imgsz480 batch8 # 方案三显式开启混合精度减少显存占用 yolo train datadatasets/data.yaml modelyolov8n.pt epochs50 imgsz640 batch8 ampTrueampTrue是混合精度训练默认就是开启的但对个别数据集可能会出现 loss 变为 nan这时可以关闭。GTX 1660 Ti 的 Turing 架构对混合精度支持一般实际收益没有 Ampere 显卡明显但至少能多塞一点 batch。要注意batch太小会让 BatchNorm 统计不稳定一般不要低于 4。如果 batch2 还 OOM说明模型尺度选大了回到yolov8n.pt这是 cpu 和低显存显卡训练 yolo 框架最稳的组合。5. 避坑排查大豆叶病yolov8训练中最常见的5个问题训练过程不会一次顺顺利利尤其是在自建数据集的时候。这一章记录几个我实际遇到过的坑每条按“现象 → 原因 → 解决”来写看完至少能省出两三天排错时间。5.1 标签文件全空或数据加载报错现象训练刚开始终端刷出一行All labels empty或者警告某个图片找不到对应的 txt 文件。原因我最早把 labelme 的 json 转成了 txt但没把 txt 放到labels/train目录而是直接放在raw/labels下yaml 里的train指向了images/trainYOLOv8 找不到对应标签。解决先手动检查datasets/labels/train下是否有和图片同名的文件再用yolo train前打印目录树。最稳妥的方法是让datasets根目录下的images和labels保持绝对对应不要用软链接。5.2 训练时loss一直是nan现象训练第一个 epoch 就出现box_loss nan之后所有指标全是 nan。原因最常见是学习率过大或者输入图片里有全黑、全白的坏图导致 BN 层统计出现问题。解决先把lr0从 0.01 降到 0.001如果还 nan检查数据集中是否混入了损坏的 jpg用PIL.Image.open逐张打开验证。5.3 mAP很低但训练集精度很高过拟合与数据泄漏现象训练集 precision 到 0.95验证集 mAP50 只有 0.3。原因大豆叶病图片经常是同一株叶片连拍如果把同一叶片的多个角度同时分到 train 和 val模型在验证集上等于开卷考试这属于典型的数据泄漏另一种原因是总 epoch 太长把训练集记死了。解决按叶片个体划分数据集确保同一株叶片的图片只出现在一个集合里同时设置patience20早停避免过拟合。如果验证集还是低检查 labelme 标注是否把病斑区域框得过大导致 ground truth 与模型预测重叠率无法上升。5.4 显存溢出OOMbatch与imgsz怎么看现象训练中途报CUDA out of memory显卡明明是 6G 显存还是崩了。原因很多时候不只是 batch 的问题而是workers和imgsz一起把显存打满。解决先用batch4 imgsz640 workers2跑稳定后再逐步加大。如果还 OOM检查后台是否有其他进程占用显存用nvidia-smi看不要一上来就怪代码。CPU 环境训练时内存占用被误认为显存ulimit -v限制也不要随便调。5.5 推理置信度过低不要盲目调阈值现象训练完后用模型预测叶片检测框有但置信度只有 0.2设置conf0.5后什么都检测不到。原因模型不是没学到特征而是训练时标签框里包含了过多背景或者病斑与叶片颜色接近模型输出难以达到高置信度。解决不要直接降低 conf 到 0.1这是自欺欺人。先回到标注把标注框收紧到只框病斑区域并重新训练推理阶段可以先设为conf0.25观察再按实际业务容忍度调。如果每一张图都低置信度多半是 train 和 val 的数据分布不一致比如训练图全是实验室平板拍摄推理图是大田复杂背景。6. 跑验证集、导出ONNX与调置信度把学到的框架串起来模型训练完最后一步是验证和落地。先对验证集做一次完整评估确认 mAP 达到你的预期再导出成 ONNX为后续部署留后路最后调整置信度让模型能真正用在叶片病害筛查流程里而不是只在 metrics 上好看。from ultralytics import YOLO # 加载训练得到的最优权重 model YOLO(runs/detect/soybean_leaf/weights/best.pt) # 对验证集做推理并保存结果 results model.predict( sourcedatasets/images/val, conf0.25, iou0.45, saveTrue, save_txtTrue ) # 打印每张图的检测框数量用于快速检查 for res in results: print(res.path, boxes:, len(res.boxes))conf0.25和iou0.45是常规起点。seaborn不必要重点看saveTrue会把标注框画到图片上save_txtTrue会输出与 YOLO 标签同格式的推理结果这两步能快速暴露模型在验证集上的漏检和误检。验证 mAP 的官方命令是yolo val modelruns/detect/soybean_leaf/weights/best.pt datadatasets/data.yaml它会输出 mAP50、mAP50-95、precision、recall 四个关键指标。对大豆叶病场景mAP50 达到 0.8 以上通常就能用于实际预筛。mAP50-95 如果比 mAP50 低很多说明检测框和标签框贴合度不够优先检查标注框精度。如果之后要部署到嵌入式设备可以导出 ONNXyolo export modelruns/detect/soybean_leaf/weights/best.pt formatonnx imgsz640导出时如果遇到算子不兼容把opset固定为 12并确认imgsz与实际输入一致。ONNX 文件可以直接用 onnxruntime 在 CPU 上跑推理这也是从 yolov8 框架学习到工程落地之间最短的一条路。最后说一个我自己的教训学 YOLOv8 框架不要一上来就囤十几个模型尺度也不要一上来就调损失函数权重。先用yolov8n.pt和最小 data.yaml 把整个链路跑通确认数据没病再谈优化。我早期训大豆叶病模型时把大部分精力花在改网络 head 上结果回头发现只是标签类别顺序写错了白白浪费了三天。先保证每个环节可见可控再动手堆参数这个习惯会让你的 yolov8 目标检测学习走得更快也更扎实。希望帮到你。本文还有配套的精品资源点击获取
返回列表