ARTICLE DETAIL

资讯详情

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

免环境YOLO训练工具:容器隔离、自动标注与模型转换实战

免环境YOLO训练工具:容器隔离、自动标注与模型转换实战 简介面向YOLO系列目标检测用户的免环境训练工具包专注解决深度学习环境搭建复杂、标注耗时、模型转换不便等痛点。工具支持YOLOv3、YOLOv4及YOLOv8多版本其中V8版本需NVIDIA显卡可加载cfg、weights、bin、param、pt等多种模型文件涵盖yolo8l/m/n/s/x常见预训练权重并提供自动标注、自动截图、模型转换与一键训练等实用功能。压缩包共14个文件以txt说明、html操作教程、doc文档和jpg示意图为主覆盖从环境准备、标注操作到模型训练与格式转换的全流程讲解便于按需查阅不同环节的细节整体仅908KB轻量易用。目前已有1124人学习下载适合希望快速上手YOLO标注与训练、或需在不搭建本地环境条件下完成模型迭代的算法工程师与初学者参考。 装环境装到怀疑人生不是一句夸张的调侃而是我在 YOLO 系列上反复经历的日常。明明算法文档写得清清楚楚最后卡住的往往不是模型本身而是 CUDA、PyTorch、torchvision 和 OpenCV 的版本乱斗。免环境训练工具要解决的就是这件事把整个 YOLO 训练链路的依赖固定成镜像或脚本让标注、转换、训练都在同一套运行时里完成把“从拉代码到跑起来”的时间压缩到十到二十分钟。它适合手里有数据集、但被环境配置反复折腾的工程师也适合需要在同一台机器上切换 YOLOv5/YOLOv8 多个版本来验证方案的团队。2. 免环境YOLO训练工具的底层设计容器隔离、依赖锁定与多版本运行时切换2.1 为什么免环境是必需YOLO这张依赖网的冲突成本比训练本身还高YOLO 系列不是单一项目而是一整套依赖树。目标检测训练至少要同时满足四层依赖底层是 CUDA 和 cuDNN中间是 PyTorch 和 torchvision再往上是模型仓库自己的 requirements.txt最外层是标注、转换、训练时用到的 numpy、opencv、onnx、onnxruntime。这四层任何一层版本对不上都会在训练中途以各种方式翻车。我在实际项目中见过最典型的场景是本机装了 PyTorch 2.x跑 YOLOv5 的某个分支时升级了依赖结果第二天 YOLOv8 的训练任务开始报torchvision::nms算子不存在。这种问题的定位成本远超单次训练的计算成本因为没人记得昨天到底升级了什么。免环境工具把这些依赖全部打包进容器镜像或 conda 环境核心思路不是“让用户不敲命令”而是让依赖关系变成可复现的固化产物。镜像一旦构建好里面哪个包是哪个版本就确定了输入同样的数据一定会得到同样的训练行为。这比任何“自动安装”脚本都可靠因为自动安装脚本仍然可能因为下载源、安装顺序和已存在的环境产生不确定性。对于多人协作的团队这份确定性尤其值钱A 同学能跑起来B 同学也能跑起来不再需要各自花半天去“修环境”。2.2 我常用的免环境搭建路径Docker镜像和一个十行启动脚本我一般会把模型仓库、依赖和常用工具脚本打到一个基础镜像里然后通过一个启动脚本来暴露少数几个常用参数。Docker 的优势是隔离彻底宿主机上装了什么完全不影响镜像内环境镜像标签天然就是版本切换的工具。下面这个启动脚本是我反复调整后的简化版核心参数压缩成了--version、--gpu、--data三个。#!/usr/bin/env bash # run_yolo.sh # 免环境YOLO训练/标注/转换的统一入口 # 用法示例 # ./run_yolo.sh --version v8 --gpu 0 --data ./datasets -- train datadataset.yaml set -e YOLO_TAGv8 GPU_ID0 DATA_DIR./datasets while [[ $# -gt 0 ]]; do case $1 in --version) YOLO_TAG$2; shift 2;; --gpu) GPU_ID$2; shift 2;; --data) DATA_DIR$2; shift 2;; --) shift; break;; *) echo 未知参数: $1; exit 1;; esac done docker run --rm \ --gpus device${GPU_ID} \ -v $(pwd):/workspace \ -v $DATA_DIR:/datasets \ -e YOLO_TAG${YOLO_TAG} \ -w /workspace \ yolo-free-env:${YOLO_TAG} \ python $这段脚本的逻辑其实就几步解析参数、把当前目录挂载进容器、把数据集目录挂载进去、用对应版本的镜像执行训练命令。--rm参数保证任务退出后容器自动删除不给磁盘留垃圾镜像。-v $(pwd):/workspace是把代码和配置实时映射进去这样在宿主机上修改的训练配置立刻在容器内生效不需要重新构建镜像。--gpus device${GPU_ID}让你可以在多卡机器上指定跑哪张卡避免抢显存。注意在 Linux 上跑这个脚本需要宿主机安装 nvidia-container-toolkit否则--gpus参数会直接报错。Windows 用户建议走 WSL2在 WSL2 里安装 Docker Desktop 和 GPU 支持后同样可以调用宿主机显卡。如果机器没有可用 GPU把--gpus那一行删掉训练命令里加devicecpu也能跑只是速度会慢很多。2.3 多版本运行时切换镜像标签、conda环境与权重差异多版本支持是这类工具的关键价值之一。YOLOv5 和 YOLOv8 的训练入口和输出格式差异很大YOLOv5 是每个模型目录单独一套脚本train.py的参数靠 argparse 解析YOLOv8 统一成yolo一个命令train/val/predict/export都是子命令。这两套逻辑很难在一个环境里共存因为它们的依赖版本要求不同。常见做法是用镜像标签隔离上面脚本里的yolo-free-env:v8和yolo-free-env:v5就是两套独立的运行时。版本切换时要特别注意预训练权重通配的问题。YOLOv5 训练出来的.pt权重无法直接加载到 YOLOv8 里用原因是网络结构定义和 head 部分的张量形状不一致即使都是物体检测也分属不同架构。我在项目里吃过这个亏想省事把 v5 模型训练出的 best.pt 直接传给 v8 的model参数Ultralytics 会尝试加载但随后在输出层报 shape mismatch。正确的做法是在每个版本自己的环境里从头训练或者使用各版本官方对应的预训练权重做起点而不是跨版本通用权重。下表是我在多版本切换时记录的映射关系可以作为内部封装时的参考。环境标签训练入口标注输出格式导出格式侧重v5python train.py每个图对应同名.txtPyTorch.pt/ ONNXv8yolo train同样式.txtONNX / TensorRT enginev8-segyolo train加 tasksegment多边形转.txtONNX带 mask 分支这层映射对自动标注尤其重要因为标注工具输出的格式必须和训练入口能对齐。比如 YOLOv8 的实例分割模型标注文件里每一行开头是类别 id后面是归一化的多边形坐标点和检测模型的class cx cy w h完全不是一回事。如果免环境工具内部没有做格式适配用户标注完却发现训练端读不进去就又回到了最原始的“自己转格式”的状态。3. 自动标注怎么做进免环境流程预测打框、格式统一与质量兜底3.1 自动标注的真实形态预标注模型打框人工确认兜底提到自动标注很多人以为是一键把成千上万张图全自动标完实际工程里不是这样。我的习惯是用一个相对成熟的模型先跑出候选框然后由人做确认和修正。这套流程叫“预标注”更准确机器负责找出可能的位置人负责判定类别和边界是否正确。以 YOLOv8 为例加载官方预训练权重在未标注的图片目录上跑推理把输出的框直接写成训练格式的 txt这一步能把人工画框的时间省掉一半以上。在实际项目中我常遇到两种需要预标注的情况。一种是刚拿到一批无标签业务图片比如学生专注度检测场景收集了教室画面但没有任何标签另一种是需要在已训练模型的基础上扩类比如已有安全帽检测模型又新增加“反光衣”类别。第一种情况用官方 COCO 预训练模型打底即可第二种情况必须用老模型先跑因为它认识旧类别再由人工补充新类别的框。下面这段代码展示的是最基本的预标注逻辑输入一个图片目录输出 YOLO 格式的标签文件。# label_gen.py # 免环境工具中的自动标注入口用预训练模型批量生成YOLO txt标签 from pathlib import Path from ultralytics import YOLO model YOLO(yolov8m.pt) # 根据项目场景换预训练模型 image_dir Path(raw_images) label_dir Path(labels) label_dir.mkdir(exist_okTrue) for img in image_dir.glob(*.jpg): results model.predict( str(img), conf0.3, # 候选框置信度阈值太低会出大量假正例 iou0.5, # NMS的IoU阈值重叠严重时调高 imgsz1280, # 大图建议用1280小目标召回率更高 verboseFalse, )[0] # results.boxes存储xyxy格式的坐标这里换算成YOLO归一化格式写盘 with open(label_dir / (img.stem .txt), w) as f: for box in results.boxes: cls int(box.cls[0]) x1, y1, x2, y2 box.xyxy[0].tolist() w x2 - x1 h y2 - y1 cx (x1 x2) / 2 / results.orig_shape[1] cy (y1 y2) / 2 / results.orig_shape[0] f.write(f{cls} {cx:.6f} {cy:.6f} {w / results.orig_shape[1]:.6f} {h / results.orig_shape[0]:.6f}\n)这段代码的关键参数是conf和iou。conf0.3是置信度阈值低于这个值的框会被过滤掉如果你发现预标注结果遗漏严重可以先降到 0.1 看看到底检出多少框再按需调回去。imgsz对大图很重要很多业务图原始分辨率是 2000 像素以上直接压缩到 640 会让小目标丢失我一般先按 1280 推理一次后续再结合人工修正决定训练尺寸。提示预标注的定位是“候选框”不是“最终标签”。我要求团队无论自动标注多准都必须人工过一遍标注工具抽查类别和边界尤其是类别混淆场景。因为预训练模型在业务数据上的表现和标注数据分布强相关只靠置信度阈值不能避免系统性错标。3.2 格式统一模型输出到YOLO txt的坐标换算脚本YOLO 系列训练要求标签文件与图片同名每一行是class cx cy w hcx、cy 是中心的相对坐标w、h 是相对宽度和高度取值范围 0 到 1。而很多公开数据集或老标注工具输出的是 VOC 格式即每个目标用左上角和右下角的绝对像素坐标表达。免环境工具需要内置一个稳定格式转换层否则标注工具、预标注脚本和训练脚本很难衔接。我在把 VOC 数据集转成 YOLO 格式时会用下面这段解析逻辑。它把xmin/ymin/xmax/ymax换算成cx/cy/w/h并且做两层保护一是把越界坐标裁剪到图片范围内二是过滤掉宽或高为零的无效框。# voc2yolo.py # 把VOC标注转成YOLO训练txt处理越界和无效框 import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names, w_img, h_img): root ET.parse(xml_path).getroot() lines [] for obj in root.findall(object): name obj.findtext(name) if name not in class_names: continue # 只保留模型需要的类别 cls_id class_names.index(name) box obj.find(bndbox) xmin max(float(box.findtext(xmin)), 0) ymin max(float(box.findtext(ymin)), 0) xmax min(float(box.findtext(xmax)), w_img) ymax min(float(box.findtext(ymax)), h_img) w xmax - xmin h ymax - ymin if w 0 or h 0: continue # 无效框直接丢弃 cx (xmin xmax) / 2 / w_img cy (ymin ymax) / 2 / h_img lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w / w_img:.6f} {h / h_img:.6f}\n) return lines这个脚本里最容易踩坑的是差一像素问题。VOC 中的像素坐标如果以图像最左上角为原点xmin对应第一列的索引是 0直接把它当像素宽度参与归一化计算通常不影响训练但如果坐标是从其他工具导出的可能存在整数边界偏移。稳妥的做法是统一用 0.5 像素中心偏移折算避免把一个贴合边界的框计算成 1 或 0 的归一化值。另一个常见错误是忘记过滤掉空标签文件图片有标注但全部被滤掉后训练端会把这张图当背景图处理造成误检背景。3.3 标注质量的三道检查空标签、类别占比、聚类抽查自动标注能省时间也会引入新的质量风险。我在每次生成完标签后都会跑三道检查再进入训练环节。第一道是检查空标签数量如果预标注跑了 2000 张图其中 600 张没有任何框要么是模型对该类目标不敏感要么是数据本身不包含目标我会把这 600 张单独抽出来看而不是直接丢进训练集。第二道是统计类别占比正常情况下类别数量分布不会极端偏斜如果某一类只有几个样本训练时很难收敛。第三道是聚类抽查把不同置信度区间的标注结果各抽十几张出来人工看一遍重点看低置信度区间是不是大量错标或漏标。注意自动标注生成的标签要严格检查边缘情况。框完全越界、框中心落在图像外、两个框互相重叠超过 80%这些情况在真实数据里很常见。训练过程中 loss 异常不稳定有一半的可能是标签本身有问题而不是学习率或模型结构的问题。4. 模型转换环节导出ONNX/TensorRT的正确姿势与高频失败点4.1 用yolo export导出ONNXdynamic、simplify、opset三个参数模型训练完不是终点绝大多数项目需要把权重导出成推理引擎能加载的格式。YOLOv8 的yolo export子命令把导出流程统一了支持 PT、TorchScript、ONNX、OpenVINO、TensorRT 等格式。我最高频使用的是导出 ONNX因为它是中间格式既能拿到 CPU 部署环境又能进一步转成 TensorRT 引擎跑 GPU。导出命令的稳定参数组合如下# 导出ONNX适配动态输入尺寸和不同推理框架 yolo export modelbest.pt formatonnx dynamicTrue simplifyTrue opset12dynamicTrue表示允许推理时动态改变输入图片尺寸这在生产环境里几乎是必须的因为业务图的宽高不固定。如果你要部署到嵌入式设备对性能压得比较死可以去掉dynamic并明确imgsz640这样导出模型的张量形状完全固定TensorRT 优化得更狠。simplifyTrue会调用 onnx-simplifier 对计算图做精简去掉一些冗余节点算不上必须但能减少部署时出现的算子兼容问题。opset控制 ONNX 算子集的版本部署到老版本推理框架时用 11 或 12 更稳妥新框架用 13 以上能保留更多操作细节。导出完成后我习惯做一件事把导出的best.onnx用 Netron 打开快速看一眼输入输出节点的名字和形状。很多部署报错都出在输入张量维度上比如 PyTorch 模型接受1x3xHxW导出的 ONNX 却带了动态轴推理代码没接入dynamic_axes就会报维度不匹配。先看模型结构再写推理代码比反复试错省时间得多。4.2 转换后先做输出对比用onnxruntime验证一致性模型转换最恼人的问题是导入 ONNX 后能跑但结果和 PyTorch 原模型明显不一样。这种事不一定来自模型结构而是前后处理不一致或者精度损失。我每次导出后都会跑一个最小验证脚本对比原模型和 ONNX 在相同输入下的输出张量差异而不是直接去部署业务逻辑。下面是一个简化的对比代码重点是比较两者的检测输出。# verify_onnx.py # 对比PyTorch和ONNX在相同输入上的输出分布判断转换是否一致 import numpy as np import onnxruntime as ort from ultralytics import YOLO pt_model YOLO(best.pt) img np.random.randint(0, 255, (640, 640, 3), dtypenp.uint8) r_pt pt_model.predict(img, imgsz640, verboseFalse)[0] r_pt_box r_pt.boxes.xyxy.cpu().numpy() # 原始模型检测框 sess ort.InferenceSession( best.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider], ) input_name sess.get_inputs()[0].name # 这里的预处理要和训练保持一致BGR转RGB、归一化到0~1 img_input img[:, :, ::-1].astype(np.float32) / 255.0 img_input np.transpose(img_input, (2, 0, 1))[None] r_onnx sess.run(None, {input_name: img_input})[0] r_onnx r_onnx[0].transpose(1, 0) # 根据输出格式决定是否转置 # 比较前40个默认框的置信度输出看平均绝对误差 from numpy.linalg import norm print(MAE:, np.abs(r_pt_box - r_onnx[:, :4]).mean())跑这个脚本时我关心的不是完全相等而是误差量级。FP32 精度下平均绝对误差小于1e-3基本没问题FP16 导出后误差到1e-2是常态。如果误差突然到了1e0甚至更大说明转换环节出了问题我会先检查预处理是否一致再看有没有用到训练时不支持的算子。4.3 TensorRT导出和FP16/INT8的精度选择在 GPU 服务器上部署我会继续把 ONNX 转成 TensorRT engine这是 YOLO 在 NVIDIA 平台上吞吐最高的方案。Ultralytics 提供了直接导出的命令也可以用 trtexec 手动转换。两种方式本质一致trtexec 更透明适合排查问题。# 方式一用yolo命令导出engine yolo export modelbest.onnx formatengine halfTrue device0 workspace4 # 方式二用tensorrt自带的trtexec工具转换 trtexec --onnxbest.onnx --saveEnginebest_fp16.engine --fp16 --workspace4096halfTrue或--fp16代表使用半精度推理显存占用减半、吞吐提升明显。如果业务对检测精度要求极高比如缺陷检测或医疗影像相关场景不建议直接用 FP16优先保留 FP32 并配合动态尺寸。workspace参数控制在转换时 TensorRT 优化算子能占用的显存上限单位是老版本里的 MB给得越大算子融合效果越好但巨型 batch 下可能显存不足。在 AGX Orin 这类边缘设备上转换和部署最好放在设备本地做因为 TensorRT 引擎和 CUDA 版本绑定很紧本机转出的 engine 换一台设备或换一个驱动版本就可能加载失败。4.4 模型转换的“翻车”记录四个高频失败点我整理过几次转换翻车的现象、原因和处理方法写在这里给后来者当参照。第一个是动态尺寸转换后推理报尺寸不匹配。现象代码里输入 1280x1280TensorRT 报Input binding size mismatch。原因导出时开了dynamicTrue但 trtexec 转换时没指定优化尺寸引擎默认绑定了最小 Shape。解决转换时明确动态范围比如--minShapesimages:1x3x640x640 --optShapesimages:1x3x1280x1280 --maxShapesimages:4x3x1280x1280。第二个是opset过高导致老框架无法加载。现象用 ONNX Runtime 1.10 部署时报Unsupported operator。原因导出时 opset 设成 17老版本推理框架不支持。解决导出参数里显式写opset12这是兼容面最广的组合YOLO 系算子在这个版本都能正常工作。第三个是 FP16 精度下漏检小目标。现象原模型在验证集mAP是 0.9转 FP16 后小目标漏掉一半。原因FP16 动态范围不足网络层归一化参数在小数值上精度丢失。解决在导出命令中改用 INT8 加校正集或者只在部分层使用 FP16实在要保精度返回 FP32。第四个是 TensorRT engine 换机器后加载报错。现象本机转换后部署到客户服务器runtime.create_engine报文件格式错误。原因TensorRT engine 和 GPU 架构、CUDA 版本强绑定。解决不要在开发机上转换后发给客户要在目标机器上用 trtexec 重新生成 engine或者改用 ONNX 配合目标机上的 TensorRT 运行时。5. 免环境YOLO训练的训练参数与避坑排查从最小可跑配置到loss炸掉5.1 训练配置一份我反复使用的数据配置文件和命令模型转换之前首先要保证训练过程本身稳定。我用 YOLOv8 训练自己的数据集时的最小可跑配置包含两部分一个指向数据集的 YAML 文件和一训练命令。数据组织结构按 Ultralytics 惯例images/train放图片、labels/train放同名 txt 标签。下面是 dataset.yaml 的标准内容。# dataset.yaml # 训练数据配置注意path是容器内或本机可访问的绝对路径 path: /datasets/safety train: images/train val: images/val nc: 2 names: 0: worker 1: helmet训练命令是yolo train datadataset.yaml modelyolov8m.pt epochs150 imgsz640 batch16 lr00.01 device0参数里最重要的三件事是model、batch和lr0。modelyolov8m.pt指的是加载官方预训练权重作为起点而不是从零训练用yolov8x.pt表示换更大的预训练模型感受野和特征表达能力更强但对显存要求更高。batch要根据显存调整16 对应 8G 显存左右跑 640 输入24G 显存可以试 32 或 64。lr0是初始学习率官方默认 0.01但换个数据集微调时我经常降到 0.001 来避免过早发散。注意如果用的是免环境容器path最好写容器内的挂载路径/datasets/safety而不是宿主机路径。因为训练进程运行在容器里相对路径和绝对路径都以容器视角解析。很多首次用容器训练的人在这里会踩坑日志一直报找不到图片或标签。5.2 增量训练与resume模型训练中断后的“后悔药”增量训练在工程中极其常见。所谓增量训练就是在已有模型的基础上继续学习新数据而不是从零开始。YOLOv8 的yolo train天然支持加载已有权重作为起点这时预训练权重就是你之前训练好的best.pt。一个常见误区是把modelcustom_best.pt和resumeTrue搞混model指定的是“从哪个权重继续训练”resume指定的是“是否恢复原来的训练状态”两者用途完全不同。# 从已有模型继续增量训练保留原有学习率调度和优化器状态 yolo train datadataset.yaml modelexp/weights/last.pt resumeTrue device0 # 加载训练好的权重用新学习率和参数从头开始训练部分场景可用 yolo train datadataset.yaml modelexp/weights/best.pt lr00.001 epochs50 device0训练中断后的恢复操作也依赖这份机制。有人跑了 80 个 epoch机器断电或进程被杀如果没存盘只能全部重来。正确的做法是训练时定期保存last.pt恢复时直接resumeTrue它会读取训练状态文件里的 epoch 数、学习率、优化器动量从断点继续。这个习惯也让我大胆地重启实验发现 loss 异常时不用急着停任务可以先等下一个 checkpoint 存下来再恢复并调整参数相当于给训练过程加了后悔药。5.3 避坑排查loss为nan、CUDA OOM、训练中断恢复训练过程最常遇到的三个问题我按现象、原因、解决三个步骤记录如下。第一个是 loss 变 NaN。现象训练前几轮 loss 还正常几十轮后显示loss: nanprecision 和 recall 降为 0。原因最常见的是学习率过高导致梯度爆炸其次是用 AMP 混合精度训练时某些层数值溢出再就是标签里有复数坐标或空标签文件干扰损失函数计算。解决先把lr0降到 0.001 或 0.0005重启训练观察如果还爆在训练命令里加ampFalse强制关闭混合精度排除浮点溢出因素同时检查标签文件去掉空标签和异常坐标。很多人在这一步直接怀疑代码或者框架其实先从这三个因素排查八成能定位。第二个是CUDA out of memory。现象进程启动后显存占用逐步上涨训练中段报 OOM。原因batch 和 imgsz 太大加上 AMP、EMA、梯度累积等额外占用。解决维持 imgsz 不变的情况下降低 batch如果 batch 降到 4 还是 OOM再加--workers 0排除数据加载器的显存占用最后考虑用梯度累积模拟大 batch比如batch8 accumulate4达到等效 batch 32 的效果。这里特别提一点免环境容器内的显存分配和宿主机可能互相干扰如果宿主机上已经有任务占用显存先确认 GPU 是否够分不够就把--gpu参数指向另一张卡。第三个是训练中断后恢复不生效。现象执行resumeTrue后从 epoch 0 重新开始而不是从断点继续。原因当前命令行传入的data或model参数和训练状态文件记录的不一致或者resumeTrue时仍然手动指定了model参数造成训练逻辑无法定位 checkpoint。解决恢复训练时不要手动传model只保留resumeTrue让工具自动去读取实验目录里的last.pt和训练状态同时确保数据集路径和原训练时一致。更换了数据库目录路径再去 resume轻则路径错误重则训练记录错乱。提示训练日志和模型权重不要只存在容器临时目录。容器的生命周期很短任务结束或容器崩溃后--rm会自动清理掉一切未挂载出来的文件。我会把实验输出路径挂载到宿主机比如-v $(pwd)/runs:/workspace/runs这样即使容器销毁权重、日志和中断恢复所需的文件都还在。6. 交付前的验证动作我的导出检查和环境锁定习惯6.1 固定输入的输出对比检查脚本我吃过一次大亏模型转换完直接丢给算法团队集成对方反馈检测结果明显变差我一开始怀疑是量化精度排查了两天才发现是导出 ONNX 时输入通道顺序处理错了PyTorch 模型内部默认 RGB而我导出时直接用 BGR 数据测试。那次之后我立了一个习惯交付 ONNX 或 TensorRT engine 前必须用同一张图跑一个对比脚本把原模型和导出模型的检测框做重叠度比对而不是只看会不会报错。# validate_export.py # 用同一张验证图比较PyTorch原模型和导出模型的检测框IoU import numpy as np import onnxruntime as ort from ultralytics import YOLO from utils.iou import box_iou # 内部工具函数计算框间IoU def validate(image_path, pt_path, onnx_path, conf0.25): pt YOLO(pt_path) sess ort.InferenceSession(onnx_path, providers[CUDAExecutionProvider]) img cv2.imread(image_path) # 注意读取通道顺序 r_pt pt.predict(img, imgsz640, confconf, verboseFalse)[0] input_name sess.get_inputs()[0].name img_rgb img[:, :, ::-1].astype(np.float32) / 255.0 img_rgb np.transpose(img_rgb, (2, 0, 1))[None] r_onnx sess.run(None, {input_name: img_rgb})[0] # 解析ONNX输出得到框再与r_pt做IoU对比 # 合格线IoU均值大于0.9且置信度排序一致这个脚本的价值不只是发现问题更是给团队一个可维护的基线。每次改动模型或导出参数都跑一遍这个脚本输出一个有数字的验证报告而不是靠人工瞄一眼效果图。我把这一步当作模型交付前必须由自动化脚本覆盖的最后一公里。6.2 环境锁定把验证结果和依赖版本一起存档如果只是把验证结果截图发群里下次再改就说不清当时环境是什么。我现在会同时保存一份requirements.txt快照和导出的 engine 文件放在同一个目录。免环境工具本身已经把依赖固定在镜像里我只需要记录用的是哪个镜像标签、哪次构建 ID。这样做的好处是三个月后上线出问题我能从存档里还原出完全一致的运行环境重新复现而不是靠记忆去猜。尤其 TensorRT engine 的 CUDA 版本耦合度高换一次环境就可能要重新导出没有环境记录相当于没有可回滚的现场。这些年用 YOLO 系列做项目最深的教训是数据质量、训练参数、模型转换、部署验证每个环节单独看都像“玄学”但只要把环境固化、把验证自动化大部分玄学都能变成可定位的工程问题。希望这套免环境工具的使用思路和排查路径能帮到你少走我走过的弯路。本文还有配套的精品资源点击获取
返回列表