YOLOv5花卉识别实战:Python+Shell自动化训练与部署指南
简介一套基于Python与Shell的YOLOv5花卉识别模型设计源码将主流目标检测算法应用于花卉图像场景覆盖数据准备、模型训练、验证推理与部署环节适合深度学习初学者、目标检测研究者和计算机视觉开发人员作为实战参考。压缩包共102个文件整体仅1.19MB包含40个YAML配置、32个Python源文件、11个YAML模板、5个Shell脚本、2张JPG示例图以及Jupyter Notebook、Docker容器化配置、Git版本管理配套文件等其中Python源文件承载训练与推理逻辑YAML/yml负责模型和数据集参数Shell脚本用于自动化环境配置与流程控制这些配置与脚本共同构成一个可直接运行的工程骨架。项目已吸引370人学习浏览目录结构清晰便于按模块拆解和复现。借助内置示例图像和Notebook可快速跑通花卉识别流程替换自定义数据集并调整超参数即可进行二次开发同时Docker与Git相关配置也有助于理解模型部署和版本管理规范是一份适合课程设计或算法入门的完整工程样例。1. 不是“python调用yolov5”而是一整套能落地到真实花圃的工程方案做花卉识别的人通常会被两类问题卡住一是拿公开数据集训练完一到自己的花棚就失灵二是训练环境、数据清洗、批量推理全靠手动复制粘贴命令跑一轮要盯一晚上屏幕。这个标题给出的解决方案是把 Python 和 Shell 组合成一个完整流水线——Python 负责模型训练、数据增强和推理Shell 负责批量做数据集划分、环境复现、循环训练和日志监控。对算法入门者、智慧农业从业者来说这套源码的意义不是让你“学会调用一个模型”而是让你能用自己的花卉数据完整走通“标注 → 训练 → 验证 → 部署”的每个环节并且每次改动数据集或超参数后都能用几行命令自动重跑不用反复烧香。2. 为什么是这个组合yolov5 做花卉检测的适应性与 PythonShell 的分工2.1 yolov5 凭什么成为花卉识别的“省心选项”花卉识别不是单纯分类它往往需要在一张照片里同时标出多朵花的位置尤其在大棚、野外场景下花被叶子挡住、花与花重叠、阳光把花瓣打白这属于典型的目标检测问题。目标检测框架见过不少Faster R-CNN 精度高但训练慢SSD 快但小目标召回差EfficientDet 调参繁琐。yolov5 是这几个里面“平均分”最稳的它继承了 YOLO 系列的单阶段检测思路检测速度够快同时引入了 PANet 来做多尺度特征融合对花朵这种大小差异极大的目标更友好。花卉场景里最头疼的小目标问题yolov5 用 Mosaic 数据增强把一个 batch 里的多张图拼接再随机裁剪相当于变相提高了小目标出现的频率模型不会只盯着大朵的月季学。还有一个容易被忽略的点yolov5 官方提供了 COCO 预训练权重里面虽然不含花卉类别但模型已经学到了通用的边缘、纹理、颜色特征。迁移学习时只需要把最后一层类别数改成自己的数据类别数量用少量花卉数据微调就能收敛对刚起步的团队和学校实验室非常合适。用 yolo 做检测还有一个额外好处模型会同时输出置信度和边界框。这意味着你不仅能判断“这是什么花”还能知道“花在画面的哪个位置”为后续的统计计数、按长势分级留了空间。如果你的需求只是给图片贴个标签那用分类网络就够了但如果你要数花量、按花朵大小估产yolov5 的检测输出才是真正能用的东西。2.2 Python 与 Shell 的分工不是拍脑袋而是两条线各管各的看这个标题里的“Python 与 Shell”第一反应可能是“语言越少越好”。实际在工程里两者分工非常清晰Python 负责所有需要复杂数据类型和第三方库的逻辑——读图片、解析标注文件、定义网络结构、计算 loss、做推理和画框Shell 负责文件系统层面和流程控制层面的工作——批量创建目录、划分数据集、循环启动多次训练、后台运行并把日志落到磁盘、定时清理中间产物。为什么不用 Python 做所有事因为一套需要反复跑的方案里Shell 做“洗数据”和“跑训练循环”有两个难以替代的优势第一Shell 对文件操作天然高效mv、find、tar这类命令直接与 kernel 交互比 Python 的shutil快且稳健第二Shell 脚本可以完全不依赖 Python 环境启动与人交互时比如运维同事半夜重启服务器只需要执行bash train_all.sh不需要先激活 conda 环境再去记一长串python train.py参数。反过来如果让 Shell 去处理 JSON 标注、调用 OpenCV 做图像变换那才是真正的灾难。我见过的成熟项目通常会把训练入口写成 Python 文件把“多轮训练流水线”封装成 Shell 脚本。Python 文件保证算法逻辑可复用、可单测Shell 脚本保证现场人员能“一把梭”。这套分工下源码目录里不只有train.py、detect.py还会有prepare_data.sh、run_experiments.sh、export_onnx.sh这样的配套脚本这才符合标题里“设计源码”的含义——设计的是整个工作流不是单个模型文件。2.3 “模型设计”到底设计什么源码里常见模块与文件结构别把“模型设计”误解为从零写神经网络。yolov5 本身有 n/s/m/l/x 五种尺度的模型结构实际操作中我们做的“设计”是围绕 yolo 构件做定制——选择哪个 backbone 作为起点、按自己的花卉类别数改检测头、调整 anchor 尺寸和输入分辨率、设置损失函数里的类别权重。这套定制过程并不需要大量原创代码而是要通过改配置文件和写少量 Python 脚本来实现。以一套典型的源码工程为例文件通常分成四块模块语言职责数据准备Python Shell标注格式校验、图片重命名、train/val 划分、生成 yaml 配置训练与验证Python加载数据、计算 loss、更新权重、计算 mAP推理与导出Python单张/批量推理、绘制结果框、导出 ONNX/TensorRT流程编排Shell多组超参实验的循环启动、日志收集、训练中断后自动续跑文件划分的原则是“Python 不涉及系统命令Shell 不涉及复杂计算”。比如统计数据集里每类的标注框数量用 Python 写一个count_labels.py但把 2000 张图片复制到训练集目录用bash copy_images.sh。这样两个语言各自保持在舒适区源码的可维护性远高于“一个.py 文件从头写到尾”的做法。在 3、4 章我会按这套划分把每一步命令和数据格式展开讲直接照着就能在自己的机器上跑通。3. 把“yolov5 训练自己的数据集”跑通的六个步骤从环境到模型导出3.1 安装环境conda 虚拟环境、vscode python 配置与依赖装填在做任何训练之前的第一步是用 conda 建一个隔离的 Python 环境。这一步特别重要yolov5 对 Python 和 PyTorch 版本有耦合关系很多人上来就在系统 Python 里直接pip install -r requirements.txt过两个月项目多了依赖冲突能把人逼疯。我一般会在项目根目录执行conda create -n flower_yolo python3.9 -y conda activate flower_yolo pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt第一行的python3.9是稳妥选择yolov5 官方对 3.8 到 3.11 都兼容但 Python 3.12 刚出来时很多旧版 PyTorch 编译的轮子没有对应版本容易装不上。第二行指定 PyTorch 下载源cu118对应 CUDA 11.8如果你的显卡驱动只支持 CUDA 12这里要换成cu121否则训练时会报 CUDA 版本不匹配。-r requirements.txt会一次性装好 numpy、opencv、matplotlib 这些运行时依赖。装完以后建议用 vscode 打开项目在命令面板里选择这个 conda 环境的解释器。如果不指定vscode 可能默认选了系统 Python结果在终端里明明python train.py能跑vscode 的调试器却导入不了 torch。这也是热词里反复出现“vscode python环境配置”的原因——环境配置不踩平后面全是黑匣子。验证环境是否可用python -c import torch; print(torch.cuda.is_available(), torch.__version__)如果输出True和版本号说明 GPU 可用如果输出False去检查 PyTorch 的 CUDA 版本和驱动不要带着一个 CPU 版 PyTorch 直接去跑后续步骤。3.2 准备花卉数据集从拍照、标注到 YOLO 格式落盘环境就绪后最花时间的来了数据。直接去网上下载公开花卉数据集可以快速验证流程但要部署到真实花圃最终必须用自己的相机采集。这里用公开数据集做演示。数据集的标注格式必须转成 YOLO 的 txt 格式每个 txt 文件对应一张图片txt 的每一行代表一个目标格式为class_id x_center y_center width height四个坐标值都归一化到 0~1 之间x_center/y_center 是框中心的相对坐标width/height 是框的相对宽高。用 labelImg 标注并选择 YOLO 格式导出时生成的目录结构一般是flower_dataset/ ├── images/ │ ├── train/ │ │ ├── rose_001.jpg │ │ └── ... │ └── val/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── rose_001.txt │ │ └── ... │ └── val/ │ └── ... └── flower.yamlflower.yaml是 yolo 训练时读取的数据配置内容如下train: flower_dataset/images/train val: flower_dataset/images/val nc: 5 names: [rose, tulip, orchid, sunflower, daisy]nc是类别总数names的顺序必须和标注文件里的 class_id 一一对应。这个顺序一旦定下来后续训练、推理、部署都不能随意改否则模型输出的类别标签就对不上。很多团队翻车就翻在这里训练时 names 是[rose,tulip]部署推理时 names 改成[tulip,rose]于是所有预测结果都错位了而模型文件本身没有错。标注时有一件事值得注意花的边缘是不规则的标框时不要为了“包住所有花瓣”而把背景框得太大。框越大训练时正样本和负样本的边界就越模糊最后推理时预测框永远比花大一圈导致看起来不太准。3.3 用 Shell 脚本批量划分训练集与验证集数据格式准备好之后需要把图片和标签按比例划分到训练集和验证集。手动去mv几千张图片不现实这个环节 Shell 就是最佳工具。下面这个脚本按 8:2 的比例随机划分#!/bin/bash IMG_DIRflower_dataset/images/all LABEL_DIRflower_dataset/labels/all TRAIN_RATIO0.8 mkdir -p flower_dataset/images/train flower_dataset/images/val mkdir -p flower_dataset/labels/train flower_dataset/labels/val ALL_IMAGES($(ls $IMG_DIR/*.jpg)) TOTAL${#ALL_IMAGES[]} TRAIN_COUNT$(echo $TOTAL * $TRAIN_RATIO | bc | awk {print int($1)}) for img in ${ALL_IMAGES[]}; do filename$(basename $img) stem${filename%.jpg} if [ $(echo $RANDOM % 100) -lt $TRAIN_COUNT ]; then cp $img flower_dataset/images/train/ cp $LABEL_DIR/$stem.txt flower_dataset/labels/train/ else cp $img flower_dataset/images/val/ cp $LABEL_DIR/$stem.txt flower_dataset/labels/val/ fi done echo train: $TRAIN_COUNT, val: $((TOTAL-TRAIN_COUNT))这里用ls 数组读取所有图片${#ALL_IMAGES[]}获取总数bc计算 8 成对应的数量echo $RANDOM % 100生成一个 0 到 99 的随机数做近似抽样。注意这个脚本是“随机抽取到训练集”所以最终训练集数量不会完全恰好等于TRAIN_COUNT误差在 1% 以内对训练影响不大如果追求严格比例可以改用shuf先打乱再按行数切分。实际操作中我会用find替代ls来避免子目录干扰find $IMG_DIR -name *.jpg | sort -R | head -n $TRAIN_COUNT另外两个容易踩的点一是 train 和 val 划分完之后要检查 labels 目录里是否存在没有 txt 的图片或者 txt 文件里某个 class_id 超出 yaml 里的 nc二是图片和标签必须严格同名哪怕扩展名大小写不同yolo 训练时都可能会报错。我习惯在划分后写一个小 Python 脚本做交叉校验import os img_dir flower_dataset/images/train label_dir flower_dataset/labels/train for img in os.listdir(img_dir): stem os.path.splitext(img)[0] if not os.path.exists(os.path.join(label_dir, stem .txt)): print(f[WARN] {img} missing label) for label in os.listdir(label_dir): iris_num len(open(os.path.join(label_dir, label)).readlines()) print(label, iris_num)这段代码会输出每个标注框数量同时把缺失的标签问题暴露出来。数据是模型的上限这个环节多花的时间后面都能省回来。3.4 修改模型配置与训练超参yolov5s.yaml 与 flower.yaml 的正确改法环境、数据都就绪后进入训练环节。训练前需要改两个配置文件数据配置和模型配置。数据配置flower.yaml上面已经写好模型配置用官方自带的yolov5s.yaml作为模板只需要改一个参数nc: 5 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] - [30,61, 62,45, 59,119] - [116,90, 156,198, 373,326]第四行的nc从 80 改为 5。depth_multiple和width_multiple是决定模型规模的关键参数0.33和0.50对应 yolov5s想换成 yolov5m 就改成0.67和0.75。anchors是三组先验框尺寸默认值是从 COCO 数据集聚类得到的。如果你发现自己的花卉标注框普遍偏小比如大量小雏菊只占图片的 1%可以考虑用python utils/autoanchor.py --cfg yolov5s.yaml --data flower.yaml重新聚类 anchor不够先别手动乱改autoanchor 会在训练前自动检查并给出建议。训练命令python train.py --data flower.yaml --cfg yolov5s.yaml --weights yolov5s.pt \ --epochs 100 --batch-size 16 --imgsz 640 \ --hyp hyp.scratch-low.yaml --name flower_experiment -m参数含义--data指向数据集 yaml--cfg是模型结构文件--weights指定预训练权重第一次用yolov5s.pt后续微调可以用上次训练的best.pt--epochs是训练轮数花卉数据量不大100 轮足够了数据量大再加--batch-size根据显存调整16G 显存用 16 稳妥显存小就减半--imgsz输入尺寸默认 640如果你的数据集里花很小可以试着提高到 768 甚至 1024但训练时间和显存占用会明显上升--hyp是超参文件默认用hyp.scratch-low.yaml就能跑一个稳定基线。-m表示多 GPU 训练如果只有一张卡就去掉。训练过程中重点观察终端输出的P、R、mAP50、mAP50-95这几个指标P是精确率预测出的框里真正有花的比例R是召回率所有花里被找出来的比例mAP50是在 IOU 阈值为 0.5 时的平均精度是判断模型“能不能用”的主要参考mAP50-95更严格它把 IOU 阈值从 0.5 到 0.95 之间都计算了一遍再取平均在竞赛里更受关注但实际部署场景里通常以mAP50为准。3.5 验证模型效果并导出成可部署的格式训练结束后runs/train/flower_experiment/weights/里会出现best.pt和last.pt。best.pt是验证集 mAP 最高的权重后续做推理和部署都用它。先用验证集看看它在真实场景下的表现python val.py --data flower.yaml --weights runs/train/flower_experiment/weights/best.pt --task val python detect.py --source flower_dataset/images/val --weights runs/train/flower_experiment/weights/best.pt --conf 0.5detect.py会遍历 val 目录下所有图片把框画到图片上并保存到runs/detect/exp/。这一步不要只在命令行看 mAP一定要人工随机抽查几十张推理结果图特别是一张图里有多朵花、有遮挡、有逆光的样本。mAP 是池化后的统计量会掩盖个别类别的崩坏肉眼抽查能最快发现“为什么月季都识别成了牡丹”这类问题。部署到生产环境时不建议直接扛一个.pt文件走天下因为 PyTorch 运行时太重。常见做法是导出 ONNX 格式python export.py --weights runs/train/flower_experiment/weights/best.pt --img 640 --batch 1 --include onnx导出 ONNX 有两个参数要注意--img必须和训练时的imgsz保持一致否则推理时会因为输入尺寸不一致而自动 resize精度会有小幅损失--batch 1表示导出为动态 batch 还是静态 batch服务端推理建议保持batch 1这样 ONNX Runtime 能做一些层融合优化。导出成功后best.onnx会出现在同目录下这个文件就是后续部署的“标准件”。3.6 用 Shell 做多轮训练自动化和日志管理训练一次成功不稀奇真正让这套源码有价值的是可以自动化跑多组实验。比如你有一台服务器想比较 yolov5s 和 yolov5m 在相同数据集上的表现或者想试试不同的imgsz手写命令重复敲既慢又容易出错。我一般会写一个run_experiments.sh#!/bin/bash set -e for model in yolov5s yolov5m; do for imgsz in 640 768; do EXP_NAME${model}_${imgsz} echo [$(date %Y-%m-%d %H:%M:%S)] start $EXP_NAME python train.py \ --data flower.yaml \ --cfg ${model}.yaml \ --weights ${model}.pt \ --epochs 100 \ --batch-size 16 \ --imgsz ${imgsz} \ --name $EXP_NAME \ log_${EXP_NAME}.txt 21 echo [$(date %Y-%m-%d %H:%M:%S)] finish $EXP_NAME, mAP: $(grep mAP50 log_${EXP_NAME}.txt | tail -n 1) done doneset -e让脚本在任何一条命令失败时立即退出避免后面的实验基于坏环境继续跑。 log_*.txt 21把标准输出和错误流都重定向到日志文件这样即使你从 ssh 断开训练也会继续跑第二天回来tail -f log_*.txt就能看到进度。假如想让训练完全后台运行可以再套一层nohup bash run_experiments.sh 。这个循环脚本的另一个好处是可以把所有实验日志统一收集起来最后用 grep 提取 mAP 指标做对比。没有这个自动化四组实验就要盯在电脑前手动提交四次那份“等待”在这个过程中非常折磨人。4. 部署到你的工作环境从本地推理到嵌入式设备的差异4.1 三种部署方式PyTorch 直推理、ONNX Runtime 与 TensorRT/OpenVINO训练好的模型最终要进入实际环境部署方式的选择直接影响推理速度和硬件成本。先看一个对比表部署方式推理速度安装复杂度硬件要求适合场景PyTorch 直接推理慢低和训练环境一致CPU 也能跑GPU 更好实验验证、本地演示ONNX Runtime中低pip install onnxruntimeCPU/GPU 都支持GPU 版要开 CUDA EP大多数服务端部署、树莓派等边缘设备TensorRT快高依赖 NVIDIA GPU 和 CUDA仅 NVIDIA GPU量产设备、高并发服务OpenVINO较快中依赖 Intel CPU/GPUIntel 平台Intel 平台边缘盒子、摄像头场景如果你的服务器是 NVIDIA 显卡优先考虑 ONNX Runtime CUDA Execution Provider安装不复杂速度比 PyTorch 直推理快两到三倍要想极致性能再用 TensorRT 做 int8 量化但模型导出和 calibration 过程非常繁琐适合做产品化之后再投入。英特尔平台的工控机或树莓派上OpenVINO 往往比 ONNX Runtime 更好因为它针对 Intel CPU 的核显做了指令集优化。4.2 用 Shell 脚本封装批量推理喂目录、出 CSV一行命令跑完部署不只是把模型跑起来还要把“推理能力”接入业务流程。比如你有一台连着高清摄像头的电脑每隔半小时拍一批花卉大棚照片需要自动识别出每种花的数量和位置并定期更新统计表。这时一个可靠的 Shell 封装就很有必要。#!/bin/bash set -e DATA_DIR/data/flower_cam/2024-06-01 OUT_DIRinference_results mkdir -p $OUT_DIR for img in $DATA_DIR/*.jpg; do python infer.py --weights model_export/best.onnx \ --source $img \ --output $OUT_DIR/$(basename $img .jpg).json done配合的infer.py用 ONNX Runtime 加载模型import onnxruntime as ort import cv2 import sys import json # 解析命令行参数 args sys.argv weights args[args.index(--weights) 1] source args[args.index(--source) 1] output args[args.index(--output) 1] sess ort.InferenceSession(weights, providers[CPUExecutionProvider]) image cv2.imread(source) # 预处理resize 到 640归一化转成 NCHW # 推理并解析 output0得到框坐标、置信度、类别 # 写入 JSON 文件 print(fsaved to {output})在for循环里每张图片都走一遍python infer.py看起来有点慢但好处是每一张图独立运行单张图挂了不会拖垮整个批次而且可以用改成并行for img in $DATA_DIR/*.jpg; do python infer.py --weights model_export/best.onnx \ --source $img \ --output $OUT_DIR/$(basename $img .jpg).json done waitwait会等所有后台推理任务结束再退出脚本。并行数不要超过 CPU 核数否则上下文切换开销反而拖慢整体速度一般并行 4 到 8 个进程最合适。4.3 树莓派与边缘设备部署时要注意的“尺寸陷阱”树莓派上跑训练好的 yolov5 是很多人的下一步但会踩到一个隐形坑树莓派的 CPU 性能远不如你的训练服务器同样一张 640×640 的图服务器上 20 毫秒推理完树莓派可能要用 1.5 秒。如果做实时视频检测1.5 秒的延迟意味着摄像头画面里的花已经移动了位置框会明显拖后腿。解决思路有三个方向一是把输入尺寸从 640 降到 416 或 320推理耗时大致按输入面积的平方下降但小目标的检测精度会明显下降花卉大棚里种的小雏菊可能会丢失二是用 int8 量化把 ONNX 模型量化成 int8 后体积缩小到原来的四分之一树莓派上的推理速度能提升 3 到 5 倍但需要拿验证集做校准否则精度掉得让人怀疑人生三是换检测头结构轻量化的 YOLOv5s 本身已经是折中产物再往小剪裁就有点“读懂暗号却没听懂意思”的味道了。我的建议是树莓派部署前先做一轮“设备能力摸底”。跑一下onnxruntime的 benchmark看看 416×416 的 int8 模型在你目标设备上的实际 FPS往返耗时能否满足业务需求。如果差得不多调大--conf阈值过滤掉低置信度框也能让视觉观感好很多如果差很多就不要硬扛边缘设备只做“抓拍上传”把推理放回服务器这是更务实的方案。5. yolov5 花卉识别避坑指南环境、数据集、训练三条线5.1 安装 PyTorch 时 CUDA 版本不匹配训练直接 “Illegal instruction (core dumped)”现象pip install torch2.0.1 -f https://download.pytorch.org/whl/cu118装完后执行python train.py刚加载权重就报Illegal instruction (core dumped)进程退出没有任何 Python 报错堆栈。原因CPU 不支持 PyTorch 编译时使用的指令集。旧一点的服务器 CPU 没有 AVX512而高版本 PyTorch 默认编译启用 AVX512直接执行就非法指令了。这不是代码的错是硬件和 wheel 包不匹配。解决尝试安装 CPU 版本或者低版本 PyTorch比如pip install torch1.13.1cpu torchvision0.14.1cpu -f https://download.pytorch.org/whl/cpu如果必须用 CUDA 版本换一个老一点的 PyTorch 2.0.1 的 cu118 轮子或者到 PyTorch 官方网站在线选择命令生成时留意 “Your CPU supports AVX” 的提示。装完后用第二章开头那段python -c import torch; print(torch.__version__)检查别直接跑训练。5.2 标注文件类别索引越界训练到一半 loss 变成 nanbest.pt 直接报废现象训练前 20 个 epoch 一切正常第 30 个 epoch 左右 loss 突然打印nanval 上的 mAP 变成 0model 权重文件越来越大但完全不能用。原因数据集里有一张或多个标注框的 class_id 大于等于nc。yolo 的 loss 计算里类别索引越界会导致矩阵访问到不存在的 row梯度回传时出现 nan。公开数据集里尤其常见手工标注的人偶尔也会把class_id5写进一个nc5的项目里。解决训练前用脚本扫描所有标注文件的类别索引把超出范围的 txt 挑出来重新标注或删掉一行。我在第三章给出的那个for label in ...小脚本基础上把int(line.split()[0])提取出来再和nc做比较即可。更稳妥的做法是在flower.yaml里把nc多设一个略大的值比如实际 5 类就设 6让索引越界不至于 nan但它会掩盖数据问题治标不治本最终还是要清理数据。5.3 数据集中某类花样本过少mAP 整体好看但那一类 mAP0现象训练 log 里 mAP50 显示 0.9看起来成绩不错但按类别单独算发现玫瑰的 AP 是 0.95雏菊的 AP 是 0.02几乎完全失效。原因花卉识别里不同花类别的样本量天然不均衡比如雏菊只在某一个批次里标注了几百张而被玫瑰和郁金香霸屏。yolo 的 loss 对每个类别平等计算样本少的类别更新梯度占比低模型只学到了大类的特征。解决第一优先做数据增强对样本少的类别做随机裁剪、旋转、翻转把它扩充到与大类别接近的水平第二在训练时调整超参文件里的cls损失权重把少样本类别的分类损失放大但这样会让总 loss 更难收敛需要额外调参最直接的是按类别检查验证输出我习惯会在val.py结束后单独跑一个几十行的脚本统计每一类的 AP低于 0.5 的类别绝不放过。5.4 Windows 下写的 Shell 脚本拿到 Linux 上执行报 “No such file or directory” 但文件明明存在现象在 Windows 上用 Notepad 或 vscode 写好prepare_data.sh通过 scp 传到 Linux 服务器后bash prepare_data.sh报错No such file or directory而ls -l明明看得到这个文件。原因Windows 保存的文本文件行尾是\r\nLinux 是\nShell 解释器读第一行时遇到\r把整个路径弄成一个不存在的文件就会产生这个误导性报错。解决在 Linux 上执行sed -i s/\r$// prepare_data.sh或者运行dos2unix prepare_data.sh转换行尾。更根本的预防方法是在 vscode 右下角把文件的行尾序列从 CRLF 改成 LF再写新的脚本。这个错误在团队协作里特别常见几乎每周都会碰到一次记在心里能省很多时间。5.5 训练到 80 个 epoch 服务器重启前 79 个 epoch 白费现象训练到了第 80 个 epoch显示器上显示 mAP50 已经到 0.93机房通知要断电等电来了重启后终端早断了模型只进行到 52 个 epoch而且没有保存 52 之后的任何权重。原因启动训练时没有设置快速备份。yolov5 的train.py每隔一定 epoch 会把last.pt写到runs/train/exp/weights/last.pt但它是一个非常新的权重而非 best.pt。断电后last.pt没有同步到磁盘数据丢了。解决训练命令里主动加--resume参数它会让 yolov5 已记录训练状态包括当前 epoch、优化器状态、学习率调度在断点处恢复训练。另外我自己会定时用rsync把整个runs/train目录同步到另一块本地磁盘while true; do rsync -av --delete runs/train/ backup_runs/; sleep 300; done每 5 分钟同步一次最多丢 5 分钟的训练进度比“没有后悔药”强太多。6. 让这套源码真正好用超参数微调、数据清洗与验证闭环训练跑通只是起点能否长期投入取决于你建立“模型在真实数据上越用越好”的闭环。我觉得最实用的一个技巧是每次训练结束不要急着部署先看一眼runs/train/*/confusion_matrix.png。这张图会告诉你模型最容易把哪两类花搞混。杜鹃花被错认为山茶往往是因为两张图都是红色花瓣、深色背景模型并没有真正学会区分的纹理特征。拿到混淆矩阵后我会把最容易混淆的那个类别的误检图单独挑出来用 Shell 命令批量复制到另一个文件夹grep -E 杜鹃|山茶 val_pred.csv | cut -d, -f1 | sort -u hard_samples.txt while read img; do cp $img hard_examples/ done hard_samples.txt然后人眼刷一遍把里面确实标错或标漏的图重新标注再补进训练集重新训练一次。这个“清洗一轮 → 训练 → 看混淆矩阵 → 再清洗”的循环是提高模型上限最有效的手段比盲目调超参数见效快得多。超参数微调方面不要一上来就动lr和weight_decay先调--imgsz和 anchor。如果你的花棚相机离花远小目标多把imgsz从 640 提到 768 往往比改任何损失权重都直接。还有一点我吃过亏用--weights yolov5x.pt做迁移学习时如果数据显示只有几千张模型容易过拟合表现反而不如yolov5s微调。先小模型跑通闭环再考虑大模型。最后说一个我自己的习惯每训一版模型就把训练命令、超参数、数据集的标注版本号记在README.md里。两周后你再想复现当时的 mAP光有一个 best.pt 是不够的因为你忘了当时用的是哪一版标注数据。这套基于 Python 与 Shell 的源码真正值得投入的不是模型结构有多新颖而是它把数据、训练、部署、迭代串成了一条可以反复走的路。照着这条路新手能跑通熟手能扩展希望帮到你。本文还有配套的精品资源点击获取