ARTICLE DETAIL

资讯详情

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

基于YOLOv5的花卉识别源码实战:从环境配置到Shell自动训练

基于YOLOv5的花卉识别源码实战:从环境配置到Shell自动训练 简介基于Python与Shell语言的YOLOv5花卉识别模型设计源码面向希望快速上手深度学习目标检测的开发者提供一套可直接运行的花卉智能识别项目。整套代码共102个文件涵盖40个YAML配置、32个Python源文件、5个Shell脚本以及模板、图像、Docker与Git配置压缩包仅1.19MB轻量且目录模块清晰便于学习、调试与二次开发。其中YAML文件负责模型参数与数据集定义Python代码实现训练、验证与推理流程Shell脚本用于自动化环境部署与启动任务各类型分工明确。项目还附带Jupyter教程与Docker配置能帮助从零搭建运行环境理解YOLOv5从数据准备到结果输出的完整过程也可作为课程设计或竞赛实践参考。目前已有370人学习适合对目标检测、YOLO系列算法感兴趣且具备一定Python基础的初学者或竞赛选手。1. 从只看叶子到把花框出来这套yolov5花卉识别源码想一次解决三件事先想一个具体的需求苗圃里有几十个品种的月季系统每天从大棚摄像头拿图要求既统计每张图里开了几朵花又标出品种。单纯给整张图打标签的分类网络最多给出“图中可能有月季”给不出位置更数不清数量。换成yolov5后输出变成了“坐标框置信度品种标签”下游统计和筛选都好做了。标题里同时出现Python和Shell说明这不是一个纯算法项目而是把算法训练、数据组织、自动重训都装进了工程化外壳Python负责模型训练与推理Shell负责环境准备、任务编排和日志管理。这套源码适合两类人一是已经跑通过yolov5但被数据划分和重训流程反复折腾的工程师二是想把基于yolov5的识别任务快速套到同行场景比如果实识别的开发者。接下来各章就按“从能跑到能调到能部署”的顺序拆开讲。2. yolov5环境配置与源码目录用Python依赖把detect.py快速跑通2.1 为什么Python虚拟环境在yolov5环境配置里不是可选项在“yolov5环境配置”这一步最常见的失败不是模型代码问题而是 torch 和 torchvision 版本错位。yolov5源码在 import torch 时不会主动检查版本等到前向推理跑到一半才报AssertionError: Torch not compiled with CUDA enabled或者干脆在数据加载阶段崩。另一个高发问题是系统里已经有了一套 OpenCV 和 numpypip 再装一套出来版本冲突。Python 虚拟环境能把这套依赖彻底隔离下面这套命令在 Linux 和 macOS 上通用。python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip pip install -r requirements.txt这条命令序列的含义是第一行在源码根目录创建名为.venv的 Python 3 虚拟环境第二行激活它激活后终端里的python和pip都指向虚拟环境内部第三行把 pip 自身升级到较新版本避免后续依赖解析出错第四行按官方 requirements.txt 一次性装入所有依赖。如果服务器上同时有 Python 3.10 和 3.11创建虚拟环境时可以用python3.10 -m venv .venv固定解释器版本。requirements.txt 里真正和训练强相关的是下面三组其余库只影响可视化或日志导出# 核心计算库 torch1.7.0 torchvision0.8.1 # 图像与数值处理 numpy1.18.5 opencv-python4.1.2 # 数据集配置解析 pyyaml5.3.1参数说明torch/torchvision 决定能否调用 GPU必须匹配 CUDA 版本一般建议直接装官方编译好的 wheelnumpy/opencv-python 负责图片解码、letterbox 缩放和 Mosaic 增强版本过低会导致部分预处理函数行为异常pyyaml 解析数据配置文件注意它区分大小写nc和names写错不会报错但训练出来的类别索引会对不上。安装时如果下载慢临时加-i指向国内 PyPI 镜像可以接受但不建议写进 requirements 调用里否则换到其他机器时行为不一致。依赖组提供的核心能力最容易踩的坑torch / torchvisionGPU 加速、模型前向与反向传播版本错位时is_available()返回 Falsenumpy / opencv-python图像解码、尺寸缩放、数据增强系统旧版残留导致_ARRAY_API not foundpyyaml读取 flower.yaml 配置字段大小写错误训练完才发现类别数不对2.2 源码目录里值得先读的三个位置下载源码后不用急着逐行读train.py。整个 yolov5 工程里对“花卉识别”这个任务来说80% 的改动都集中在三个位置。第一是data/目录里面放的是数据集配置 yaml 文件yolov5 官方提供了 coco.yaml、coco128.yaml 等参考配置复制一份改成自己的数据路径即可。第二是models/目录里面按 yolov5s、yolov5m、yolov5l、yolov5x 存放网络结构定义花卉目标通常不大s 或 m 级别已经够用。第三是utils/目录存放 loss 计算、指标统计、数据增强逻辑一般不需要动但排错时要回来看。一个常见的误解是“改模型结构必须重写整个 models 文件”。其实 yolov5 的模型定义是高度参数化的比如在models/yolov5s.yaml里通过depth_multiple和width_multiple控制网络深度和宽度。对花卉识别这种类别数少、目标尺寸小的场景保持默认结构、只调nc是最稳妥的做法。真正的调节空间在训练参数和数据处理上。2.3 用两轮最小训练验证环境环境装好以后先用一个很小的配置跑两轮训练确认数据加载、模型初始化、梯度回传都正常。下面这条命令是标准的 yolov5 训练入口python train.py \ --data flower.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 2逻辑说明--data指向数据集配置文件里面描述图片路径、标签路径、类别数和类别名--weights指定预训练权重yolov5s.pt 是官方在 COCO 上预训练的最小规模权重用迁移学习的方式微调到花卉数据上收敛速度远快于从零训练--img是输入图片的短边尺寸640 是官方平衡点--batch是每批样本数--epochs设为 2 只是为了验证链路通不通。跑完以后看两处终端里是否出现all 0.000之类的 mAP 输出以及runs/train/目录下是否生成了weights/best.pt。如果这两处都正常说明 torch、数据管道、模型结构三个环节都没问题。这时再把--epochs改成实际值开始正式训练。注意第一次运行会自动下载 yolov5s.pt 预训练权重提前手动下载好放到源码根目录可以省时间。3. 用yolov5训练自己的数据集花卉图片的目录布局、标注格式与flower.yaml写法3.1 图像和标签的目录约定比想象中更严格yolov5 训练自己的数据集时不要求图片放在特定目录但要求标注信息刚好落在它期望的位置。官方推荐的结构是datasets/flowers/images/train和datasets/flowers/labels/train一一对应验证集也保持同样的配对关系。图片名和标签文件名必须完全一致只是后缀不同IMG_001.jpg对应IMG_001.txt。这个对应关系一断训练时会出现大量WARNING: ignoring corrupt image或No labels found的报错。花卉数据还有一个容易被忽视的问题图像来源杂。手机拍的、相机拍的、网上抓的尺寸差异很大而 yolov5 的标签是按归一化坐标写的和图片原始尺寸无关所以图片分辨率不会直接导致标签失效。真正的风险在于过小的图片。如果原始图片只有 200x200而训练输入是 640x640缩放时花朵的纹理信息会损失明显。采集数据时尽量保证短边不低于 640。3.2 YOLO格式标注的四个数字容易写错的是后两个花卉识别的标注文件每行代表一个目标格式是class x_center y_center width height其中坐标全部归一化到 0 到 1。x_center 是目标框中心点横坐标除以图片宽度y_center 是中心点纵坐标除以图片高度width 是框宽度除以图片宽度height 是框高度除以图片高度。前面两个好理解后面两个经常被写成像素值导致训练时 loss 震荡不止。标注工具的选择上LabelImg 和 X-AnyLabeling 都可以输出 YOLO 格式但建议在导出后做一轮自动化校验。下面这个 Python 脚本会遍历原始图片和标签目录找出缺文件、越界坐标和宽高为负的异常样本# check_labels.py 校验花卉标注文件 from pathlib import Path img_dir Path(datasets/flowers/images/train) lbl_dir Path(datasets/flowers/labels/train) for img in img_dir.glob(*.jpg): lbl lbl_dir / (img.stem .txt) if not lbl.exists(): print(f[missing] {lbl}) continue for line in lbl.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: print(f[format] {lbl}: {line}) continue _, cx, cy, w, h parts cx, cy, w, h map(float, (cx, cy, w, h)) if not (0 cx 1 and 0 cy 1 and w 0 and h 0): print(f[invalid] {lbl}: {line})逻辑说明脚本逐个读取训练集图片检查同名标签文件是否存在再逐行解析五个字段。除了格式完整性还验证了框是否越界归一化中心点必须落在 0 到 1 之间宽高必须为正。任何一条不满足都直接打印出来。参数说明类别号从 0 开始编号比如玫瑰是 0、郁金香是 1这个数字必须和后续flower.yaml中的names顺序一一对应否则训练完的预测结果就全错位了。3.3 flower.yaml 的字段与一次实际的数据划分yaml 配置是数据集和训练脚本之间的桥梁。一个可用的flower.yaml长这样path: datasets/flowers train: images/train val: images/val nc: 5 names: 0: rose 1: tulip 2: sunflower 3: daisy 4: orchid参数说明path是数据集根目录推荐写相对路径相对源码根目录解析train和val是训练集和验证集的图片目录路径yolov5 会自动去同名标签目录找对应的 txtnc是类别总数names是类别名列表索引从 0 开始。注意path和train拼起来才是完整路径train: images/train表示datasets/flowers/images/train。数据划分是另外一个高频困惑点。花卉数据集收集起来往往是一整批放在一个目录里需要按比例切出训练集和验证集。下面这段脚本做了两件事按 8:2 的比例随机划分同时跳过没有标注文件的图片避免脏数据混进训练集。# split_dataset.py 按8:2划分花卉数据 import random, shutil from pathlib import Path src_img Path(raw_images) src_lbl Path(raw_labels) base Path(datasets/flowers) parts [images/train, images/val, labels/train, labels/val] for p in parts: (base / p).mkdir(parentsTrue, exist_okTrue) images [p for p in src_img.iterdir() if p.suffix in (.jpg, .jpeg)] random.seed(2024) random.shuffle(images) cut int(len(images) * 0.8) for i, img in enumerate(images): lbl src_lbl / (img.stem .txt) if not lbl.exists(): continue tag train if i cut else val shutil.copy(img, base / fimages/{tag} / img.name) shutil.copy(lbl, base / flabels/{tag} / lbl.name) print(f已划分: train{cut}, val{len(images)-cut})这段脚本把raw_images里的图片随机打乱前 80% 作为训练集后 20% 作为验证集同时复制对应的 txt 标签文件。逻辑上需要注意两点random.seed(2024)固定随机种子保证多次运行结果一致方便复现训练效果lbl.exists()的判断把未标注图片跳过。运行完后检查datasets/flowers/images/train和datasets/flowers/labels/train里的文件数量是否一致。3.4 花卉类别的特殊性相似花型和类间不平衡和基于yolov5的水果识别相比花卉识别的难点不在“长得不一样”的类别而在“长得太像”的类别。玫瑰和月季、雏菊和部分菊花人类肉眼都容易混淆。这种情况下标注一致性比数据量更重要同一个人完成全部标注比多人分工的标签噪声小得多。另外标注框的边界要统一比如整朵花还是包含花茎这个决定一旦混了模型就很难学。类间不平衡在花卉数据里也很常见。有些品种很容易拍到几百张有些只有几十张。yolov5 的自适应 anchor 不会自动解决类别不平衡常用的做法是为稀有类别多采样。手工复制图片意义不大更好的办法是在线增强时提高这类图片的参与概率或者直接用--cls参数调整类别损失权重。这些细节在训练参数里体现后面章节会展开。4. 用Shell脚本编排训练与断点重跑一个train.sh的工程化模板4.1 为什么训练编排这层要交给Shell而不是PythonPython 代码负责训练本身但训练之外的任务——激活虚拟环境、检查数据集是否就位、备份旧权重、按日期归档日志——用 Shell 脚本天然更顺手。原因有三个入口单一、日志直观、容易和定时任务对接。一个训练任务往往要连续跑几个小时甚至几天中间机器重启或者 SSH 断连Shell 脚本配合nohup或 cron 能保证任务不依赖终端存在。另一个实际好处是团队成员都能看懂脚本里的变量和流程不需要深入 Python 代码就能安全改参数。4.2 train.sh 完整模板与参数表下面这份train.sh是日常训练花卉模型时最常见的一套工程化封装包含环境激活、目录创建、训练执行、权重归档四步。#!/usr/bin/env bash set -euo pipefail EXPERIMENT_NAMEflowers_v1 DATA_CFGflower.yaml WEIGHTSyolov5s.pt EPOCHS100 BATCH16 IMG640 TIMESTAMP$(date %Y%m%d_%H%M%S) RUN_DIRruns/${EXPERIMENT_NAME}/${TIMESTAMP} mkdir -p ${RUN_DIR} cd $(dirname $0) source .venv/bin/activate if [[ ! -f data/${DATA_CFG} ]]; then echo 数据集配置不存在: data/${DATA_CFG} exit 1 fi python train.py \ --data data/${DATA_CFG} \ --weights ${WEIGHTS} \ --img ${IMG} \ --batch ${BATCH} \ --epochs ${EPOCHS} \ --project runs/${EXPERIMENT_NAME} \ --name ${TIMESTAMP} \ 21 | tee ${RUN_DIR}/train.log if [[ -f ${RUN_DIR}/${TIMESTAMP}/weights/best.pt ]]; then cp ${RUN_DIR}/${TIMESTAMP}/weights/best.pt ${RUN_DIR}/best.pt echo 训练完成权重保存在 ${RUN_DIR}/best.pt else echo 未找到 best.pt训练可能中断 exit 1 fi逻辑说明set -euo pipefail是 shell 脚本入门阶段就该养成习惯的三件套-e让脚本在命令出错时立即退出-u让未定义变量直接报错而不是静默展开为空pipefail保证tee管道中前一个命令失败时整体返回失败。TIMESTAMP用时间戳区分每次实验避免同一实验多轮训练时文件互相覆盖。训练结束后检查best.pt是否存在存在就复制到带时间戳的目录不存在就退出并给出错误码。脚本里各变量含义如下表训练时只需改顶部变量区不用动下面命令变量作用推荐值参考EXPERIMENT_NAME实验代号用于区分不同花卉批次flowers_v1DATA_CFG数据集配置文件名flower.yamlWEIGHTS预训练权重路径yolov5s.ptEPOCHS完整训练轮数100 起步BATCH每批样本数取决于显存16 到 64IMG输入图短边像素640 或 9604.3 shell中常见坑引号、路径切换与中断恢复第一次写这种脚本的人最容易踩三个坑。第一个是路径问题脚本用cd $(dirname $0)把工作目录切到脚本所在目录解决“从别的目录调用脚本时相对路径失效”的问题。如果不加这句脚本只能从源码根目录运行换目录就报找不到文件。第二个是变量没加引号--data ${DATA_CFG}在路径含空格时会拆成两个参数养成所有变量都加${}和双引号的习惯能避免这类问题。第三个是训练中断后没有断点重跑机制yolov5 本身支持--resume参数可以从上次保存的last.pt继续训练但前提是训练日志和权重还在原来的runs/目录里所以脚本里用时间戳建目录、不清理旧文件这些都是为了让--resume能用。断点恢复的常见做法是单独留一行命令python train.py --resume runs/flowers_v1/20260601_091500/train/weights/last.pt--resume不需要再传--data、--batch等参数它会自动从训练记录中读取上次配置。适合的场景包括训练到一半服务器断电、显存不够导致 OOM 退出、调整了学习率策略想从上一轮继续。注意恢复时使用的代码版本要尽量保持和上次训练一致否则权重结构对不上会直接报错。4.4 日志按日期轮转模型按效果留存长时间跑训练实验runs/目录会快速膨胀。一个可用的经验是训练日志按日期归档模型权重按指标留存。把train.log复制到带日期的目录里是最基本的轮转更进一步的方案是用find mtime定期清理超过 N 天的旧日志避免磁盘被占满。在智本文还有配套的精品资源点击获取
返回列表