MSPN50

0 downloads 0 likes on ModelScope updated

Model card

MSPN50

1. 摘要

  1. 身份编号:fwb0042

  2. 类型与序号:MSPN50 方案1

  3. 进展:

    item status detail
    方案选择与硬件资源划分 1
    模型导出 1
    ~导出模型精度一致,提供python验证脚本 1
    cpu处理部分c++工程移植 1
    ~c++工程精度一致,提供验证脚本 1
    ~c++工程性能优化 1
    icraft编译通过 1
    ~存在量化精度调优工作 1
    ~完成基本pyrtcrt搭建与结果验证 1
    ~完成pyrtcrt精度测试与对齐 1
    完成上板耗时测试 1
    ~使用运行时提供的api进行基本性能优化 1
    ~存在整体方案各环节优化或优化方案的设计 1
    提出bug数 0

2. 方案简介

2.1 算法流程介绍

模型或算法背景:

  • MSPN(Multi-Stage Pose Network)是一种用于高精度多人人体姿态估计的框架,它通过多阶段、由粗到精的级联方式来逐步提升关键点定位的准确性。
  • 源仓库链接:https://github.com/open-mmlab/mmpose (作为MMPOSE项目的一部分)
  • 开发团队:微软亚洲研究院 (MSRA) (文方、李程、方慧芝等人主导开发)
  • 发布时间:2019年
  • 性能水平:MSPN在当时的COCO姿态估计挑战赛上取得了顶尖成绩。其核心思想——多阶段 refinement 和 跨阶段特征聚合——被证明是提升姿态估计精度的极其有效的方法,对后续的高精度姿态估计算法产生了深远影响。

在COCO val2017数据集上,使用ResNet-50作为主干的MSPN50与其他方法的性能对比:

Model Backbone Input Size GFLOPs Params(M) AP AP50 AP75 AR AR50
Hourglass Hourglass 256x192 19.7 25.1 66.9 - - - -
CPN ResNet-50 384x288 13.5 32.8 72.1 91.4 79.5 78.5 95.0
Simple Baseline ResNet-50 256x192 5.3 25.6 70.4 88.6 78.3 76.3 92.5
MSPN50 (2-stage) ResNet-50 256x192 12.2 29.1 74.1 90.0 81.6 79.5 93.8
MSPN50 (4-stage) ResNet-50 256x192 24.1 32.0 75.1 90.4 82.3 80.4 94.2

算法流程梳理:

运行时工程整体介绍

模型结构上的创新与意义:

MSPN的核心是一个级联式的多阶段架构,每个阶段都包含一个编码器-解码器结构,并将前一阶段的预测结果作为后一阶段的先验知识,进行逐步精炼。

人体检测框输入 ——> Stage 1 ——> 粗略热力图 ——> Stage 2 ——> 精炼热力图 ——> ... ——> Final Stage ——> 最终高精度热力图

MSPN模型结构

MSPN的架构可以分解为以下几个关键部分:

1. 多阶段级联 (Multi-Stage Cascade):

  • 整个网络由多个(通常是2或4个)结构相似的子网络(即“阶段”)串联而成。
  • 第一阶段:接收裁剪后的人像图片,进行一次完整的姿态估计,生成一个初步的、较为粗糙的关键点热力图。
  • 后续阶段:每个后续阶段的输入不仅包含原始图像下采样后的特征,还融合了前一阶段输出的热力图。这相当于告诉当前阶段:“上一阶段认为关键点大概在这个位置,请你在这个基础上进行更精细的定位。”

2. 单阶段内的结构 (Intra-Stage Architecture):

  • 每个阶段内部都是一个经典的编码器-解码器(Encoder-Decoder)结构。
  • 编码器 (Downsample):通常采用标准的卷积神经网络(如ResNet-50)作为主干,负责从图像中提取多层次的特征。
  • 解码器 (Upsample):采用一系列反卷积(Deconvolution)或上采样+卷积层,逐步将高层语义特征恢复到较高的分辨率,并最终生成与输入尺寸1/4大小的热力图。

3. 跨阶段连接 (Cross-Stage Connection):

  • 这是MSPN设计的精髓。它并非简单地将前一阶段的热力图直接送入下一阶段,而是设计了精巧的跨阶段特征聚合单元。
  • 在每个尺度上(编码和解码过程中),当前阶段的特征图都会与前一阶段对应尺度的特征图进行融合。这通常通过一个1x1卷积进行压缩,然后进行元素相加(Element-wise Sum)来实现。
  • 这种设计确保了后一阶段不仅能看到前一阶段的最终预测,还能利用其所有中间层的丰富特征,从而实现更有效的信息传递和精炼。

4. 监督信号 (Supervision):

  • 多阶段监督:与只监督最终输出不同,MSPN在每个阶段的末尾都计算损失并进行监督。这相当于为每个子网络都设置了明确的学习目标,避免了梯度消失问题,使得整个深层网络能够被有效训练。

基本组件:

ResNet-50:作为每个阶段编码器的强大主干网络,用于提取高质量的视觉特征。 反卷积层 (Deconvolution Layer):在解码器中用于上采样,恢复特征图的分辨率。 跨阶段特征融合单元:通过1x1卷积和元素相加,将前一阶段的中间特征高效地传递给当前阶段。

前后处理

前处理:

  1. 人体检测与裁剪:
    • 首先,需要一个目标检测器(如YOLO, SSD)来检测出图像中的每个人。
    • 根据检测框对每个人进行裁剪,并将裁剪出的人像区域作为MSPN50的输入。
    • (为测试单模型部署效果,此处采用直接用裁剪后的图片作为输入)
  2. 尺寸调整与归一化:
    • 将裁剪出的人像区域缩放到模型所需的固定输入尺寸(如 256x192),并保持固定的宽高比。
    • 进行像素值的归一化(如减均值、除以标准差)。
      # 读取原始图像
        ori_img = cv2.imread(image_path)
        self.ori_height, self.ori_width = ori_img.shape[:2]
      
        # 调整尺寸 (保持宽高比填充到模型输入尺寸)
        input_height, input_width = self.input_shape[2], self.input_shape[3]
        scale = min(input_width / self.ori_width, input_height / self.ori_height)
        new_width = int(self.ori_width * scale)
        new_height = int(self.ori_height * scale)
      
        # 填充黑边使图像符合模型输入尺寸
        resized_img = cv2.resize(ori_img, (new_width, new_height))
        padded_img = np.zeros((input_height, input_width, 3), dtype=np.uint8)
        dx = (input_width - new_width) // 2
        dy = (input_height - new_height) // 2
        padded_img[dy:dy + new_height, dx:dx + new_width] = resized_img
      

后处理:

  1. 热力图解码:
    • 模型输出一组热力图,每张图对应一个关键点。
    • 在每张热力图中找到响应值(概率)最高的像素点位置 (x, y)。
      heatmap=np.array(output_tensors[0]).transpose(0,3,1,2)
        batch_size, num_kpts, H, W = heatmap.shape
        heatmap = heatmap[0]  # 取batch中的第一个
      
        keypoints = []
        for i in range(num_kpts):
            hm = heatmap[i]
            max_loc = np.unravel_index(np.argmax(hm), hm.shape)
      
  2. 坐标映射:
    • 将从热力图中解码出的坐标,从模型的输入尺寸(256x192)反向映射回原始图像的坐标系中。
      score = hm[max_loc]
        x=max_loc[1]*(256/64)
        y=max_loc[0]*(192/48)
        # 从热图坐标转换到输入图像坐标
        x = (x - pad_info['padding'][0]) / pad_info['scale']
        y = (y - pad_info['padding'][1]) / pad_info['scale']
      
        # 限制在图像范围内
        x = np.clip(x, 0, ori_shape[0] - 1)
        y = np.clip(y, 0, ori_shape[1] - 1)
      
        keypoints.append([x, y, score])
      

2.2 硬件资源分配

psin模式:由电脑端传入测试图片

前处理:图片的resize和pad等由CPU完成,从PS->PL由FPGA完成

网络主体:npu

后处理热力图解码等:fpga

后处理坐标映射等:cpu

可视化:cpu

4. 模型导出说明

关于模型源码:

模型源码为OPENMMLAB的MMPOSE库,具体安装参考如下链接: https://mmpose.readthedocs.io/zh-cn/latest/installation.html

MMPOSE源码仓库链接为:https://github.com/open-mmlab/mmpose.git

从MMPOSE导出模型为ONNX需要安装OPENMMLAB的MMDEPLOY库,具体安装参考如下链接: https://mmdeploy.readthedocs.io/zh-cn/stable/get_started.html#mmdeploy

MMDEPLOY源码仓库链接为:https://github.com/open-mmlab/mmdeploy.git

关于模型导出: 确保文件夹结构如下所示:

├── mmdeploy >openmmlab用于部署导出文件的代码

├── mmpose >openmmlab关于关键点检测的代码

路径切到mmdeploy中,然后并下载好对应预训练模型,放在对应位置,执行下面代码,模型输出在rtmpose-ort文件夹下

python tools/deploy.py configs/mmpose/pose-detection_onnxruntime_static.py ../mmpose/configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_mspn50_8xb32-210e_coco-256x192.py ../mmpose/ckpt/mspn50_coco_256x192.pth demo/resources/human-pose.jpg --work-dir rtmpose-ort/rtmpose-m --device cpu --show --dump-info

不额外做处理,直接将pt模型导出为onnx

5. cpu处理部分c++工程移植说明

1. CPU环节主要实现的内容

  • 1.读取并设置输入yaml文件中的相关参数,以及载入测试图片等功能
  • 2.图片的resize和pad,以符合模型输入尺寸
  • 3.在模型后处理部分,负责将热力图解码得到的坐标映射到原图中,并实现可视化
  • 4.在MSPN50_task_queue.cpp中,还负责控制多线程实现并行计算
  • 5.在16bit精度的工程中,由于无法使用硬算子实现热力图解码,这一部分也由CPU实现

2. 做了哪些测试验证移植后准确性

  • 分别做了仿真阶段以及实际Socket上板阶段的精度测试,经测试精度一致,符合准确性
  • 分别做了在pyrt和crt下的上板精度测试,经过测试精度一致,符合准确性

3.各环节耗时

在8bit条件下,耗时如下图所示 8bit各阶段耗时

在16bit条件下,耗时如下图所示 16bit各阶段耗时

在混合精度条件下,耗时如下图所示 mixbit各阶段耗时

6. ICRAFT编译


编译通过

  • 16bit下推理测试结果:

    框架动态图推理 导出静态模型推理 解析组件仿真 量化组件仿真 指令组件仿真
    alt text alt text alt text alt text alt text
  • 8bit,16bit,混合精度的精度测试结果

  • 8bit测试结果:

      --- Evaluation Summary ---
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.570
      Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.851
      Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.631
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.558
      Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.590
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.699
      Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.910
      Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.771
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.665
      Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.748
      ------------------------
    
  • 16bit测试结果:

      --- Evaluation Summary ---
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.582
      Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.856
      Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.646
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.561
      Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.620
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.709
      Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.917
      Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.781
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.676
      Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.758
      ------------------------
    
  • 混合精度测试结果:

      --- Evaluation Summary ---
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.574
      Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.854
      Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.635
      Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.553
      Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.617
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 20 ] = 0.704
      Average Recall     (AR) @[ IoU=0.50      | area=   all | maxDets= 20 ] = 0.916
      Average Recall     (AR) @[ IoU=0.75      | area=   all | maxDets= 20 ] = 0.777
      Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets= 20 ] = 0.670
      Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets= 20 ] = 0.753
      ------------------------
    
  • 编译过程的一些特殊说明

    • 量化校准集尽量选择经过目标识别后的裁剪图片,或者数据集中人物占图片比例较大的图片

7. 上板测试与优化

  1. 优化前各环节耗时统计(表格)

带imk,speedmode=false,compressftmp=false,fpga_argmax=false, socket上板 优化前各阶段耗时

  1. 优化后耗时统计(表格)

带imk,speedmode=true,compressftmp=true,fpga_argmax=true, socket上板 8bit各阶段耗时

  1. 优化项与效果

优化项1:合并算子

设置speedmode=true,compress=true实现算子合并,加速计算 运行时工程整体介绍

可见总耗时由原来的200ms左右变为10ms左右

优化项2:后处理上,去除cast算子,并用FPGA算子替代CPU实现热力图解码

运行时工程整体介绍

可见总耗时由10ms左右变为7ms左右

优化项3:采用多线程并行计算

采用多线程并行计算的方法,将整体runtime分为三个线程:

1.input_thread: 负责读取裁剪图片并输入imk算子完成搬数。

2.icore_thread[n]: 负责完成模型的前向推理工作。

3.post_thread:负责完成模型的后处理工作。

运行时工程整体介绍

在多线程条件下,运行工程吞吐率约在60-70FPS,单线程条件下,运行工程吞吐率约在40FPS,提升约150%

优化项4:前处理部分采用imagemake硬算子,缩短数据输入时间

  1. 运行时工程的整体介绍
运行时工程整体介绍

如上图流程图所示,整个流程展示了从模型部署、数据处理到硬件加速推理和结果生成的完整解决方案。程序首先通过解析 YAML 配置文件来初始化各项参数,包括模型路径、运行模式(硬件仿真或上板运行)、数据集位置以及后处理选项。随后,它加载预编译的神经网络模型(.json 和 .raw 文件),并根据配置创建与目标设备的会话(Session),完成模型的部署和资源分配。

在主处理循环中,程序逐一读取输入图像。对每张图像,它会执行一个保持纵横比的预处理操作:将图像缩放并填充(padding)至模型所需的输入尺寸,然后转换为框架所需的 Tensor 格式。该 Tensor 被送入 session.forward() 接口进行硬件加速推理。

推理完成后,程序进入后处理阶段。它从输出的热图(heatmap)中提取关键点坐标。此过程支持两种模式:一种是纯软件实现的 argmax 算法,在 CPU 上计算;另一种是高性能的硬件加速模式,调用 FPGA 上的 argmax 逻辑直接计算坐标。最后,程序将提取出的坐标经过反向变换,映射回原始图像尺寸,并将结果可视化(绘制关键点)或保存到指定目录。