1 倡导
我们来做个游戏吧,游戏内容是:
从 MNN 的 二进制模型文件 中找到我们想要的 目标数据!
游戏的部分基础信息可以在 这篇文章 中找到一二。
2 游戏说明
2.1 我们有 mnn模型文件 的信息
一个简单的 Conv + Conv + Relu 三层网络,其全部内容如下图:
游戏模型文件 json信息2.2 我们有 mnn模型文件 的二进制信息
左边第一列为我加的索引列,二进制信息统一使用16进制打印,即一个数字是8位即一个字节
游戏模型文件 二进制信息2.3 游戏目标
在 2.2 的 二进制信息 中找到 2.1 中json信息 目标字段的存储位置。
举例:
2.1 图中倒数第二行的 "0",在 2.2 图中对应 02行的 第6组 开始的五个字节 : 01 00 00 00 30
2.1 图中倒数第二行的 "5",在 2.2 图中对应 02行的 第4组 开始的五个字节 : 01 00 00 00 35
有趣么?马上开始我们的游戏
3 带你玩
3.1 Mission 1 : 找到 tensorName "0"、"5"、"6"
0563.1.1 找到MNN:Net 的数据起点
MNN 的二进制模型文件 的前四个字节是 根节点的偏移,它指向一个 继承于 flatbuffers::Table 名为 MNN::Net 数据结构
寻找MNN::Net结构内存起点0x0000001C = 28
为什么是 0x0000001C,而不是0x1C000000呢?因为 小端模式
28位置 即 MNN::Net 结构的数据内存数据起点位置
3.1.2 寻找 字符串指针数组 的数据起点
MNN::Net 有一个tensorName的属性,即为我们的目标,
它是一个 字符串指针的数组(Vector<flatbuffers::Offset<flatbuffers::String>>)
让我们找打它!
3.1.1 节我们得到了位置28,那么:
- 28位置信息为 0x00000016 = 22,代表 28位置之前的22个字节 均为MNN::Net 的 属性位置信息;
- VT_TENSORNAME = 18,即 属性位置信息 18位置开始的2个字节 存储 tensorName 的 数据位置,28 - 22 + 18 = 24 : 0x0010 = 16;
- MNN::Net指针位置加上 数据位置的值 获得 数据偏移,即:28 + 16 = 44 : 0x000008 = 8
- 44 + 8 = 52,即我们要找的 字符串指针数组 的数据起点在 52位置
3.1.3 寻找 字符串的数据起点
3.1.1 节我们得到了位置52,并且知道,这个位置是一个 字符串的指针数组,那么
- 52 : 0x00000003 = 3,表示数组的长度为3,所以后面3个4字节就是代表三个指针的值
- 即: 三个指针的 位置 分别为 : 56、60、64,
对应的值 分别为 : 0x0000001c、0x00000010、0x00000004,
换算成十进制 分别为 : 28、16、4 - 那么 三个字符串指针 分别指向 :
56 + 28 = 84
60 + 16 = 76
64 + 4 = 68
即 三个字符串的数据起点位置
寻找 字符串的数据起点
3.1.4 解析字符串
字符串的 前四个字节 描述 字符串的长度
- 我们发现三个字符串的 长度都是1
84 :0x00000001 = 1
76 :0x00000001 = 1
68 :0x00000001 = 1
那么紧跟其后的 1个字节 即为字符串的有效值 - 即获取3个有效值:0x30、0x35、0x36
- 查阅一下 ASCII码对照表 吧,他们就是'0', '5', '6',任务1完成
3.2 Mission2:找到Conv0 的 bias
再来个稍微复杂点的任务练练手!找到Conv0 的 bias
任务2目标3.2.1 确认分析思路
看下MNN的数据结构,确认下任务完成思路
MNN模型数据结构如图,我们要先找到oplists,然后找到 第二个op(第一个是Input),然后找到 Convolution2D 的数据,找到我们要的 bias 值
3.2.2 寻找 oplists 数据起点
oplists的数据结构是Vector<flatbuffers::Offset<Op>>,即一个Op指针的数组
- 28 - 22 + 10 = 16 : 0x0008 = 8
- 28 + 8 = 36 : 0x00000048 = 72
- 36 + 72 = 108
108位置 即为 oplists 的数据起点
3.2.3 寻找 第二个Op 数据起点
第一个Op为Input,我们找第二个Op
- 108 + 4 * 2 = 116 : 0x00000130 = 304
- 116 + 304 = 420
即 第二个Op位置在420
3.2.4 寻找 Convolution2D 数据起点
- 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数组的位置。 bias 的数据结构为:Vector<float>,即一个浮点数数组
- 488 : 0x00000005 = 5,bias数组长度为5
所以bias的五个值位置 分别为 : 492、496、500、504、508,
值均为 0x3f000000,是 float数据结构的二进制形式存储值
3.2.6 解析float
0x3f000000 转为二进制为:0011 1111 0000 0000 0000 0000 0000 0000
使用 Float结构拆解 为1 + 8 + 23,即 : 0 01111110 00000000000000000000000
得到:
符号位:0,正数
指数:01111110 : 126 -> 正负指数表示 -> 126 - 127 = -1,即 指数 -1
底数:00000000000000000000000,补1后为:1.0,即 底数 1.0
计算值:底数1.0 小数点 左移1位(-1) 得到:0.1,为二进制小数,转换为10进制即0.5。
任务2完成!
结语
感兴趣的小伙伴也可以尝试在 2.2的二进制文件 中寻找下 2.1Json文件 的其他信息哦,或许还会有新的发现[坏笑]。
比如试试找一下:Conv1 的 weight 数据
资源
文中使用的MNN模型
MNN GitHub 地址
FlatBuffers MNN模型存储结构基础
FlatBuffers GitHub 地址
鸡汤
对任何事物追根究底的研究会找到意想不到的乐趣!
将那份复杂用最容易理解的方式讲出来!
自我学习后是 知, 有效表达后为 晓 ~
网友评论