简介这份资源面向计算机视觉初学者与目标检测方向的开发者围绕YOLOv5与YOLOv11相关实现展开可用于自动驾驶、视频监控等实时检测场景的学习与实验。压缩包共143个文件约1.71MB以53个Python脚本、49个YAML与11个YML配置为主另含Shell脚本、Markdown文档、Jupyter笔记本及多套Dockerfile覆盖模型训练、环境配置与容器化部署等环节。其中train.py承担数据加载、模型配置与训练循环tutorial.ipynb提供交互式教学示例README中文文档与贡献指南则帮助读者快速上手并参与开源协作。资源已有1388人学习下载适合希望系统理解YOLO系列工程结构、复现训练流程并掌握配置管理的中级读者参考。1. 从 yolov5 到 yolov11一条产线模型升级的务实路线如果你手头有一个跑了大半年的 yolov5 检测服务最近又被要求“看看能不能换到 yolov11”那你不是一个人。我见过太多团队卡在这个节点上老模型稳定、调参经验足、部署脚本都磨平了新模型精度看着诱人但一想到重训、重导、重测、重部署头皮就发麻。yolov5-yolov11 这条升级链路本质上不是“换个权重文件”那么简单它牵扯到数据格式兼容、训练策略差异、导出算子支持、推理后处理对齐这一整条流水线。这篇文章面向的是已经跑通过至少一个 YOLO 版本、手里有标注数据、需要把模型从 v5 迁到 v11 的工程师。我会按“先搞清楚差异在哪、再动手跑通最小闭环、最后把坑一个个填上”的顺序讲不堆概念只讲能复现的步骤和参数。你跟着走完至少能拿到一个可对比的 baseline知道值不值得继续投入。2. 先搞清楚 v5 和 v11 到底差在哪别急着换权重2.1 架构演进不是线性替换而是训练策略和标签分配的代际差很多人以为 v5 到 v11 就是 backbone 换深了、neck 加宽了实际动手才发现真正影响你迁移成本的是标签分配策略和损失函数的变化。v5 用的是基于宽高比的 anchor 匹配加上跨网格的正样本扩充v8 之后逐步转向无 anchor 的 TAL 动态分配到 v11 这套机制已经比较成熟。这意味着什么你原来在 v5 上调好的 anchor 尺寸、正样本阈值、甚至数据增强里的 mosaic 概率直接搬到 v11 上很可能不 work。我一般会先把 v5 的训练配置和 v11 的默认配置拉出来做一张对照表重点看三个东西是否使用 anchor、正样本分配方式、分类损失用的是 BCE 还是 VFL。这三项决定了你后面改配置的方向。另一个容易被忽略的是输入分辨率与 stride 的配合。v5 时代大家习惯 640×640 配 stride 32v11 在同样输入下对小目标的响应会略有不同因为它的 neck 融合路径变了。如果你做的是小目标检测比如 PCB 缺陷或者遥感图像直接换权重不调输入尺寸mAP 掉几个点很正常。我的做法是先用同一份验证集把 v5 和 v11 的官方预训练权重都跑一遍推理只看不训对比每类 AP 的变化找出哪些类别对架构变化敏感再决定要不要针对性补数据。2.2 用一份最小脚本量化差异而不是凭感觉在动手迁移之前我强烈建议先写一个对比脚本把两个模型在同一批图片上的输出拉齐。下面这段代码做三件事加载 v5 和 v11 的模型、对同一目录图片推理、按类别统计检测框数量和置信度分布。你只需要把模型路径和图片目录换成自己的。import cv2 import numpy as np from pathlib import Path from ultralytics import YOLO # v11 用这个接口v5 可以用 torch.hub 或本地加载 def run_inference(model_path, img_dir, conf_thres0.25, iou_thres0.45): model YOLO(model_path) stats {} for img_path in Path(img_dir).glob(*.jpg): img cv2.imread(str(img_path)) results model.predict(img, confconf_thres, iouiou_thres, verboseFalse) for r in results: boxes r.boxes if boxes is None: continue for cls_id, conf in zip(boxes.cls.cpu().numpy(), boxes.conf.cpu().numpy()): cls_id int(cls_id) stats.setdefault(cls_id, []).append(float(conf)) return stats # 分别跑 v5 和 v11对比每类的平均置信度和检测数量 v5_stats run_inference(yolov5s.pt, val_images) v11_stats run_inference(yolo11s.pt, val_images) for cls_id in sorted(set(v5_stats) | set(v11_stats)): v5_confs v5_stats.get(cls_id, []) v11_confs v11_stats.get(cls_id, []) print(fclass {cls_id}: v5 count{len(v5_confs)}, v11 count{len(v11_confs)}, fv5 mean_conf{np.mean(v5_confs):.3f}, v11 mean_conf{np.mean(v11_confs):.3f})这段脚本的关键参数是conf_thres和iou_thres我一般先用 0.25 和 0.45 跑一轮如果发现某一类 v11 的检测数量明显少于 v5就把 conf 降到 0.1 再看判断是漏检还是置信度整体偏低。逻辑说明v5 和 v11 的默认后处理阈值可能不同直接对比原始输出会失真所以统一用同一组阈值推理。参数说明model.predict里的conf控制输出框的最低置信度iou控制 NMS 的合并阈值这两个值在迁移对比阶段不要频繁改固定住才能看出模型本身的差异。2.3 数据格式兼容性检查你的标注能不能直接喂给 v11v5 时代很多人用的是 YOLO txt 格式每行class x_center y_center width height归一化到 0-1。v11 的训练接口默认也吃这个格式但有一个隐藏差异v5 对越界坐标的容忍度较高v11 在数据加载阶段会做更严格的校验如果你的标注里有负数或者大于 1 的值v5 可能默默截断v11 直接报错或者跳过。我踩过一次坑一批标注里有个别框的 width 写成了 1.02v5 训练正常换 v11 后那个类别的 AP 直接掉了一半排查半天才发现是数据加载时被过滤了。检查方法很简单跑一段脚本扫一遍所有标签文件import os from pathlib import Path def check_labels(label_dir): issues [] for txt_file in Path(label_dir).glob(*.txt): with open(txt_file) as f: for line_num, line in enumerate(f, 1): parts line.strip().split() if len(parts) ! 5: issues.append(f{txt_file}:{line_num} 字段数不对: {len(parts)}) continue cls, x, y, w, h map(float, parts) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): issues.append(f{txt_file}:{line_num} 坐标越界: {x},{y},{w},{h}) return issues problems check_labels(labels/train) for p in problems[:20]: print(p) print(f共发现 {len(problems)} 个问题)逻辑说明这段脚本只做基础校验不修复数据目的是在训练前把明显有问题的标注找出来。参数说明判断条件里w和h必须大于 0因为宽高为 0 的框在 v11 里会被直接丢弃x和y允许等于 0 或 1但超出这个范围就要处理。如果你的数据集里越界标注很多建议先写一个修复脚本做截断而不是直接删掉否则某些类别样本会不够。3. 跑通最小迁移闭环从 v5 权重到 v11 训练3.1 环境准备与版本选择别在依赖上翻车v5 和 v11 对 PyTorch 和 CUDA 的要求不一样。v5 的老代码在 torch 1.7 上都能跑v11 一般需要 torch 1.13 以上如果你用的是 50 系显卡还得上更新的版本。我一般会新建一个 conda 环境不跟老环境混用避免把线上服务的依赖搞崩。下面是我常用的环境搭建命令你可以根据自己显卡驱动微调。conda create -n yolo11 python3.10 -y conda activate yolo11 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python numpy逻辑说明先建独立环境再装 torch最后装 ultralytics。参数说明cu118对应 CUDA 11.8如果你的驱动只支持到 11.7就换成cu117ultralytics这个包同时支持 v8 和 v11 系列但注意它不包含 v5 的原始实现v5 模型需要用 torch.hub 或者本地代码加载。如果你要在同一个脚本里对比 v5 和 v11建议 v5 用torch.hub.load加载v11 用YOLO类加载两者输出格式略有不同后处理要分开写。3.2 用 v5 的权重做初始化能省一半训练时间如果你只是想把现有 v5 模型升级到 v11 架构而不是从零训一个新模型最省力的做法是用 v11 的预训练权重做 backbone 初始化然后把 v5 的检测头权重迁移过去。但这里有个现实问题v5 和 v11 的层名和结构不完全对应直接 load_state_dict 会报一堆 missing keys。我一般用两种策略一是只迁移 backbone 的前几层让 v11 自己训 neck 和 head二是如果数据量够大干脆不用 v5 权重直接用 v11 官方预训练模型微调。下面这段代码演示第一种策略只加载匹配的层。import torch from ultralytics import YOLO # 加载 v11 模型结构 model YOLO(yolo11s.yaml) # 只建结构不加载权重 # 加载 v5 权重 v5_weights torch.load(yolov5s.pt, map_locationcpu) v5_state v5_weights[model].state_dict() if model in v5_weights else v5_weights # 只迁移名称和形状都匹配的层 model_state model.model.state_dict() matched {} for k, v in v5_state.items(): if k in model_state and model_state[k].shape v.shape: matched[k] v model.model.load_state_dict(matched, strictFalse) print(f迁移了 {len(matched)} 层总层数 {len(model_state)})逻辑说明先建 v11 结构再逐层比对 v5 权重只加载名称和形状都一致的层。参数说明strictFalse允许部分加载不匹配的层保持随机初始化map_locationcpu避免 GPU 内存不足。注意这段代码只适用于 v5 和 v11 层名有重叠的情况实际跑的时候你会发现匹配上的层可能只有 backbone 的前几十层这已经够用了neck 和 head 让 v11 自己学。3.3 训练配置的关键参数学习率和 warmup 怎么设v11 的默认训练配置和 v5 差别不小最明显的是学习率调度和 warmup 轮数。v5 时代大家习惯用 0.01 的初始学习率配 cosine 衰减v11 在同样数据集上可能更适合 0.001 到 0.005 的初始学习率因为它的正样本分配更激进学习率太大会导致早期震荡。我一般会先用官方默认配置跑 10 个 epoch看 loss 曲线如果分类 loss 下降很慢就把学习率调大一点如果 box loss 震荡厉害就调小。下面是一个我常用的训练命令基于 v11 的 CLI 接口yolo detect train \ datacoco128.yaml \ modelyolo11s.yaml \ pretrainedyolo11s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.005 \ lrf0.01 \ warmup_epochs3 \ warmup_momentum0.8 \ cos_lrTrue \ close_mosaic10 \ device0逻辑说明data指定数据集配置model指定结构pretrained加载预训练权重。参数说明lr0是初始学习率lrf是最终学习率比例warmup_epochs是预热轮数close_mosaic表示最后 10 个 epoch 关闭 mosaic 增强让模型在真实分布上收敛。如果你的数据集小于 1000 张把epochs降到 50 左右warmup_epochs降到 1否则容易过拟合。batch根据显存调8G 显存跑yolo11s用 16 差不多跑yolo11m就得降到 8。3.4 验证与导出确认迁移后的模型能落地训练完不是终点你得确认导出的模型在推理端和 v5 的行为一致。v11 支持导出 ONNX、TensorRT、OpenVINO 等格式但不同格式对算子的支持不一样。我一般先导出 ONNX用 onnxruntime 跑一遍对比 PyTorch 输出的差异如果差异在 1e-3 以内再导 TensorRT。下面这段代码演示导出 ONNX 并做一致性检查。from ultralytics import YOLO import onnxruntime as ort import numpy as np # 导出 ONNX model YOLO(runs/detect/train/weights/best.pt) model.export(formatonnx, imgsz640, simplifyTrue) # 用 onnxruntime 推理 session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name dummy np.random.randn(1, 3, 640, 640).astype(np.float32) onnx_out session.run(None, {input_name: dummy}) # 对比 PyTorch 输出 import torch pt_out model.model(torch.from_numpy(dummy)) print(fONNX 输出形状: {[o.shape for o in onnx_out]}) print(fPyTorch 输出形状: {[o.shape for o in pt_out]})逻辑说明导出时开simplifyTrue会做算子融合减少 ONNX 节点数。参数说明imgsz必须和训练时一致否则输出框坐标会错位对比形状只是第一步实际部署前还要用真实图片跑一遍看 NMS 后的框是否对齐。如果 ONNX 和 PyTorch 输出差异大先检查导出时的 opset 版本v11 一般用 opset 12 以上太低会丢算子。4. 避坑与排查迁移路上最容易翻车的五个点4.1 现象训练 loss 正常下降但验证集 mAP 始终为 0原因v11 的数据加载器对类别索引有要求如果你的标注里类别从 1 开始而不是 0v5 可能容忍v11 会直接忽略这些框。解决检查data.yaml里的names列表长度和标注里的最大类别 ID 是否匹配标注类别必须从 0 开始连续。4.2 现象导出 ONNX 后推理结果全乱框位置偏移原因v11 的导出默认包含后处理如果你在推理端又做了一次 NMS相当于重复处理。解决导出时加nmsFalse让 ONNX 只输出原始预测后处理在推理端统一做或者导出时保留 NMS推理端直接拿结果。4.3 现象v5 上 mAP 0.85 的模型换 v11 后掉到 0.6原因大概率是输入归一化方式不同。v5 默认用 0-1 归一化v11 在某些导出模式下要求 0-255 输入。解决检查推理前的预处理确保和训练时一致用同一张图分别跑 v5 和 v11 的 PyTorch 推理对比预处理后的 tensor 数值范围。4.4 现象训练到一半报显存不足batch 已经降到 4原因v11 的默认 mosaic 增强会拼接 4 张图显存占用是单图的 4 倍。解决把mosaic概率降到 0.5 或者关掉用close_mosaic提前关闭或者改用yolo11n这种小模型先跑通流程。4.5 现象TensorRT 推理速度比 PyTorch 还慢原因导出 TensorRT 时用了 FP32没有开 FP16 或 INT8。解决导出时加halfTrue如果精度允许再用 INT8 量化校准另外检查 TensorRT 的 workspace 大小默认可能太小导致反复编译。5. 进阶技巧用 v5 的验证集反向筛选 v11 的难例迁移做完之后我一般不会直接全量上线而是用 v5 的验证集跑一遍 v11把 v11 漏检而 v5 检到的图片挑出来这些就是 v11 在当前数据分布下的难例。具体做法是用 v5 和 v11 分别推理验证集按图片名和类别做匹配找出 v11 置信度低于 0.1 而 v5 高于 0.5 的样本。这些样本通常集中在某几个类别或者某种光照条件下把它们单独拿出来做一轮针对性微调比盲目加数据有效得多。下面这段代码演示如何筛选难例import cv2 import numpy as np from pathlib import Path from ultralytics import YOLO v5_model YOLO(yolov5s.pt) # 假设已转换为 ultralytics 兼容格式 v11_model YOLO(best.pt) def get_detections(model, img_path, conf0.1): img cv2.imread(str(img_path)) results model.predict(img, confconf, verboseFalse) dets {} for r in results: boxes r.boxes if boxes is None: continue for cls_id, conf_val in zip(boxes.cls.cpu().numpy(), boxes.conf.cpu().numpy()): dets.setdefault(int(cls_id), []).append(float(conf_val)) return dets hard_examples [] for img_path in Path(val_images).glob(*.jpg): v5_dets get_detections(v5_model, img_path) v11_dets get_detections(v11_model, img_path) for cls_id, v5_confs in v5_dets.items(): v11_confs v11_dets.get(cls_id, []) if max(v5_confs) 0.5 and (not v11_confs or max(v11_confs) 0.1): hard_examples.append((str(img_path), cls_id)) break print(f找到 {len(hard_examples)} 张难例) for path, cls in hard_examples[:10]: print(f{path} 类别 {cls})逻辑说明分别用两个模型推理按类别对比最大置信度找出 v5 高置信而 v11 低置信的样本。参数说明conf0.1是为了拿到更多候选框避免因为阈值过滤掉真实难例判断条件里的 0.5 和 0.1 可以根据你的业务容忍度调整。拿到难例列表后我一般会人工看一遍确认不是标注错误然后把这些图片复制到一个单独目录在下一轮训练时给它们更高的采样权重或者直接过采样。另一个实用技巧是冻结 backbone 做小学习率微调。如果你发现 v11 在某个类别上始终不如 v5可以把 v11 的 backbone 冻结只训 head学习率设成 0.0001跑 20 个 epoch通常能把这个类别的 AP 拉回来。这个操作在 ultralytics 里可以通过freeze参数实现比如freeze10表示冻结前 10 层。我自己的习惯是迁移初期不冻结让模型充分适应新架构如果某个类别掉点严重再冻结 backbone 做针对性修复。这套流程走下来v5 到 v11 的迁移基本能控制在两周内完成精度不降甚至略有提升。希望帮到你。本文还有配套的精品资源点击获取