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: GRUS # 芯片型号
PB_FILE: model_with_ckpt.pb # 输入的 PB 文件
OUTPUT_FILE: model.h # 输出的 NPU 文件名
NPU_UNIT: NPU32 # NPU 设备类型
COMPRESS: true # 模型全连接权重压缩
CONV2D_COMPRESS: false # 模型卷积权重不压缩
OUTPUT_TYPE: c_code # NPU 文件的类型
INPUT_OPS:
Feats: [1, 784]
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] # 输出结点名称,状态结点放在前面
FP16_OUT_OPS: [State_c0_out,State_c1_out,State_c2_out] # 当前输出状态直接赋给下一次的输入,无需转成fp32
FUSE_BN: true # BN参数合并到卷积中(如果有的话)
# WEIGHT_CACHE_SIZE: 0 # 权重无需分开放置,不需要设置
注意
这里的 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
model.h
,并且打印出模型需要的内存信息:
------------------------
Memory allocation info:
Mem0(ops): 0
Mem1(data): 15520
Mem2(instruction): 468
Mem3(in): 3104
Mem4(out): 1796
Mem5(tmp content): 0
Mem6(weights): 84052
Total NPU Size (Mem0+Mem1+Mem2+Mem5+Mem6): 100040
Total Memory Size: 104940
------------------------
Compile OK.
2. 执行 NPU 文件*
详细部署指南请阅读NPU模型部署指南。