SenseVoice模型静态导出与QNN编译完整指南

部署运行你感兴趣的模型镜像

SenseVoice模型静态导出与QNN编译完整指南

概述

本文档详细介绍如何将SenseVoice语音识别模型从PyTorch格式导出为静态ONNX模型,并进一步编译为适配高通硬件平台的QNN-ONNX模型的完整流程。该流程适用于QCS8550等高通芯片平台,能够充分利用硬件加速能力提升推理性能。

1. 环境准备

1.1 依赖安装

# 安装基础依赖
pip install torch onnx funasr-onnx modelscope

# 安装Qualcomm AI Hub
pip install qai_hub

# 安装音频处理依赖
pip install librosa numpy

1.2 模型准备

确保已经下载了SenseVoice模型文件:

# 模型下载
modelscope download --model iic/SenseVoiceSmall --local_dir ./

# 模型目录结构
./models/ASR/SenseVoice-Small/
├── model.pt
├── config.yaml
├── am.mvn
└── chn_jpn_yue_eng_ko_spectok.bpe.model

2. 静态模型导出

2.1 静态导出配置

静态导出需要固定输入shape,这有助于模型优化和部署。我们提供了多种预设配置:

# export_static_config.py

class StaticInputConfig:
    # 批次大小配置
    BATCH_SIZE = 1
    
    # 序列长度配置
    SEQ_LEN = 300  # 中等音频长度
    
    # 特征维度配置
    FEAT_DIM = 560
    
    # 语言和文本规范化配置
    LANGUAGE_ID = 0      # 中文
    TEXTNORM_ID = 15    # 文本规范化
预设配置选项
  • SHORT_AUDIO: 适合短音频快速推理 (SEQ_LEN=100)
  • MEDIUM_AUDIO: 平衡性能和准确性 (SEQ_LEN=300)
  • LONG_AUDIO: 适合长语音识别 (SEQ_LEN=512)
  • BATCH_PROCESSING: 适合批量处理 (BATCH_SIZE=4)

2.2 静态导出实现

静态导出的核心是修改模型的forward方法,使其支持固定输入shape:

# export_sensevoice_static.py

def static_export_forward(self, speech, speech_lengths, language, textnorm, **kwargs):
    """
    静态版本的导出forward方法,使用固定输入shape
    """
    # 确保所有输入都在正确的设备上
    device = speech.device
    language = language.to(device)
    textnorm = textnorm.to(device)
    
    # 生成查询向量
    language_query = self.embed(language).unsqueeze(1)
    textnorm_query = self.embed(textnorm).unsqueeze(1)
    
    # 拼接输入
    speech = torch.cat((textnorm_query, speech), dim=1)
    
    # 生成事件和情感查询
    event_emo_query = self.embed(torch.LongTensor([[1, 2]]).to(device)).repeat(
        speech.size(0), 1, 1
    )
    input_query = torch.cat((language_query, event_emo_query), dim=1)
    speech = torch.cat((input_query, speech), dim=1)
    
    # 更新语音长度
    speech_lengths_new = speech_lengths + 4
    
    # 通过编码器
    encoder_out, encoder_out_lens = self.encoder(speech, speech_lengths_new)
    
    if isinstance(encoder_out, tuple):
        encoder_out = encoder_out[0]

    # CTC输出
    ctc_logits = self.ctc.ctc_lo(encoder_out)
    
    return ctc_logits, encoder_out_lens

2.3 执行静态导出

# 使用中等音频预设导出模型
from export_sensevoice_static import export_static_model

export_static_model(
    model_path="./models/ASR/SenseVoice-Small/",
    output_path="./models/ASR/senseVoice-small-static",
    preset="MEDIUM_AUDIO"
)
导出结果

导出后的模型信息:

============================================================
ONNX模型基本信息
============================================================
模型文件路径: ./models/ASR/senseVoice-small-static/model_static.onnx
ONNX版本: 7
生产者信息: pytorch 2.5.1

============================================================
模型输入信息 (4 个输入)
============================================================
Input 1: speech
  数据类型: float32
  形状: [1, 300, 560]

Input 2: speech_lengths
  数据类型: int32
  形状: [1]

Input 3: language
  数据类型: int32
  形状: [1]

Input 4: textnorm
  数据类型: int32
  形状: [1]

============================================================
模型输出信息 (2 个输出)
============================================================
Output 1: ctc_logits
  数据类型: float32
  形状: [1, 300, 25055]

Output 2: encoder_out_lens
  数据类型: int32
  形状: [1]

3. QNN模型编译

3.1 编译配置

编译过程需要指定输入规格和目标设备:

# compile.py

# 定义输入规格
input_spec = {
    "speech": ((1, 300, 560), "float32"),
    "speech_lengths": ((1,), "int32"),
    "language": ((1,), "int32"),
    "textnorm": ((1,), "int32"),
}

3.2 设备选择

自动选择合适的高通设备:

# 获取可用设备列表
available_devices = hub.get_devices()

# 尝试找到QCS8550相关的设备
device = None
for dev in available_devices:
    if "qcs8550" in dev.name.lower() or "snapdragon" in dev.name.lower():
        device = dev
        break

# 如果没有找到QCS8550设备,使用第一个可用设备
if device is None:
    device = available_devices[0]

3.3 提交编译任务

# 提交编译任务到Qualcomm AI Hub
compile_job = hub.submit_compile_job(
    model=onnx_model_path,
    input_specs=input_spec,
    device=device,
    name="sensevoice-small-qcs8550",
    options="--target_runtime precompiled_qnn_onnx --output_names ctc_logits,encoder_out_lens"
)

# 等待编译完成
compile_job.wait()

# 检查编译状态
job_status = compile_job.get_status()
if job_status.code == 'FAILED':
    print(f"编译失败: {job_status.message}")
else:
    print("编译成功完成!")

3.4 性能分析

编译完成后可以进行性能分析:

# 下载编译后的模型
target_model = compile_job.get_target_model()

# 运行性能分析
profile_job = hub.submit_profile_job(
    model=target_model,
    device=device,
    name="sensevoice-small-qcs8550"
)

profile_job.wait()
profile_status = profile_job.get_status()

4. 完整流程示例

4.1 一键导出和编译

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

import os
import logging
from export_sensevoice_static import export_static_model
from compile import convert_model_to_qcs8550

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def complete_pipeline():
    """
    完整的导出和编译流程
    """
    # 步骤1: 导出静态模型
    logger.info("=== 步骤1: 导出静态ONNX模型 ===")
    try:
        export_static_model(
            model_path="./models/ASR/SenseVoice-Small/",
            output_path="./models/ASR/senseVoice-small-static",
            preset="MEDIUM_AUDIO"
        )
        logger.info("静态模型导出成功!")
    except Exception as e:
        logger.error(f"静态模型导出失败: {e}")
        return False
    
    # 步骤2: 编译QNN模型
    logger.info("=== 步骤2: 编译QNN模型 ===")
    static_model_path = "./models/ASR/senseVoice-small-static/model_static.onnx"
    
    if not os.path.exists(static_model_path):
        logger.error(f"静态模型文件不存在: {static_model_path}")
        return False
    
    try:
        result_path = convert_model_to_qcs8550(
            static_model_path,
            output_dir="./outputs",
            job_name="sensevoice-small-qcs8550",
            output_name="sensevoice-small-qcs8550"
        )
        
        if result_path:
            logger.info(f"QNN模型编译成功!结果保存在: {result_path}")
            return True
        else:
            logger.error("QNN模型编译失败")
            return False
            
    except Exception as e:
        logger.error(f"QNN模型编译过程中发生错误: {e}")
        return False

if __name__ == "__main__":
    success = complete_pipeline()
    if success:
        print("\n=== 流程完成 ===")
        print("1. 静态ONNX模型已导出到: ./models/ASR/senseVoice-small-static/")
        print("2. QNN模型已编译到: ./outputs/sensevoice-small-qcs8550/")
        print("3. 模型可在高通QCS8550等硬件平台上运行")
    else:
        print("\n流程执行失败,请检查错误信息")

4.2 测试验证

# test_run.py

class StaticSenseVoiceTest:
    """
    基于静态ONNX模型的SenseVoice测试类
    """
    
    def __init__(self, model_dir: str = "./models/ASR/senseVoice-small-static"):
        # 初始化模型
        self.model_dir = Path(model_dir)
        self.model_file = self.model_dir / "model_static.onnx"
        
        # 加载配置和初始化组件
        self.config = read_yaml(str(self.config_file))
        self.tokenizer = SentencepiecesTokenizer(bpemodel=str(self.tokenizer_file))
        self.frontend = WavFrontend(**self.config["frontend_conf"])
        self.ort_infer = OrtInferSession(str(self.model_file), device_id="cpu", intra_op_num_threads=4)
        
        # 模型参数
        self.target_seq_len = 300
        self.feat_dim = 560
        
    def infer(self, feats: np.ndarray, feats_len: int, language: str = "auto", textnorm: str = "woitn") -> str:
        """
        使用静态模型进行推理
        """
        # 获取语言和文本规范化ID
        lid = self._get_lid(language)
        tnid = self._get_tnid(textnorm)
        
        # 填充特征到固定长度 [300, 560]
        padded_feats = self.pad_features(feats)
        
        # 准备输入数据
        speech_input = padded_feats.reshape(1, self.target_seq_len, self.feat_dim).astype(np.float32)
        speech_lengths = np.array([min(feats_len, self.target_seq_len)], dtype=np.int32)
        language_input = np.array([lid], dtype=np.int32)
        textnorm_input = np.array([tnid], dtype=np.int32)
        
        # 执行推理
        outputs = self.ort_infer([speech_input, speech_lengths, language_input, textnorm_input])
        ctc_logits, encoder_out_lens = outputs
        
        # 处理输出并返回结果
        return self._decode_output(ctc_logits, encoder_out_lens, feats_len)

5. 部署和使用

5.1 硬件要求

  • 支持的高通芯片: QCS8550, QCS8450, QCS6490等
  • 操作系统: Android, Linux
  • 内存: 建议4GB以上
  • 存储: 建议500MB以上可用空间

5.2 部署步骤

  1. 模型部署:

    # 将编译后的模型文件复制到目标设备
    cp -r ./outputs/sensevoice-small-qcs8550 /path/to/device/
    
  2. 依赖库安装:

    # 在目标设备上安装Qualcomm SDK
    apt-get install qnn-sdk
    
  3. 运行推理:

    // C++示例代码
    #include "QnnManager.h"
    
    // 初始化QNN运行时
    QnnManager qnn_manager;
    qnn_manager.initialize("/path/to/model");
    
    // 准备输入数据
    std::vector<float> speech_input(1 * 300 * 560);
    std::vector<int32_t> speech_lengths = {300};
    std::vector<int32_t> language = {0};
    std::vector<int32_t> textnorm = {15};
    
    // 执行推理
    auto outputs = qnn_manager.infer({
        {"speech", speech_input},
        {"speech_lengths", speech_lengths},
        {"language", language},
        {"textnorm", textnorm}
    });
    
    // 获取结果
    auto ctc_logits = outputs["ctc_logits"];
    auto encoder_out_lens = outputs["encoder_out_lens"];
    

5.3 性能优化建议

  1. 输入长度优化:

    • 根据实际应用场景选择合适的SEQ_LEN
    • 短音频使用SHORT_AUDIO预设,长音频使用LONG_AUDIO预设
  2. 批处理优化:

    • 对于批量处理场景,使用BATCH_PROCESSING预设
    • 合理设置BATCH_SIZE以平衡内存使用和吞吐量
  3. 硬件加速:

    • 确保启用NPU加速
    • 使用适当的精度设置(FP16/INT8)
  4. 内存管理:

    • 及时释放不需要的内存
    • 使用内存池减少分配开销

6. 故障排除

6.1 常见问题

问题1: 静态导出失败
错误: AttributeError: 'NoneType' object has no attribute 'modules'

解决方案: 检查模型路径是否正确,确保模型文件完整。

问题2: 编译超时
错误: 编译任务超时

解决方案: 增加编译超时时间,或者简化模型结构。

问题3: 设备不兼容
错误: 找不到合适的编译设备

解决方案: 检查设备列表,选择兼容的设备进行编译。

6.2 调试技巧

  1. 日志分析:

    # 启用详细日志
    logging.basicConfig(level=logging.DEBUG)
    
  2. 模型验证:

    # 验证ONNX模型
    import onnx
    model = onnx.load("model_static.onnx")
    onnx.checker.check_model(model)
    
  3. 输入输出检查:

    # 检查模型输入输出
    print(f"输入: {[inp.name for inp in model.graph.input]}")
    print(f"输出: {[out.name for out in model.graph.output]}")
    

模型编译结果

在这里插入图片描述

在这里插入图片描述

模型性能测试

在这里插入图片描述

在这里插入图片描述

7. 总结

本指南详细介绍了SenseVoice模型从静态导出到QNN编译的完整流程,包括:

  1. 环境准备: 安装必要的依赖和模型文件
  2. 静态导出: 使用固定输入shape导出ONNX模型
  3. QNN编译: 将ONNX模型编译为适配高通硬件的格式
  4. 部署使用: 在目标设备上部署和运行模型
  5. 性能优化: 提供优化建议和最佳实践
  6. 故障排除: 解决常见问题和调试技巧

通过遵循本指南,您可以成功地将SenseVoice模型部署到高通硬件平台,充分利用硬件加速能力,实现高效的语音识别功能。

附录

A.1 参考文档

A.2 代码仓库

A.3 许可证

  • SenseVoice模型遵循Apache 2.0许可证
  • Qualcomm AI Hub SDK遵循相应的许可证条款

联系方式

模型获取

  • 公众号回复“voiceqnnonnx”

您可能感兴趣的与本文相关的镜像

PyTorch 2.8

PyTorch 2.8

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值