RDK X5部署yolov8,全int16量化完成后python部署识别不到

root@OE-X5-CPU-1-2-8:/open_explorer/yolov8n_detect_bayese_640x640_nchw# pip list | grep horizon

horizon-nn 1.1.0

horizon_tc_ui 1.24.3

#####################################################################################################################

python3.10

#####################################################################################################################

以下为执行hrt_model_exec model_info --model_file <your_bin_model_file.bin>返回的结果,附yaml,hb_mapper*.log文件onnx模型太大超出限制无法上传

root@OE-X5-CPU-1-2-8:/open_explorer/yolov8n_detect_bayese_640x640_nchw# hrt_model_exec model_info --model_file yolov8n_detect_bayese_640x640_nchw.bin

hrt_model_exec model_info --model_file yolov8n_detect_bayese_640x640_nchw.bin

I0000 00:00:00.000000 16 vlog_is_on.cc:197] RAW: Set VLOG level for “*” to 3

core[0] open!

core[1] open!

[HBRT] set log level as 0. version = 3.15.55.0

[DNN] Runtime version = 1.24.5_(3.15.55 HBRT)

[A][DNN][packed_model.cpp:247][Model](2025-03-17,19:17:29.910.242) [HorizonRT] The model builder version = 1.24.3

Load model to DDR cost 294.319ms.

This model file has 1 model:

[yolov8n_detect_bayese_640x640_nchw]

---------------------------------------------------------------------

[model name]: yolov8n_detect_bayese_640x640_nchw

input[0]:

name: images

input source: HB_DNN_INPUT_FROM_DDR

valid shape: (1,3,640,640,)

aligned shape: (1,3,640,640,)

aligned byte size: 1228800

tensor type: HB_DNN_IMG_TYPE_RGB

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (1228800,409600,640,1,)

output[0]:

name: output0

valid shape: (1,80,80,64,)

aligned shape: (1,80,80,64,)

aligned byte size: 1638400

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (1638400,20480,256,4,)

output[1]:

name: 326

valid shape: (1,40,40,64,)

aligned shape: (1,40,40,64,)

aligned byte size: 409600

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (409600,10240,256,4,)

output[2]:

name: 334

valid shape: (1,20,20,64,)

aligned shape: (1,20,20,64,)

aligned byte size: 102400

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (102400,5120,256,4,)

output[3]:

name: 342

valid shape: (1,80,80,1,)

aligned shape: (1,80,80,1,)

aligned byte size: 25600

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (25600,320,4,4,)

output[4]:

name: 350

valid shape: (1,40,40,1,)

aligned shape: (1,40,40,1,)

aligned byte size: 6400

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (6400,160,4,4,)

output[5]:

name: 358

valid shape: (1,20,20,1,)

aligned shape: (1,20,20,1,)

aligned byte size: 1600

tensor type: HB_DNN_TENSOR_TYPE_F32

tensor layout: HB_DNN_LAYOUT_NCHW

quanti type: NONE

stride: (1600,80,4,4,)

---------------------------------------------------------------------

config_yolov8_detect_nchw.yaml-
hb_mapper_checker.log-
hb_mapper_makertbin.log

https://github.com/D-Robotics/rdk\_model\_zoo/tree/main

  • 目前问题在于:-

    1. 需要安装Model Zoo的最新的commit的README来修改输出头,并且注意输出头的顺序-

    2. 按照要求移除反量化节点-

    类别数: NUM_CLASSES, example: NUM_CLASSES=80-
    输入分辨率: H x W, example: 640 x 640;-
    Stride = 8, 16, 32;-
    H_8 = H // 8, example: 640 // 8 = 80;-
    W_8 = W // 8, example: 640 // 8 = 80;-
    H_16 = H // 16, example: 640 // 16 = 40;-
    W_16 = W // 16, example: 640 // 16 = 40;-
    H_32 = H // 32, example: 640 // 32 = 20;-
    W_32 = W // 32, example: 640 // 32 = 20;-

  • output[0]: 1 x H_8 x W_8 x NUM_CLASSES, example: 1 x 80 x 80 x 80

意义:Stride=8 的小目标感受野的目标分类分数信息的特征图-

  • output[1]: 1 x H_8 x W_8 x 64, example: 1 x 80 x 80 x 64 (X3, X5, Ultra部署方案中移除反量化节点)

意义:Stride=8 的小目标感受野的目标Bounding Box定位信息特征图-

  • output[2]: 1 x H_16 x W_16 x NUM_CLASSES, example: 1 x 40 x 40 x 40

意义:Stride=16 的中目标感受野的目标分类分数信息的特征图-

  • output[3]: 1 x H_16 x W_16 x 64, example: 1 x 40 x 40 x 64

意义:Stride=16 的小目标感受野的目标Bounding Box定位信息特征图 (X3, X5, Ultra部署方案中移除反量化节点)-

  • output[4]: 1 x H_32 x W_32 x NUM_CLASSES, example: 1 x 20 x 20 x 20

意义:Stride=32 的大目标感受野的目标分类分数信息的特征图-

  • output[5]: 1 x H_32 x W_32 x 64, example: 1 x 20 x 20 x 64

意义:Stride=32 的小目标感受野的目标Bounding Box定位信息特征图 (X3, X5, Ultra部署方案中移除反量化节点)-

请问修改输出头是这个吗

defforward(self, x): bboxes = [self.cv2[i](x[i]).permute(0, 2, 3, 1).contiguous() for i inrange(self.nl)] clses = [self.cv3[i](x[i]).permute(0, 2, 3, 1).contiguous() for i inrange(self.nl)] return (bboxes, clses)

能详细指下路吗,我没注意到哪里有更改输出头的内容

佬,你找到了吗,我也没找到,如果找到了能指一下路嘛,万分感谢!!

你好!根据你提供的 hrt_model_exec 输出和之前的讨论,问题非常明确:你的模型输出头顺序与 RDK X5 部署代码(后处理逻辑)不匹配。

核心结论

你当前的模型输出顺序是:

  1. output0: (1, 80, 80, 80) → 分类 (Cls) (Stride 8)
  2. output1: (1, 80, 80, 64) → 框 (Box) (Stride 8)
  3. output2: (1, 40, 40, 64) → 框 (Box) (Stride 16) (注意这里顺序变了)
  4. output3: (1, 40, 40, 1) → 分类 (Cls) (Stride 16)
  5. …以此类推

RDK Model Zoo 的 Python 部署代码 期望的顺序是(针对 X5 优化版):
先所有尺度的 Box,再所有尺度的 Cls,或者严格按照 Box, Cls, Box, Cls… 的特定交替顺序(取决于具体 README 版本,但通常是成对出现且 Box 在前)。

从 CauchyKesai 的提示来看,官方推荐的 X5 部署顺序是:每个尺度下,先 Box (64通道),后 Cls (80通道)
即期望顺序:

  • Output 0: Box (80x80x64)
  • Output 1: Cls (80x80x80)
  • Output 2: Box (40x40x64)
  • Output 3: Cls (40x40x80)

你的模型目前是 Cls, Box, Box, Cls… 这种混乱顺序,导致后处理代码读取数据时,把分类分数当成了坐标,把坐标当成了分数,因此检测不到任何目标。


解决步骤

第一步:修改 YOLOv8 导出代码 (关键)

你需要修改 Ultralytics YOLOv8 的导出逻辑,调整 cv2 (Box) 和 cv3 (Cls) 的输出顺序。

找到你的 YOLOv8 代码中的 head.pyyolo.py (通常在 ultralytics/nn/head.py),定位到 forward 函数。

原始代码 (可能导致 Cls 在前):

def forward(self, x):
    # 原始逻辑可能是先分类后检测,或者顺序不一致
    bboxes = [self.cv2[i](x[i]).permute(0, 2, 3, 1).contiguous() for i in range(self.nl)]
    clses = [self.cv3[i](x[i]).permute(0, 2, 3, 1).contiguous() for i in range(self.nl)]
    # 如果这里直接 return (clses, bboxes) 或者 append 顺序不对,就会出错
    return torch.cat([x for x in zip(clses, bboxes)], dim=1) # 错误示例

修改后的代码 (适配 RDK X5):
确保在构建 ONNX 时,输出的顺序是 Box, Cls, Box, Cls, Box, Cls

def forward(self, x):
    # x 是 P3, P4, P5 三个特征层
    bboxes = [self.cv2[i](x[i]).permute(0, 2, 3, 1).contiguous() for i in range(self.nl)]
    clses = [self.cv3[i](x[i]).permute(0, 2, 3, 1).contiguous() for i in range(self.nl)]
    
    # 重点:按照 Box, Cls 的顺序成对拼接
    # 结果顺序:[Box_80, Cls_80, Box_40, Cls_40, Box_20, Cls_20]
    outputs = []
    for i in range(self.nl):
        outputs.append(bboxes[i])
        outputs.append(clses[i])
    
    return outputs  # 返回一个列表,ONNX 导出时会变成 6 个输出节点

导出 ONNX 命令:
确保导出时不添加额外的后处理节点(如 NMS),保持原始输出。

yolo export model=yolov8n.pt format=onnx opset=11 simplify=True
# 或者使用你自定义的导出脚本,确保上述 forward 逻辑生效

第二步:验证 ONNX 输出顺序

在转换之前,务必检查 ONNX 模型的输出节点顺序。
使用 Netron 打开 ONNX 文件,或者用 Python 检查:

import onnx
model = onnx.load("yolov8n.onnx")
for i, out in enumerate(model.graph.output):
    print(f"Output {i}: {out.name}, Shape: {[d.dim_value for d in out.type.tensor_type.shape.dim]}")

期望输出:

  • Output 0: Shape [..., 64] (Box)
  • Output 1: Shape [..., 80] (Cls)
  • Output 2: Shape [..., 64] (Box)
  • Output 3: Shape [..., 80] (Cls)

如果顺序不对,重新修改代码导出。

第三步:移除反量化节点 (根据 CauchyKesai 提示)

RDK X5 部署时,通常要求模型内部不包含反量化 (Dequantize) 节点,让输出保持 INT16/INT8,由后端或后处理统一处理,或者工具链会自动处理。

  • hb_mapper 转换配置中,确认是否开启了 remove_dequantize 选项。
  • 或者在导出 ONNX 时,确保没有硬编码的 Dequantize 算子。
  • 查看你的 hrt_model_exec 输出,目前 quanti type: NONEtensor type: F32,这说明模型可能是浮点模型或者反量化已经在模型内执行了。如果是全 INT16 量化,这里应该显示 INT16
    • 注意:如果你做的是全 INT16 量化,但 hrt_model_exec 显示输出是 F32,说明模型最后加了反量化节点。RDK X5 的高性能部署通常建议输出保持量化值 (INT16),或者确认你的 Python 推理代码是否支持自动反量化。
    • 修正:在 hb_mapper 的配置文件 (yaml) 中,设置 output_quantize: false 或者在模型结构中移除最后的 Dequantize 层,让输出为 INT16,然后在 Python 代码中手动反量化,或者使用支持 INT16 输出的 API。

第四步:重新转换并验证

  1. 使用修正后的 ONNX 运行 hb_mapper
  2. 再次运行 hrt_model_exec model_info --model_file your_new_model.bin
  3. 确认输出顺序
    • Output 0: [..., 64] (Box)
    • Output 1: [..., 80] (Cls)
  4. 确认 quanti type 符合预期(如果是 INT16 部署,这里应体现量化信息)。

第五步:Python 部署代码适配

确保你的 Python 推理代码 (rdk_model_zoo 中的 demo) 是按 Box, Cls 顺序读取的。
参考 rdk_model_zoo 中 YOLOv8 的 postprocess.py

# 伪代码示例
# 假设 outputs 是模型返回的列表
box_80 = outputs[0]  # (1, 80, 80, 64)
cls_80 = outputs[1]  # (1, 80, 80, 80)
box_40 = outputs[2]  # (1, 40, 40, 64)
cls_40 = outputs[3]  # (1, 40, 40, 80)
# ...

资源指引

  1. RDK Model Zoo YOLOv8 目录:
    请拉取最新代码查看具体的 README_cn.mdexport.py 示例。
    git clone https://github.com/D-Robotics/rdk_model_zoo.git
    cd rdk_model_zoo/samples/vision/detection/YOLOv8
    # 仔细阅读 README_cn.md 中关于 "模型导出" 和 "输出头顺序" 的章节
    
  2. 相关帖子参考:

总结

你现在的模型输出顺序是 Cls, Box, Box, Cls…,而代码期望的是 Box, Cls, Box, Cls…
行动点:修改 YOLOv8 的 forward 函数,调整 cv2 (Box) 和 cv3 (Cls) 的 append 顺序,重新导出 ONNX,重新量化,重新部署。

如果在修改 head.py 时遇到困难,可以直接复制 rdk_model_zoo 仓库中提供的修改版 head.pyyolo.py 到你的训练工程中。