YOLOv8-Pose NCNN安卓部署

YOLOv8-Pose NCNN安卓部署

前言

YOLOv8-Pose NCNN安卓部署
在这里插入图片描述

目前的帧率可以稳定在30帧左右,下面是这个项目的github地址:https://github.com/gaoxumustwin/ncnn-android-yolov8-pose

介绍

在做YOLOv8-Pose NCNN安卓部署的时候,在github上发现了https://github.com/eecn/ncnn-android-yolov8-pose已经有大佬实现了这一操,并且也给出了它是如何导出ncnn模型的;

该作者是使用YOLOv8的官方给出的NCNN模型导出的方式,我按照这个方式导出ncnn模型,并观察其网络结构如下:

在这里插入图片描述

这个网络结构有一些错综复杂,感觉是有大量的计算图追踪,但是其安卓部署的时候速度在20帧左右也还可以接受,并且YOLOv8的官方给出了NCNN推理的python代码

https://github.com/ultralytics/ultralytics/tree/a007668e1fa8d5d586e6daa3924d65cfb139b8ac/examples/YOLOv8-NCNN-Python-Det-Pose-Cls-Seg-Obb

由于之前看到了三木君大佬,在23年的时候在知乎发布的一篇文章,只导出模型的Backbone + Neck,这种思路在玩瑞芯微设备的时候多次接触,三木君大佬也给出了原因:

  • 只导出模型,不导出后处理方便对模型进行公平的测速,MMYOLO 里面的算法多种多样,而且大多是 Backbone + Neck 结构,这样测速才能较为公平体现模型的速度差异。
  • 有些模型需要部署到嵌入式设备,导出后处理的话相当与会有海量的加减乘除要计算,由于模型应用会通过置信度过滤很多检测结果,会导致大多计算都是多余的。
  • 有些推理后端对后处理算子支持很差,可能会无法转换到对应后端的模型,或者转换后的模型无法充分利用 BPU/TPU/NPU/GPU 加速等等问题存在。
  • 很多要部署的模型都会用 c/c++ 重写后处理加速,后处理代码可以多线程计算,异步计算效率会更高。

同时在CSDN上发现了有佬根据上面的思路编写了NCNN代码并且给出了NCNN代码的github地址

此时只需要参照前面的yolov8安卓部署实现把这个只导出模型的Backbone + Neck的YOLOv8-Pose NCNN代码进行简单改写就可以实现

环境

我使用的ultralytics版本如下:

pip install ultralytics==8.2.98

安装好ultralytics后,后面的操纵需要更改源码,所以需要知道ultralytics安装的路径,可以使用下面的方式进行查询

>>> import ultralytics
>>> ultralytics.__version__
'8.2.98'
>>> ultralytics.__path__
pathto\ultralytics

修改

模型导出时的网络修改

修改pathto\ultralytics\nn\modules\head.py文件中的POSE类,在其forward函数中加如下代码

if self.export or torch.onnx.is_in_onnx_export():
    results = self.forward_pose_export(x)
    return tuple(results)

同时在POSE类加上新如下函数

def forward_pose_export(self, x):
    results = []
    for i in range(self.nl):
        dfl = self.cv2[i](x[i]).permute(0, 2, 3, 1).contiguous()
        cls = self.cv3[i](x[i]).permute(0, 2, 3, 1).contiguous()
        kpt = self.cv4[i](x[i]).permute(0, 2, 3, 1).contiguous()
        results.append(torch.cat((cls, dfl, kpt), -1))
    return results

修改后的整体代码效果如下:

class Pose(Detect):
    """YOLOv8 Pose head for keypoints models."""

    def __init__(self, nc=80, kpt_shape=(17, 3), ch=()):
        """Initialize YOLO network with default parameters and Convolutional Layers."""
        super().__init__(nc, ch)
        self.kpt_shape = kpt_shape  # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible)
        self.nk = kpt_shape[0] * kpt_shape[1]  # number of keypoints total

        c4 = max(ch[0] // 4, self.nk)
        self.cv4 = nn.ModuleList(nn.Sequential(Conv(x, c4, 3), Conv(c4, c4, 3), nn.Conv2d(c4, self.nk, 1)) for x in ch)

    def forward(self, x):
        """Perform forward pass through YOLO model and return predictions."""
        if self.export or torch.onnx.is_in_onnx_export():
            results = self.forward_pose_export(x)
            return tuple(results)

        bs = x[0].shape[0]  # batch size
        kpt = torch.cat([self.cv4[i](x[i]).view(bs, self.nk, -1) for i in range(self.nl)], -1)  # (bs, 17*3, h*w)
        x = Detect.forward(self, x)
        if self.training:
            return x, kpt
        pred_kpt = self.kpts_decode(bs, kpt)
        return torch.cat([x, pred_kpt], 1) if self.export else (torch.cat([x[0], pred_kpt], 1), (x[1], kpt))

    def forward_pose_export(self, x):
        results = []
        for i in range(self.nl):
            dfl = self.cv2[i](x[i]).permute(0, 2, 3, 1).contiguous()
            cls = self.cv3[i](x[i]).permute(0, 2, 3, 1).contiguous()
            kpt = self.cv4[i](x[i]).permute(0, 2, 3, 1).contiguous()
            results.append(torch.cat((cls, dfl, kpt), -1))
        return results

这种代码修改的导出方式并不影响训练的过程,仅在导出的时候会起到效果

更换激活函数并重头开始训练

YOLOv8默认使用的激活函数是SiLU,为了能更快的提升速度这里我换成了计算更高效的ReLU,并且更换激活函数后在转化成NCNN模型进行优化的时候会将卷积和ReLU融合之后,推理速度进一步得到了一些提升

更换激活函数后,需要对原有的Pytorch模型进行重新训练再导出ONNX

修改pathto\ultralytics\nn\modules\conv.py中的第39行左右的default_act = nn.SiLU() 修改为 default_act = nn.ReLU()

修改完成后使用训练脚本进行训练

from ultralytics import YOLO

model = YOLO('yolov8n-pose.yaml').load('yolov8n-pose.pt')  

results = model.train(data='coco-pose.yaml', epochs=100, imgsz=640, workers=4, batch=64, project='Pose_runs', name='pose_n_relu')

**注意: **

1、 .load(‘yolov8n-pose.pt’)时加载预训练模型,虽然预训练的激活函数不一样,但是我测试了以下发现加上预训练模型前面的epoch的结果损失很

2、上面的数据使用的是coco2017数据集,训练环境为4090显卡,训练10个epoch接近一个小时

3、coco-pose.yaml、yolov8n-pose.yaml均是ultralytics默认配置文件

导出的ONNX名字修改

如果需要修改输出的名称则要去修改 pathto\ultralytics\engine\exporter.py 中的 export_onnx函数

ONNX导出

编写一个export.py的导出onnx的python代码

from ultralytics import YOLO

# load a pretrained model
model = YOLO('pathto\best.pt') # 训练得到的权重

# export onnx
model.export(format='onnx', opset=11, simplify=True, dynamic=False, imgsz=640)

NCNN转化和优化

下面是onnx转化为NCNN的代码和对NCNN模型进行fp16的优化

./onnx2ncnn yolov8n-pose.onnx yolov8n-pose-sim.param yolov8n-pose-sim.bin
./ncnnoptimize yolov8n-pose-sim.param  yolov8n-pose-sim.bin yolov8n-pose-opt-fp16.param yolov8n-pose-opt-fp16.bin 1

在使用ncnnoptimize对relu激活函数的onnx模型进行优化的时候,你会发现relu和卷积会进行算子融合

(base) gx@RUKN0DC:/mnt/e/ubuntu_20.04/ncnn/build/install/bin$ ./ncnnoptimize yolov8pose-relu.param yolov8pose-relu.bin yolov8pose-relu-opt.param yolov8pose-relu-opt.bin 1
fuse_convolution_activation /model.0/conv/Conv /model.0/act/Relu
fuse_convolution_activation /model.1/conv/Conv /model.1/act/Relu
fuse_convolution_activation /model.2/cv1/conv/Conv /model.2/cv1/act/Relu
fuse_convolution_activation /model.2/m.0/cv1/conv/Conv /model.2/m.0/cv1/act/Relu
fuse_convolution_activation /model.2/m.0/cv2/conv/Conv /model.2/m.0/cv2/act/Relu
fuse_convolution_activation /model.2/cv2/conv/Conv /model.2/cv2/act/Relu
fuse_convolution_activation /model.3/conv/Conv /model.3/act/Relu
fuse_convolution_activation /model.4/cv1/conv/Conv /model.4/cv1/act/Relu
fuse_convolution_activation /model.4/m.0/cv1/conv/Conv /model.4/m.0/cv1/act/Relu
fuse_convolution_activation /model.4/m.0/cv2/conv/Conv /model.4/m.0/cv2/act/Relu
fuse_convolution_activation /model.4/m.1/cv1/conv/Conv /model.4/m.1/cv1/act/Relu
fuse_convolution_activation /model.4/m.1/cv2/conv/Conv /model.4/m.1/cv2/act/Relu
fuse_convolution_activation /model.4/cv2/conv/Conv /model.4/cv2/act/Relu
fuse_convolution_activation /model.5/conv/Conv /model.5/act/Relu
fuse_convolution_activation /model.6/cv1/conv/Conv /model.6/cv1/act/Relu
fuse_convolution_activation /model.6/m.0/cv1/conv/Conv /model.6/m.0/cv1/act/Relu
fuse_convolution_activation /model.6/m.0/cv2/conv/Conv /model.6/m.0/cv2/act/Relu
fuse_convolution_activation /model.6/m.1/cv1/conv/Conv /model.6/m.1/cv1/act/Relu
fuse_convolution_activation /model.6/m.1/cv2/conv/Conv /model.6/m.1/cv2/act/Relu
fuse_convolution_activation /model.6/cv2/conv/Conv /model.6/cv2/act/Relu
fuse_convolution_activation /model.7/conv/Conv /model.7/act/Relu
fuse_convolution_activation /model.8/cv1/conv/Conv /model.8/cv1/act/Relu
fuse_convolution_activation /model.8/m.0/cv1/conv/Conv /model.8/m.0/cv1/act/Relu
fuse_convolution_activation /model.8/m.0/cv2/conv/Conv /model.8/m.0/cv2/act/Relu
fuse_convolution_activation /model.8/cv2/conv/Conv /model.8/cv2/act/Relu
fuse_convolution_activation /model.9/cv1/conv/Conv /model.9/cv1/act/Relu
fuse_convolution_activation /model.9/cv2/conv/Conv /model.9/cv2/act/Relu
fuse_convolution_activation /model.12/cv1/conv/Conv /model.12/cv1/act/Relu
fuse_convolution_activation /model.12/m.0/cv1/conv/Conv /model.12/m.0/cv1/act/Relu
fuse_convolution_activation /model.12/m.0/cv2/conv/Conv /model.12/m.0/cv2/act/Relu
fuse_convolution_activation /model.12/cv2/conv/Conv /model.12/cv2/act/Relu
fuse_convolution_activation /model.15/cv1/conv/Conv /model.15/cv1/act/Relu
fuse_convolution_activation /model.15/m.0/cv1/conv/Conv /model.15/m.0/cv1/act/Relu
fuse_convolution_activation /model.15/m.0/cv2/conv/Conv /model.15/m.0/cv2/act/Relu
fuse_convolution_activation /model.15/cv2/conv/Conv /model.15/cv2/act/Relu
fuse_convolution_activation /model.16/conv/Conv /model.16/act/Relu
fuse_convolution_activation /model.18/cv1/conv/Conv /model.18/cv1/act/Relu
fuse_convolution_activation /model.18/m.0/cv1/conv/Conv /model.18/m.0/cv1/act/Relu
fuse_convolution_activation /model.18/m.0/cv2/conv/Conv /model.18/m.0/cv2/act/Relu
fuse_convolution_activation /model.18/cv2/conv/Conv /model.18/cv2/act/Relu
fuse_convolution_activation /model.19/conv/Conv /model.19/act/Relu
fuse_convolution_activation /model.21/cv1/conv/Conv /model.21/cv1/act/Relu
fuse_convolution_activation /model.21/m.0/cv1/conv/Conv /model.21/m.0/cv1/act/Relu
fuse_convolution_activation /model.21/m.0/cv2/conv/Conv /model.21/m.0/cv2/act/Relu
fuse_convolution_activation /model.21/cv2/conv/Conv /model.21/cv2/act/Relu
fuse_convolution_activation /model.22/cv2.0/cv2.0.0/conv/Conv /model.22/cv2.0/cv2.0.0/act/Relu
fuse_convolution_activation /model.22/cv2.0/cv2.0.1/conv/Conv /model.22/cv2.0/cv2.0.1/act/Relu
fuse_convolution_activation /model.22/cv3.0/cv3.0.0/conv/Conv /model.22/cv3.0/cv3.0.0/act/Relu
fuse_convolution_activation /model.22/cv3.0/cv3.0.1/conv/Conv /model.22/cv3.0/cv3.0.1/act/Relu
fuse_convolution_activation /model.22/cv4.0/cv4.0.0/conv/Conv /model.22/cv4.0/cv4.0.0/act/Relu
fuse_convolution_activation /model.22/cv4.0/cv4.0.1/conv/Conv /model.22/cv4.0/cv4.0.1/act/Relu
fuse_convolution_activation /model.22/cv2.1/cv2.1.0/conv/Conv /model.22/cv2.1/cv2.1.0/act/Relu
fuse_convolution_activation /model.22/cv2.1/cv2.1.1/conv/Conv /model.22/cv2.1/cv2.1.1/act/Relu
fuse_convolution_activation /model.22/cv3.1/cv3.1.0/conv/Conv /model.22/cv3.1/cv3.1.0/act/Relu
fuse_convolution_activation /model.22/cv3.1/cv3.1.1/conv/Conv /model.22/cv3.1/cv3.1.1/act/Relu
fuse_convolution_activation /model.22/cv4.1/cv4.1.0/conv/Conv /model.22/cv4.1/cv4.1.0/act/Relu
fuse_convolution_activation /model.22/cv4.1/cv4.1.1/conv/Conv /model.22/cv4.1/cv4.1.1/act/Relu
fuse_convolution_activation /model.22/cv2.2/cv2.2.0/conv/Conv /model.22/cv2.2/cv2.2.0/act/Relu
fuse_convolution_activation /model.22/cv2.2/cv2.2.1/conv/Conv /model.22/cv2.2/cv2.2.1/act/Relu
fuse_convolution_activation /model.22/cv3.2/cv3.2.0/conv/Conv /model.22/cv3.2/cv3.2.0/act/Relu
fuse_convolution_activation /model.22/cv3.2/cv3.2.1/conv/Conv /model.22/cv3.2/cv3.2.1/act/Relu
fuse_convolution_activation /model.22/cv4.2/cv4.2.0/conv/Conv /model.22/cv4.2/cv4.2.0/act/Relu
fuse_convolution_activation /model.22/cv4.2/cv4.2.1/conv/Conv /model.22/cv4.2/cv4.2.1/act/Relu
Input layer images without shape info, shape_inference skipped
Input layer images without shape info, estimate_memory_footprint skipped

下面是ONNX、NCNN模型以及NCNN优化后的三个模型结构简单对比

在这里插入图片描述

下面是不同的激活函数最后NCNN优化后的网络结构简单对比

在这里插入图片描述

安卓代码的修改

参考这两个代码进行修改

https://github.com/eecn/ncnn-android-yolov8-pose

https://github.com/Rachel-liuqr/yolov8s-pose-ncnn

有以下几个修改的地方:

  1. 将sigmoid函数修改为了使用快速指数fast_exp的sigmoid
  2. 将 cv::dnn::NMSBoxes 修改了使用纯C++代码的实现

具体的代码过程,有兴趣的可以去查看

本人水平高,代码应该还有更大的优化空间!!

参考资料

https://github.com/eecn/ncnn-android-yolov8-pose

https://github.com/ultralytics/ultralytics/tree/a007668e1fa8d5d586e6daa3924d65cfb139b8ac/examples/YOLOv8-NCNN-Python-Det-Pose-Cls-Seg-Obb
https://blog.csdn.net/Rachel321/article/details/130381788

https://github.com/Rachel-liuqr/yolov8s-pose-ncnn

https://zhuanlan.zhihu.com/p/622596922

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/926852.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【热门主题】000077 物联网智能项目:开启智能未来的钥匙

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【热…

【STM32学习】TB6612FNG驱动芯片的学习,驱动电路的学习

目录 1、TB6612电机驱动芯片 1.1如下是芯片的引脚图: 1.2如下图是电机的控制逻辑: 1.3MOS管运转逻辑 1.3典型应用电路 2、H桥驱动电路 2.1、单极模式 2.2、双极模式 2.3、高低端MOS管导通条件 2.4、H桥电路设计 2.5、自举电路 3、电气特性 3…

day01(Linux底层)基础知识

目录 导学 基础知识 1、Bootloader是什么 2、Bootloader的基本作用 3、入式中常见的Bootloader有哪些 4、Linux系统移植为什么要使用bootloader 5、uboot和Bootloader之间的关系 6.Uboot的获取 7、uboot版本命名 8、uboot版本选择 9、uboot的特点 10.Uboot使用 导学…

OpenFeign 服务调用

1.简介 微服务架构中使用 OpenFeign 进行服务调用, OpenFeign 提供了一种简洁的方式来定义和处理服 务间的调用。 OpenFeign 作为一个声明式的、模块化的 HTTP 客户端,通过 「接口」 的定义和 「注解」 的使用,简化了微服务之间的通信调用。…

superset load_examples加载失败解决方法

如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …

k8s集成skywalking

如果能科学上网的话,安装应该不难,如果有问题可以给我留言 本篇文章我将给大家介绍“分布式链路追踪”的内容,对于目前大部分采用微服务架构的公司来说,分布式链路追踪都是必备的,无论它是传统微服务体系亦或是新一代…

深度学习Python基础(2)

二 数据处理 一般来说PyTorch中深度学习训练的流程是这样的: 1. 创建Dateset 2. Dataset传递给DataLoader 3. DataLoader迭代产生训练数据提供给模型 对应的一般都会有这三部分代码 # 创建Dateset(可以自定义) dataset face_dataset # Dataset部分自定义过的…

【Git教程 之 安装】

Git教程 之 安装 Git教程 之 安装Windows系统安装gitLinux安装gitmacOS安装Git Git教程 之 安装 Windows系统安装git 首先从官网上直接下载安装 选择对应的操作系统,我电脑是Windows,所以我演示以Windows为主 点进去 点击Click here to download 下…

ElasticSearch学习记录

服务器操作系统版本:Ubuntu 24.04 Java版本:21 Spring Boot版本:3.3.5 如果打算用GUI,虚拟机安装Ubuntu 24.04,见 虚拟机安装Ubuntu 24.04及其常用软件(2024.7)_ubuntu24.04-CSDN博客文章浏览阅读6.6k次&#xff0…

【AI】数据,算力,算法和应用(3)

三、算法 算法这个词,我们都不陌生。 从接触计算机,就知道有“算法”这样一个神秘的名词存在。象征着专业、权威、神秘、高难等等。 算法是一组有序的解决问题的规则和指令,用于解决特定问题的一系列步骤。算法可以被看作是解决问题的方法…

Java代码操作Zookeeper(使用 Apache Curator 库)

1. Zookeeper原生客户端库存在的缺点 复杂性高:原生客户端库提供了底层的 API,需要开发者手动处理很多细节,如连接管理、会话管理、异常处理等。这增加了开发的复杂性,容易出错。连接管理繁琐:使用原生客户端库时&…

SAP SD学习笔记17 - 投诉处理3 - Credit/Debit Memo依赖,Credit/Debit Memo

上一章讲了 请求书(发票)的取消。 SAP SD学习笔记16 - 请求书的取消 - VF11-CSDN博客 再往上几章,讲了下图里面的返品传票: SAP SD学习笔记14 - 投诉处理1 - 返品处理(退货处理)的流程以及系统实操&#…

Flink--API 之Transformation-转换算子的使用解析

目录 一、常用转换算子详解 (一)map 算子 (二)flatMap 算子 (三)filter 算子 (四)keyBy 算子 元组类型 POJO (五)reduce 算子 二、合并与连接操作 …

qt QToolBox详解

1、概述 QToolBox是Qt框架中的一个控件,它提供了一个带标签页的容器,用户可以通过点击标签页标题来切换不同的页面。QToolBox类似于一个带有多页选项卡的控件,但每个“选项卡”都是一个完整的页面,而不仅仅是标签。这使得QToolBo…

MySQL 复合查询

实际开发中往往数据来自不同的表,所以需要多表查询。本节我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE 来演示如何进行多表查询。表结构的代码以及插入的数据如下: DROP database IF EXISTS scott; CREATE database IF NOT EXIST…

分布式系统中的Dapper与Twitter Zipkin:链路追踪技术的实现与应用

目录 一、什么是链路追踪? 二、核心思想Dapper (一)Dapper链路追踪基本概念概要 (二)Trace、Span、Annotations Trace Span Annotation 案例说明 (三)带内数据与带外数据 带外数据 带…

RealESRGAN技术详解(附代码)

一、背景与动机 1.研究背景 1.1 图像超分辨率的挑战 图像超分辨率是一个长期存在的计算机视觉问题,它旨在从低分辨率(LR)图像中恢复出高分辨率(HR)图像。由于成像系统的局限性、传输过程中的压缩、存储空间的限制以及…

零拷贝相关知识点(一)

前言 大家好,我是程序员田螺。 零拷贝是老生常谈的问题啦,大厂非常喜欢问。比如Kafka为什么快,RocketMQ为什么快等,都涉及到零拷贝知识点。最近技术讨论群几个伙伴分享了阿里、虾皮的面试真题,也都涉及到零拷贝。因此…

Warcraft Logs [Classic] [WCL] exe download, set up, setting upload data

Warcraft Logs [Classic] [WCL] exe download, set up, setting & upload data WCL客户端下载,安装,配置和如何上传数据 Warcraft Logs - Combat Analysis for Warcraft 漫长的22分钟下载真的够慢的 到此为止,WCL客户端才安装完成 双击…

DIY搭建网站(学术个人介绍主页)

本教程介绍了如何创建并管理一个基于GitHub Pages的个人网站。首先,需要在GitHub上创建一个遵循特定命名规则的新仓库,例如用户名.github.io,以便建立个人站点。接着,通过Fork一个开源模板代码仓库并添加index.html文件来构建主页…