ARTICLE DETAIL

资讯详情

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

基于Python与Shell的YOLOv5花卉识别:从数据集制作到训练调参实战

基于Python与Shell的YOLOv5花卉识别:从数据集制作到训练调参实战 简介这是基于Python与Shell语言实现的YOLOv5花卉识别模型设计源码面向深度学习入门者、计算机视觉方向学习者以及需要快速构建目标检测项目的开发者。项目专注于花卉智能识别涵盖从数据集配置、模型训练到推理部署的完整流程可直接用于课程设计或实际场景的二次开发。压缩包共102个文件大小仅1.19MB主要包括40个YAML配置文件、32个Python源文件、11个YAML模板、5个Shell脚本以及少量图像与说明文档各类型文件分工清晰便于理解YOLOv5的配置体系、训练逻辑与自动化处理思路。已有370人学习浏览代码结构完整、注释清楚尤其适合希望掌握YOLOv5工程实现细节的读者参考借鉴。1. 拿到yolov5花卉识别源码包后先搞清楚Python和Shell各自在干什么标题写的是“基于Python与Shell语言的yolov5花卉识别模型设计源码”很多刚入门的读者第一反应是把它当成一个“下载完就能跑”的黑匣子但真正让这份源码产生价值的恰恰是Python与Shell两条线怎么分工Python在训练阶段负责数据预处理、标签转换、模型训练和推理结果解析Shell脚本则像一条流水线把环境检查、数据集批量整理、训练启动与日志回收串起来。对想把yolov5训练自己的数据集这条路走通的人来说这套源码的实用价值在于提供了一个可复用的骨架换一个业务场景你只需要改数据目录和类别名。这篇笔记把环境准备、数据集加工、训练调参、避坑和批量推理一条线讲完。2. 拆源码包结构Python逻辑与Shell脚本的分工线2.1 打开源码包先找三个入口train.py、detect.py与data目录拿到源码包先别急着跑先花十分钟搭一张目录视图。最常见做法是直接用tree命令把结构打出来先定位文件再谈运行。tree -L 2 -d输出里你会看到这几个关键目录models存放网络结构定义utils存放数据增强、损失函数、通用工具data目录里放着数据集配置文件和类别说明scripts或sh目录放Shell辅助脚本。别小看这一步我见过不少人把数据文件塞到models目录里后面训练时报错找半天。这套源码的真正入口是三个Python文件train.py负责模型训练detect.py负责推理检测val.py负责验证集评估。而Shell脚本通常不直接参与计算它负责的是“人跟机器之间的脏活”——检查环境、批量改名、启动训练、盯日志。为什么要把Shell单独拆一层因为训练一个花卉模型不是一条命令跑完就结束的事你需要一套可重复执行的流程而Shell脚本恰好是组织和记录这套流程最轻的方式。看代码时注意区分Python逻辑里变动最频繁的是data.yaml和模型配置Shell脚本里变动最频繁的是路径变量。把路径集中写在Shell脚本头部是我一贯的做法改数据集位置时只改一处其余代码不用动。2.2 环境体检脚本把Python版本、GPU和依赖一次查清yolov5对运行环境有一套要求但报错信息往往不直观。与其等train.py跑一半抛异常不如在进入训练前用Shell脚本把环境底牌翻开。#!/bin/bash # env_check.sh - 训练前环境体检 set -e echo [1/4] 检查 Python 版本... python3 --version python3 -c import sys; assert sys.version_info (3, 8), 需要 Python 3.8 或更高版本 echo Python 版本正常 echo [2/4] 检查 PyTorch 与 CUDA... python3 -c import torch; print(torch:, torch.__version__); print(cuda:, torch.cuda.is_available()) echo [3/4] 检查 GPU 是否可用... if command -v nvidia-smi /dev/null; then nvidia-smi --query-gpuname,memory.total --formatcsv,noheader else echo 没有找到 nvidia-smi将使用 CPU 训练速度会慢很多 fi echo [4/4] 检查关键 Python 依赖... python3 -c import yaml, cv2, numpy; print(yaml, opencv-python, numpy 已安装) echo 环境体检完成脚本里用set -e保证前面检查失败就立刻退出而不是带着坏环境继续跑。重要的事情在早期失败比训练跑了三个小时后失败要划算。command -v nvidia-smi用于探测显卡驱动如果输出为空后续训练就要有心理准备哪怕能跑也是CPU硬扛花卉数据集成百上千张图片CPU训练一轮可能以小时计。实际用的时候很多人会把这段脚本放在源码包根目录并把第二步改成检查yolov5的requirements.txt是否安装完整。我一般再加一个额外的检查项确认pip list里torch和torchvision版本能对得上版本错配是import torch时最容易翻车的点。2.3 虚拟环境与依赖固定给“后悔药”留一个快照Python项目最忌讳的就是把依赖装在全局环境里。你的源码包里最好带一个setup_env.sh作用是创建独立虚拟环境并固定依赖版本。#!/bin/bash # setup_env.sh - 创建虚拟环境并安装依赖 set -e if [ ! -d venv ]; then python3 -m venv venv echo 虚拟环境 venv 已创建 fi source venv/bin/activate pip install --upgrade pip setuptools wheel pip install -r requirements.txt pip freeze requirements_lock.txt echo 依赖安装完成已生成 requirements_lock.txt逻辑说明第一次运行会创建venv目录之后每次重复执行不会重复创建source venv/bin/activate激活环境pip freeze把当前实际安装的版本冻结到lock文件相当于给环境留了个后悔药。当你在VSCode里配置Python解释器时直接选中这个venv目录下的python就不会出现“命令行能跑、IDE里报ModuleNotFoundError”的问题。依赖管理这块有个常见误区直接照搬yolov5的requirements.txt不管版本。实际项目中我会把opencv-python换成本机编译版本把PyYAML固定在能解析中文注释的版本。因为花卉数据集的类别名称往往是英文单词但如果你的data.yaml里写了中文注释PyYAML版本不对会直接解析报错这一条不少人都踩过。3. 花卉数据集加工从原始图片到yolov5能吃的标准格式3.1 花卉类别怎么定先保底五类跑通再扩容花卉识别本质上是目标检测任务不是分类任务所以每个类别需要的不只是“有这张图”而是“有足够多的带框标注”。类别数量直接影响训练难度类别越多相似花卉之间的误检率越高。常见的做法是先定5个差异明显的类别比如玫瑰、郁金香、向日葵、雏菊、兰花这五类花型差异大边框特征清晰很适合作为第一个可用版本。公开数据集方面Oxford 102 Flowers这类数据集能直接下载到带标签的花卉图片但要注意其标注格式是分类标签不是检测框。想直接用在yolov5上要么自己标要么做格式转换。自采数据时每类建议至少备100到300张图片覆盖不同角度、不同光照和不同背景。数量少不是不能训练而是mAP会不稳定很容易出现过拟合信号。3.2 用Shell批量清洗图片后缀规范化与坏图剔除从网上下载或相机导出的图片文件名和后缀常常不统一有的叫“flower (1).jpg”有的是png还有的其实是webp但后缀写成了jpg。yolov5在读取数据时对图片格式敏感批量清洗这一步用Shell跑最快。#!/bin/bash # prepare_images.sh - 数据集图片预处理 set -e RAW_DIR./raw_flowers CLEAN_DIR./datasets/flowers/images mkdir -p $CLEAN_DIR # 统一文件名规则类别_序号.jpg find $RAW_DIR -type f \( -iname *.jpg -o -iname *.jpeg -o -iname *.png \) | while read -r img; do cls$(basename $(dirname $img)) idx$(basename $img | tr -cd 0-9) cp $img $CLEAN_DIR/${cls}_${idx}.jpg done # 剔除无法解码的坏图调用 Python 做完整性校验 python3 EOF from PIL import Image import glob, os for path in glob.glob(./datasets/flowers/images/*.jpg): try: with Image.open(path) as im: im.verify() except Exception as e: os.remove(path) print(f移除坏图: {path} ({e})) EOF echo 图片清洗完成有效图片数: $(ls $CLEAN_DIR | wc -l)find命令匹配三种常见图片后缀然后按“类别_序号.jpg”重命名这样后续做数据划分时可以从文件名直接反推类别归属。tr -cd 0-9把文件名里的数字提取出来做序号避免重名覆盖。最后的Python块用PIL的verify()检查图片是否能完整解码verify只校验文件结构不加载像素速度很快。这里有个容易被忽略的点Shell的find与while read组合在文件名含空格时依然安全因为read默认按行读取完整路径。如果你用for img in $(find ...)文件名含空格就会裂成两段这是Shell脚本里最常见的老坑。3.3 标注格式转换从XML到YOLO的归一化txt标注工具常用labelimg它是图形界面工具导出格式有两种选择Pascal VOC的XML以及YOLO的txt。第一次标注时建议直接选YOLO格式省去转换环节。但如果拿到的数据集是XML格式就必须转换。YOLO要求每个目标占一行格式是类别id、中心点x、中心点y、框宽w、框高h全部归一化到0到1之间。# voc2yolo.py - 将VOC XML标注转换为YOLO txt格式 import xml.etree.ElementTree as ET import os def convert_xml_to_yolo(xml_path, out_dir, 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) out_lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_list: print(f跳过未定义类别: {cls_name}) continue cls_id class_list.index(cls_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) # 坐标归一化并限制在[0,1]区间 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) txt_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(txt_path, w) as f: f.write(\n.join(out_lines)) # 使用示例 class_list [rose, tulip, sunflower, daisy, orchid] convert_xml_to_yolo(annotations/rose_001.xml, labels, class_list)转换逻辑的核心是把XML里的绝对坐标xmin、ymin、xmax、ymax换算成中心点加宽高的归一化坐标。除以图像宽高这一步不能省YOLO训练时会把输入图缩放到640x640如果标签没有归一化训练时坐标全乱。转换时class_list的顺序就是训练时类别的唯一标识这个顺序在后续data.yaml里必须保持一致。3.4 数据划分与data.yaml顺序错了等于白训训练集和验证集要分开目录存放常见比例是9:1或8:2。划分逻辑不复杂但必须保证同一张图片的jpg和txt同时落到同一个集合里。#!/bin/bash # split_data.sh - 按8:2划分训练集与验证集 set -e IMG_DIR./datasets/flowers/images LABEL_DIR./datasets/flowers/labels TRAIN_IMG./datasets/flowers/train/images VAL_IMG./datasets/flowers/val/images TRAIN_LBL./datasets/flowers/train/labels VAL_LBL./datasets/flowers/val/labels mkdir -p $TRAIN_IMG $VAL_IMG $TRAIN_LBL $VAL_LBL find $IMG_DIR -name *.jpg | sort all_files.txt total$(wc -l all_files.txt) val_count$(( total / 5 )) # 取前20%作为验证集 head -n $val_count all_files.txt val_files.txt # 其余80%作为训练集 tail -n $((val_count 1)) all_files.txt train_files.txt while read -r img_path; do base$(basename $img_path .jpg) cp $img_path $VAL_IMG/$base.jpg cp $LABEL_DIR/$base.txt $VAL_LBL/$base.txt done val_files.txt while read -r img_path; do base$(basename $img_path .jpg) cp $img_path $TRAIN_IMG/$base.jpg cp $LABEL_DIR/$base.txt $TRAIN_LBL/$base.txt done train_files.txt echo 划分完成训练集 $(ls $TRAIN_IMG | wc -l) 张验证集 $(ls $VAL_IMG | wc -l) 张sort之后再做划分保证同一类别的图片不会全部挤进验证集。需要注意如果你的源图片是按类别子目录组织的现在全部平铺到一个目录里文件名的“类别_序号”前缀就显得至关重要它让你能快速反查类别和原始路径。划分完成后data.yaml的写法是固定的# data.yaml - 花卉数据集配置 train: ./datasets/flowers/train/images val: ./datasets/flowers/val/images nc: 5 names: [rose, tulip, sunflower, daisy, orchid]names列表的顺序必须与标注txt里的类id完全一致这是yolov5最容易出现隐性错误的地方。train和val路径建议用绝对路径或相对源码包根目录的路径不要写死成某台机器的/home路径方便源码包整体迁移。4. 训练调参与Shell训练脚本从跑通到跑稳4.1 最小训练命令先用10轮验证管线再上完整训练第一次训练不建议直接上来就跑100轮。花卉数据集如果每类只有一两百张图完整训练一轮需要几分钟到几十分钟不等如果管线里有隐藏问题比如标签读取失败、data.yaml路径写错跑100轮就是几个小时的白白浪费。我的做法是先用小规模数据把管线跑通一遍# 最小训练命令10轮小batch快速验证数据管线 python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 10 \ --project runs/quick_test这段命令的关键参数--weights yolov5s.pt使用预训练权重做迁移学习而不是从零随机初始化花卉这种中等规模数据集迁移学习收敛速度快得多--img 640是输入分辨率yolov5默认处理640x640你的原始图片会被自动缩放填充--batch 16是一个在8GB显存显卡上比较稳妥的起步值。跑完10轮后去看runs/quick_test目录下的结果重点看两点训练loss是否在第一轮就明显下降验证集的mAP是否不再是0。如果第一轮loss纹丝不动大概率是标签读取有问题如果mAP一直是0检查类别顺序是否错位。管线验证通过后再启动正式训练。4.2 关键训练超参数这几项改了才叫调优yolov5的超参数集中在hyp.scratch.yaml文件里不修改也能跑但效果只能说“能出结果”。真正值得动手的核心超参数如下表参数默认值作用我的调整建议lr00.01初始学习率数据集小或类别差异大降到0.005更稳lrf0.05最终学习率与初始学习率的比值保持默认训练后期自动降学习率momentum0.937动量影响梯度更新方向保持默认weight_decay0.0005L2正则化强度过拟合明显时升到0.001warmup_epochs3.0前几轮用较小学习率预热数据集小时升到5.0hsv_h / hsv_s / hsv_v0.015 / 0.7 / 0.4色相、饱和度、明度随机增强花卉对颜色敏感hsv_s降到0.5防颜色失真degrees0.0随机旋转角度花卉照片多为自然拍摄可设5.0flipud0.0上下翻转概率花朵很少有倒着拍的保持0fliplr0.5左右翻转概率保持默认mosaic1.0马赛克数据增强小数据集建议开启但前10轮可关掉调参最推荐的做法是在train.py命令里用--hyp指定一份自己的hyp.yaml不要直接改源码包里的文件。这样你可以同时跑多个实验对比不同参数组合的效果而不会把原始配置弄脏。花卉识别的特殊性在于类别间的差异集中在颜色和花瓣形状数据增强里的hsv扰动如果太强玫瑰可能被增强成颜色怪异的图模型反而学不到稳定的颜色特征。4.3 用Shell包一层训练后台运行、日志、进程守护训练一旦上了几十轮终端窗口一关进程就没了。Shell脚本的价值在这里体现得最明显nohup挂后台、写日志、按需重启。#!/bin/bash # train_runner.sh - 后台启动训练并管理日志 set -e EXP_NAMEflowers_v1 LOG_FILEruns/train_$EXP_NAME.log PID_FILEruns/train_$EXP_NAME.pid mkdir -p runs # 如果已有训练进程在跑先提示不要重复启动 if [ -f $PID_FILE ] kill -0 $(cat $PID_FILE) 2/dev/null; then echo 检测到训练进程仍在运行PID: $(cat $PID_FILE) echo 如需重启先执行: kill $(cat $PID_FILE) exit 1 fi # 后台启动训练输出写入日志 nohup python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --patience 20 \ --project runs/train \ --name $EXP_NAME \ $LOG_FILE 21 echo $! $PID_FILE echo 训练已启动PID: $! echo 实时查看进度: tail -f $LOG_FILE逻辑说明PID_FILE用来记录进程号防止重复启动多个训练抢占显存。--patience 20表示验证集mAP连续20轮不提升就自动早停这个参数相当于给训练上了保险丝省电省时间。日志里可以直接tail -f查看loss变化。当你想中断训练并保留现有权重时kill掉进程即可best.pt和last.pt都会保留在runs/train/flowers_v1/weights目录下。4.4 训练过程中盯什么loss曲线与mAP判读训练启动后别只看控制台打印的loss数字。进入runs/train/flowers_v1目录yolov5会自动生成results.png这张图是判断训练健康状况的核心依据。看这张图有一个基本法则box_loss和cls_loss在训练集上应该持续下降val/box_loss和val/cls_loss应该同步下降或小幅波动。如果训练集loss下降但验证集loss持续上升就是典型的过拟合信号此时应增加数据增强强度或增大weight_decay。另一个重要的指标是mAP_0.5也就是IoU阈值为0.5时的平均精度花卉识别这种以检测出花为目标的任务mAP_0.5达到0.8以上就可以考虑部署了。mAP_0.5:0.95是更严格的指标但不必强求它对边框精度的要求极高适用于精确定位场景。这条路径上还有个隐蔽问题--project参数指定输出目录后每次训练会生成不同的exp子目录。Shell脚本里写死EXP_NAME可以规避“找不到best.pt在哪”的问题。5. 花卉识别训练中的五个常见坑现象、原因、解决5.1 训练一切正常但推理结果全部偏向某一个类别现象loss正常下降mAP也能到0.7以上但用detect.py跑新图片时所有检测框都指向同一个花卉类别比如全部识别成玫瑰。原因最常见的是标注txt里的类id与data.yaml里names的顺序不一致。比如标注时class_list是[rose, tulip, ...]但data.yaml里写成了[tulip, rose, ...]模型学到的是“类别0是玫瑰”但推理时类别0被解释成郁金香于是一切都错位。解决检查标注txt文件第一列的数字再用一个简单的Python脚本扫描全数据集输出每个类id出现的次数并与data.yaml的names一一比对。这个问题在花卉这类类间相似度高的任务上尤其致命一旦错位模型在验证集上的指标仍然是高的因为训练和验证是同错位体系只有到新图片上才暴露。5.2 Shell脚本在Windows上执行报“坏的解释器”或命令找不到现象源码包里有shell脚本但在Windows上通过Git Bash或WSL运行时第一行就报错或者出现“$\r: command not found”的提示。原因Windows下编辑文本默认使用CRLF换行符而Linux/Mac的bash只认LF。脚本第一行的#!/bin/bash被解析成“/bin/bash\r”解释器路径直接错误。此外路径分隔符Windows用反斜杠bash不认。解决用sed命令批量转换换行符或下载源码包后在Linux环境里先执行一遍格式化。转换命令是sed -i s/\r$// *.sh。更彻底的做法是给所有shell脚本加一个前置检查在脚本开头加exec命令配合tr -d \r清理输入。但最实用的经验是所有shell脚本的编辑和修改都在Linux环境里完成不在Windows记事本里碰它。5.3 训练到20轮左右突然OOM显存溢出现象前几轮正常跑到中途报CUDA out of memory进程直接被杀死。原因显存占用不是恒定的。yolov5的mosaic数据增强会在训练过程中动态拼接四张图输入尺寸比单张图更占显存另外训练后期模型开始产生更复杂的梯度计算图。如果--batch和--img设置的余量不够前几轮不爆后面必爆。解决两步走。第一步把--batch从16降到8或把--img从640降到512。第二步在训练命令里加--cache ram把图片预加载到内存减少磁盘IO和显存峰值。如果数据集本身不大还有一种做法是换用更小的模型yolov5n.pt作为预训练权重花卉类别数量少小模型的精度损失完全可接受但显存占用直接减半。5.4 训练loss不降反升甚至出现NaN现象第一轮loss正常下降训练到第5轮左右loss开始震荡上升偶尔打印出nan。验证集mAP始终是0。原因学习率过大是首要嫌疑。warmup结束后学习率走到高位恰好这时模型进入非线性拟合阶段参数更新步长过大导致损失发散。另一个常见原因是数据集里混入了损坏图片或标注异常的txt比如某个标注的坐标值为负或宽高为0这类样本会在反向传播时产生异常梯度。解决把lr0从0.01降到0.005同时把warmup_epochs从3.0提到5.0给模型更长的预热期。同时用脚本扫描所有txt文件过滤坐标不在[0,1]区间或宽高为0的标注行。做这两步后绝大多数loss发散问题都能解决如果仍然出现NaN检查图片里是否有纯白或纯黑图片这种图片在归一化时容易出现除零。5.5 训练结束但Shell脚本里找不到best.pt现象训练日志显示完成但脚本里指定的路径runs/train/flowers_v1/weights/best.pt不存在目录里只有last.pt。原因yolov5的exp命名机制是自动递增的。当你用--name flowers_v1运行时如果该目录已存在会自动改成flowers_v12、flowers_v13。Shell脚本里写死的路径就落空了。解决不要在脚本里拼死版本号用通配符或动态查找来定位最新权重文件。常见做法是BEST_PT$(find runs/train -name best.pt -printf %T %p\n | sort -nr | head -1 | cut -d -f2-) echo 使用权重: $BEST_PT这段命令按修改时间排序取出最新的best.pt不受exp目录递增的影响。把路径做成变量后续推理和转部署都用这个变量是Shell脚本管理训练产物的正确姿势。6. 推理侧的后处理过滤类别、批量跑图与最终验证6.1 detect.py只是第一步按类别ID过滤结果训练完拿到best.pt用detect.py做推理时输出结果包含每个检测框的类别ID、置信度和坐标。但在真实的花卉识别场景里你可能只想看某一个类别或者只想保留高置信结果。此时需要写一行后处理逻辑。# postprocess.py - 推理结果过滤 import torch # 加载模型假设已经跑过一次 detect.py拿到的是原始输出 results torch.load(runs/detect/exp/results.pt, map_locationcpu) target_class 0 # 只保留 rose conf_thres 0.5 for det in results.pred: if det is not None: # 过滤类别ID和置信度 mask (det[:, 5] target_class) (det[:, 4] conf_thres) filtered det[mask] print(f保留 {len(filtered)} 个目标)yolov5的后处理链路本身包含NMSdetect.py里已经执行过。写自定义过滤是因为默认输出会附带所有类别的结果业务上往往只关心特定花种提前过滤能减少后续处理的无效计算。conf_thres设到0.5是比较合理的区间太低会引入大量误检太高会漏掉小花朵。6.2 Shell循环批量推理跑完整个验证集并输出CSV单张图片推理验证的是模型能力批量推理验证的是系统稳定性。Shell循环配合detect.py的--source参数可以一口气跑完整个验证集。#!/bin/bash # batch_infer.sh - 批量推理验证集并统计结果 set -e BEST_PT$(find runs/train -name best.pt -printf %T %p\n | sort -nr | head -1 | cut -d -f2-) VAL_DIR./datasets/flowers/val/images python detect.py \ --weights $BEST_PT \ --source $VAL_DIR \ --conf-thres 0.4 \ --iou-thres 0.5 \ --save-txt \ --project runs/detect \ --name batch_val echo 推理完成结果保存在 runs/detect/batch_val/labels/ echo 统计各文件检测框数量: wc -l runs/detect/batch_val/labels/*.txt | tail -20这段脚本的意义不只是跑批它把第六章前面的动态路径查找、类别过滤思路都串了起来。--save-txt会把每张图片的检测结果存成txt文件每一行是一个检测框wc -l统计每个文件的行数能快速发现哪些图片完全没有被检测到或者哪些图漏检严重。如果验证集整体漏检率高别急着调模型先回去检查数据集里是否有类别不平衡问题。这套流程跑通之后你可以把同样的Shell思路迁移到其他部署环境比如把Python推理逻辑打包成接口再用Shell做定期数据更新。花卉识别的最终价值不在训练曲线而在对新图片稳定输出正确结果。我自己最后的习惯是每次训练完一定用验证集里最难的几类图测试一遍用事实说话不对着loss曲线自嗨。如果推理结果不如预期回过头先查数据标注再查后处理阈值最后才动模型结构。希望这些经验对你有用愿你的花卉模型第一次推理就准。本文还有配套的精品资源点击获取
返回列表