跳转至

NPU 模型格式说明*

  • NPU编译器使用 这章节中,我们知道跑在 GX830X 上的模型文件是用 gxnpuc config.yaml 生成。
  • 本文对模型文件的结构做一个简单的说明
    model.h
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    // This file is automatically generated by NPU compiler.
    // 模式的输入输出大小+模型运行所占用的空间:sizeof(in_out) + sizeof(cmd_content) + sizeof(weight_content) + sizeof(data_content) + sizeof(cache_content)
    const unsigned int total_size = 570774;
    
    // 模型运行所占用的空间:sizeof(cmd_content) + sizeof(weight_content) + sizeof(data_content) + sizeof(cache_content)
    const unsigned int npu_size = 561864;
    
    // gxnpuc 编译器版本
    const char *version = "1.6.0rc2";
    
    // 模型对应的 pb 文件的 md5 值
    const char *pb_md5 = "96bc67e9594bd5d73eb95c10f00782bb";
    
    // 模型编译的时间
    const char *model_info = "(20240327095040)";
    
    // 模型输入输出量化的Q值
    #define FEATS_Q 12
    #define STATE_C0_Q 10
    #define STATE_C1_Q 10
    #define STATE_C2_Q 11
    #define STATE_C3_Q 10
    #define STATE_C0_OUT_Q 10
    #define STATE_C1_OUT_Q 10
    #define STATE_C2_OUT_Q 11
    #define STATE_C3_OUT_Q 10
    #define MODEL_RNN_OUT_Q 10
    
    typedef short npu_data_t;
    
    // 模型的输入结构
    struct input {
        npu_data_t Feats[1][25][40];
        npu_data_t State_c0[1][4][96];
        npu_data_t State_c1[1][4][96];
        npu_data_t State_c2[1][4][96];
        npu_data_t State_c3[1][5][96];
    } __attribute__ ((packed));
    
    // 模型的输出结构
    struct output {
        npu_data_t State_c0_out[1][4][96];
        npu_data_t State_c1_out[1][4][96];
        npu_data_t State_c2_out[1][4][96];
        npu_data_t State_c3_out[1][5][96];
        npu_data_t Model_rnn_out[1][1][191];
    } __attribute__ ((packed));
    
    // 模型的输入+输出结构,对于循环网络来说,方案上依据该结构图设立两个以上的buffer,其中一个buffer作为模型的输入地址, 下一个buffer的 State_c0 作为模型的输出地址,这样可以节省一次模型输出拷贝到下一时刻输入的这个拷贝动作。
    struct in_out {
        npu_data_t Feats[1][25][40];
        npu_data_t State_c0[1][4][96];
        npu_data_t State_c1[1][4][96];
        npu_data_t State_c2[1][4][96];
        npu_data_t State_c3[1][5][96];
        npu_data_t Model_rnn_out[1][1][191];
    } __attribute__ ((packed));
    
    // 模型的指令数组,只读
    const unsigned char cmd_content[2084] __attribute__ ((aligned(4))) = {
        0x47, 0x00, 0x00, 0x00, 0xf2, 0x0a, 0x00, 0xc0, 0x00, 0x02,
        0x02, 0x00, 0x28, 0x00, 0x19, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x05, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00,
        ......
        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x0e, 0x00, 0x10,
        0x00, 0x09, 0x00, 0x40,
    };
    
    // 模型的权重数组,只读
    const unsigned char weight_content[128166] __attribute__ ((aligned(4))) = {
        0x00, 0x02, 0x00, 0xfe, 0x00, 0x50, 0x00, 0x02, 0x20, 0x00,
        0xe0, 0xff, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
        0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x54, 0x00, 0x01,
        ......
        0x06, 0x06, 0xda, 0xe3, 0x32, 0xf3, 0x27, 0xe8, 0x74, 0xe8,
        0xc9, 0x2d, 0x58, 0xeb, 0x27, 0x28, 0x3d, 0xfe, 0xb1, 0x13,
        0x5f, 0x01, 0x56, 0xfd, 0x7d, 0x15, 0x73, 0x0d, 0x5c, 0x05,
        0xff, 0x08, 0xa0, 0x3f,
    };
    
    // 模型运行时所需要临时空间
    unsigned char data_content[13680] __attribute__ ((aligned(4)));
    
    // 模型运行时分配 SRAM 中存储权重和 data 的内存大小
    unsigned char cache_content[0] __attribute__ ((aligned(4)));