美文网首页MNN实践GammaPerf
做个游戏吧?玩儿转Mnn模型存储结构!

做个游戏吧?玩儿转Mnn模型存储结构!

作者: Chriszzzz | 来源:发表于2019-08-01 11:56 被阅读0次

    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"

    056

    3.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 的数据起点
    找到 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
    寻找 Op & Convolution2D

    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 地址

    鸡汤
    对任何事物追根究底的研究会找到意想不到的乐趣!
    将那份复杂用最容易理解的方式讲出来!
    自我学习后是 , 有效表达后为 ~

    相关文章

      网友评论

        本文标题:做个游戏吧?玩儿转Mnn模型存储结构!

        本文链接:https://www.haomeiwen.com/subject/mwztdctx.html