AOE工程实践-NCNN组件
作者:杨科
NCNN是腾讯开源的一个为手机端极致优化的高性能神经网络前向计算框架。在AOE开源工程里,我们提供了NCNN组件,下面我们以SqueezeNet物体识别这个Sample为例,来讲一讲NCNN组件的设计和用法。
直接集成NCNN缺点
为SqueezeNet接入NCNN,把相关的模型文件,NCNN的头文件和库,JNI调用,前处理和后处理相关业务逻辑等。把这些内容都放在SqueezeNet Sample工程里。这样简单直接的集成方法,问题也很明显,和业务耦合比较多,不具有通用性,前处理后处理都和SqueezeNcnn这个Sample有关,不能很方便地提供给其他业务组件使用。深入思考一下,如果我们把AI业务,作为一个一个单独的AI组件提供给业务的同学使用,会发生这样的情况:

每个组件都要依赖和包含NCNN的库,而且每个组件的开发同学,都要去熟悉NCNN的接口,写C的调用代码,写JNI。所以我们很自然地会想到要提取一个NCNN的组件出来,例如这样:

AOE SDK里的NCNN组件
在AOE开源SDK里,我们提供了NCNN组件,下面我们从4个方面来讲一讲NCNN组件:
- NCNN组件的设计
- 对SqueezeNet Sample的改造
- 应用如何接入NCNN组件
- 对NCNN组件的一些思考
NCNN组件的设计
NCNN组件的设计理念是组件里不包含具体的业务逻辑,只包含对NCNN接口的封装和调用。具体的业务逻辑,由业务方在外部实现。在接口定义和设计上,我们参考了TF Lite的源码和接口设计。目前提供的对外调用接口,主要有以下几个:
// 加载模型和param
void loadModelAndParam(...)
// 初始化是否成功
boolean isLoadModelSuccess()
// 输入rgba数据
void inputRgba(...)
// 进行推理
void run(...)
// 多输入多输出推理
void runForMultipleInputsOutputs(...)
// 得到推理结果
Tensor getOutputTensor(...)
// 关闭和清理内存
void close()
新的代码结构如下:
├── AndroidManifest.xml
├── cpp
│ └── ncnn
│ ├── c_api_internal.h
│ ├── include
│ ├── interpreter.cpp
│ ├── Interpreter.h
│ ├── jni_util.cpp
│ ├── jni_utils.h
│ ├── nativeinterpreterwrapper_jni.cpp
│ ├── nativeinterpreterwrapper_jni.h
│ ├── tensor_jni.cpp
│ └── tensor_jni.h
├── java
│ └── com
│ └── didi
│ └── aoe
│ └── runtime
│ └── ncnn
│ ├── Interpreter.java
│ ├── NativeInterpreterWrapper.java
│ └── Tensor.java
└── jniLibs
├── arm64-v8a
│ └── libncnn.a
└── armeabi-v7a
└── libncnn.a
- Interpreter,提供给外部调用,提供模型加载,推理这些方法。
- NativeInterpreterWrapper是具体的实现类,里面对native进行调用。
- Tensor,主要是一些数据和native层的交互。
AOE NCNN组件有以下几个特点:
- 支持多输入多输出。
- 使用ByteBuffer来提升效率。
- 使用Object作为输入和输出(实际支持了ByteBuffer和多维数组)。
下面我们来说说具体是如何做的。
如何支持多输入多输出
为了支持多输入和多输出,我们在Native层创建了一个Tensor对象的列表,每个Tensor对象里保存了相关的输入和输出数据。Native层的Tensor对象,通过tensor_jni提供给java层调用,java层维护这个指向native层tensor的“指针”地址。这样在有多输入和多输出的时候,只要拿到这个列表里的对应的Tensor,就可以就行数据的操作了。
ByteBuffer的使用
ByteBuffer,字节缓存区处理子节的,比传统的数组的效率要高。
DirectByteBuffer,使用的是堆外内存,省去了数据到内核的拷贝,因此效率比用ByteBuffer要高。
当然ByteBuffer的使用方法不是我们要说的重点,我们说说使用了ByteBuffer以后,给我们带来的好处:
1,接口里的字节操作更加便捷,例如里面的putInt,getInt,putFloat,getFloat,flip等一系列接口,可以很方便的对数据进行操作。
2,和native层做交互,使用DirectByteBuffer,提升了效率。我们可以简单理解为java层和native层可以直接对一块“共享”内存进行操作,减少了中间的字节的拷贝过程。
如何使用Object作为输入和输出
目前我们只支持了ByteBuffer和MultiDimensionalArray。在实际的操作过程中,如果是ByteBuffer,我们会判断是否是direct buffer,来进行不同的读写操作。如果是MultiDimensionalArray,我们会根据不同的数据类型(例如int, float等),维度等,来对数据进行读写操作。
对SqueezeNet Sample的改造
集成AOE NCNN组件以后,让SqueezeNet依赖NCNN Module,SqueezeNet Sample里面只包含了模型文件,前处理和后处理相关的业务逻辑,前处理和后处理可以用java,也可以用c来实现,由具体的业务实现来决定。新的代码结构变得非常简洁,目录如下:
├── AndroidManifest.xml
├── assets
│ └── squeeze
│ ├── model.config
│ ├── squeezenet_v1.1.bin
│ ├── squeezenet_v1.1.id.h
│ ├── squeezenet_v1.1.param.bin
│ └── synset_words.txt
└── java
└── com
└── didi
└── aoe
└── features
└── squeeze
└── SqueezeInterpreter.java
其他的AI业务组件对NCNN组件的调用,都可以参考SqueezeNet这个Sample。
应用如何接入NCNN组件
对NCNN组件的接入,有两种方式
直接接入

通过AOE SDK接入

两种接入方式比较:
| 功能特性 | 直接接入 | 通过AOE SDK接入 |
|---|---|---|
| 易用性 | 容易 | 容易 |
| 稳定性 | 不能确定,依赖实现方的实现 | 高,安卓有独立进程机制,更加稳定,推理过程不影响主进程 |
| 模型配置 | 无 | 有 |
| 模型下载和动态升级 | 无 | 有 |
| 模型配置 | 无 | 有 |
| 模型准确率,性能等数据分析 | 无 | 有 |
| 提供图像处理工具包 | 无 | AOE SDK提供了Vision组件 |
| 支持模型加密 | 要看具体的模型和框架 | AOE SDK提供了模型加密解密组件 |
通过比较,我们更建议是通过AOE SDK来对我们的NCNN组件进行接入。
对NCNN组件的总结和思考
通过对NCNN组件的封装,现在业务集成NCNN更加快捷方便了。之前我们一个新的业务集成NCNN,可能需要半天到一天的时间。使用AOE NCNN组件以后,可能只需要1-2小时的时间。
当然NCNN组件目前还存在很多不完善的地方,我们对NCNN还需要去加深学习和理解。后面会通过不断的学习,持续的对NCNN组件进行改造和优化。
欢迎大家来使用和提建议
AoE (AI on Edge,终端智能,边缘计算) 是一个终端侧AI集成运行时环境 (IRE),帮助开发者提升效率。 https://github.com/didi/aoe
Github地址:

欢迎star~
AOE工程实践-NCNN组件的更多相关文章
- AES加密原理和AOE工程实践
在AI业务的开发的过程中,我们常常需要对模型文件进行加密.我们从以下几个方面来说一说AES的加密原理以及AOE里的工程实践. 常见的加密算法 AOE对模型加密需求的思考 AES的加密原理 AOE工程实 ...
- AOE工程实践-银行卡OCR里的图像处理
作者:杨科 近期我们开发了一个银行卡 OCR 项目.需求是用手机对着银行卡拍摄以后,通过推理,可以识别出卡片上的卡号. 工程开发过程中,我们发现手机拍摄以后的图像,并不能满足模型的输入要求.以 And ...
- 不加班的秘诀:如何通过AOE快速集成NCNN?
作为我司头发储量前三的程序员 始终仗着头发多奋斗在加班的第一线 时时灵魂拷问自己 年轻人,你凭什么不加班? 虽然我没有女朋友但是,我有代码呀 但我不明白的是,隔壁工位那个,到岗比我迟,下班比我早,天天 ...
- webpack 从入门到工程实践
from:https://www.jianshu.com/p/9349c30a6b3e?utm_campaign=maleskine&utm_content=note&utm_medi ...
- 基于menu小插件探索工程实践
目录 一.准备工作 1.C/C++环境搭建 2.VSCode的配置 (1) 安装插件: (2) 设置配置文件: 二.工程化编程实战 1.模块化设计 2.可重用设计:进一步抽象 menu的进一步优化 可 ...
- panguan(判官):一个自研的任务执行引擎的工程实践
来某厂接近半年了,几乎没写过C++代码,说实话还真的有点手生.最近刚好有一个需求,然而我感觉我也没有办法用C++以外的语言去实现它.于是还是花了几天时间用C++完成编码,这是一个简单的任务执行引擎,它 ...
- 公司简介 - CCDI悉地国际-工程实践专业服务的引领者
公司简介 - CCDI悉地国际-工程实践专业服务的引领者 关于悉地国际 CCDI悉地国际(以下简称"CCDI")创立于1994年,是在城市建设和开发领域从事综合专业 ...
- Linux开源模块迁移概述暨交叉编译跨平台移植总结--从《嵌入式Linux驱动模板简洁和工程实践》
本文摘录<嵌入式Linux驱动模板简洁和工程实践>一本书"开发和调试技术". Linux强大的是,有那么多的开源项目可以使用.通常非常需要可以通过寻找相关的源模块被定义 ...
- LDA工程实践之算法篇之(一)算法实现正确性验证(转)
研究生二年级实习(2010年5月)开始,一直跟着王益(yiwang)和靳志辉(rickjin)学习LDA,包括对算法的理解.并行化和应用等等.毕业后进入了腾讯公司,也一直在从事相关工作,后边还在yiw ...
随机推荐
- 『开发技术』Windows极简安装使用face_recognition
face_recognition是一个强大.简单.易上手的人脸识别开源项目,并且配备了完整的开发文档和应用案例,特别是兼容树莓派系统.此项目是世界上最简洁的人脸识别库,你可以使用Python和命令行工 ...
- c#异常后重试操作
private void TryConnect(System.Action action) { int retries = 3; whi ...
- 2、大型项目的接口自动化实践记录--接口测试简介及RequestsLibrary关键字简介
1.接口测试简介 1)先简单介绍下接口测试,那么什么是接口测试呢? 百科的回答:接口测试是测试系统组件间接口的一种测试.接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点. 看起来有 ...
- jmeter界面字体修改
实际应用中发现,同样是win10系统,显示器屏幕尺寸大小不同,jmeter界面字体展示也不一样,标准屏幕还可以,大屏幕下不能自动适应屏幕大小放大而且还变的更小.在查询解决方法时,发现有朋友出现类似情况 ...
- pythonday06数据类型(四)
今日内容 1.集合 2内存相关 1.集合set 无序 无重复 v = {1,2,3,4,5,6,99,100} # 疑问:v = {} """ None int v1 = ...
- Android实现多语言so easy
微信公众号:CodingAndroid CSDN:http://blog.csdn.net/xinpengfei521声明:本文由CodingAndroid原创,未经授权,不可随意转载! 最近,我们公 ...
- centos7之Python3.74安装
安装版本:Python3.74 系统版本:centos7 系统默认安装Python2.7,保留. 安装/usr/bin/Python3 安装需要root权限. 安装Python3的准备工作: 1.安装 ...
- let 、const 、var、function声明关键字的新理解
今天在群里看到大佬们讨论let .const 的提升问题,有个大佬问 三种声明都在什么阶段提升? 什么阶段?这个真不清楚,以前是只知道let.const存在死区,没有变量提升,一下子就懵了 后经手 ...
- Java学习|多线程学习笔记
什么是线程? 可以理解为进程中独立运行的字任务. 使用多线程: 1.继承Thread类:从源码可以看到,Thread累实现了Runnable接口. 如果多次调用st ...
- (十七)c#Winform自定义控件-基类窗体
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...