1 倡導(dǎo)
我們來(lái)做個(gè)游戲吧,游戲內(nèi)容是:
從 MNN 的 二進(jìn)制模型文件 中找到我們想要的 目標(biāo)數(shù)據(jù)!
游戲的部分基礎(chǔ)信息可以在 這篇文章 中找到一二。
2 游戲說(shuō)明
2.1 我們有 mnn模型文件 的信息
一個(gè)簡(jiǎn)單的 Conv + Conv + Relu 三層網(wǎng)絡(luò),其全部?jī)?nèi)容如下圖:

2.2 我們有 mnn模型文件 的二進(jìn)制信息
左邊第一列為我加的索引列,二進(jìn)制信息統(tǒng)一使用16進(jìn)制打印,即一個(gè)數(shù)字是8位即一個(gè)字節(jié)

2.3 游戲目標(biāo)
在 2.2 的 二進(jìn)制信息 中找到 2.1 中json信息 目標(biāo)字段的存儲(chǔ)位置。
舉例:
2.1 圖中倒數(shù)第二行的 "0",在 2.2 圖中對(duì)應(yīng) 02行的 第6組 開(kāi)始的五個(gè)字節(jié) : 01 00 00 00 30
2.1 圖中倒數(shù)第二行的 "5",在 2.2 圖中對(duì)應(yīng) 02行的 第4組 開(kāi)始的五個(gè)字節(jié) : 01 00 00 00 35

有趣么?馬上開(kāi)始我們的游戲
3 帶你玩
3.1 Mission 1 : 找到 tensorName "0"、"5"、"6"

3.1.1 找到MNN:Net 的數(shù)據(jù)起點(diǎn)
MNN 的二進(jìn)制模型文件 的前四個(gè)字節(jié)是 根節(jié)點(diǎn)的偏移,它指向一個(gè) 繼承于 flatbuffers::Table 名為 MNN::Net 數(shù)據(jù)結(jié)構(gòu)

0x0000001C = 28
為什么是 0x0000001C,而不是0x1C000000呢?因?yàn)?小端模式
28位置 即 MNN::Net 結(jié)構(gòu)的數(shù)據(jù)內(nèi)存數(shù)據(jù)起點(diǎn)位置
3.1.2 尋找 字符串指針數(shù)組 的數(shù)據(jù)起點(diǎn)
MNN::Net 有一個(gè)tensorName的屬性,即為我們的目標(biāo),
它是一個(gè) 字符串指針的數(shù)組(Vector<flatbuffers::Offset<flatbuffers::String>>)
讓我們找打它!

3.1.1 節(jié)我們得到了位置28,那么:
- 28位置信息為 0x00000016 = 22,代表 28位置之前的22個(gè)字節(jié) 均為MNN::Net 的 屬性位置信息;
- VT_TENSORNAME = 18,即 屬性位置信息 18位置開(kāi)始的2個(gè)字節(jié) 存儲(chǔ) tensorName 的 數(shù)據(jù)位置,28 - 22 + 18 = 24 : 0x0010 = 16;
- MNN::Net指針位置加上 數(shù)據(jù)位置的值 獲得 數(shù)據(jù)偏移,即:28 + 16 = 44 : 0x000008 = 8
- 44 + 8 = 52,即我們要找的 字符串指針數(shù)組 的數(shù)據(jù)起點(diǎn)在 52位置
3.1.3 尋找 字符串的數(shù)據(jù)起點(diǎn)
3.1.1 節(jié)我們得到了位置52,并且知道,這個(gè)位置是一個(gè) 字符串的指針數(shù)組,那么
- 52 : 0x00000003 = 3,表示數(shù)組的長(zhǎng)度為3,所以后面3個(gè)4字節(jié)就是代表三個(gè)指針的值
- 即: 三個(gè)指針的 位置 分別為 : 56、60、64,
對(duì)應(yīng)的值 分別為 : 0x0000001c、0x00000010、0x00000004,
換算成十進(jìn)制 分別為 : 28、16、4 - 那么 三個(gè)字符串指針 分別指向 :
56 + 28 = 84
60 + 16 = 76
64 + 4 = 68
即 三個(gè)字符串的數(shù)據(jù)起點(diǎn)位置
尋找 字符串的數(shù)據(jù)起點(diǎn)
3.1.4 解析字符串
字符串的 前四個(gè)字節(jié) 描述 字符串的長(zhǎng)度
- 我們發(fā)現(xiàn)三個(gè)字符串的 長(zhǎng)度都是1
84 :0x00000001 = 1
76 :0x00000001 = 1
68 :0x00000001 = 1
那么緊跟其后的 1個(gè)字節(jié) 即為字符串的有效值 - 即獲取3個(gè)有效值:0x30、0x35、0x36
- 查閱一下 ASCII碼對(duì)照表 吧,他們就是'0', '5', '6',任務(wù)1完成
3.2 Mission2:找到Conv0 的 bias
再來(lái)個(gè)稍微復(fù)雜點(diǎn)的任務(wù)練練手!找到Conv0 的 bias

3.2.1 確認(rèn)分析思路
看下MNN的數(shù)據(jù)結(jié)構(gòu),確認(rèn)下任務(wù)完成思路

如圖,我們要先找到oplists,然后找到 第二個(gè)op(第一個(gè)是Input),然后找到 Convolution2D 的數(shù)據(jù),找到我們要的 bias 值
3.2.2 尋找 oplists 數(shù)據(jù)起點(diǎn)
oplists的數(shù)據(jù)結(jié)構(gòu)是Vector<flatbuffers::Offset<Op>>,即一個(gè)Op指針的數(shù)組
- 28 - 22 + 10 = 16 : 0x0008 = 8
- 28 + 8 = 36 : 0x00000048 = 72
- 36 + 72 = 108
108位置 即為 oplists 的數(shù)據(jù)起點(diǎn)

3.2.3 尋找 第二個(gè)Op 數(shù)據(jù)起點(diǎn)
第一個(gè)Op為Input,我們找第二個(gè)Op
- 108 + 4 * 2 = 116 : 0x00000130 = 304
- 116 + 304 = 420
即 第二個(gè)Op位置在420
3.2.4 尋找 Convolution2D 數(shù)據(jù)起點(diǎn)
- 420 : 0x00000010 = 16
- 420 - 16 + 8 = 412 : 0x000C = 12
*420 + 12 = 432 : 0x00000028 = 40 - 432 + 40 = 472
即 Convolution2D的位置在472

3.2.5 分析Convolution2D
- 472 : 0x0000000A = 10
- 472 - 10 + 8 = 474 : 0x000C = 12
- 472 + 12 = 484 : 0x00000004 = 4
- 484 + 4 = 488 ,即bias float數(shù)組的位置。 bias 的數(shù)據(jù)結(jié)構(gòu)為:Vector<float>,即一個(gè)浮點(diǎn)數(shù)數(shù)組
- 488 : 0x00000005 = 5,bias數(shù)組長(zhǎng)度為5
所以bias的五個(gè)值位置 分別為 : 492、496、500、504、508,
值均為 0x3f000000,是 float數(shù)據(jù)結(jié)構(gòu)的二進(jìn)制形式存儲(chǔ)值
3.2.6 解析float
0x3f000000 轉(zhuǎn)為二進(jìn)制為:0011 1111 0000 0000 0000 0000 0000 0000
使用 Float結(jié)構(gòu)拆解 為1 + 8 + 23,即 : 0 01111110 00000000000000000000000
得到:
符號(hào)位:0,正數(shù)
指數(shù):01111110 : 126 -> 正負(fù)指數(shù)表示 -> 126 - 127 = -1,即 指數(shù) -1
底數(shù):00000000000000000000000,補(bǔ)1后為:1.0,即 底數(shù) 1.0
計(jì)算值:底數(shù)1.0 小數(shù)點(diǎn) 左移1位(-1) 得到:0.1,為二進(jìn)制小數(shù),轉(zhuǎn)換為10進(jìn)制即0.5。
任務(wù)2完成!
結(jié)語(yǔ)
感興趣的小伙伴也可以嘗試在 2.2的二進(jìn)制文件 中尋找下 2.1Json文件 的其他信息哦,或許還會(huì)有新的發(fā)現(xiàn)[壞笑]。
比如試試找一下:Conv1 的 weight 數(shù)據(jù)
資源
文中使用的MNN模型
MNN GitHub 地址
FlatBuffers MNN模型存儲(chǔ)結(jié)構(gòu)基礎(chǔ)
FlatBuffers GitHub 地址
雞湯
對(duì)任何事物追根究底的研究會(huì)找到意想不到的樂(lè)趣!
將那份復(fù)雜用最容易理解的方式講出來(lái)!
自我學(xué)習(xí)后是 知, 有效表達(dá)后為 曉 ~
