如何部署流式模型
时间序列模型如今被应用在许多领域,例如,音频领域。而音频模型在部署时通常有两种模式:
Offline模式:模型需要一次性接收完整的音频数据(例如整个语音文件),然后进行整体处理。
Streaming模式:流式模式下,模型逐帧(逐块)接收音频数据,实时处理并输出中间结果。
在本教程中,我们来介绍如何使用 ESP-PPQ 量化流式模型,并使用 ESP-DL 部署量化后的流式模型。
准备工作
模型量化
如何转换为流式模型
时间序列模型种类繁多,这里仅以 Temporal Convolutional Network(TCN) 为例,不熟悉的可自行查找资料了解,这里不过多介绍其细节。其它模型需根据自身情况,量体裁衣。
该示例代码中构建了一个 TCN 模型: models.py (模型非完整,仅用于演示)。
ESP-PPQ 提供了自动流式转换功能,可以简化创建流式模型的过程。通过 auto_streaming=True 参数,ESP-PPQ 自动处理流式推理所需的模型转换。
备注
Offline 模式,模型输入是一段完整数据,input shape 在时间维度上的 size 一般比较大(例如
[1, 16, 15])。Streaming 模式,模型输入是连续数据,在时间维度上的 size 较小,匹配实时处理的块大小(例如
[1, 16, 3])。
自动流式转换
ESP-PPQ 通过量化过程中的 auto_streaming=True 参数提供自动流式转换功能。启用此标志后,ESP-PPQ 会自动转换模型以支持流式推理:
分析模型结构以识别适当的分块点
创建内部状态管理以在块之间保持上下文
生成适合流式场景的优化代码
以下是如何使用自动流式功能的示例:
# 导出非流式模型
quant_ppq_graph = espdl_quantize_torch(
model=model,
espdl_export_file=ESPDL_MODEL_PATH,
calib_dataloader=dataloader,
calib_steps=32, # 校准步数
input_shape=INPUT_SHAPE, # 离线模式的输入形状
inputs=None,
target=TARGET, # 量化目标类型
num_of_bits=NUM_OF_BITS, # 量化位数
dispatching_override=None,
device=DEVICE,
error_report=True,
skip_export=False,
export_test_values=True,
verbose=1, # 输出详细日志信息
)
# 使用自动转换导出流式模型
quant_ppq_graph = espdl_quantize_torch(
model=model,
espdl_export_file=ESPDL_STEAMING_MODEL_PATH,
calib_dataloader=dataloader,
calib_steps=32,
input_shape=INPUT_SHAPE,
inputs=None,
target=TARGET,
num_of_bits=NUM_OF_BITS,
dispatching_override=None,
device=DEVICE,
error_report=True,
skip_export=False,
export_test_values=False,
verbose=1,
auto_streaming=True, # 启用自动流式转换
streaming_input_shape=[1, 16, 3], # 流式模式的输入形状
streaming_table=None,
)
模型部署
参考示例 , 该示例使用预生成的数据来模拟实时数据流。
在流式模式下,模型按时间接收数据块,而不是要求一次性获得整个输入。流式模型依次处理这些块,同时在块之间保持内部状态。部署代码负责将输入分解为适当的块并将其馈送到模型。见 app_main.cpp 如下代码块:
dl::TensorBase *run_streaming_model(dl::Model *model, dl::TensorBase *test_input)
{
std::map<std::string, dl::TensorBase *> model_inputs = model->get_inputs();
dl::TensorBase *model_input = model_inputs.begin()->second;
std::map<std::string, dl::TensorBase *> model_outputs = model->get_outputs();
dl::TensorBase *model_output = model_outputs.begin()->second;
if (!test_input) {
ESP_LOGE(TAG,
"Model input doesn't have a corresponding test input. Please enable export_test_values option "
"in esp-ppq when export espdl model.");
return nullptr;
}
int test_input_size = test_input->get_bytes();
uint8_t *test_input_ptr = (uint8_t *)test_input->data;
int model_input_size = model_input->get_bytes();
uint8_t *model_input_ptr = (uint8_t *)model_input->data;
int chunks = test_input_size / model_input_size;
for (int i = 0; i < chunks; i++) {
// assign chunk data to model input
memcpy(model_input_ptr, test_input_ptr + i * model_input_size, model_input_size);
model->run(model_input);
}
return model_output;
}
这种方法允许模型通过将长序列分解为更小、更易管理的块来高效处理。每个块依次馈送到模型中,内部状态自动维护以确保跨块的连续性。
备注
块的数量是根据完整输入大小与流式模型输入大小的比率计算的。
ESP-DL 流式模型自动处理内部状态管理,使部署变得简单。
流式模型的输出应与等效离线模型输出的最后部分匹配。