ARTICLE DETAIL

资讯详情

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

YOLOv5花卉识别:Python+Shell打造自动化训练流水线

YOLOv5花卉识别:Python+Shell打造自动化训练流水线 简介面向深度学习初学者与计算机视觉开发者这份基于Python和Shell的YOLOv5花卉识别模型设计源码提供了从数据配置、模型训练到结果推理的完整工程化实现适合用于花卉分类识别场景的快速落地与学习。压缩包共102个文件、体积仅1.19MB内部按功能划分清晰40个yaml文件承载模型与数据集参数配置32个Python源文件实现训练、验证与推理核心逻辑11个yml模板便于快速调整配置5个Shell脚本可自动化批处理与训练流程另有dockerfile、gitignore等工程化配套文件。目前已有371人学习下载适合需要快速上手YOLOv5目标检测的开发者参考。内容同时包含示例笔记本、样例图像、开源许可文件与Docker部署配置可帮助用户从零搭建环境、调试模型参数并部署到容器中是一份兼顾理论理解与动手实践的入门级完整源码。1. 关于这个源码你需要知道的不是“模型”而是流水线拿到这个标题别急着把它当成又一个“深度学习炼丹脚本”。它真正的落点是用 Python 写 YOLOv5 的训练与推理逻辑再用 Shell 把数据准备、环境初始化、批量跑批这些脏活自动化。我见过太多人在花卉识别这类小项目上翻车翻得最多的不是模型本身而是——数据集没整理干净、训练到一半显存爆了、脚本重跑一次把标注文件覆盖了。这套源码存在的意义恰恰是把这些坑用 Shell 脚本和 Python 脚本一层层填平让模型训练可以一键重跑、结果可复现。适合谁用三种人想用现成 YOLOv5 快速做植物/花卉分类检测的算法工程师在 Linux 服务器上跑实验、又被重复性文件操作折磨的生物信息或农学方向研究者还有刚入门目标检测、想找一条完整落地路径的学生。顺着这个标题走一遍你会得到一个能切换数据集继续训练的工作流而不是一个只是“能跑通”的玩具。2. 为什么是 YOLOv5 Python Shell选型理由与最小环境搭建2.1 三个组件各管哪一段先说 YOLOv5。比起 YOLOv8 和 YOLOv9它已经不新了但当一个项目把“源码”作为交付物时v5 的生态成熟度恰好是优势官方仓库的train.py、detect.py、utils/结构稳定第三方教程数量最多遇到报错几乎都能搜到解决方案。花卉识别的目标通常不算太小也没有极端密集排列v5 的 YOLOv5s 或 YOLOv5m 就够用不需要上大模型换那 2%~3% 的 mAP换来的是更快的训练迭代速度和更低的显存要求。Shell 在这里不是替代 Python而是做Python 不擅长的事批量文件重命名、按比例划分数据集、循环遍历目录清理缓存、用nohup挂后台训练。Python 做这些当然也能做但你每次换一批花卉图片就要改一次脚本参数而在 Shell 里用findforcp就能写出一段不依赖 Python 环境的小工具放到任何 Linux 机器上直接跑。2.2 搭建一个不会再“跑着跑着崩掉”的 Python 环境很多新手按 README 装依赖上来就pip install -r requirements.txt结果 PyTorch 和 CUDA 版本不匹配训练时直接报AssertionError: CUDA out of memory。我一般先把 PyTorch 装好再装 YOLOv5 的依赖顺序不能反。# 1. 创建独立虚拟环境避免污染系统 Python conda create -n yolo-flower python3.8 -y conda activate yolo-flower # 2. 先按 CUDA 版本装 PyTorch这里以 CUDA 11.3 为例 pip install torch1.10.0cu113 torchvision0.11.0cu113 \ --extra-index-url https://download.pytorch.org/whl/cu113 # 3. 再安装 YOLOv5 的依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 4. 验证 GPU 是否可用 python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))这条命令序列有几个关键决策。用 conda 而不是系统 Python 直接装是为了让torch、opencv-python这些包的版本可以被隔离控制后面换了项目不至于互相踩。PyTorch 必须单独先装因为 YOLOv5 的requirements.txt里的 PyTorch 条目往往是指定 CPU 版或旧版直接跑会把你刚配好的 GPU 版覆盖掉。装完后先别急着写训练脚本跑一句python train.py --data coco128.yaml --epochs 1 --batch-size 2做冒烟测试确认环境通得过。这里用coco128.yaml是 YOLOv5 自带的迷你数据集只有 128 张图一分钟能跑完一个 epoch专门用来验证环境。真正训练花卉数据时再换成自己的data.yaml。2.3 Shell 环境里两个必调的参数Shell 脚本在训练流程里通常承担“环境初始化 启停管理”的角色。我常用两个参数一个是set -e一个是set -u。前者让脚本在任意一条命令失败时立即退出避免后面步骤拿着半成品数据继续跑后者让未定义的变量直接报错防止拼路径时把空字符串悄悄拼进去最后训练找不到数据集。#!/bin/bash set -e set -u # 激活虚拟环境 source /opt/conda/etc/profile.d/conda.sh conda activate yolo-flower # 清理上一次训练留下的缓存避免 pytorch 复用损坏的 dataloader find ./datasets/flower/images -name *.cache -delete # 启动训练日志落盘后台运行 nohup python train.py \ --data datasets/flower/data.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --project runs/train_flower \ train_flower.log 21 echo Training started, PID: $!nohup ... 用$!打印进程号是为了你在 SSH 断线后还能用kill -9 进程号找回控制权。find ... -delete清理 cache 文件是我吃过亏后用上的习惯如果上次训练被强行终止残留的.cache文件会导致下一次训练在数据加载阶段随机卡死。3. 把花卉图片变成 YOLOv5 吃得下的数据集标注、转换与 Shell 批量脚本3.1 数据标注后的格式转换花卉识别的数据集一般两种来源一是公开数据集如 Flower-102、Oxford 17 Flowers拿到的是 JPEG 图加标签文件二是自己用手机或相机拍的花要自己标注。不管哪种YOLOv5 只认一种格式——每张图对应一个同名.txt每行是类别id x_center y_center width height前两个是归一化后的中心坐标后两个是归一化后的宽和高。自己标注最顺手的工具是 LabelImg输出 Pascal VOC 的 XML 格式因为它带图形界面、标注框是拖拽式的。但 YOLOv5 不认 XML必须转成 txt。这个转换脚本值得自己写一遍因为它会反复用到import os import xml.etree.ElementTree as ET from pathlib import Path def convert_voc_to_yolo(xml_path: str, class_names: list, out_dir: str): tree ET.parse(xml_path) root tree.getroot() # 读取图片宽高归一化时会用到 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 坐标归一化注意除以的是图片宽高 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 防止边界值为负或超过1标注软件手滑时容易出现 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) # 输出文件名必须和图片名一致只换后缀 out_path Path(out_dir) / (Path(xml_path).stem .txt) out_path.write_text(\n.join(lines), encodingutf-8) # 示例把 annotations 目录下所有 xml 转成 labels 目录下的 txt if __name__ __main__: classes [daisy, sunflower, rose, tulip] xml_dir datasets/flower/annotations label_dir datasets/flower/labels os.makedirs(label_dir, exist_okTrue) for xml_file in Path(xml_dir).glob(*.xml): convert_voc_to_yolo(str(xml_file), classes, label_dir) print(fconverted: {xml_file.name})这段脚本的核心逻辑就两个从 XML 里读出bndbox的绝对坐标再通过图片宽高归一化到[0,1]区间。边界值 clamp 到 1.0 那一步不能省——我遇到过标注时手拉出的框比图片还大不 clamp 的话训练时 YOLOv5 的utils/datasets.py会在 resize 阶段直接报负值异常。另一个容易忽略的是 class 列表的顺序。classes里的顺序决定 txt 文件里类别id的值后面data.yaml里的names列表必须和这里保持完全一致错一个序训练出的模型预测的类别就全部错位而且你很难发现除非去推理结果上一张一张确认。3.2 用 Shell 脚本做数据集的切分和重命名数据集准备好后训练前还要做两件事按比例划分 train/val以及把文件名批量处理成标准格式。这两件用 Python 写也行但 Shell 更快尤其在服务器上直接操作时#!/bin/bash set -e DATASET_DIRdatasets/flower IMAGES_DIR$DATASET_DIR/images LABELS_DIR$DATASET_DIR/labels TRAIN_DIR$DATASET_DIR/train VAL_DIR$DATASET_DIR/val # 1. 清空上一次的划分结果保证可重复执行 rm -rf $TRAIN_DIR $VAL_DIR mkdir -p $TRAIN_DIR/images $TRAIN_DIR/labels mkdir -p $VAL_DIR/images $VAL_DIR/labels # 2. 把所有图片随机打乱取前80%做训练集后20%做验证集 find $IMAGES_DIR -name *.jpg | shuf all_images.txt TOTAL$(wc -l all_images.txt) TRAIN_NUM$((TOTAL * 80 / 100)) head -n $TRAIN_NUM all_images.txt train_images.txt tail -n $((TRAIN_NUM 1)) all_images.txt val_images.txt # 3. 按列表复制图片和对应的标签文件 while read img_path; do filename$(basename $img_path) stem${filename%.jpg} cp $img_path $TRAIN_DIR/images/$filename cp $LABELS_DIR/$stem.txt $TRAIN_DIR/labels/$stem.txt done train_images.txt while read img_path; do filename$(basename $img_path) stem${filename%.jpg} cp $img_path $VAL_DIR/images/$filename cp $LABELS_DIR/$stem.txt $VAL_DIR/labels/$stem.txt done val_images.txt echo Train: $TRAIN_NUM, Val: $((TOTAL - TRAIN_NUM))用shuf随机打乱是这里的核心技巧。很多新手写这类脚本时直接ls然后按顺序取前 80%如果采集照片时有明显的时序和光照变化这样划分出的验证集可能全是同一时间段的花评估结果的参考价值就打了折扣。shuf之后head/tail的组合保证每次执行划分结果不同但set -e又把可重复性控制住——脚本中途出错会在写坏数据集之前停下来。文件复制这里用basename取出文件名是为了兼容图片在深层子目录里的情况。find默认会输出完整路径如果直接用cp的路径拼接目录结构一旦多一层就会把路径拼错。标签文件用${stem%.jpg}去掉后缀再拼.txt这依赖图片文件和标签文件的主干名完全一致所以标注阶段就要定好命名规范——我见过最痛的翻车就是一批图片叫IMG_001.jpg标注文件因为批量导出变成001.txt训练时一张图都读不到标签loss 一路飘在 8 附近不下降。4. 训练与调参datasets 配置、超参数与训练命令全解4.1 一个能正常吃到数据的 data.yaml数据集目录建好后YOLOv5 不会自动发现它需要写data.yaml告诉训练器去哪里找图。这个文件经常被人写错尤其是路径字段写相对路径时训练器的工作目录变了就会找不到文件。# datasets/flower/data.yaml train: datasets/flower/train/images val: datasets/flower/val/images nc: 4 names: [daisy, sunflower, rose, tulip]train和val指向的是图片目录不是包含 images 和 labels 的父目录YOLOv5 会在同级的labels子目录里自动找对应 txt。这里我写的是相对路径如果你在yolov5/目录下执行train.py它就能正确解析但如果哪天把脚本挪到别的目录调用相对路径就会失效。names的拼写不能错是names不是classes。YOLOv5 在yaml加载阶段会检查nc和len(names)是否一致不一致就报错这一点反而是好事能帮你提前发现标注转换时类别列表配错了。4.2 官方超参数文件怎么看不玄学但都有自己的脾气YOLOv5 的超参数在data/hyps/hyp.scratch-low.yaml里训练时可以覆盖。我建议新手只动四个参数lr0、batch_size、mosaic、fliplr。参数默认值作用调参建议lr00.01初始学习率花卉数据集如果只有几千张0.01 偏大降到 0.005 起步mosaic1.0是否启用马赛克增强数据集低于 2000 张时保持在 1.0能显著涨点图片本身分辨率高、目标小时谨慎调低fliplr0.5水平翻转概率花卉通常对称可以开到 0.8batch_size由显存决定每次迭代样本数显存不够时优先从 64 降到 16别动imgsz训练时的完整命令python train.py \ --data datasets/flower/data.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 32 \ --imgsz 640 \ --hyp data/hyps/hyp.scratch-low.yaml \ --project runs/train_flower \ --name flower_v1--weights yolov5s.pt是指定预训练权重。第一次跑会从 GitHub 自动下载如果你在无外网环境需要提前把权重文件放到yolov5/目录下。--project和--name把训练产物归档到runs/train_flower/flower_v1/里面权重文件叫best.pt验证集 mAP 最高和last.pt最后一轮。验证集 mAP 是在训练过程中每轮结束自动计算的不需要额外写验证脚本。训练完后打开runs/train_flower/flower_v1/results.csv或者看results.png重点看三列metrics/mAP_0.5、metrics/mAP_0.5:0.95、train/box_loss。如果 mAP 在最后 20 个 epoch 还在涨说明 100 轮不够可以加--epochs 150续训如果 mAP 涨到某个值后开始掉或波动说明已经过拟合回去取best.pt就行。4.3 花卉识别在训练阶段的特殊之处花卉识别和通用目标检测的差别主要在两点。一是类别间视觉相似度高比如雏菊和蒲公英在早期训练阶段输出几乎一样容易混淆这时候把mosaic保持 1.0 能显著降低过拟合因为它把四张图拼成一张模型被迫学局部特征而不是记忆整张图的颜色分布。二是数据集规模普遍偏小大多数个人花识别的项目就几千张图甚至几百张这时候epochs拉到 300 也可能没完全收敛需要靠--patience 50提前停止来省时间。另一个常被忽略的训练参数是--workers。默认值是 8如果你是在 Windows 上跑--workers 0才能稳定Windows 下 DataLoader 的多进程经常内存溢出在 Linux 上 8 没问题。这个参数用不好就会遇到进程崩溃在数据加载阶段而报错信息是 Python 的多进程错误和模型一行关系都没有排查起来很折磨人。Shell 在这段流程里的作用是“参数记录”。我每次训练都会把这一次用到的命令写成一个train_v1.sh存进项目目录下次调整只改参数名不改命令结构。这样不管是换数据集还是调超参数都有一条可追溯的命令历史而不是靠脑袋记——这个习惯在模型版本迭代到第三个版本时就会觉得是救命稻草。5. 避坑清单花卉识别项目里最常见的 5 个坑现象、原因与解法5.1 训练 loss 不降反升或者从一开始就在 8 附近徘徊现象box_loss和cls_loss在前 10 个 epoch 没有明显下降甚至曲线往上走。原因最常见的是标注文件有问题。YOLOv5 在 dataloader 阶段会自动过滤无标注的图片如果过滤比例很高等于模型拿到的训练样本里大量图片没有真实框损失无法正常收敛。其次是学习率过大导致梯度震荡。解决训练前统计一下标签文件的非空占比。写一行 Shell 排查find labels -name *.txt -size 0 | wc -l如果空文件超过 5%先回去查标注转换脚本。然后把lr0从 0.01 调到 0.005 再跑。5.2 CUDA out of memory 报错位置不定现象训练刚开始几轮正常某次迭代直接报torch.cuda.OutOfMemoryError。原因显存溢出不一定发生在模型前向传播也可能发生在--batch-size过大导致的梯度累积阶段以及 mosaic 增强时图像拼接缓存。解决先减--batch-size从 32 减到 16再不行减到 8。同时把--workers从 8 降到 4——dataloader 的缓存数据也会占显存。如果单卡显存只有 6G直接用 yolov5s 且--imgsz 480牺牲一点精度换稳定。5.3 Shell 脚本在 Windows 上开发、Linux 上跑报错bad interpreter现象明明脚本逻辑没问题执行时却提示/bin/bash^M: bad interpreter: No such file or directory。原因Windows 下编辑器默认把换行符写成\r\n而 Linux 只认\n。这个\r被 Shell 当成文件名的一部分。解决脚本上传到服务器后先跑一次sed -i s/\r$// train.sh或者用dos2unix train.sh。这是每个用 Shell 编排训练的人都会至少撞一次的血泪经验。预防的办法是开发时就把编辑器设置成 Unix 换行。5.4 训练正常但推理时对某些花卉品种彻底无感知现象验证集 mAP 有 0.85但拿手机拍的同一品种不同角度照片去测完全检测不到。原因这是经典的“数据集偏置”。如果你的训练集照片都是平视视角拍的花模型学到的纹理特征集中在花蕊颜色和花瓣形状上换成俯拍或逆光照片特征分布偏移就失效了。YOLOv5 的数据增强能缓解一部分但救不了数据采样本身的单一性。解决在划分数据集前刻意保留一部分不同角度、不同光照条件的图作为独立测试集。用--val指向这幅测试集重跑detect.py看看 mAP 跌幅。如果跌幅超过 10%说明数据集本身需要补样本而不是盲目调超参数。5.5 训练中断后续跑loss 曲线出现断层甚至数值异常现象用--resume续跑后前几个 epoch 的 loss 和中断前完全对不上。原因优化器的动量状态没有完整恢复。YOLOv5 的 resume 是从last.pt恢复权重和 epoch 编号但如果你换了--batch-size或改了学习率调度器相关参数Adam 的动量缓冲会失效。解决续跑时不要改任何与优化相关的参数命令里只保留--resume和--epochs。如果非改不可干脆从头重训不要为了省那 20 个 epoch 引入了这个玄学问题。6. 推理落地从detect.py到自定义 Python 推理脚本以及导出 ONNX 的实际价值6.1 一条命令把模型跑在图片和视频上训练结束后最直接的使用方式是官方detect.pypython detect.py \ --weights runs/train_flower/flower_v1/weights/best.pt \ --source datasets/flower/test_imgs/ \ --conf-thres 0.5 \ --iou-thres 0.45 \ --save-txt \ --project runs/detect_flower \ --name flower_test_v1--conf-thres 0.5是置信度阈值花卉识别场景下调到 0.25 更合理——花朵之间重叠多模型天然会把低置信度的框压掉阈值设太高容易出现“该检出的没检出”。--save-txt会额外输出每个框的类别、置信度和归一化坐标后续接统计脚本非常有用。6.2 更实用的做法用 torch.hub 加载模型写进你自己的 Python 代码detect.py只能做整套流程演示实际集成时你需要的是在 Flask 接口或批处理脚本里直接调用模型。YOLOv5 官方仓库支持 hub 模式加载这样你就不用把推理逻辑写死在 YOLOv5 目录内部。import torch from PIL import Image # 直接从本地路径加载模型不依赖 yolov5 仓库目录 model torch.hub.load(yolov5, custom, pathruns/train_flower/flower_v1/weights/best.pt, sourcelocal) # 推理单张图片 img Image.open(test_imgs/sunflower_001.jpg) results model(img, size640) # 输出 pandas DataFrame包含每个检测框的信息 df results.pandas().xyxy[0] print(df.filter(items[name, confidence, xmin, ymin, xmax, ymax])) # 批量处理一个目录下的所有图片 import glob for img_path in glob.glob(test_imgs/*.jpg): res model(Image.open(img_path), size640) res.pandas().xyxy[0].to_csv( fresults/{img_path.split(/)[-1].replace(.jpg, .csv)}, indexFalse)torch.hub.load中sourcelocal表示不联网去 GitHub 拉官方权重而是直接用本地已经 clone 好的 YOLOv5 目录。这一点在服务器上非常关键——很多生产环境没有外网不加这个参数模型加载会卡死在试图下载官方模型的那一步。模型加载后的size640必须和训练时的--imgsz一致否则检测精度会可感知地下降尤其是小花目标。6.3 导出 ONNX跨平台推理和边缘设备部署的第一步训练好的.pt文件只能在 PyTorch 环境里跑如果你想把模型部署到手机或嵌入式设备通常要转成 ONNX 再转推理引擎格式。YOLOv5 自带导出脚本注意导出时的两个参数python export.py \ --weights runs/train_flower/flower_v1/weights/best.pt \ --include onnx \ --img-size 640 \ --batch-size 1 \ --simplify--simplify会调用 onnx-simplifier 对计算图做常量折叠和冗余节点消除转出来的 ONNX 文件体积更小、推理时延更低。--batch-size 1保证导出的是单张图推理版本模型输入维度被固定为[1, 3, 640, 640]。导出 ONNX 有一个常被忽略的坑训练时的--imgsz决定模型对输入尺寸的适应性。如果你是在 640 下训练的导出后只用 640 推理最有效调成 416 虽然能跑但精度差异极大。不要图快就去做尺寸缩放除非重新用对应尺寸微调模型。我的习惯是训练结束确认best.pt在本地测试集上的 mAP 没有明显下跌后马上导出 ONNX 再做一次标注数据外的对照推理用手机拍 30 张不同场景的花图作为“人工 holdout 集”。这一步最能暴露模型在真实环境中好不好使——比任何 TensorBoard 曲线都有说服力。等你导出过几次模型、部署过一两回就会发现这份源码真正的价值不是让你认识 YOLOv5而是教会你围绕它搭一套不依赖 IDE、命令行直接重跑的数据供应与训练流水线。希望帮到你。本文还有配套的精品资源点击获取
返回列表