之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(1)-RFCN NNIE LoadModell函数参数解析,通过对LoadModel函数参数的解析,能够很好理解.wk文件的具体内容,为方便为对其他不同模型.wk加载时如何进行修改给出参照。
在RFCN demo中把RFCN的.wk模型文件通过函数导出模型参数,具体如下所示:
1 static SAMPLE_SVP_NNIE_MODEL_S s_stRfcnModel = {0}; 2 3 HI_CHAR *pcModelName = "./data/nnie_model/detection/inst_rfcn_resnet50_cycle_352x288.wk"; 4 5 //函数输入参数 6 SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName,&s_stRfcnModel);
SAMPLE_COMM_SVP_NNIE_LoadModel函数定义原型如下:
1 SAMPLE_COMM_SVP_NNIE_LoadModel( 2 HI_CHAR * pszModelFile, 3 SAMPLE_SVP_NNIE_MODEL_S *pstNnieModel)
LoadModel函数下SAMPLE_SVP_NNIE_MODEL_S参数定义:
1 typedef struct hiSAMPLE_SVP_NNIE_MODEL_S 2 { 3 SVP_NNIE_MODEL_S stModel; 4 SVP_MEM_INFO_S stModelBuf;//store Model file 5 }SAMPLE_SVP_NNIE_MODEL_S;
给出SAMPLE_SVP_NNIE_MODEL_S结构体下SVP_NNIE_MODEL_S参数定义:
1 /*NNIE model*/ 2 typedef struct hiSVP_NNIE_MODEL_S 3 { 4 SVP_NNIE_RUN_MODE_E enRunMode;/*枚举类型,网络模型运行模式*/ 5 6 HI_U32 u32TmpBufSize; /*temp buffer size 辅助内存大小*/ 7 HI_U32 u32NetSegNum; /*网络模型中 NNIE 执行的网络分段数,取值[1,8]*/ 8 SVP_NNIE_SEG_S astSeg[SVP_NNIE_MAX_NET_SEG_NUM];/*网络在 NNIE 引擎上执行的段信息*/ 9 SVP_NNIE_ROIPOOL_INFO_S astRoiInfo[SVP_NNIE_MAX_ROI_LAYER_NUM]; /*ROIPooling info*/ 10 11 SVP_MEM_INFO_S stBase; /*网络其他信息*/ 12 }SVP_NNIE_MODEL_S;
enRunModel:为枚举类型,表示网络模型的运行模式,有SVP_NNIE_RUN_MODE_CHIP(只能在Chip上运行),以及SVP_NNIE_RUN_MODE_FUNC_SIM(只能用于PC端功能仿真)两个枚举值。可以通过打印可以看到RFCN网络模型的运行情况。
u32TempBufSize:为辅助内存大小。
u32NetSegNum:为网络模型中NNIE执行的网络分段数,取值为1~8。这里的分段是指模型执行中可能会分成多段,一些段在NNIE上执行,一些段在CPU或DSP上执行,给出图示之前,首先先给出SVP对扩展层的参考设计:
u32NetSegNum就是指有多少段是在NNIE上执行的,如果一个网络模型全部都是在NNIE上执行,那么这个u32NetSegNum就是1。由上图可知FasterRCNN网络的NNIE执行分为两段,即u32NetSegNum = 2。
astSeg[SVP_NNIE_MAX_NET_SEG_NUM]:这个参数是一个结构体数组,SVP_NNIE_MAX_NET_SEG_NUM在hi_nnie.h中定义为8,这个数组表示每一段NNIE网络的各段的具体信息,具体信息有哪些,来看SVP_NNIE_MODEL_S结构体下SVP_NNIE_SEG_S这个结构体:
1 /***************************************************************/ 2 /*Segment information*/ 3 typedef struct hiSVP_NNIE_SEG_S 4 { 5 SVP_NNIE_NET_TYPE_E enNetType; /*网络段的类型*/ 6 HI_U16 u16SrcNum; /*网络段的输入节点数*/ 7 HI_U16 u16DstNum; /*网络段的输出节点数*/ 8 HI_U16 u16RoiPoolNum; /*网络段中包含的 RoiPooling 以及 PSRoiPooling layer 数*/ 9 10 HI_U16 u16MaxStep; /*RNN/LSTM 网络中序列的最大“帧数”*/ 11 12 HI_U32 u32InstOffset; 13 HI_U32 u32InstLen; 14 15 SVP_NNIE_NODE_S astSrcNode[SVP_NNIE_MAX_INPUT_NUM];/*网络段的第 i 个输入节点信息, SVP_NNIE_MAX_INPUT_NUM为16*/ 16 17 SVP_NNIE_NODE_S astDstNode[SVP_NNIE_MAX_OUTPUT_NUM];/*网络段的第 i 个输出节点信息, SVP_NNIE_MAX_OUTPUT_NUM为16*/ 18 19 HI_U32 au32RoiIdx[SVP_NNIE_MAX_ROI_LAYER_NUM_OF_SEG]; /*Roipooling info index 网络段的第 i 个 RoiPooling 或者 PsRoiPooling 在SVP_NNIE_MODEL_S 中 SVP_NNIE_ROIPOOL_INFO_S 数组的下标,SVP_NNIE_MAX_ROI_LAYER_NUM_OF_SEG为2*/ 20 }SVP_NNIE_SEG_S;
SVP_NNIE_MODEL_S结构体SVP_NNIE_SEG_S结构体下enNetType参数为枚举类型,具体如下所示:
1 /*Network type 例子见后面表格 */ 2 typedef enum hiSVP_NNIE_NET_TYPE_E 3 { 4 SVP_NNIE_NET_TYPE_CNN = 0x0, /* Non-ROI input cnn net,普通的CNN\DNN网络类型 */ 5 SVP_NNIE_NET_TYPE_ROI = 0x1, /* With ROI input cnn net,有RPN层输出框信息的网络类型*/ 6 SVP_NNIE_NET_TYPE_RECURRENT = 0x2, /* RNN or LSTM net */ 7 8 SVP_NNIE_NET_TYPE_BUTT 9 }SVP_NNIE_NET_TYPE_E;
包含4种类型:SVP_NNIE_NET_TYPE_CNN表示普通的的CNN网络, SVP_NNIE_NET_TYPE_ROI有RPN层输出框信息的网络类型,这里其实就是指Faster RCNN的NNIE模型中的Proposal层,这个层包含RPN输出框信息,且由CPU来执行。SVP_NNIE_NET_TYPE_RECURRENT则表示RNN循环神经网络或者LSTM长短期记忆网络。
SVP_NNIE_MODEL_S结构体SVP_NNIE_SEG_S结构体下u16SrcNum:表示这个段的输入节点数,即这个段网络有多少个输入,也是后面的astSrcNode数组的元素的有效个数。
SVP_NNIE_MODEL_S结构体SVP_NNIE_SEG_S结构体下u16DstNum:表示这个段的输出节点数,即这个段网络有多少个输出,也是后面的astDstNode数组的元素的有效个数。
SVP_NNIE_MODEL_S结构体SVP_NNIE_SEG_S结构体下astSrcNode与astDstNode:表示这个段的输入和输出节点的具体信息,其类型为SVP_NNIE_NODE_S,具体如下:
1 /*Node information*/ 2 typedef struct hiSVP_NNIE_NODE_S 3 { 4 SVP_BLOB_TYPE_E enType; /*节点的类型*/ 5 union 6 { 7 struct 8 { 9 HI_U32 u32Width; /*节点内存形状的宽*/ 10 HI_U32 u32Height; /*节点内存形状的高*/ 11 HI_U32 u32Chn; /*节点内存形状的通道数*/ 12 }stWhc; 13 14 HI_U32 u32Dim; /*节点内存的向量维度*/ 15 }unShape; 16 17 HI_U32 u32NodeId; /*节点在网络中的 Id*/ 18 HI_CHAR szName[SVP_NNIE_NODE_NAME_LEN];/*Report layer bottom name or data layer bottom name*/ 19 }SVP_NNIE_NODE_S;
SVP_NNIE_MODEL_S结构体SVP_NNIE_SEG_S结构体SVP_NNIE_NODE_S结构体下enType是枚举类型,其类型SVP_BLOB_TYPE_E如下:
1 /*Blob type*/ 2 typedef enum hiSVP_BLOB_TYPE_E 3 { 4 SVP_BLOB_TYPE_S32 = 0x0, /*Blob 数据元素为 S32 类型*/ 5 6 SVP_BLOB_TYPE_U8 = 0x1, /*Blob 数据元素为 U8 类型*/ 7 8 /*channel = 3*/ 9 SVP_BLOB_TYPE_YVU420SP = 0x2, /*Blob 数据内存排布为 YVU420SP*/ 10 11 /*channel = 3*/ 12 SVP_BLOB_TYPE_YVU422SP = 0x3,/*Blob 数据内存排布为 YVU422SP*/ 13 14 SVP_BLOB_TYPE_VEC_S32 = 0x4, /*Blob 中存储向量,每个元素为 S32 类型*/ 15 16 SVP_BLOB_TYPE_SEQ_S32 = 0x5,/*Blob 中存储序列,数据元素为 S32 类型*/ 17 18 SVP_BLOB_TYPE_BUTT 19 }SVP_BLOB_TYPE_E;
(以Fast RCNN为例)通过打印输出SVP_NNIE_MODEL_S结构体中的astSeg,即打印两段NNIE网络信息的输入输出节点信息,具体如下:
从打印的信息,我们首先看段与节点的类型,这个对于以后的分析有用,因为后面的一些初始化操作会根据不同的类型有不同的操作,如下表:
将以上打印与下面网络图结合,这里的节点名szName,个人的理解是,如果作为输入节点,则显示的是该层的bottom的名字,如果作为输出节点,则显示top的名字。第1段有1个输入节点,即data层的输入。输出节点的数量打印显示是4个输出,而下面的网络图中只有3个输出,即conv5, rpn_bbox_pred, rpn_cls_prob_reshape,打印显示多了一个rpn_cls_score。在RuyiStudio的网络图里rpn_cls_score是在第一段的中间,并非作为输出,为什么会把它当输出呢?玄机就在网络描述文件.prototxt里面,我们来看rpn_cls_score这一层,如下:
1 layer { 2 name: "rpn_cls_score" 3 type: "Convolution" 4 bottom: "rpn/output" 5 top: "rpn_cls_score_report" 6 convolution_param { 7 num_output: 18 # 2(bg/fg) * 9(anchors) 8 kernel_size: 1 pad: 0 stride: 1 9 weight_filler { type: "gaussian" std: 0.01 } 10 bias_filler { type: "constant" value: 0 } 11 } 12 }
这一层的top输出是rpn_cls_score_report,即rpn_cls_score加了后缀_report。在《HiSVP开发指南》的3.2.7章节,如下:
可以看到,中间层的top加上_report后当作该段的一个输出,因此从打印那里可以看到这一段有rpn_cls_score作为输出。