之後隨筆將更多筆墨著重於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作為輸出。