NNIE 是 Neural Network Inference Engine 的简称,是海思媒体 SoC 中专门针对神经网络特别是深度学习卷积神经网络进行加速处理的硬件单元,能够了解为 CNN 加速器,即 NPU 模块,支撑现有大部分的揭露网络。
海思供给的软件资源包首要包含 PC 端模型量化编译及仿真东西 + 板端模型推理 SDK(NNIE 和 runtime)。留意:NNIE 配套软件及东西链仅支撑以 Caffe 结构,运用其他结构的网络模型需要转化为 Caffe 结构下的模型。
一,PC端东西链
PC 端东西链资源首要包含以下部分:
-
nnie_mapper(tool/nnie/linux/mapper)目录:简称mapper,完结caffe模型优化及编译的功用,将练习好的caffe模型编译成能在Hi35xx芯片或许在仿真库中能够运转的数据指令文件。 - 仿真库(software\x64目录):
PC端仿真成果与芯片上运转成果完全一致,包含时间、带宽等。 - 仿真
sample工程:包含仿真sample源代码供开发者学习参考。 - 模型包(software\data): 包含若干
sample中用到的网络的caffe模型文件及对应的NNIE mapper配置文件、wk文件、图画文件等。 -
Windows版 IDE(tool\nnie\windows目录)东西RuyStudio:集成Windows版的NNIE mapper和仿真库,用户能够将仿真Sample工程导入运转、调试;IDE还集成了代码编辑、编译、调试、履行、画框、相似度比对等功用,详细参考 “RuyiStudio东西运用攻略”章节。
NNIE 开发流程如下图所示。
在 Caffe 结构上练习模型并运用 NNIE 的 mapper 东西转化为 wk 模型等步骤都在 PC 端离线的。经过设置不同的形式,mapper 能够将 *.caffemodel 转化成在仿真器、仿真库或板端上可加载履行的数据指令文件。一般在开发前期,用户可运用仿真器对练习出来的模型进精度、性能、带宽进行开始评估,符合用户预期后再运用仿真库进行完整功用的仿真,最终将程序移植到板端。
二,SDK 结构解读
海思 3519 平台的 SDK 结构非常大,包含了很多硬件模块的代码,如 DPU、SVP、VIO 等模块,由于是做 DL 模型推理的使命,所以只需关注 ~/mpp/sample/SVP 目录下的 hirt 和 nnie 目录代码。整个 SDK 结构首要目录解读如下:
mpp
├── include SDK头文件
├── lib SDK动态库和静态库文件
└── sample
├── common sample公共程序和头文件
└── svp SVP模块参考程序
├── common SVP模块公共程序和头文件
└── nnie NNIE模块参考程序
├── sample_nnie_main.c NNIE参考程序main进口
├── sample_nnie_software 网络推理CPU履行部分,包含后处理
├── sample NNIE参考程序完结代码
└── data 测验数据目录
├── nnie_image 测验图片
└── nnie_model 测验模型
└── hirt 基于NNIE开发的上层runtime软件体系
├── common runtime公共程序和偶文件
├── include runtime模块的头文件
├── src runtime示例程序
└── plugins 自界说算子程序编译的动态库目录
- 海思
3519硬件为了快速访问内存首地址或许跨行访问数据,在运用DDR4时,为提高访存效率,主张首地址运用256字节对齐,stride运用256字节的奇数倍对齐。 - 典型的
RGB\HSV\LAB图画Planar格局存储,NNIE默许以B\G\R、H\S\V、L\A\B次序按通道平面进行存储。
2.1,NNIE运转流程
NNIE 示例程序文件非常大,多达 3000 多行,所有的 cv(computer vision) 功用函数都放在一个 c 代码文件中,没有按模块区分和用 cpp 进行 OOP 方法编程,这给咱们阅读代码带来一些困难。NNIE 模型运转作业流程如下,更多底层 api 的界说可参考源代码文件 sample/sample_nnie.c 和 sample_comm_nnie.c。
可知 NNIE 总的流程能够分为 5 部分:体系初始化、加载模型、填充数据、模型推理和网络后处理。nnie/hirt 代码中,如果输入图画是离线的图画文件(如 Planar 格局存储的二进制文件),能够运用 fopen 打开文件,然后 fread 函数读取数据;如果是 png/jpg 图片,则需要运用 opencv 的 c api cvLoadImage 函数读取图画数据。
除了和体系相关都是一些操作,NNIE 运转的第一步便是加载模型 SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName, &s_stCnnModel),该函数是对底层 HI_MPI_SVP_NNIE_LoadModel 函数的封装,模型加载函数其作业流程图如下:
2.1.1,首要 API 阐明
NNIE 模块供给了创立使命和查询使命的基本接口。 用户依据需求调用相应的算子接口创立使命,指定是否堵塞等候成果,即 bInstant 类型,并记载该使命回来的 handle 号。依据回来的 handle 号,指定堵塞方法,能够查询到该使命的完结状态。在 NNIE 模型运转涉及到的首要 API 阐明如下:
-
HI_MPI_SVP_NNIE_LoadModel:从用户事先加载到 buf 中的模型中解分出网络模型。 -
HI_MPI_SVP_NNIE_GetTskBufSize:获取给定网络使命各段辅佐内存巨细。 -
HI_MPI_SVP_NNIE_Forward:多节点输入输出的 CNN 类型网络猜测。 -
HI_MPI_SVP_NNIE_ForwardWithBbox:多个节点 feature map 输入。 -
HI_MPI_SVP_NNIE_UnloadModel:卸载模型。 -
HI_MPI_SVP_NNIE_Query:查询使命是否完结。 -
HI_MPI_SVP_NNIE_AddTskBuf:记载 TskBuf 地址信息。 -
HI_MPI_SVP_NNIE_RemoveTskBuf:移除 TskBuf 地址信息。
2.1.2,NNIE重要结构体变量解析
海思的 《HiSVP API 参考文章》只要涉及到 NNIE 和 Runtime 相关的 API 接口参数和函数描绘,但是和 Sample(比如)相关的 API 描绘并没有,需要咱们前往 SDK 代码自行阅读和了解。
1,对于 NNIE 的模型运转过程来说,有 3 个 CNN 模型参数结构体是非常重要的,模型运转流程中调用的底层 api 的输入参数首要便是这三个结构体,了解这 3 个结构体有助于咱们了解 SDK 代码。这些结构体的初始化代码如下所示。
static SAMPLE_SVP_NNIE_MODEL_S s_stCnnModel = {0}; // 初始化模型参数结构体为 {0}
static SAMPLE_SVP_NNIE_PARAM_S s_stCnnNnieParam = {0}; // 初始化 NNIE 履行参数结构体 为 {0}
static SAMPLE_SVP_NNIE_CNN_SOFTWARE_PARAM_S s_stCnnSoftwareParam = {0}; // 初始化 CNN 模型得到 TopN 参数的结构体
留意 SVP_NNIE_MODEL_S 结构体也是 SAMPLE_SVP_NNIE_PARAM_S 结构体的成员之一,海思 NNIE SDK 代码中,结构体之间经常是多层嵌套的,以 SAMPLE_SVP_NNIE_PARAM_S 结构体为例,其内部最多嵌套了 5 层结构体,比如要想获取 u32Height(表明设定输入图画的高度)变量的值,需知其界说的结构体跳转关系如下:
SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam;
u32Height = pstNnieParam-->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stWhc.u32Height;
SAMPLE_SVP_NNIE_PARAM_S NNIE 履行参数结构体代码界说如下。它包含了 SVP_NNIE_MODEL_S、SVP_MEM_INFO_S 和 SAMPLE_SVP_NNIE_SEG_DATA_S 等结构体成员。
/*NNIE Execution parameters */
typedef struct hiSAMPLE_SVP_NNIE_PARAM_S
{
SVP_NNIE_MODEL_S* pstModel; // 界说 NNIE 模型结构体
HI_U32 u32TmpBufSize;
HI_U32 au32TaskBufSize[SVP_NNIE_MAX_NET_SEG_NUM];
SVP_MEM_INFO_S stTaskBuf;
SVP_MEM_INFO_S stTmpBuf;
SVP_MEM_INFO_S stStepBuf; //store Lstm step info
SAMPLE_SVP_NNIE_SEG_DATA_S astSegData[SVP_NNIE_MAX_NET_SEG_NUM]; //each seg's input and output blob
SVP_NNIE_FORWARD_CTRL_S astForwardCtrl[SVP_NNIE_MAX_NET_SEG_NUM];
SVP_NNIE_FORWARD_WITHBBOX_CTRL_S astForwardWithBboxCtrl[SVP_NNIE_MAX_NET_SEG_NUM];
}SAMPLE_SVP_NNIE_PARAM_S;
SVP_NNIE_MODEL_S 结构体界说 NNIE 模型参数信息,其结构体代码如下:
/* NNIE model, 界说 NNIE 模型参数信息结构体 */
typedef struct hiSVP_NNIE_MODEL_S
{
SVP_NNIE_RUN_MODE_E enRunMode; // 网络模型运转形式
HI_U32 u32TmpBufSize; /*temp buffer size,辅佐内存 tmpBuf 巨细*/
HI_U32 u32NetSegNum; // 网络模型中 NNIE 履行的网络分段数,取值规模:[1, 8]
// SVP_NNIE_MAX_NET_SEG_NUM NNIE将网切成的最大段数:8
SVP_NNIE_SEG_S astSeg[SVP_NNIE_MAX_NET_SEG_NUM]; // 网络在 NNIE 引擎上履行的段信息。
SVP_NNIE_ROIPOOL_INFO_S astRoiInfo[SVP_NNIE_MAX_ROI_LAYER_NUM]; /*ROIPooling info, 网络模型中 RoiPooling 以及 PsRoiPooling 的信息数组 */
SVP_MEM_INFO_S stBase; // 网络其他信息
}SVP_NNIE_MODEL_S; // SVP_NNIE_MODEL_S 等于 struct hiSVP_NNIE_MODEL_S
2,除了和模型参数相关的结构体,网络层的 Blob 数据结构体也很重要。NNIE 的网络层输入输出数据 (tensor)用 BLOB 表明,依据不同的数据类型,BLOB 也有不同的内存排布类型,部分 BLOB 解说如下所示。
数据类型和数据结构的界说代码在 ~/mpp/include/hi_comm_svp.h 文件中,其中 BLOB 结构体 SVP_BLOB_S 界说如下:
/* 界说多个接连存放的 blob 信息。*/
typedef struct hiSVP_BLOB_S
{
SVP_BLOB_TYPE_E enType; /*Blob 类型, enType 取值规模[SVP_BLOB_TYPE_S32, SVP_BLOB_TYPE_BUTT)。*/
HI_U32 u32Stride; /*Stride, a line bytes num,Blob 中单行数据对齐后的字节数*/
HI_U64 u64VirAddr; /*virtual addr*/
HI_U64 u64PhyAddr; /*physical addr*/
// 表明接连内存块的数目,若一帧数据对应一个块,则表明 blob 中有 u32Num 帧
HI_U32 u32Num; /*N: frame num or sequence num,correspond to caffe blob's n*/
union
{
struct
{
HI_U32 u32Width; /*W: frame width, correspond to caffe blob's w*/
HI_U32 u32Height; /*H: frame height, correspond to caffe blob's h*/
HI_U32 u32Chn; /*C: frame channel,correspond to caffe blob's c*/
}stWhc;
struct
{
HI_U32 u32Dim; /*D: vecotr dimension, 向量的长度*/
HI_U64 u64VirAddrStep; /*T: virtual adress of time steps array in each sequence,数组地址,数组元素表明每段序列有多少个向量*/
}stSeq;
}unShape;
}SVP_BLOB_S;
BLOB 中存储了 Tensor 数据的首物理和虚拟内存地址,宽、高、通道数的维度信息,以及接连内存块数目(帧数)信息。
2.1.3,重要概念
-
网络分段:对于NNIE不支撑的某些网络层节点,编译器支撑用户对网络进行分段,不支撑的部分编译器不会编译,由用户自己用CPU完结。 -
句柄(handle):用户在调用 DSP 处理使命时,体系会为每个使命分配一个 handle,用于标识不同的使命。 -
查询(query):用户依据体系回来的 handle,调用 HI_MPI_SVP_DSP_Query 能够查询对应算子使命是否完结。 - 及时刷
cache:NNIE 硬件只能从DDR中获取数据。为避免 NNIE 输入输出数据不被CPU cahce 干扰,用户需要调用HI_MPI_SYS_MmFlushCache` 接口刷 cache。 - 跨度(
stride):一行有效数据的 byte 数目 + 为硬件快速跨越到下一行补齐的一些无效 byte 数目,如下图所示。
2.2,runtime作业流程
runtime 模块是基于 nnie 底层 api 做的上层封装,使得模型运转逻辑更明晰直观,代码上手难度也更低。其作业流程图如下图所示。
三,参考资料
- 《HiSVP 开发攻略》
- 《HiSVP API 参考》
“本文正在参加人工智能创作者扶持计划 ”





