ARTICLE DETAIL

资讯详情

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

基于Python与Shell的YOLOv5花卉识别模型从训练到部署全流程

基于Python与Shell的YOLOv5花卉识别模型从训练到部署全流程 简介这是一套基于Python与Shell语言实现的YOLOv5花卉识别模型源码专为计算机视觉入门者及深度学习开发者设计解决花卉种类自动识别问题可直接用于目标检测教学或实际分类场景。压缩包共102个文件大小仅1.19MB其中包含40个YAML配置、32个Python源文件、5个Shell脚本、11个YAML模板以及Dockerfile、Jupyter Notebook等辅助内容配置与脚本分别承担数据集参数定义、模型训练推理和自动化流程管理结构清晰便于快速上手。已有371人学习下载。通过源码可掌握YOLOv5从环境搭建、数据配置到训练与推理的完整链路理解PyTorch实现细节并借助Shell脚本简化批处理与部署操作适合作为毕业设计、竞赛或学习深度学习目标检测的参考工程。1. 这个 YOLOv5 花卉识别项目到底解决什么问题带“基于 Python 与 Shell 语言的 yolov5 花卉识别模型设计源码”这个标题的项目拆开看其实就两件事用 Python 驱动 YOLOv5 完成训练和推理用 Shell 脚本把环境检查、数据准备、训练入口和批量识别这些“脏活”串成一条不用反复手敲的流水线。花卉识别本身不是新问题难点在于很多花卉形态接近、拍摄背景杂乱模型容易把“绣球”和“菊花”混在一起所以项目真正考验的不是会调用 detect.py而是怎么把数据集、超参数和部署链路调到一个能稳定复现的效果。适合谁想自己从零训练一个能用的花卉检测模型的学生、做园林植物数字化采集的工程师以及需要快速给已有检测系统换“新物种”的算法岗同事。一个反直觉的结论先说在前面换一版更干净的花卉标注数据往往比重选一个更大的预训练模型带来的提升更明显。2. 准备数据集一朵花一个标签比换预训练模型更管用花卉识别模型的精度上限在数据标注阶段就被定死了。YOLOv5 自己带的 COCO 预训练权重不认识“月季”和“蔷薇”的区别微调时能把相似类别分到多细取决于你的标注里有没有把“同类花在不同光照、不同角度”的样子都框进去。我一般会在动手训练前把数据集准备当成一个独立工程来推进而不是随手丢一批图片进训练脚本。2.1 花卉图片从哪来自采、公开花卉集与标注规范三个来源按成本排序公开花卉数据集只用下载解压但类别分布和标注框质量不可控网图爬取覆盖广但清洗成本高到让你怀疑人生团队或自己拍摄是质量最可控的缺点是样本量天然受限。常见做法是“公开集打底 自采补缺口”把公开集里容易混淆的类挑出来重点看再针对你实际部署场景补拍。标注规范比标注工具更要先定清楚。我给花卉项目定的边界是一朵独立完整的花算一个目标花苞和半开状态如果紧贴主花就并入同一框不单独标一簇花在画面里能明显分辨出单朵时逐朵标分辨不出时按整簇标一个框。这个规则直接决定了模型学到的“花”的语义粒度宁可少标不要错框错框比漏标更伤模型。数据目录按 YOLOv5 约定组织之后训练参数可以少改两处datasets/ └── flower/ ├── images/ │ ├── train/ # 训练集图片.jpg │ └── val/ # 验证集图片.jpg └── labels/ ├── train/ # 与图片同名的 .txt 标签 └── val/Shell 命令在这一步就能派上用场用find统计图片数量、检查图片与标签是否一一对应比在 Python 里写一堆 IO 逻辑来得更快这也是标题里 Shell 语言最常见的落点。2.2 把 VOC 或 JSON 标注转成 YOLO 格式转换脚本与参数说明YOLOv5 的标签格式是每张图片对应一个同名.txt每一行是class x_center y_center width height四个坐标全部归一化到 01。公开花卉数据集里很大一部分是 VOC 格式的 XML 标注不转换直接用train.py 大概率读不到标签。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_label_path, class_list): 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.findall(object): name obj.find(name).text if name not in class_list: continue cls_id class_list.index(name) box obj.find(bndbox) x_min float(box.find(xmin).text) y_min float(box.find(ymin).text) x_max float(box.find(xmax).text) y_max float(box.find(ymax).text) x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_label_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: classes [daisy, dandelion, rose, sunflower, tulip] xml_dir annotations/ out_dir labels/ os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue stem xml_name.replace(.xml, ) voc_to_yolo(os.path.join(xml_dir, xml_name), os.path.join(out_dir, stem .txt), classes)这段脚本的逻辑核心是先取原图宽高再把绝对坐标换算成归一化坐标。参数说明里有两个关键点classes的列表顺序就是训练时类别 id 的顺序中途增删类别会导致已有训练权重分类头失效必须重新训练坐标保留 6 位小数在大多数场景下够用但如果你做的是密集小目标花卉检测建议保留到 8 位减少归一化误差累加。2.3 train/val 划分用固定随机种子保证可复现花卉数据里常见一个坑按文件顺序前 80% 做 train、后 20% 做 val结果某一种花全在验证集里训练时模型根本没学过这一类。我习惯按类别做分层划分保证每个类别在 train 和 val 里的比例一致。import os import random from collections import defaultdict random.seed(42) # 固定随机种子划分结果可复现 label_dir labels/train_val_all/ train_out train.txt val_out val.txt val_ratio 0.2 class_to_files defaultdict(list) for label_name in os.listdir(label_dir): if not label_name.endswith(.txt): continue label_path os.path.join(label_dir, label_name) with open(label_path, r) as f: first_line f.readline().strip() if not first_line: continue cls_id first_line.split()[0] class_to_files[cls_id].append(label_name.replace(.txt, )) # 图片文件名不含后缀 train_lines, val_lines [], [] for cls_id, stems in class_to_files.items(): random.shuffle(stems) split_idx int(len(stems) * (1 - val_ratio)) for stem in stems[:split_idx]: train_lines.append(f/absolute/path/to/images/{stem}.jpg) for stem in stems[split_idx:]: val_lines.append(f/absolute/path/to/images/{stem}.jpg) with open(train_out, w) as f: f.write(\n.join(train_lines)) with open(val_out, w) as f: f.write(\n.join(val_lines))这段代码的关键不是随机本身而是“按类别”这三个字。class_to_files把同属一个类别的样本聚在一起再各自按比例切分从机制上避免了某一类整组失踪。训练时在 data 配置文件的train和val字段填这两个 txt 的路径即可。val_ratio对小型花卉数据集按 0.2 起步如果总样本量不到 500 张建议用 0.15给训练多留一点数据。3. 用 Python 和 Shell 把训练链路搭起来数据集就位后下一步是把 YOLOv5 的训练调用、超参传递和环境准备串成一条命令。这一步是 Shell 语言在项目里的核心价值它不是一个“可选装饰”而是把训练从“一行命令”变成“一套可复用的流程”。3.1 环境与依赖Python 版本先定住torch 按设备选YOLOv5 对 Python 版本的要求并不苛刻但如果你环境里同时有 3.7 和 3.12建议用虚拟环境固定在 3.83.10 之间这个区间对着 torch 的官方兼容表基本无坑。安装依赖的基本原则是分两步先装 torch 系列再装 requirements.txt 里的其余包。顺序反了可能出现 numpy 版本冲突因为 torch 的预编译包自带 numpy 约束后装的 numpy 可能被强制回退。python -m venv .venv source .venv/bin/activate pip install --upgrade pip # 先装 torch 和 torchvision版本以你的 CUDA 版本为准 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r yolov5/requirements.txt参数说明里有两点要讲透--index-url指向 pytorch 官方 wheel 源是为了让 torch 和 torchvision 的版本与 CUDA 库精确匹配不要用默认 PyPI 源装 torch装出来的多半是 CPU 版或版本错配。如果你没有独立显卡去掉--index-url让 pip 安装 CPU 版 torch 反而更小更快训练慢一点但不至于跑不起来。这一步和“python 安装 numpy 库”等热搜词关联的场景都在这里解决的是同一个问题环境装不对后面全白搭。3.2 训练参数怎么定花卉场景下的超参数基线YOLOv5 的train.py参数很多但花卉识别这个任务真正要调的只有四五个。我给出一个经过验证的起点组合再讲每个参数背后的道理。参数建议值小数据集优先参数含义与选择理由--epochs100小数据集 50 轮欠拟合200 轮以上容易过拟合100 轮是验证模型潜力最经济的区间--batch-size16显存不够时优先减半比直接降分辨率保留的信息更多--imgsz640YOLOv5 默认训练尺寸适配合大多数公开花卉图的分辨率--workers4CPU 核心数的一半以上有时反而拖慢4 是稳妥起点--cache不指定小数据集没必要全部缓存进内存浪费 RAM--patience50验证指标 50 轮不提升就早停省时间--imgsz 640是一个值得较真的点如果你的花卉图片里单朵花占画面比例很小模型学的是“花丛”而不是“花”这种情况下把--imgsz提到 960或者对图片做滑窗切块再训练效果往往比加大模型深度更直接。超参数调整的原则是小步试先固定其他参数只动--imgsz或--batch-size一个变量否则多个变量同时变化时你根本不知道是哪个改动起的作用。3.3 Shell 入口脚本一条命令完成环境检查与启动训练直接敲python train.py --data ...的问题在于换一台机器、隔一个月回来跑你可能忘了该激活哪个环境、数据集软链接有没有建好。把这些检查写进 Shell 脚本训练就从“碰运气”变成“走流程”。#!/bin/bash # run_train.sh set -e # 任何一步失败立即退出避免带着错误往下跑 SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) cd $SCRIPT_DIR # 1. 激活虚拟环境常见做法脚本里显式指定 python 路径更稳 VENV_PATH$SCRIPT_DIR/.venv/bin/python if [ ! -x $VENV_PATH ]; then echo [ERROR] 虚拟环境不存在请先执行环境安装步骤 exit 1 fi # 2. 数据集软链接存在且指向有效才继续 DATA_LINKdatasets/flower if [ ! -d $DATA_LINK/images/train ]; then echo [ERROR] 数据集目录不完整检查 $DATA_LINK exit 1 fi # 3. 启动训练日志同时输出到终端和文件 $VENV_PATH train.py \ --data data/flowers.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --patience 50 \ 21 | tee train_$(date %Y%m%d_%H%M).log echo [INFO] 训练结束日志已保存脚本里两个容易被新手忽略的设计set -e保证脚本在任何一步失败时立刻终止否则数据集路径写错时脚本会一路空跑BASH_SOURCE[0]让脚本可以在任意目录下被调用自动切到脚本所在目录避免“在别处执行时找不到相对路径”的经典问题。tee把训练输出同时送到终端和日志文件后台跑长训练时随时可以tail -f看进度。这一步就是 Shell 语言在项目里不可替代的位置它不是语言层面的炫技而是把 Python 训练变成可审计、可重跑的流程。4. 避坑数据与 Shell 阶段最常见的翻车现场这个项目最耗时间的往往不在训练本身而是数据路径、标注格式和 Shell 环境这三类问题反复出现。下面五条是从实际踩坑里沉淀出来的每条按“现象 → 原因 → 解决”展开。4.1 训练时提示“No labels found in ...”现象训练脚本明明启动了但前几个 epoch 的 box 损失不降日志里还出现找不到标签的警告。原因最常见的是 labels 目录下的.txt文件名和 images 目录下的.jpg文件名不对应其次是标签文件内容为空YOLOv5 会跳过空标签。解决用一段短 Shell 命令做交叉核对比肉眼挨个看快得多cd datasets/flower for img in images/train/*.jpg; do base$(basename $img .jpg) if [ ! -f labels/train/$base.txt ]; then echo 缺少标签: $base fi done这段脚本遍历每张训练图片检查同名标签是否存在只输出缺失项。跑完再统计空标签find labels/train -name *.txt -size 0把空文件找出来重新标注或删除图片两个动作做完再重启训练。4.2 Shell 脚本在 Windows 上写好传到 Linux 上就报错现象run_train.sh执行时报$\r: command not found或者脚本不按预期换行执行。原因Windows 编辑器默认用 CRLF\r\n换行Linux 只认 LF多出来的\r被当成命令的一部分。解决用sed批量转换一行命令永久解决sed -i s/\r$// run_train.sh这个命令把行尾的\r替换为空-i直接写回原文件。之后在 Windows 上继续编辑建议在编辑器里把行尾格式固定为 LF否则每次传文件都要重复这步。4.3 训练时 CUDA out of memory现象--batch 16在 8G 显存的显卡上报 OOM弹出一大段 PyTorch 的显存栈信息。原因花卉数据里常有大尺寸图片--imgsz 640时模型输入不是瓶颈瓶颈在 batch 过大导致激活值缓存撑爆显存也有可能是--workers 4的预加载把内存占满后间接拖慢显存释放。解决优先把 batch 降到 8 或 4不是降--imgsz。显存不够的本质是数据并行太多降 batch 是保精度最温和的手段如果 batch 降到 2 还是不够再考虑--imgsz 416起步。另一种方案是启用梯度累积但 YOLOv5 的--nbs参数用来模拟更大的 batch不是人人清楚我一般建议小显存用户直接降 batch 更省心。4.4 自定义数据集类别数没改训练后全部分类到 COCO 类别现象模型训练完成验证时预测框的位置全对但类别标签全是人、车、狗等 COCO 类别。原因data/flowers.yaml里的nc类别数还写着 80或者没写names列表。YOLOv5 加载自定义数据时如果nc不是 80会自动重建分类头但如果你沿用默认的coco128.yaml改了个文件名忘记改nc和names模型就会用 COCO 的语义去分类。解决检查数据配置文件里的三处字段nc必须与你的花卉类别数一致names按训练时的类别顺序列出来train和val指向实际数据集路径。改完删掉runs/train/下的旧实验目录再重启训练避免加载到缓存里的旧配置。4.5 远程训练断连训练进度没保存现象用 SSH 在服务器上启动训练本地网络断开后远程进程被终止前几十个 epoch 白跑。原因没有用后台会话工具进程随 SSH 会话退出而被系统清理。解决让训练跑在screen或tmux会话里断开重连后继续看日志。下面是用tmux挂训练的最简做法tmux new -s train_session bash run_train.sh # 按 Ctrlb 再按 d 脱离会话SSH 断开也不影响 # 重连后执行tmux attach -t train_session养成开训练就先进 tmux 的习惯后网络波动基本不再影响实验进度。5. 部署与验证从 pt 模型到 Shell 批量推理训练产出的best.pt只代表“模型在验证集上表现不错”离真正能拿去识别一文件夹照片还有两步模型格式转换和批量推理封装。这一章把这两步走完你就拥有了一个可以脱离训练环境独立工作的花卉识别工具链。5.1 导出模型pt 转 ONNX 的参数取舍需要转换格式的原因很简单best.pt只能在 PyTorch 环境下跑而实际部署目标往往是 RK 系列板子、昇腾设备或纯 CPU 服务器这些环境不一定装得下 PyTorch但普遍能跑 ONNX。我在嵌入式设备上做花卉识别时常用到rk3568这类平台转换链路的起点都是 ONNX。python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --imgsz 640 \ --batch 1 \ --opset 12 \ --simplify参数里值得说明的是--opset 12这个算子集版本在 RKNN 和 OpenVINO 工具链里兼容性较好。--simplify会调用 onnx-simplifier 做图优化去掉冗余节点对嵌入式部署的推理速度有明显帮助。--batch 1是固定动态批次的坑如果你想导出后支持动态输入尺寸需要加--dynamic但嵌入端推理我建议固定--imgsz 640动态尺寸在 RKNN 转换时反而容易出问题。5.2 Shell 批量推理for 循环调用 detect 并整理结果部署场景里最常见的诉求不是单张图片而是“把这个目录下两百张花卉照片全部识别并把结果汇总成一张表”。这时候 Shell 的 for 循环配合 Python 推理脚本正好发挥各自的长处。#!/bin/bash # run_batch_infer.sh INPUT_DIR$1 OUTPUT_DIRresults_$(date %Y%m%d) mkdir -p $OUTPUT_DIR for img in $INPUT_DIR/*.jpg; do base$(basename $img .jpg) python detect.py \ --weights best.pt \ --source $img \ --project $OUTPUT_DIR \ --name $base \ --exist-ok \ --conf-thres 0.4 \ --iou-thres 0.45 \ --save-txt \ --save-conf done echo [INFO] 推理完成结果目录: $OUTPUT_DIR这个脚本的逻辑是逐张调用 detect.py 并把结果独立保存到以图片名命名的子目录里。为什么不用--source 整个目录一次跑完因为单张调用时如果某一张图格式损坏后面的图还能继续跑一次跑整个目录时一张坏图会中断整个批次排错成本高。--save-txt会输出每个检测框的类别、坐标和置信度--save-conf把置信度写进 txt 而不是只画在图上之后做指标统计直接解析 txt 即可。5.3 后处理参数置信度阈值和 NMS 阈值在花卉场景怎么调“yolov5 后处理”在花卉识别里主要是两个阈值参数含义不同调整方向也不同。参数默认值花卉场景建议判断依据--conf-thres0.250.350.45conf 偏低会有大量误检调高到 0.5 后漏检明显增加就说明模型本身对该类置信度就低--iou-thres0.450.40.5密集花朵相互遮挡时iou 阈值高了会把两朵相邻花合并成一个框低了会出现一朵花上叠三四个框调阈值的血泪经验是先看 mAP 再调 conf。mAP 高但实际场景误检多说明验证集和部署场景分布有偏移单纯调高 conf 治标不治本mAP 本身就低调阈值救不回来得回炉训练。iou-thres在目标密集场景更值得花时间花卉比一般物体更常见重叠遮挡可以把 0.4、0.45、0.5 三档各跑一遍同一批测试图用肉眼数一下漏检和重复框的数量再定。6. 进阶用一次多折验证和混淆矩阵确认模型真的能落地训练完一个模型最容易产生的错觉是“验证集 mAP 到 0.9项目稳了”。实际部署时你会发现验证集图片和训练集出自同一批数据分布模型记住场景的能力比想象中强得多。我现在的习惯是不管训练效果多好都要额外做两件验证——多折交叉验证和野外新环境测试。多折验证的做法是把数据分成 5 折轮流用 4 折训练、1 折验证最后把 5 次验证结果平均。这个操作在你的数据集规模只有几百张时尤其关键它能暴露“模型只是记住了训练集里某个拍摄场景”的隐患还能顺便给出一个更诚实的精度区间——比如 5 折的 mAP50 分别是 0.87、0.83、0.79、0.85、0.81你就能知道浮动范围而不是只盯着一次实验里的 0.87。混淆矩阵是另一个不可替代的视角。跑一次python val.py --data data/flowers.yaml --weights best.pt --conf-thres 0.4 --iou-thres 0.45 --save-conf然后去看runs/val/exp/confusion_matrix.png这张图。重点不是看对角线上的数字而是找哪些类互相串菊花和向日葵串说明两者花盘形态相似绣球和丁香串说明模型在学“一团花的整体形状”而不是“花瓣纹理”。看到哪两类串就去数据里补哪两类的边界样本这个方向的收益比继续加 epochs 大得多。最后一件事真正拿到现场拍摄的一张新花照片让它走进一个没在训练集里出现过的背景。花卉项目最常见的翻车就是训练集全是纯色背景的标本照部署到花园里背景变成草地和栅栏模型精度断崖式下降。我现在做花卉项目收尾前都会强制自己留 20 张“野外照片”做冒烟测试这 20 张不参与训练、不参与验证只在最后推理时跑一遍肉眼确认每朵花的框位和类别。这套“多折验证 混淆矩阵 新场景冒烟测试”的组合能在模型交付前把大多数隐性坑提前翻出来。我曾经在一个灌木花卉项目里为了赶进度跳过了多折验证结果模型在第二周新采集的数据上 mAP 掉了将近 10 个点从那次之后我就坚持所有模型上线前至少跑两折验证这类翻车只值一次学费。希望这些流程和坑位能帮你把这个方向的模型做得更稳一点也希望你的花卉识别项目不只在训练集上好看而是在真实花园里也能认得出每一朵花。本文还有配套的精品资源点击获取
返回列表