PyTorch模型安卓部署流程(NCNN)

news2024/9/22 6:13:10

        上一篇介绍了PyTorch模型部署流程(Onnx Runtime)的相关部署流程,再来简单的回顾一下~

深度学习模型部署介绍

        模型部署指让训练好的深度学习模型在特定环境中运行的过程。模型部署会面临的难题:

  • 运行模型所需的环境难以配置。深度学习模型通常是由一些框架编写,比如 PyTorch、TensorFlow。由于框架规模、依赖环境限制,框架不适合在手机、开发板等生产环境中安装。
  • 深度学习模型的结构通常比较庞大,需要大量算力才能满足实时运行的需求。运行效率需要优化。

        因为这些难题的存在,模型部署不能靠简单的环境配置与安装完成。目前模型部署有一条流行的流水线:

        为了让模型最终能够部署到某一环境上,可以使用任意一种深度学习框架来定义网络结构,并通过训练确定网络中的参数。之后,模型的结构和参数会被转换成一种只描述网络结构的中间表示,一些针对网络结构的优化会在中间表示上进行。最后,用面向硬件的高性能编程框架(如 CUDA,OpenCL)编写,能高效执行深度学习网络中算子的推理引擎会把中间表示转换成特定的文件格式,并在对应硬件平台上高效运行模型。

        那么,如果我们想在手机端运行我们的深度学习模型需要怎么做呢?接下来为大家介绍关于端侧深度学习的部署流程,大家了解了基本概念之后可以尝试运行官方demo实例,并根据自己需求进行改进,这样就可以在自己的手机上部署深度学习模型啦~

端侧深度学习模型部署流程

        主要技术路线为:训练框架训练模型->模型转化为ONNX格式->ONNX格式转化为其他格式(NCNN,TNN,MNN的模型格式)->在对应的推理框架中部署。

        端侧深度学习的应用可以分成如下几个阶段:

  • 模型训练阶段,主要解决模型训练,利用标注数据训练出对应的模型文件。面向端侧设计模型时,需要考虑模型大小和计算量;
  • 模型压缩阶段,主要优化模型大小,可以通过剪枝、量化等手段降低模型大小,以便在端上使用;
  • 模型部署阶段,主要实现模型部署,包括模型管理和部署、运维监控等;
  • 端侧推理阶段,主要完成模型推理,即加载模型,完成推理相关的所有计算;

ONNX

        ONNX(Open Neural Network Exchange)是 Facebook 和微软在 2017 年共同发布的,用于标准描述计算图的一种格式。目前,在数家机构的共同维护下,ONNX 已经对接了多种深度学习框架和多种推理引擎。因此,ONNX 被当成了深度学习框架到推理引擎的桥梁,就像编译器的中间语言一样。由于各框架兼容性不一,通常只用 ONNX 表示更容易部署的静态图。

        其中torch.onnx.export 是 PyTorch 自带的把模型转换成 ONNX 格式的函数。前三个参数分别是要转换的模型、模型的任意一组输入、导出的 ONNX 文件的文件名。

        从 PyTorch 的模型到 ONNX 的模型,PyTorch提供了一种叫做追踪(trace)的模型转换方法:给定一组输入,再实际执行一遍模型,即把这组输入对应的计算图记录下来,保存为 ONNX 格式。export 函数用的就是追踪导出方法,需要给任意一组输入,让模型跑起来。测试图片是三通道256x256 大小的,这里也构造一个同样形状的随机张量。

        opset_version 表示 ONNX 算子集的版本。input_names, output_names 是输入、输出 tensor 的名称。代码运行成功,目录下会新增一个 srcnn.onnx 的 ONNX 模型文件

NCNN

        ncnn是一个为手机端极致优化的高性能神经网络前向计算框架。ncnn从设计之初深刻考虑手机端的部署和使用。 无第三方依赖,跨平台,手机端 cpu 的速度快于目前所有已知的开源框架。 基于 ncnn,开发者能够将深度学习算法轻松移植到手机端高效执行, 开发出人工智能 APP,将 AI 带到你的指尖。 ncnn目前已在腾讯多款应用中使用,如:QQ,Qzone,微信,天天P图等。

部署实践

a.模型文件转为ONNX文件

Pytorch保存模型有两种方法: 第一种方法只保存模型参数。第二种方法保存完整模型。

只保存模型参数:

print(model.state_dict().keys()) # 输出模型参数名称
 
# 保存模型参数到路径"./data/model_parameter.pkl"
torch.save(model.state_dict(), "./data/model_parameter.pkl")
new_model = Model()                                                  # 调用模型Model
new_model.load_state_dict(torch.load("./data/model_parameter.pkl"))  # 加载模型参数    
new_model.forward(input)                                             # 进行使用

 

直接保存完整模型:

torch.save(model, './data/model.pkl')        # 保存整个模型
new_model = torch.load('./data/model.pkl')   # 加载模型

        目前大多数应用都会仅仅保存模型参数,用的时候创建模型结构,然后加载模型。一般我们下载的预训练模型都是只保存模型参数的文件,这种模型文件不能直接拿来推理,需要重新创建并加载模型结构,然后进行保存和转换。以下为模型转换为ONNX的代码:需要先创建模型结构并加载模型然后才能进行保存。

模型以静态输入保存和转换:

# onnx模型导入
import onnx
import torch
import torch.nn as nn
import torchvision.models as models

class Net(nn.Module):
    def __init__(self, prior_size):
         …………………

    def forward(self, x):
        …………………
        return x

model = Net(prior_size)
model.load_state_dict(torch.load('xxx.model', map_location='cpu'))
model.eval()

input_names = ['input']
output_names = ['output']
# 规定输入尺寸
x = torch.randn(1, 3, 240, 320, requires_grad=True)
torch.onnx.export(model, x, 'best.onnx', input_names=input_names, output_names=output_names, verbose='True')

模型以动态输入保存和转换:

# onnx模型导入
import onnx
import torch
import torch.nn as nn
import torchvision.models as models

class Net(nn.Module):
    def __init__(self, prior_size):
         …………………

    def forward(self, x):
        …………………
        return x

model = Net(prior_size)
model.load_state_dict(torch.load('xxx.model', map_location='cpu'))
model.eval()

input_names = ['input']
output_names = ['output']
# 规定输入尺寸
x = torch.randn(1, 3, 240, 320, requires_grad=True)
# 动态输入设置
dynamic_axes = {'input': {0: 'batch_size', 1: 'channel', 2: "height", 3: 'width'},
                'output': {0: 'batch_size', 1: 'channel', 2: "height", 3: 'width'}}
torch.onnx.export(model, x, 'best_dynamic.onnx', input_names=input_names, output_names=output_names, verbose='True',dynamic_axes=dynamic_axes)

验证模型转换是否成功:

# 验证onnx模型
import onnx

model_onnx = onnx.load("best.onnx")  # 加载onnx模型
onnx.checker.check_model(model_onnx)  # 验证onnx模型是否成功导出
# 如果没有报错,表示导出成功
# #测试onnx 模型
import onnxruntime
import numpy as np

# 创建会话
# session = onnxruntime.InferenceSession("best.onnx", providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
session = onnxruntime.InferenceSession("best.onnx", providers=['CPUExecutionProvider'])
x = np.random.randn(1, 3, 240, 320)
ort_input = {session.get_inputs()[0].name: x.astype(np.float32)}
ort_output = session.run(None, ort_input)
# 如果没有报错,表示执行成功

b.ONNX格式模型->端侧推理框架模型(NCNN格式)

(1)直接用模型转换工具,输入ONNX文件,生成NCNN文件(.param & .bin)

https://convertmodel.com/#input=onnx&output=onnx

(2)使用代码进行转化

将onnx转换为ncnn模型前,需要简化onnx模型,以免出现不可编译的情况。

首先安装onnx-smiplifier

pip install onnx-simplifier

然后简化onnx模型

python3 -m onnxsim my_mobileface.onnx my_mobileface-sim.onnx

        onnx转换为ncnn,需要使用在ncnn/build/tools/onnx2ncnn,需要在Ubuntu中先安装ncnn,Ubuntu中安装ncnn方法,可以按这个链接安装,我已验证过。如果之前安装过opencv,就直接装ncnn就行。装好之后,找到onnx2ncnn文件的路径,输入下面命令。

./onnx2ncnn my_mobileface-sim.onnx my_mobileface.param my_mobileface.bin

        生成的.bin与.param文件就是在Android上需要使用的NCNN模型文件。

c.Android studio项目中使用模型文件

 强烈建议可以先跑通ncnn官方给的几个在Android端部署的例子,然后可以根据自己的需求进行改进,下面是ncnn使用样例:

• https://github.com/nihui/ncnn-android-squeezenet

• https://github.com/nihui/ncnn-android-styletransfer

• https://github.com/nihui/ncnn-android-mobilenetssd

• https://github.com/moli232777144/mtcnn_ncnn

• https://github.com/nihui/ncnn-android-yolov5

 • https://github.com/xiang-wuu/ncnn-android-yolov7

 • https://github.com/shaoshengsong/qt_android_ncnn_lib_encrypt_example

搭建ncnn环境

从 https://github.com/Tencent/ncnn/releases 下载NCNN压缩包,然后提取压缩包内容到app/src/main/jni, 然后修改app/src/main/jni/CMakeLists.txt中的ncnn_DIR路径。

我的CMakeLists.txt配置如下:

project(styletransferncnn)
cmake_minimum_required(VERSION 3.4.1)

set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20230517-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)

add_library(styletransferncnn SHARED styletransferncnn_jni.cpp)

target_link_libraries(styletransferncnn ncnn)

配置好 ncnn 后,就可以在安卓端运行和推理深度学习模型文件,比如配置好上述要求后就可以运行ncnn官方给出的样例。下图为在安卓端运行图像分类的示例:

下图为我自己根据官方样例改进部署图像超分的示例:

左图为超分前的图像,右图为超分后的图像。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

驱动设备的IOCTL

一、ioctl操作实现 已知成员的地址获得所在结构体变量的地址: container_of(成员地址,结构体类型名,成员在结构体中的名称) long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg); 功能:对相应设备做指定的控制操作&…

GB28181设备接入侧录像查询和录像下载技术探究之实时录像

技术背景 我们在对接GB28181设备接入侧的时候,除了常规实时音视频按需上传外,还有个重要的功能,就是本地实时录像,录像后的数据,在执法记录仪等前端设备留底,然后,到工作站拷贝到专门的平台。 …

订单系统、报名、预约、表单系统 定制开发功能展示

安装教程环境说明:正常情况下PHP5.3-5.6、阿帕奇、mysql安装即可 安装说明: 1、上传源码压缩包到网站根目录(这个请去问下空间商哪个是根目录,每家服务器商不一样,我们也不能确定,请确定是根目录再安装&am…

Java设计模式之行为型-解释器模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 解释器模式是指给定一个语言(表达式),来表示它的文法,并定义一个解释器,使用该解释器来解释语言中的句子(表达式&a…

Linux基础内容(25)—— 线程控制和线程结构

Linux基础内容(24) —— 线程概念_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131294692?spm1001.2014.3001.5501 目录 1.线程操作 1.线程创建问题 2.线程终止问题 1.exit退出 2.pthread_exit退出 3.直接退出 3…

只需一个提示词解除GPT-4的字符限制!

ChatGPT的内存有限,GPT-3.5-turbo的限制为4897个令牌,而GPT-4的最大限制为8192。如果您在使用GPT-4进行聊天时超过8192个令牌(约6827个单词),它就会开始遗忘。我想出了一种新的技巧,可以轻松将对话扩展10倍。 这种技巧不会将对话中的每个字都保存到内存中。当您去开会时,会有人…

0502事务原理-InnoDB引擎-MySQL-数据库

1 概述 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。 事务特性 原子性(Atomatic):事…

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式:在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI):在RTX-309…

外包软件定制开发中关于沟通障碍及对应解决方案

引言 外包软件定制开发在当今的商业环境中越来越常见。它为公司提供了许多好处,包括降低成本、加速交付和专注于核心业务。然而,沟通障碍常常是外包软件定制开发中的一个重要挑战。由于外包团队和客户位于不同的地理位置、文化和语言差异,沟…

Python 列表 sort()函数使用详解

「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:小白零基础《Python入门到精通》 sort函数使用详解 1、升序降序2、sort()和sorted()的区别3、切片排序4、指定排序…

【C++】设计模式-单例模式

目录 一、单例模式 单例模式的三个要点 针对上述三要点的解决方案 常用的两类单例模式 二、懒汉模式实现 1.基本实现 2.锁静态成员析构单例 3.双层检查锁定优化 4.双层检查锁定智能指针 三、饿汉模式实现 1.基础实现 2.嵌套内部类解决内存泄漏 3.智能指针解决内存泄…

一种用于RBF神经网络的新型自适应内核研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

vuejs源码之虚拟dom中的vnode

在虚拟dom中,vnode是比较重要的。 什么是vnode 在vuejs中,有一个Vnode类 使用它可以实例不同类型的vnode实例,而不同类型的vnode实例各自表示不同类型的dom元素。 例如dom元素有文本节点,元素节点,注释节点等。 co…

Spring IoC及DI依赖注入

Spring 1.Spring的含义: Spring 可从狭义与广义两个角度看待 狭义的 Spring 是指 Spring 框架(Spring Fremework) 广义的 Spring 是指 Spring 生态体系 2.狭义的 Spring 框架 Spring 框架是企业开发复杂性的一站式解决方案 Spring 框架的核心是 IoC 容器和 AO…

数据库java中jdbcTemplate的事务问题

1.什么都不设置事务是默认提交的 两次获取的连接是不是一样的 参考文献(重磅): (542条消息) JdbcTemplate的事务控制_jdbctemplate transactionmanager_DayDayUp丶的博客-CSDN博客 PostMapping("/pinYin22")CrossOriginTransactionalpublic String pinYin22(HttpS…

【js实现语言国际化】使用json配置文件实现

需求:使用js让项目实现中文简体、繁体跟英文的切换,实现语言国际化 首先准备三种json配置文件: en.json {"textOne": "Today is Monday","textTwo": "Tomorrow is Tuesday","textThree"…

F#奇妙游(14):F#实现WPF的绑定

WPF中的绑定 绑定在UI开发中是一个非常重要的概念,它可以让我们的UI界面和数据模型之间建立起联系,当数据模型发生变化时,UI界面也会随之变化,反之亦然。这样的好处是显而易见的,我们不需要手动去更新UI界面&#xff…

金智教育IPO过会:计划募资约6亿元,郭超、史鸣杰为实控人

7月13日,深圳证券交易所披露的信息显示,江苏金智教育信息股份有限公司(下称“金智教育”)获得上市委会议通过。据贝多财经了解,金智教育于2022年6月30日递交上市申请材料,先后递交了6个版本的招股书&#x…

NDK OpenGL与OpenCV实现大眼萌特效

NDK​系列之OpenGL与OpenCV实现大眼萌特效,本节主要是在上一节OpenGL仿抖音极快极慢录制特效视频上增加大眼萌的特效。 OpenGL视频特效系列: NDK OpenGL渲染画面效果 NDK OpenGL离屏渲染与工程代码整合 NDK OpenGL仿抖音极快极慢录制特效视频 NDK O…

通讯录实现

普通版 需求 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址 提供方法: 添加联系人信息删除指定联系人信息查找指定联系人信息修改指定联系人信息显示所有联系人信息清空所有联系人以名字排序所有联系…