28 YOLOv8 ONNX输出的cls与box提取详解——从[1,84,8400]到检测框

YOLOv8 ONNX输出的cls与box提取详解------从[1,84,8400]到检测框

配套代码 :本文末附完整可运行的 Python 代码演示,可复制到本地直接运行

一句话总结 :YOLOv8 默认导出 ONNX 后,输出 [1, 84, 8400]------通道 0-3 是 box 坐标(值域 0~640),通道 4-83 是 cls 置信度(值域 0~1,已过 Sigmoid)。box 值域是 cls 的 ~700 倍。


目录

  • [YOLOv8 ONNX输出的cls与box提取详解------从[1,84,8400]到检测框](#YOLOv8 ONNX输出的cls与box提取详解——从[1,84,8400]到检测框)
    • 目录
    • [一、ONNX 输出结构](#一、ONNX 输出结构)
    • [二、8400 个 anchor 的来源](#二、8400 个 anchor 的来源)
    • [三、box 通道 (0-3)](#三、box 通道 (0-3))
      • [DFL 解码已在 ONNX 图中完成](#DFL 解码已在 ONNX 图中完成)
      • [坐标系:已经映射到 640×640 输入图像](#坐标系:已经映射到 640×640 输入图像)
    • [四、cls 通道 (4-83)](#四、cls 通道 (4-83))
      • [Sigmoid 在哪里执行的?](#Sigmoid 在哪里执行的?)
      • [提取 cls 的常用指标](#提取 cls 的常用指标)
    • [五、box 和 cls 的值域差异](#五、box 和 cls 的值域差异)
    • 六、代码演示
    • 七、总结
    • 自测

一、ONNX 输出结构

YOLOv8 默认导出的 ONNX 模型只有 1 个输出 output0,shape 为 [1, 84, 8400]

复制代码
output0 = [1, 84, 8400]
          │   │   └── 8400 个检测锚点 (anchor)
          │   │        = 80×80 + 40×40 + 20×20
          │   └── 84 个通道
          │       0-3:   box 坐标 (cx, cy, w, h)
          │       4-83:  cls 分类置信度 (80 个 COCO 类别)
          └── batch 维度,恒为 1

提取代码:

python 复制代码
box    = output0[0, 0:4, :]      # [4, 8400]  --- cx, cy, w, h
cls_all = output0[0, 4:84, :]     # [80, 8400] --- 80 个类别
cls_per_anchor = cls_all.max(axis=0)  # [8400] --- 每个 anchor 的最高置信度

二、8400 个 anchor 的来源

YOLOv8 有 3 个检测头,每个检测头输出一个特征图,三个检测头的输出在 ONNX 图中 Concat 拼接:

检测头 特征图 anchor 数量 anchor 索引范围 负责
P3 (浅层) 80×80 6400 0 ~ 6399 小目标
P4 (中层) 40×40 1600 6400 ~ 7999 中目标
P5 (深层) 20×20 400 8000 ~ 8399 大目标
合计 --- 8400 --- ---

浅层/中层/深层:这个叫法来自 FPN(Feature Pyramid Network),指网络深度------P3 离输入最近(下采样 3 次),感受野小,细节多,适合小目标;P5 离输入最远(下采样 5 次),感受野大,语义强,适合大目标。


三、box 通道 (0-3)

通道 含义 值域 说明
0 cx 0 ~ 640 目标中心点 x 坐标
1 cy 0 ~ 640 目标中心点 y 坐标
2 w 0 ~ 640 目标宽度
3 h 0 ~ 640 目标高度

值域特点 :box 坐标的值域大约是 0 ~ 637 (最大不超过输入尺寸 640),是一个大值域

DFL 解码已在 ONNX 图中完成

这 4 个值已经是最终坐标,不需要在后处理中再做 DFL 解码。对比两种导出模式:

复制代码
9 输出模式(旧):
  ONNX 输出: 64 个原始 logits → 后处理中做 DFL 解码

1 输出模式(默认):
  ONNX 图中: 64 个 logits → Softmax → Conv(dfl) → Sub → Div → 最终坐标
  ONNX 输出: 4 个坐标值,已解码完毕,拿过来直接用

所以 output0[0, 0:4, :] 取出来的 cx, cy, w, h 就是可以直接用的最终坐标:

python 复制代码
cx, cy, w, h = output0[0, 0:4, i]   # 直接就是最终坐标,无需再解码

坐标系:已经映射到 640×640 输入图像

DFL 解码得到的坐标经过缩放(ONNX 图中由 Mul 按各检测头 stride 8/16/32 放大),将坐标从特征图像素空间映射到了 640×640 的输入图像空间。因此值域 0~640 就是输入图像上的像素位置:

复制代码
特征图坐标 (0~80)  →  ×stride(8/16/32)  →  640×640 输入图像坐标 (0~640)

注意:这个坐标是相对于 640×640 的 letterbox 输入,不是原图。如果原图不是 640×640,需要按 letterbox 的 scale 和 dx/dy 反算回原图坐标。

python 复制代码
box = output0[0, 0:4, :]   # shape [4, 8400]

# 典型输出(bus.jpg,640×640):
# cx: min=3.66, max=635.63, mean=320.00
# cy: min=3.06, max=637.28, mean=321.99
#  w: min=6.27, max=456.73, mean=66.87
#  h: min=4.23, max=607.05, mean=79.59

四、cls 通道 (4-83)

通道 含义 值域 说明
4 类别 0 0 ~ 1 已过 Sigmoid
5 类别 1 0 ~ 1 已过 Sigmoid
... ... ... ...
83 类别 79 0 ~ 1 已过 Sigmoid

值域特点 :cls 置信度的值域是 0 ~ 1 ,是一个小值域

Sigmoid 在哪里执行的?

Sigmoid 不在后处理代码中执行 ,而是在 ONNX 图内部已经完成。三个检测头的 cls 特征在 ONNX 图中先各自卷积(cv3),经 Reshape 后 Concat 合并为 [1, 80, 8400],最后统一过一个 Sigmoid 激活:

复制代码
ONNX 图内部:
  3 个检测头 cv3 卷积(各 80 通道 cls 特征)
    → Reshape → Concat 合并 [1, 80, 8400]
    → Sigmoid 激活 → 存入 output0[4:83]

所以从 output0 中取出的 cls 值已经是 0~1 的置信度,不需要再做任何激活函数

提取 cls 的常用指标

python 复制代码
cls = output0[0, 4:, :].max(axis=0)   # [8400] --- 每个 anchor 的最高置信度

cls.max()           # 所有 anchor 中最高 cls 置信度
cls.mean()          # 所有 anchor 的 cls 均值
(cls > 0.25).sum()  # 置信度 > 0.25 的 anchor 数量
(cls > 0.50).sum()  # 置信度 > 0.50 的 anchor 数量

五、box 和 cls 的值域差异

同一张图上,box 和 cls 的值域差异巨大:

python 复制代码
# 典型输出(同一张 bus.jpg):
# box 通道:  min=3.06, max=637.28, mean=197.11   ← 大值域
# cls 通道:  min=0.00, max=0.88,   mean=0.0001   ← 小值域

box 的值域是 cls 的 ~700 倍。 这个差异在两个值域合并到同一个张量后,对 INT8 量化有决定性影响------量化尺度会由 box 的大值域决定,cls 的小值域通道在量化后可能损失大部分精度,甚至被压成 0。


六、代码演示

完整代码演示如下(可直接复制运行):

bash 复制代码
# 依赖: onnxruntime, numpy, opencv-python

打印内容:

序号 内容
1 output0 整体结构(shape / dtype / ndim)
2 box 逐通道统计(cx, cy, w, h 的 min/max/mean/std)
3 box 前 20 个 anchor 值
4 cls 整体统计(min/max/mean/std)
5 cls 各阈值 anchor 数(>0.0, >0.01, >0.05, >0.10, >0.25, >0.50, >0.75, >0.90)
6 80 个类别逐类统计(每类的 max/mean/>0.25 的 anchor 数)
7 Top-30 高置信度 anchor(cls_max + cls_argmax + 4 个 box 坐标)
8 Top-10 的 80 个类别通道详细值(展开每个 anchor 的 cls 向量)
9 cls 分布直方图(文本,10 个区间柱状图)
10 三个检测头分别的 cls 分布(P3/P4/P5)

核心代码片段:

python 复制代码
import onnxruntime as ort
import numpy as np

# 加载模型
session = ort.InferenceSession("yolov8n.onnx", providers=['CPUExecutionProvider'])

# 预处理图片为 [1, 3, 640, 640]
x = preprocess("bus.jpg")

# 推理
outputs = session.run(None, {"images": x})
output0 = outputs[0]  # [1, 84, 8400]

# 拆分 box 和 cls
box = output0[0, 0:4, :]        # [4, 8400]
cls_all = output0[0, 4:84, :]   # [80, 8400]

# 每个 anchor 的最高置信度
cls_per_anchor = cls_all.max(axis=0)  # [8400]

# 高置信度 anchor
high_conf = (cls_per_anchor > 0.25).sum()
print(f"cls > 0.25 的 anchor 数: {high_conf}")

# Top-K 详细打印
top_indices = np.argsort(cls_per_anchor)[::-1][:10]
for idx in top_indices:
    print(f"anchor {idx}: cls={cls_per_anchor[idx]:.4f}, "
          f"box=({box[0,idx]:.1f}, {box[1,idx]:.1f}, "
          f"{box[2,idx]:.1f}, {box[3,idx]:.1f})")

七、总结

  1. 输出结构[1, 84, 8400],通道 0-3 是 box,通道 4-83 是 cls
  2. anchor 来源:P3(80×80) + P4(40×40) + P5(20×20) = 8400 个
  3. box 值域:0~640,大值域
  4. cls 值域:0~1,小值域,已过 Sigmoid
  5. 值域差:box 是 cls 的 ~700 倍,值域悬殊让单一量化尺度难以同时覆盖两者,量化时小值域通道会显著损失精度

自测

Q1:output0 的 shape 是什么?box 和 cls 分别在哪几个通道?
点击查看答案

output0 shape = [1, 84, 8400]

  • box: 通道 0-3(cx, cy, w, h)
  • cls: 通道 4-83(80 个 COCO 类别)

Q2:8400 个 anchor 是怎么来的?
点击查看答案

三个检测头的特征图拼接:

  • P3 (80×80) = 6400 个
  • P4 (40×40) = 1600 个
  • P5 (20×20) = 400 个

合计 8400 个。

Q3:cls 还需要做 Sigmoid 吗?
点击查看答案

不需要。Sigmoid 在 ONNX 图内部已完成(三个检测头的 cls 特征 Concat 合并后统一执行),output0[4:83] 取出的值已经是 0~1 的置信度。

Q4:box 和 cls 的值域差多少?为什么这个差异重要?
点击查看答案

box 值域 0~640,cls 值域 0~1,box 是 cls 的 ~700 倍。在 INT8 量化时,量化尺度由 box 的大值域决定,cls 的小值域通道会被压成 0,分类信息随之丢失。


一句话记住output0[0, 0:4, :] 取 box(值域 0~640),output0[0, 4:84, :].max(axis=0) 取每个 anchor 的最高 cls(值域 0~1),box 值域是 cls 的 ~700 倍。

相关推荐
动物园猫3 小时前
桑叶病害目标检测数据集:16,000张图像 | 目标检测
人工智能·目标检测·计算机视觉
ʜᴇɴʀʏ4 小时前
ICCV 2025 | STEP-DETR:基于超级教师与伪标签引导文本查询的半监督目标检测
人工智能·目标检测·计算机视觉·transformer
江畔柳前堤12 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
江畔柳前堤13 小时前
HBM:大语言模型时代的「算力血液」——从内存墙到带宽革命的深度拆解
服务器·人工智能·windows·目标检测·语言模型·自然语言处理·软件工程
guo_xiao_xiao_18 小时前
YOLO人物肖像发型分类目标检测数据集
yolo·目标检测·分类
guo_xiao_xiao_1 天前
YOLO室内训练场橙色环形训练圈目标检测数据集-156张
人工智能·yolo·目标检测
动物园猫2 天前
无人机灾害场景人体目标检测数据集:10,000张图像 | 目标检测
人工智能·目标检测·无人机
向哆哆2 天前
铁路障碍物目标检测数据集分享(适用于YOLO系列深度学习分类检测任务)
深度学习·yolo·目标检测
weixin_468466852 天前
目标检测精度上限与影响因素分析
图像处理·人工智能·目标检测·计算机视觉·图像分类·coco·检测精度