首页/新闻资讯/正文详情

从环境对齐到mAP复核:YOLOv5目标检测全链路实战

发布时间:2026/11/3 19:38:26 来源:云帆数科 栏目:资讯中心
从环境对齐到mAP复核:YOLOv5目标检测全链路实战
简介这份文档面向具备一定Python与计算机视觉基础的入门级研究人员和工程技术人员系统讲解YOLOv5目标检测框架在本地环境的搭建与首次运行既可作为理论教学材料也能为实际工程项目提供参考。内容从操作系统、Python版本及GPU加速所需的CUDA、cuDNN版本要求讲起依次覆盖依赖库安装、虚拟环境创建、官方源码获取、预训练模型下载与示例检测脚本运行并对weights、source、img、conf、iou等参数含义作出说明最后简要引介基于自定义数据集训练模型的进阶方向。资源包为单个docx文档大小约19KB便于离线查阅与随查随用。已有239人学习适合初次安装并使用YOLOv5、希望掌握目标检测实操流程的读者按步骤对照练习并在此基础上尝试调整实验条件、排错与定制化训练。1. 环境没对齐时YOLOv5 的出框结果能离谱到什么程度有人把仓库 clone 下来依赖装完python detect.py一跑终端不报错输出图里同一辆车被框成了三个重叠的框置信度还都写着 0.9。这不是模型坏了是 torch 落到了 CPU 版、NMS 的阈值又开得太松两个问题叠在一起。YOLOv5 这套东西的门槛从来不在代码本身而在「Python 版本—PyTorch 轮子—CUDA 运行时—cuDNN」这条链子上任何一环错位目标检测的结果就会从「差一点」直接掉到「没法看」。下面按真实落地顺序拆先把版本对齐再把权重放对位置然后才是detect.py的几个参数怎么调、自定义数据集怎么训、翻车了看哪里。刚把 YOLOv5 拉下来准备跑第一张图的人能照着走已经能跑但结果不稳、想搞清楚边界的人也能对上号。2. 版本对齐从驱动倒推 CUDA再定 torch 与 Python环境这一步最容易出的错是顺序反了。多数人先想「我要装哪个 PyTorch 版本」再去凑 CUDA最后发现驱动根本带不动。合格的顺序是反过来的先看显卡驱动能支持到哪一档 CUDA 运行时再选对应的 torch 轮子最后才定 Python 小版本。YOLOv5 对 Python 的要求是 3.7 以上实际用 3.8 最稳因为 3.9 之后部分依赖的轮子在小众平台上会缺。2.1 从 nvidia-smi 倒推驱动版本才是天花板打开终端先跑一条命令别急着装任何东西。# 查看显卡型号、驱动版本、驱动支持的最高 CUDA 运行时版本 nvidia-smi # 只要关键字段方便脚本里判断 nvidia-smi --query-gpuname,driver_version --formatcsv,noheader输出右上角那行CUDA Version: 11.7经常被误读成「本机装了 CUDA 11.7」。它不是。它表示这版驱动最多能支撑到 CUDA 11.7 的运行时本机可能一个 CUDA Toolkit 都没装。这一点必须先拧过来否则后面所有版本判断都是错的。真正决定装哪个 torch 的是这张对照关系驱动支持的 CUDA 上限可选的 torch 轮子后缀常见搭配11.3 及以上cu113torch 1.10 ~ 1.1211.7 及以上cu117torch 1.13 ~ 2.011.8 及以上cu118torch 2.0 ~ 2.212.1 及以上cu121torch 2.1 以上选后缀的原则只有一条轮子的 cuXXX 不能高于驱动上限。高一点点也不行表现是torch.cuda.is_available()直接返回 False或者能返回 True 但第一次 kernel 启动就抛no kernel image is available for execution on the device。这条报错是血泪经验里出现频率最高的一条看到它基本可以直接判定版本错配不用去查模型代码。还有一个常见误解装 torch 不需要单独装 CUDA Toolkit也不需要单独装 cuDNN。官方发布的 CUDA 版 wheel 已经把对应的 CUDA 运行时和 cuDNN 动态库打包进去了pip install完就能用。单独装一套 Toolkit 反而可能因为LD_LIBRARY_PATH指向了另一套版本导致运行时找不到库。除非要自己编译自定义算子否则别碰 Toolkit。2.2 conda 建环境与安装次序先 torch后 requirements环境隔离用 conda 最省事主要图它能干净地删掉重来。# 建一个独立环境Python 小版本固定别用 latest conda create -n yolov5_env python3.8 -y conda activate yolov5_env # 先装带 CUDA 的 torch-f 指定官方轮子索引避免 pip 从默认源拉到 CPU 版 pip install torch1.12.1cu113 torchvision0.13.1cu113 \ -f https://download.pytorch.org/whl/torch_stable.html # 确认装进来的确实是 cu113 版本不是被替换成 CPU 版 python -c import torch; print(torch.__version__, torch.version.cuda)这里的关键是次序。先装 torch再去装 YOLOv5 的requirements.txt。原因很实际requirements.txt里对 torch 的约束通常写得很松类似torch1.8.0pip 在解析依赖时如果发现当前环境的版本「满足条件」一般不会动它但如果先装 requirements它可能顺手拉一个最新版 torch 进来把你刚配好的 CUDA 版本顶掉而且不会报错只会在运行时表现为各种莫名其妙的失败。git clone https://github.com/ultralytics/yolov5.git cd yolov5 git checkout v6.1 # 固定 tag别直接跟 main行为会漂 pip install -r requirements.txt # 装完再验一次确认 torch 没被换掉 python -c import torch; print(torch.__version__, torch.version.cuda)关于 CPU 版还有一个平台差异要注意在 Linux 上pip install torch从默认源拉到的通常是 CUDA 版在 Windows 上默认给的是 CPU 版。所以 Windows 用户如果只写pip install torch torchvision装完is_available()一定是 False这跟显卡好坏没关系。想装 GPU 版必须显式指定cuXXX后缀和-f索引地址。提示整个安装过程建议把每条命令的实际输出记下来尤其是版本号那几行。后面出问题时第一件事就是核对这几行有没有变。2.3 三条验证命令把「装完了」变成「装对了」pip list里能看到包不等于环境能用。按下面三段依次验证每一段失败对应的问题类型都不一样。import torch # 第一段版本信息确认编译时带的是哪套 CUDA print(torch:, torch.__version__) # 期望形如 1.12.1cu113 print(cuda runtime:, torch.version.cuda) # 期望 11.3None 说明是 CPU 轮子 print(cudnn:, torch.backends.cudnn.version()) print(available:, torch.cuda.is_available()) # 第二段设备信息确认能看到卡 if torch.cuda.is_available(): print(device count:, torch.cuda.device_count()) print(device name:, torch.cuda.get_device_name(0)) print(capability:, torch.cuda.get_device_capability(0)) # 如 (8, 6) # 第三段真跑一次矩阵乘确认 kernel 能起来、显存能分配 dev cuda if torch.cuda.is_available() else cpu x torch.randn(1024, 1024, devicedev) y x x print(matmul ok:, y.shape, y.device)三段各自的分工要清楚。第一段看的是「编译期信息」torch.version.cuda为 None 就说明装的是 CPU 轮子后面不用看了。第二段看的是「驱动和硬件能不能被枚举到」is_available()为 True 但device_count()为 0 这种情况极少一般出现在容器没挂显卡设备。第三段才是真正的黑匣子检测点前两段都通过、第三段在x x处崩掉八成是驱动版本低于轮子要求或者显存被别的进程占满。只看is_available()就以为万事大吉是后面训练阶段 OOM 的第一大来源。2.4 cuDNN 的两个开关benchmark 与 deterministic跑到这一步环境就算通了但如果后面要做对比实验还有一个开关值得提前定下来。import torch # 输入尺寸固定时开启cuDNN 会自动挑最快的卷积算法 torch.backends.cudnn.benchmark True # 需要严格可复现时开启会限制算法选择速度会降 torch.backends.cudnn.deterministic True # 同时固定随机种子 torch.manual_seed(0)benchmark打开后cuDNN 会在第一次前向时对每种卷积配置做小规模试跑选出最快的那一个并缓存下来。输入尺寸固定比如一直是 640×640时收益明显能快 10% 到 20%输入尺寸每次都不一样时反而会拖慢因为每次都要重新选。deterministic则是另一回事它关掉那些结果不确定的算法代价是速度下降换来同一份数据两次训练的结果能对上。做消融实验时必须开跑正式训练时一般关掉。这两个开关经常被混为一谈实际上一个管快、一个管稳目标相反。3. 权重与 detect.py第一次推理必须盯住的五个参数环境通了接下来是让模型真的吐出一个框。这一段有两个容易翻车的点权重文件放错位置或下载不完整以及detect.py的参数名在不同版本之间改过。3.1 仓库目录职责先知道每个文件夹是干什么的固定到某个 tag 之后目录结构基本稳定。搞清职责后面改配置时才知道该动哪个文件。路径职责什么时候会动它models/模型结构 yaml如yolov5s.yaml改类别数、改 anchorsdata/数据集描述 yaml如coco128.yaml训练自定义数据集utils/数据加载、增强、NMS、指标计算一般不改排查时可读detect.py推理入口调参、批量跑train.py训练入口训练时用val.py验证入口算 mAP训练后复核export.py导出 ONNX / TorchScript部署前utils/平时不用碰但出问题时它是第一现场。比如框重叠得离谱去utils/general.py里找non_max_suppression那个函数看iou_thres是怎么参与计算的比在网上翻十篇帖子管用。3.2 权重下载与完整性校验预训练权重首次运行detect.py时脚本会自动拉但自动拉有两个问题网络抖动时可能拿到一个截断的文件以及版本和你 clone 的代码对不上。手动下更可控。# 手动下载注意 tag 要和代码 checkout 的版本一致 wget https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.pt # 看文件大小yolov5s 在 14MB 量级明显偏小就是没下完 ls -lh yolov5s.pt大小只能粗略判断更靠谱的是直接把文件读进来检查结构。import torch # 加载权重map_location 保证在无 GPU 的机器上也能检查 ckpt torch.load(yolov5s.pt, map_locationcpu) # 完整的 checkpoint 通常带这三个键 print(keys:, list(ckpt.keys())) model ckpt[model] print(classes:, model.nc) print(param tensors:, len(list(model.parameters())))三个键里model是结构本体ema是训练时的指数滑动平均副本opt是训练参数快照。只做推理时加载的是ema或model两者权重略有差异但精度接近。如果torch.load直接抛UnpicklingError或者EOFError基本可以确定文件不完整删掉重下不用怀疑代码。3.3 detect.py 的五个核心参数第一次跑命令写短一点一次只改一个变量。python detect.py \ --weights yolov5s.pt \ --source ./samples \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0 \ --project runs/detect --name demo --exist-ok参数逐个说--weights指模型文件路径写相对路径时是相对于仓库根目录不是相对于你当前 shell 所在目录这点经常有人搞混。--source可以是一张图、一个目录、一段视频、一个摄像头编号写0也可以是一个 txt 文件里面每行一个路径。传目录时会递归处理其中的图片和视频。--img是推理时的输入边长。模型内部会把短边缩放到这个值长边按比例缩放再做 letterbox 填充。这个值不要求和训练时完全一致但差距太大精度会掉。训练用 640、推理用 320小目标的召回会明显下降。--conf-thres是置信度阈值低置信度的候选框在这里被直接丢掉。调低会召回更多但误检变多调高反之。--iou-thres是 NMS 的 IoU 阈值控制「两个框重叠到什么程度算同一个目标」。这个值调高比如 0.7会保留更多重叠框适合密集场景调低比如 0.3会 aggressively 合并密集场景里相邻的两个目标会被合成一个。注意参数名在版本之间改过。较早的版本用--conf和--iouv6 之后改成了--conf-thres和--iou-thres。写成旧名字不会报「未知参数」而是走默认值结果就是你调了半天阈值发现输出没变化。跑之前先python detect.py --help看一眼当前版本认哪些名字。3.4 输出目录与结果复核跑完之后结果落在runs/detect/demo/下。不加--exist-ok的话同名目录会被自动改成demo2、demo3连跑几次就分不清哪次是哪次了所以固定加这个参数。# 同时保存标注文本方便脚本化复核 python detect.py \ --weights yolov5s.pt --source ./samples \ --img 640 --conf-thres 0.25 --iou-thres 0.45 \ --save-txt --save-conf \ --project runs/detect --name demo --exist-ok # 输出目录结构 # runs/detect/demo/ # image1.jpg 带框的可视化图 # labels/image1.txt 每行: class cx cy w h conf归一化坐标 ls runs/detect/demo head -3 runs/detect/demo/labels/*.txt--save-txt存的是归一化后的中心点坐标和宽高五列加上--save-conf的第六列置信度。这个格式和训练用的标注格式完全一致所以可以直接拿它做二次筛选把所有 conf 低于某个值的行删掉再当伪标签用。坐标是相对原图宽高归一化到 0 到 1 的反算像素坐标时记得乘回原图尺寸不要乘成--img的值这是另一个高频错误。复核结果不能只看图好不好看。抽十张图把labels/*.txt的行数和图上肉眼可见的目标数对一遍差得多就说明阈值有问题再挑一张小目标多的图把--img从 640 改成 1280 重跑如果小目标突然被检出来了说明原来的输入尺寸就是瓶颈。4. 自定义数据集训练从标注 txt 到 mAP 曲线的完整链路预训练模型能认八十类但业务场景里的目标大概率不在里面。训练自定义数据集这条链路卡人的地方几乎全在数据格式和配置对不上而不是训练脚本本身。4.1 标注格式五列归一化坐标一个都不能错YOLO 的标注是一个图对应一个 txt每行一个目标五列class cx cy w h。全部是归一化到 0 到 1 的浮点数cx cy是框中心点w h是宽高都除以原图对应边长。# 假设原图 1920x1080一个类别 0 的目标框在 (400,300) 到 (800,700) # 中心点: ((400800)/2/1920, (300700)/2/1080) (0.3125, 0.4630) # 宽高: ((800-400)/1920, (700-300)/1080) (0.2083, 0.3704) 0 0.3125 0.4630 0.2083 0.3704如果手上是 VOC 的 xml需要转一道。下面这个脚本做的事很单一但边界情况都处理了。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, classes, out_dir): 把单个 VOC xml 转成 YOLO txt坐标做 clamp 防止越界 tree ET.parse(xml_path) root tree.getroot() size root.find(size) w float(size.find(width).text) h float(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in classes: # 类别不在名单里直接跳过不要静默当 0 类 continue cls_id classes.index(name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 标注越界是常见脏数据先夹到图内再归一化 x1, x2 max(0.0, x1), min(w, x2) y1, y2 max(0.0, y1), min(h, y2) if x2 x1 or y2 y1: # 夹完之后退化成零面积丢掉 continue cx (x1 x2) / 2.0 / w cy (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines))脚本里两处判断值得单独讲。第一处是类别过滤VOC 里的类别名和你的names列表必须严格一致大小写和空格都算不同对不上就跳过不要顺手塞成 0 类否则训练出来会把两种目标混成一类而且 loss 曲线看起来还挺正常属于最难查的一类问题。第二处是坐标 clamp人工标注里出现xmax大于图片宽度是家常便饭不夹的话归一化后会大于 1训练时数据增强做随机裁剪会算出负面积直接给 loss 送一个 nan 进去。4.2 data.yaml 的四个字段与目录约定# data/mydata.yaml path: ../datasets/mydata # 数据集根目录相对这个 yaml 文件所在位置 train: images/train # 相对 path可以是目录也可以是 txt 列表文件 val: images/val nc: 3 # 类别数必须等于 names 的长度 names: [person, helmet, vest]目录结构按约定摆好脚本会自动去同级的labels/找标注datasets/mydata/ images/train/ a.jpg b.jpg ... images/val/ c.jpg ... labels/train/ a.txt b.txt ... # 文件名与图片同名扩展名不同 labels/val/ c.txt ...train和val写目录时脚本会把目录下所有图片收进来并按「把images替换成labels、扩展名替换成.txt」的规则去找标注。这个替换规则意味着路径里必须包含images这一段如果你的目录叫imgs它就找不到标注而且不会报「找不到标注」只会报标注数量为 0。这是新手最容易卡住的地方。4.3 模型 yaml 的 nc 与 anchors# 复制一份改别直接改原文件后面做对比还要用 cp models/yolov5s.yaml models/yolov5s_mydata.yaml # 只改类别数这一行 # 原: nc: 80 # 改: nc: 3anchors那九组数值一般不用动。它们是 COCO 上聚类出来的先验框尺寸换到新数据集上不是最优但作为初始值够用微调几十个 epoch 会自己收敛过去。只有当目标尺寸分布和自然图像差得特别远比如全是几百像素的大目标或者全是十几像素的小字时才值得重新聚类一遍 anchors。--cfg和--weights一起给的时候有个细节结构按--cfg重建权重按--weights加载加载时如果检测头部分的形状和当前nc对不上那部分会被跳过并在日志里打印出来。这是预期行为不是错误。所以「改了 nc 之后检测头的权重从零开始学」是正常的不要因为看到 skip 日志就去改代码。4.4 train.py 参数与显存估算python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/mydata.yaml \ --cfg models/yolov5s_mydata.yaml \ --weights yolov5s.pt \ --device 0 \ --workers 4 \ --cache ram \ --project runs/train --name exp1 --exist-ok参数含义调整建议--img训练输入边长小目标多就上调到 960 或 1280显存吃紧先降 batch 再降 img--batch单卡批大小8G 显存配 640 大概能到 8~16从 8 试起--epochs训练轮数小数据集 100 到 300配合早停--weights初始权重写yolov5s.pt是迁移学习写空字符串是从头训--cache缓存方式ram最快但吃内存disk折中数据集大就别开--workers数据加载进程数Windows 上超过 0 容易出多进程问题先设 0显存这一块给个经验区间yolov5simg 640batch 16大约占 8GB 上下换成yolov5m同配置大概翻倍。显存不够时的降级顺序是先降batch到 8、4、2再关掉--cache最后才降--img。因为降输入尺寸对精度的伤害比降 batch 大得多batch 太小会影响 BatchNorm 的统计量稳定性但那个影响是可恢复的。4.5 训练日志里的三个信号训练起来之后日志会刷得很快重点盯三处。第一处是三个 lossbox_loss、obj_loss、cls_loss。正常形态是前 10 到 20 个 epoch 快速下降然后进入缓慢下降。box_loss长期不降说明框回归没学到东西先怀疑标注坐标算错obj_loss不降说明模型分不清前景背景通常是负样本太多或者数据集里空标注图片占比过高cls_loss在单类别任务里本来就接近 0不用管。第二处是验证集的mAP0.5和mAP0.5:0.95。前者宽松后者严格两个都涨才说明真的在变好。只涨前者不涨后者通常是把框放大了换召回实际用起来框会偏松。第三处是学习率。默认的 cosine 调度会从0.01量级一路降到接近 0。如果 loss 在中途突然炸开看时间点是不是正好在 warmup 结束、学习率跳到峰值的那个 epoch是的话把--warmup-epochs调大或者初始 lr 调小。5. 踩坑排查五类高频翻车现场的现象、原因与解决这些坑按出现频率排每条都按「看到什么—为什么—怎么修」写。修的时候一次只动一个变量别三处一起改否则修好了也不知道是哪一处起的作用。5.1 torch.cuda.is_available() 返回 False现象装完 torchpython -c import torch; print(torch.cuda.is_available())输出 False显卡在nvidia-smi里明明看得见。原因三种可能按概率排。装的是 CPU 版轮子torch.version.cuda为 None驱动版本低于轮子要求的 CUDA 上限在 Windows 上用了默认源安装默认给的就是 CPU 版。解决先打印torch.__version__和torch.version.cuda确认后缀是cuXXX。是 CPU 版就卸掉重装注意卸载要用pip uninstall torch torchvision两个一起卸只卸一个会留下不匹配的依赖。确认轮子没问题再看nvidia-smi的驱动版本和 CUDA 上限低于轮子要求就升级驱动。这三步走完还是 False检查是不是在容器里跑但没挂--gpus。5.2 detect.py 报权重加载失败或结果全空现象torch.load抛UnpicklingError或者跑完没有任何报错但输出目录里一张带框的图都没有labels 全是空文件。原因权重文件下载不完整是第一种尤其是自动下载被中断过。第二种是传给--weights的路径相对的是仓库根目录而不是 shell 当前目录实际加载到了另一个同名文件或者压根没加载上。第三种是推理时的类别和权重不匹配但这个会报形状错误而不是空结果可以排除。解决先看文件大小是否在合理量级再用 3.2 那段代码把文件读一遍能读出model键就说明文件完好。路径问题就统一改成绝对路径跑一次能跑通说明是相对路径的基准搞错了。确认权重没问题但还是全空把--conf-thres降到 0.1 重跑如果这时候有框了说明模型对这个场景本身就不敏感不是环境问题该走微调路线。5.3 训练 loss 变 nan 或 obj_loss 完全不降现象前几个 epoch 正常某个 epoch 之后 loss 打印成nan后面全部失效或者从头到尾 loss 都在一个值附近小幅波动不下降。原因nan 有九成是数据问题标注坐标越界、宽高为 0、图片文件损坏读了半张剩下一成是学习率过大导致梯度爆炸。obj_loss 不降则多半是正负样本失衡数据集里背景图太多或者标注框小到和 anchor 完全对不上。解决nan 先跑一遍数据体检把所有 txt 读出来检查每一行的五列是否都在 [0,1] 内、w h是否大于 0图片用 PIL 逐张verify()一遍找出损坏文件。数据干净就把初始 lr 从 0.01 降到 0.001 试一个短周期。obj_loss 的问题则是先统计每张图的平均目标数小于 1 的话要么补充目标图要么把--hyp里的box和obj损失权重做个调整常见做法是直接把背景图抽掉一部分。5.4 mAP 长期为 0 或极低现象训练能跑完loss 也在降但val出来的 mAP 一直是 0或者低到 0.01 量级。原因最典型的是 data.yaml 里的路径配置和实际目录结构对不上尤其是train/val写目录时脚本按规则去找labels找不到实际加载到的标注数是 0模型学了个寂寞。其次是nc和names长度不一致或者 txt 里的 class id 超出了nc - 1。还有一种隐蔽情况是图片和标注文件名大小写不一致在 Linux 上就直接找不到了。解决训练启动时日志第一段会打印数据集统计包括图片数、目标数、每类实例数先看这个数字对不对。目标数为 0 就是路径问题。数字对但 mAP 还是 0把val.py单独跑一次用--save-txt输出预测结果和标注对比几个样本通常一眼就能看出是类别全错还是框全偏。5.5 显存 OOM 或 DataLoader 卡住不动现象训练启动几秒后抛CUDA out of memory或者进度条长时间停在 0CPU 占用拉满但 GPU 空转。原因OOM 一般是--img和--batch的组合超过显存或者开了--cache ram把内存和显存一起挤爆。卡住不动多半是--workers开太大在 Windows 上尤其明显多个进程抢同一个数据集句柄会死锁。解决OOM 先把 batch 减半重试还不行就关掉--cache最后才降--img。另外训练前用nvidia-smi看有没有别的进程占着显存残留的 python 进程不清掉会一直占着。卡住不动就把--workers设成 0 跑一次能跑通说明是多进程问题之后在 Linux 上逐步加到 4 或 8在 Windows 上就老实待着 0。6. 用 val.py 与 TTA 复核把「看起来能用」变成「数据上能用」训练完拿到best.pt很多人看一眼验证集的图就交付了。图好看不代表指标可信指标可信也不代表换了推理方式还成立。我一般会强制走两步复核成本很低但能挡掉大部分「线下好用线上崩」的情况。第一步是用val.py重算一遍并且开 TTA。python val.py \ --weights runs/train/exp1/weights/best.pt \ --data data/mydata.yaml \ --img 640 \ --task val \ --augment \ --project runs/val --name tta --exist-ok--task val表示只做验证不算推理速度--augment打开 TTA会把输入做翻转和多尺度后各跑一次再融合。TTA 通常能把 mAP0.5 抬 1 到 2 个点代价是推理耗时翻几倍。这一步的目的不是刷高分数而是看「开了 TTA 之后涨多少」涨得多说明模型对输入变化敏感、鲁棒性一般几乎不涨说明模型已经比较稳。两种结果对应不同的上线策略比单看一个绝对数值有用。第二步是导出 ONNX用另一个运行时跑同一张图比对输出的形状和数量级。python export.py \ --weights runs/train/exp1/weights/best.pt \ --include onnx \ --img 640 640 \ --batch 1 \ --opset 12import numpy as np import onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) inp sess.get_inputs()[0] print(input:, inp.name, inp.shape) # 期望 [1, 3, 640, 640] # 造一个同形状的输入确认能跑通并看输出维度 x np.random.randn(1, 3, 640, 640).astype(np.float32) outs sess.run(None, {inp.name: x}) for o in outs: print(output:, o.shape) # 形如 (1, 25200, 5 nc)输出最后一维等于5 nc其中 5 是cx cy w h conf。这个数字对不上说明导出时的--img和训练时不一致或者nc没跟着改。ONNX 这条路的坑主要集中在预处理PyTorch 那条链路里 letterbox 的填充值、RGB 顺序、归一化系数都是在代码里写死的换到别的运行时如果忘了照抄输出会看起来「有框但位置全偏」比直接不出框更难查。所以我习惯在导出后固定拿同一张图分别用detect.py和 ONNX 各跑一次把两组框的坐标打印出来对比差值在几个像素内才算对齐。从那以后我每次训完一个模型不管 deadline 多紧val.py --augment和 ONNX 对齐这两步都强制走一遍宁可多花十分钟也不想在上线之后才发现验证集分数是虚的。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

DeepSeek医疗内网本地化部署与微调实战
DeepSeek医疗内网本地化部署与微调实战

简介:面向医疗信息化从业者、算法工程师与医院数据平台建设者,这份PDF文档围绕DeepSeek本地化部署与医疗数据私有化训练展开,重点解决敏感病历在合规环境下如何完成模型训练与应用落地的问题。内容从私有化部署背景与法规要求切入&#xff0c… · 2026/11/3 19:38:26

蜜罐部署排障:Pentbox、Cowrie与Defnet端口日志实战
蜜罐部署排障:Pentbox、Cowrie与Defnet端口日志实战

简介:《三种蜜罐的搭建与使用方法》是一份面向网络安全初学者、渗透测试入门者及实验教学场景的实操型文档,围绕 Defnet、Pentbox 与 Cowrie 三类蜜罐的部署和使用展开,帮助读者理解低交互与中交互蜜罐在攻击诱捕、行为监测和日志记录中的差异… · 2026/11/3 19:38:26

HCIP-Datacom 821题库解析:路由交换与安全易错点
HCIP-Datacom 821题库解析:路由交换与安全易错点

简介:面向准备HCIP-Datacom 821认证考试的网络工程师与在校考生,这份PDF题库以单选题为主,覆盖路由交换、IPv6、WLAN、安全与组播等考试常见模块,适合已进入总复习阶段、希望用题目检验知识覆盖面的考生。题目配有答案和简要解析&… · 2026/11/3 19:38:26

CodeBuddy:提升技术团队效能的智能协同平台
CodeBuddy:提升技术团队效能的智能协同平台

1. 项目概述:CodeBuddy的定位与核心价值 第一次听说CodeBuddy这个工具是在去年的一次技术沙龙上。当时团队正被多项目并行管理的混乱所困扰——需求文档散落在不同成员的硬盘里,代码版本频繁冲突,任务进度全靠微信群里的碎片化沟通。直到遇见… · 2026/11/3 20:14:46

开源模型评价体系:技术指标与生态健康度的多维评估
开源模型评价体系:技术指标与生态健康度的多维评估

1. 开源模型评价体系的现状与挑战开源模型近年来呈现爆发式增长,从自然语言处理到计算机视觉,各类预训练模型层出不穷。但一个关键问题始终困扰着开发者:如何客观评价这些开源模型的质量?目前主流的评价方式存在几个明显缺陷&… · 2026/11/3 20:14:46

端口访问被拒绝?从报错到根因的排查链路与实战指南
端口访问被拒绝?从报错到根因的排查链路与实战指南

1. 端口访问被拒绝,先别急着改防火墙端口访问被拒绝这件事,几乎每个跟服务器、容器、本地开发环境打过交道的人都撞上过。现象很统一:浏览器转圈半天最后甩出一句“无法访问此网站”,或者命令行里干脆利落地回一句Connection refu… · 2026/11/3 20:14:46

Deepoc具身模型外拓板:智能家居的春节解决方案
Deepoc具身模型外拓板:智能家居的春节解决方案

1. Deepoc具身模型外拓板:智能家居的春节解决方案春节假期本该是家人团聚的温馨时刻,但大扫除、年货采购、亲友接待这些琐事常常让人疲惫不堪。最近我在测试的Deepoc具身模型外拓板,配合家用机器人使用,意外发现它能完美解决这些痛… · 2026/11/3 20:14:46

D750Ex与J750E/Ex测试系统安装与故障排查实战指南
D750Ex与J750E/Ex测试系统安装与故障排查实战指南

简介:这份资源是Teradyne官方发布的《D750Ex和J750E/Ex测试系统服务手册》Rev 1334版PDF,面向半导体测试设备的技术人员、维护工程师与操作人员,用于解决D750Ex及J750E/Ex测试系统在安装、拆卸、日常维护和故障排查中的实际问题,属… · 2026/11/3 20:14:46

支付系统核心架构设计与实践指南
支付系统核心架构设计与实践指南

1. 支付核心系统设计概述 支付系统作为现代商业基础设施的核心组件,其设计质量直接影响着企业的资金流转效率和用户体验。一套优秀的支付核心系统应当具备高可用性、强一致性、可扩展性和安全性等关键特性。在实际项目中,我们通常会面临交易峰值波动、资… · 2026/11/3 20:13:46

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码