TensorFlow示例2*
FSMN 结构模型编译流程*
FSMN 是近年来语音识别模型中一种常用的结构。其隐藏层中有一个记忆模块。下面的例子说明 gxnpuc 如何支持这种带记忆模块的模型结构,
1. 生成 NPU 文件*
1.1 编写推理模型*
一般来说,推理模型和训练模型略有差异。推理模型中会去掉只在训练中需要用到的运算,如Dropout
,并加载训练时产生的 CKPT 文件,最终生成推理模型的 PB 文件。
对于 FSMN 模型来说,由于模型中带有记忆模块,而 NPU 内部是不带状态的,所以我们编写推理模型时,还需要把模型的记忆作为模型的输入状态和输出状态,运行 NPU 时当前帧的输出状态就是下一帧的输入状态。
具体见下面代码。
inputs = tf.placeholder(tf.float32, [batch_size, frame_len, feat_dim*win_len], "Feats") # 指定输入 feats 名字
state1_in = tf.placeholder(tf.float32, [batch_size, 3, linear_size[0], name="State_c0") # 指定输入 state0 名字
state2_in = tf.placeholder(tf.float32, [batch_size, 4, linear_size[1], name="State_c1") # 指定输入 state1 名字
state3_in = tf.placeholder(tf.float32, [batch_size, 5, linear_size[2], name="State_c2") # 指定输入 state2 名字
states = (state1_in, state2_in, state3_in)
...
cnn_outputs = cnn_layer(inputs, seq_len, cnn_info, tf.nn.relu, fusedbn)
outputs, states = fsmn_layer(cnn_outputs, memory_size, linear_size, hidden_size, states, tf.nn.relu, keep_prob)
...
state1_out = tf.identity(states[0], name="State_c0_out") # 指定输出 state0 名字
state2_out = tf.identity(states[1], name="State_c1_out") # 指定输出 state1 名字
state3_out = tf.identity(states[2], name="State_c2_out") # 指定输出 state2 名字
phone_prob = tf.identity(outputs, name="phone_prob") # 指定模型最终输出名字
...
with tf.compat.v1.Session() as sess:
tf.global_variables_initializer().run(session=sess)
saver = tf.train.Saver()
saver.restore(sess, "model.ckpt") # 加载训练时生成的 CKPT 文件
...
tf.train.write_graph(sess.graph_def, "./", "model.pb") # 生成推理模型的 PB 文件
1.2 生成 CKPT 和 PB 文件,并把 CKPT 和 PB 文件合并成 FROZEN_PB 文件*
可参考生成CKPT和PB文件 和 把CKPT和PB文件合并成FROZEN_PB文件
1.3 编辑 NPU 配置文件*
编辑配置文件config.yaml
文件,含义见注释。
config.yaml
CORENAME: APUS # 芯片型号
PB_FILE: model_with_ckpt.pb # 输入的 PB 文件
IN_FEATS_FILE: feats.txt # 输入特征值,尽可能包含模型应用的场景
QUANT_FILE: quant.yaml # 输出的量化文件
OUTPUT_FILE: model.h # 输出的 NPU 文件名
COMPRESS: true # 模型全连接权重压缩
OUTPUT_TYPE: c_code # NPU 文件的类型
INPUT_OPS:
Feats: [1, 15, 40]
State_c0: [1, 3, 64]
State_c1: [1, 4, 64]
State_c2: [1, 5, 64]
OUTPUT_OPS: [State_c0_out,State_c1_out,State_c2_out,phone_prob] # 输出结点名称,状态结点放在前面
FUSE_BN: true # BN参数合并到卷积中(如果有的话)
MAX_CACHE_SIZE: 6402 # 开辟 CACHE 内存,存储权重和 data
USE_DATA_CACHE: true # data 存储在CACHE中
注意
这里的 Feats,State_c0,State_c1,State_c2,State_c0_out,State_c1_out,State_c2_out,phone_prob 跟模型的输入、输出的 op 名字是一一对应的。
注意
配置项OUTPUT_OPS
中,状态输出结点需要放在模型实际输出结点前面。
1.4 编译*
首先使用 gxnpuc 工具编译生成量化文件
$ gxnpuc config.yaml -q
model.h
$ gxnpuc config.yaml
------------------------
Memory allocation info:
Mem1(data): 11432
Mem2(instruction): 3368
Mem3(in): 2736
Mem4(out): 1666
Mem5(cache): 6300
Mem6(weights): 127298
Total NPU Size (Mem0+Mem1+Mem2+Mem5+Mem6): 148398
Total Memory Size: 152800
------------------------
Compile OK.
各内存区域说明如下:
内存区域 | 说明 |
---|---|
Mem1(data) | 中间数据内存 |
Mem2(instruction) | 指令内存 |
Mem3(in) | 输入数据内存 |
Mem4(out) | 输出数据内存 |
Mem5(cache) | SRAM中权重和 data内存 |
Mem6(weights) | 权重内存 |
2. 执行 NPU 文件*
NPU 文件生成后,需要把模型部署到 GX830X 开发板上运行。