mace micro 代码分析: netdef 和Graph文件的生成

文件micro_net_def_data.h的内容就是class NetDef的一个对象object: 可以通过分析文件micro_net_def_data.h的byte 内容,分析其意义,理解NetDef文件保存了什么信息。

测试方法:推断一个输入数据

./micro/tools/cmake/cmake-build-host.sh -DMACE_MICRO_ENABLE_EXAMPLES=ON -DMACE_MICRO_ENABLE_TOOLS=OFF -DMICRO_MODEL_NAME=har -DMICRO_DATA_NAME=har

代码用了好多宏定义,展开的方法

 g++ -E micro/model/operator_def.cc -o operator_def.i -I.

 

读取文件内容的代码

int main()
{
    for (int j=0; j<24; j++) {
        for (int i=0; i<8; i++) {
            printf("%02x ", kNetDef[0x2c + 0x5a4 + 0x98c+ 8*j+i]);
            //printf("%02x ", kGraphData[0xb4+8*j + i]);
        }   
        printf("\n"); 
    }   
    return 0;
}

NetDef对应的pb文件message 和对应的class: data member and 获得model信息的function

pb文件定义

message NetDef {
  repeated OperatorDef op = 1;
  repeated Argument arg = 2;
  repeated ConstTensor tensors = 3;
  optional DataType data_type = 4 [default = DT_FLOAT];

  repeated InputOutputInfo input_info = 100;
  repeated InputOutputInfo output_info = 101;
}

对应class实现

class NetDef
 private:
  SerialArray<OperatorDef> ops_;
  SerialArray<Argument> args_;
  SerialArray<ConstTensor> tensors_;
  SerialInt32 data_type_;
  SerialArray<InputOutputInfo> input_infos_;
  SerialArray<InputOutputInfo> output_infos_;
};

对应文件内容分析

SerialArray是基本class 表示repeart的pb message: size 和offset类似于指针:间接寻址

SerialArray是一个 template struct这里的typename T的T 并没有在struct中出现,会生成多个不同名称的SerialArray?

template<typename T>
struct SerialArray {
  offset_size_t size_;
  offset_size_t offset_;
  SerialArray() : size_(0), offset_(0) {}
};

SerialArray<xxx>是个class,且默认构造函数使用初始化列表初始化变量为0

另外,protocol buffer的repeat 是个数组,表示为SerialArray: size 和offset (在对象内的offset, 不是从文件开头算起的offset)

class NetDef一个实例化对象对应的内容

class NetDef: 一个NetDef包括ops/ args/ const tensor/ data_type/ input/output/info
 private:
  SerialArray<OperatorDef> ops_;
  SerialArray<Argument> args_;
  SerialArray<ConstTensor> tensors_;
  SerialInt32 data_type_;
  SerialArray<InputOutputInfo> input_infos_;
  SerialArray<InputOutputInfo> output_infos_;
};

对着上面这个class的data member可以看出

kNetDef的开头是 class NetDef赋值的data member

ops_: 8个算子OperatorDef
08 00 00 00 2c 00 00 00
args_ 
03 00 00 00 8c 02 00 00
tensors_
09 00 00 00 04 03 00 00
data_type_: DT_FLOAT = 1: 直接是内容
01 00 00 00 
input_infos_
01 00 00 00 68 05 00 00 
output_infos_
01 00 00 00 90 05 00 00

下面分析下简单的InputOutputInfo

描述输入输出使用的是同一个class: InputOutputInfo: 内容是名字,形状:yml文件里设定的

message InputOutputInfo {
  optional string name = 1;
  optional int32 node_id = 2;
  repeated int32 dims = 3;
  optional int32 max_byte_size = 4;  // only support 32-bit len
  optional DataType data_type = 5 [default = DT_FLOAT];
  optional int32 data_format = 6 [default = 1];  // NHWC
  optional float scale = 7;
  optional int32 zero_point = 8;
}

class InputOutputInfo 

 private:
  SerialString name_; //tensor name
  SerialInt32 node_id_;
  SerialArray<SerialInt32> dims_;
  SerialInt32 max_byte_size_;
  SerialInt32 data_type_;
  SerialInt32 data_format_;
  SerialFloat scale_;
  SerialInt32 zero_point_;
};

01 00 00 00 68 05 00 00 (输入)

有一个input

10 00 00 00 88 09 00 00:name: len:0x10, offset in InputOutputInfo: 0x0988: InputOutputInfo in NetDef的offset 是0x0568: kNetDef[0x568+0x988+...]对应的内容与是63 6f 6e 76 32 64 5f 69: "conv2d_input:0"
00 00 00 00: node_id: 0
04 00 00 00 98 09 00 00: dims:4, dims的内容:kNetDef[0x568+0x998+..], shape: 01 00 00 00, 1c 00 00 00, 1c 00 00 00 01 00 00 00(1, 28, 28, 1)
00 00 00 00: max_byte_size 
01 00 00 00: data_type输入数据类型
01 00 00 00: data format 
00 00 00 00: scale
00 00 00 00: zero_point

01 00 00 00 90 05 00 00(输出)

14 00 00 00 80 09 00 00: name kNetDef[0x590 + 0x980+..]: 64 65 6e 73 65 5f 31 2f 53 6f 66 74 6d 61 78 3a 30: "dense_1/Softmax:0"
00 00 00 00 : node_id没有意义?
02 00 00 00 94 09 00 00: dims: 01 00 00 00 0a 00 00 00: (1, 10)
00 00 00 00 
01 00 00 00 
01 00 00 00 
00 00 00 00 
00 00 00 00 

NetDef的OpetatorDef

message OperatorDef {
  repeated string input = 1;
  repeated string output = 2;
  optional string name = 3;
  optional string type = 4;
  optional int32 device_type = 5;
  repeated Argument arg = 6;
  repeated OutputShape output_shape = 7;
  repeated DataType output_type = 8;
  repeated QuantizeActivationInfo quantize_info = 9;

  // for mace it is mem_id, for micro, it is mem_offset
  repeated int32 mem_id = 10;

  // for hexagon mace-nnlib
  optional uint32 node_id = 100;
  optional uint32 op_id = 101;
  optional uint32 padding = 102;
  repeated NodeInput node_input = 103;
  repeated int32 out_max_byte_size = 104;  // only support 32-bit len
}  

class OperatorDef

 private:
  SerialArray<SerialString> inputs_;
  SerialArray<SerialString> outputs_;
  SerialString name_;
  SerialString type_;

  SerialInt32 device_type_;
  SerialArray<Argument> args_;
  SerialArray<OutputShape> output_shapes_; //为什么要描述每个op的 output shape 和datatype是为了分配内存?
  SerialArray<DataType> output_types_;
  SerialArray<QuantizeActivationInfo> quantize_info_;
  SerialArray<SerialInt32> mem_offsets_;
};

ops_: 8个算子OperatorDef
08 00 00 00 2c 00 00 00

NetDef里包含了8个OperatorDef对象,从0x2C开始依次layout 8个NetDef对象。如果能找到OperatorDef中的inputs的具体内容,其他都是同样的方法。注意要加上上级的offset还有到array开头的offset

0x2c处对应的内容:

03 00 00 00 8c 05 00 00 : (0x2c+0x58c)[要加上OpDef在NetDef中offset:0x2c]的内容是依次layout的3个SerialString: 10 00 00 00 74 09 00 00, 10 00 00 00 7c 09 00 00, 10 00 00 00 84 09 00 00

input1的字符串内容: 0x2c+ 0x58c + 0x974: "conv2d_input:0", input2: 0x2c + 0x58c+ 0x97c + 8(还要有个数组索引offset): "conv2d/kernel:0": input3: 0x2c + 0x58c+ 0x984 + 2*8: "conv2d/bias:0"


01 00 00 00 a4 05 00 00 :outputs_ : "conv2d/Relu:0"
0c 00 00 00 ac 05 00 00 : name: "conv2d_act"
08 00 00 00 b8 05 00 00 :type: "Conv2D"
00 00 00 00 : device type

07 00 00 00 c0 05 00 00 : args

01 00 00 00 d8 06 00 00 : output_shapes

00 00 00 00 38 00 00 00 :output_types_ 个数是0,还有offset?

00 00 00 00 40 00 00 00 : quantize_info_个数是0

01 00 00 00 e0 06 00 00 : mem_offsets_ 1个memory: 0x2c+0x6e0: 0 这个memory是用来存放输出的。


Opdef 输出outputshape

class OutputShape : public Serialize {                                                                                                                                                                      
 private:
  SerialArray<SerialInt32> dims_;
};

0x2c + 0x6d8: 04 00 00 00 c8 08 00 00: 4个 int32

0x2c + 0x6d8 + 0x8c8: (outpushape: 1, 28, 28, 32) 01 00 00 00 1c 00 00 00 1c 00 00 00 20 00 00 00

OpDef Argument

class Argument : public Serialize {

 private:
  SerialString name_;
  SerialFloat f_;
  SerialInt32 i_;
  SerialBytes s_;
  SerialArray<SerialFloat> floats_;
  SerialArray<SerialInt32> ints_;
};  

07 00 00 00 c0 05 00 00 : args

1]
04 00 00 00 80 09 00 00 :T
00 00 00 00 01 00 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
00 00 00 00 24 00 00 00
2]
10 00 00 00 5c 09 00 00 : 0x2c + 0x5c0 +0x95c+ 5*8(数组开始到这里的offset): "framework_type"
00 00 00 00 04 00 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
00 00 00 00 24 00 00 00 
3]
0c 00 00 00 44 09 00 00 :"data_format"
00 00 00 00 e8 03 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
00 00 00 00 24 00 00 00 
4]
08 00 00 00 28 09 00 00 :"padding"
00 00 00 00 01 00 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
00 00 00 00 24 00 00 00 
5]
08 00 00 00 08 09 00 00 "strides": 0x2c + 0x5c0 +0x908+ 4*5*8
00 00 00 00 00 00 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
02 00 00 00 10 09 00 00 :[0x2c + 0x5c0 +0x910 + 4*5*8]: 01 00 00 00, 01 00 00 00
6]
0c 00 00 00 f0 08 00 00 : "dilations"
00 00 00 00 00 00 00 00 
00 00 00 00 14 00 00 00 
00 00 00 00 1c 00 00 00 
02 00 00 00 fc 08 00 00 
7]
0c 00 00 00 dc 08 00 00 :"activation"
00 00 00 00 00 00 00 00 
08 00 00 00 e8 08 00 00 : "RELU"
00 00 00 00 1c 00 00 00 
00 00 00 00 24 00 00 00 

 

class CostTensor

 private:
  SerialArray<SerialInt32> dims_;
  DataType data_type_;
  SerialArray<SerialFloat> float_datas_;
  SerialArray<SerialInt32> int32_datas_;
  SerialString name_;
  SerialInt32 offset_;
  SerialInt32 data_size_;
  SerialFloat scale_;
  SerialInt32 zero_point_;
  SerialFloat minval_;
  SerialFloat maxval_;
  SerialBool quantized_;
  SerialUint32 node_id_;
};

tensors {
  dims: 32
  dims: 1
  dims: 3
  dims: 3
  data_type: DT_FLOAT
  name: "conv2d/kernel:0"
  offset: 0
  data_size: 288
}

09 00 00 00 04 03 00 00: 9个const tensor object

04 00 00 00 f8 0a 00 00 : dims_ (32, 3, 3, 1) 和print的不一样?特殊处理了
01 00 00 00 : data type(float)

00 00 00 00 10 00 00 00

00 00 00 00 18 00 00 00

10 00 00 00 08 0b 00 00 : string name : 0x304+ 0xb08: "conv2d/kernel:0"

00 00 00 00 20 01 00 00 : offset(0), data size(0x120)

00 00 00 00 00 00 00 00 :scale, zero

00 00 00 00 00 00 00 00 : min max

00 00 00 00 00 00 00 00 : quantized, node id

Graph data 文件

message OpContext {
  optional int32 op_idx = 1;
  // The input info of downstream operator is the output info of upstream
  // operator, so there is no output info defined here
  repeated uint32 input_infos = 2;
  repeated OutputShape output_resize_shapes = 3;
}

message Graph {
  repeated OpContext op_contexts = 1;
  repeated uint32 input_op_idxs = 2;
  // The output info of the last operator, which is not recorded in opcontext,
  // is the output of graph
  repeated uint32 output_infos = 3;
}

//--------------------------------------------------------------
void Serialize::Uint2OpIOInfo(const OpIOInfo *info) const {
  //const_cast修改类型的const/volatile属性
  OpIOInfo *io_info = const_cast<OpIOInfo *>(info);
  //按uint32_t解释*info指向的内容,处理后更改OpIOInfo的内容
  uint32_t info_data = *(reinterpret_cast<uint32_t *>(io_info));
  io_info->op_def_idx_ = (info_data & 0xffff0000) >> 16;//高16位: def_idx_
  io_info->output_idx_ = (info_data & 0x0000ffff);//低16位: output_idx
}

 

struct OpIOInfo //哪个Op, op的第几个output? {    这里的命名有点问题,比较令人困惑, 其实他们的意思是                                                                                                                                                                                  
  uint16_t op_def_idx_;
  uint16_t output_idx_;
}

Operator的input虽然都用 data struct: OpIOInfo表示,但意义还是差别挺大的
tensor分为三类:
1. OpIOInfo表示model的input时: op_def_idx:是固定值kIdxModelInput: 0xfffe, 
   output_idx_表示的是input的Index(model可以有多个input)
2. OpIOInfo表示model的训练参数时: op_def_idx:是固定值kIdxConstTensor: 0xffff, 
   output_idx_表示的是net_def_->tensor(input_info->output_idx_)
3. OpIOInfo表示是某个Operator的输出时:op_def_idx是operator的Index: engine_config_->net_def_->op(op_def_idx)
   output_idx_表示的是operator的输出index(一个operator可以有多个输出)


class OutputShape : public Serialize {                                                                                                                                                                      
 private:
  SerialArray<SerialInt32> dims_;
}

//oprator的描述: opIndex, inputs(OpIoInfo), OutputShape
class OpContext : public Serialize {
 protected:
  SerialUint32 op_idx_;
  SerialArray<OpIOInfo> input_infos_;
  SerialArray<model::OutputShape> output_resize_shapes_;
}

//Graph的描述: opContexts/inpuOPIndex/outputInfos
class Graph : public Serialize {
 protected:
  SerialArray<OpContext> op_contexts_;
  SerialArray<SerialUint32> input_op_idxs_;
  SerialArray<OpIOInfo> output_infos_;
};

0x00:
09 00 00 00 18 00 00 00 //OpContext: size:9, offset:0x18
01 00 00 00 cc 00 00 00 //SerialUint32: size: 1, offset:0xcc
01 00 00 00 d0 00 00 00 //OpIOInfo: size: 1, offset 0xd0

0xcc:
00 00 00 00

0xd0:OpIOInfo
00 00 08 00 //转换为int32: 00 08 00 00 ->08: op_index, output_index(输出的index) 

//第一个OpContext
0x18: OpContext
00 00 00 00 //0
03 00 00 00 bc 00 00 00 
01 00 00 00 c8 00 00 00 

0x18 + 0xbc:OpIOInfo

00 00 fe ff //ff fe 00 00
00 00 ff ff //ff ff 00 00
01 00 ff ff //ff ff 00 01

0x18 + 0xc8:model::OutputShape
04 00 00 00 
84 00 00 00
0x18 + 0xc8+ 0x84: [1, 89, 2, 128]
01 00 00 00 
59 00 00 00 
02 00 00 00 
80 00 00 00

//第2个OpContext
01 00 00 00 //1
01 00 00 00 bc 00 00 00 
01 00 00 00 c0 00 00 00

0x18+ 5*8 +0xbc:OpIOInfo
00 00 00 00:-> 00 00 00 00: 第0个op的第0个output
0x18+ 5*8 +0xc0:
04 00 00 00 88 00 00 00
[1, 44, 1, 128]
01 00 00 00 
2c 00 00 00 
01 00 00 00 
80 00 00 00

//第3个OpContext
02 00 00 00 //2
02 00 00 00 b4 00 00 00 
01 00 00 00 bc 00 00 00

00 00 01 00: 00 01 00 00
08 00 ff ff


02 00 00 00 88 00 00 00
0x18 + 2*5*4 + 0xbc + 0x88:
01 00 00 00 
00 16 00 00

怎样使用mace micro

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值