- 示例工程
- 人工智能
- 嵌入式
- 边缘计算
- 计算机视觉
- 模型优化
【免费下载链接】rknn_model_zoo
本文是 rknn_model_zoo 仓库中 examples/whisper/export_onnx.md 的完整技术指南,系统讲解如何在 RKNN 开发流程中导出 OpenAI Whisper 语音识别模型的 ONNX 权重:既包括将默认 30 秒输入长度改为 20 秒的关键源码修改,也包括保持原始 30 秒长度的标准导出流程,以及导出后 Python/C++ 两套 Demo 中必须同步修改的参数。读完本文,你可以独立完成 whispertiny/base/medium三种模型 encoder/decoder 的 ONNX 导出、onnxsim 精简与后续 RKNN 转换部署。
一、为什么需要单独导出 Whisper 的 ONNX 模型
Whisper 是 OpenAI 开源的通用语音识别模型,支持多语种语音识别、语音翻译与语种识别。在 rknn_model_zoo 的 Whisper 示例 中,整个识别流程被拆分为两个可独立推理的 ONNX 模型:
- encoder(编码器):将音频的 log-mel 频谱特征编码为语义向量,导出脚本中其输入名为
x,输出名为out; - decoder(解码器):接收 token 序列与 encoder 输出向量,自回归地生成识别文本,导出脚本中其输入名为
tokens与audio,输出名为out。
导出工具位于 examples/whisper/python/export_onnx.py,其核心逻辑是加载 OpenAI 官方 whisper 权重后,分别对model.encoder与model.decoder调用torch.onnx.export,并统一采用opset_version=12,最后用onnxsim对两个模型做常量折叠与图精简,确保导出的 ONNX 更干净、更适合后续 RKNN 工具链解析。
需要特别说明的是:仓库官方脚本当前仅支持tiny、base、medium三种模型规格,small与large暂不支持,这是导出前需要确认的前提。
二、环境准备:安装指定版本的 openai-whisper
导出脚本依赖 OpenAI 官方 Python 包,仓库固定使用 2023-11-17 版本:
pip install openai-whisper==20231117安装后,whisper 的源码会落在 site-packages 中(例如~/python3.8/site-packages/whisper/),后续对audio.py与model.py的修改都在这个安装路径下进行。导出的 ONNX 模型默认保存到examples/whisper/model/目录,命名规则为whisper_encoder_{MODEL_TYPE}.onnx与whisper_decoder_{MODEL_TYPE}.onnx。
三、导出 20 秒输入长度的 ONNX 模型
默认的 whisper 模型按 30 秒音频输入设计(CHUNK_LENGTH = 30),而本示例的 RKNN Demo 默认按 20 秒处理,因此需要先修改官方源码,再执行导出脚本。
3.1 修改官方源码
第一步:修改whisper/audio.py中的CHUNK_LENGTH
在安装包路径(如~/python3.8/site-packages/whisper/audio.py)中找到音频分块长度定义:
CHUNK_LENGTH = 30 # 修改为: CHUNK_LENGTH = 20该常量决定了 log-mel 频谱的输入长度,是模型输入尺寸的根基,导出时生成数据、后续 Demo 的前处理都必须与它保持一致。
第二步:修改whisper/model.py中的位置编码逻辑
model.py中原本对输入形状与位置编码形状做严格断言,并把整张位置编码加到x上。改为 20 秒后输入序列变短,需要放宽断言并改用切片索引:
assert x.shape[1:] == self.positional_embedding.shape, "incorrect audio shape" # 注释掉上面的断言,改为: # assert x.shape[1:] == self.positional_embedding.shape, "incorrect audio shape" x = (x + self.positional_embedding).to(x.dtype) # 改为: x = (x + self.positional_embedding[-x.shape[1]:, :]).to(x.dtype)self.positional_embedding[-x.shape[1]:, :]表示按当前输入的实际序列长度截取位置编码的后半段,从而兼容任意短于完整长度的输入序列。
3.2 执行导出命令
cd python python export_onnx.py --model_type <MODEL_TYPE> --n_mels <N_MELS(optional)> # 例如: python export_onnx.py --model_type base --n_mels 80参数说明:
| 参数 | 必填 | 含义 | 取值范围 / 默认值 |
|---|---|---|---|
--model_type | 是 | 指定模型规格 | tiny、base、medium(small/large暂不支持) |
--n_mels | 否 | 指定梅尔滤波器数量 | 如80、128,默认80 |
从 export_onnx.py 源码可以看到,导出前的数据生成流程为:以sample_rate = 16000生成1 × 40 × 16000的随机音频 → 经whisper.pad_or_trim裁剪/补齐 →whisper.log_mel_spectrogram(audio, n_mels=n_mels)得到 mel 特征 → 送入model.encoder得到encoder_output;同时以max_tokens = 12生成随机 token 序列x_tokens,作为 decoder 的输入。随后依次导出:
- encoder:
torch.onnx.export(model.encoder, (x_mel), ..., input_names=["x"], output_names=["out"], opset_version=12) - decoder:
torch.onnx.export(model.decoder, (x_tokens, encoder_output), ..., input_names=["tokens", "audio"], output_names=["out"], opset_version=12)
导出完成后脚本会对两个模型分别执行onnxsim.simplify并重新保存,并打印保存路径。
四、导出原始 30 秒输入长度的 ONNX 模型
如果希望保持 whisper 原始的 30 秒输入设计(例如与官方推理流程对齐),则无需修改任何官方源码,直接执行导出命令即可:
pip install openai-whisper==20231117 cd python python export_onnx.py --model_type <MODEL_TYPE> --n_mels <N_MELS(optional)> # 例如: python export_onnx.py --model_type base --n_mels 80参数含义与上一节完全一致。注意,这里默认的CHUNK_LENGTH仍为 30,模型输入为 30 秒对应的特征尺寸;后续 Python/C++ Demo 中的CHUNK_LENGTH也必须相应设为 30,否则前处理与模型输入尺寸不匹配。
五、导出后的关键参数与源码对应关系
导出 ONNX 只是第一步,真正决定推理正确性的是 Demo 侧一系列与输入长度、模型维度强相关的宏/常量。它们分散在 Python 与 C++ 两套实现中,且与 export_onnx.py 导出的模型必须严格对齐。
5.1 Python Demo 中的对齐参数
在 examples/whisper/python/whisper.py 顶部定义了一组核心常量:
SAMPLE_RATE = 16000 N_FFT = 400 HOP_LENGTH = 160 CHUNK_LENGTH = 20 # 需与模型输入长度一致 N_SAMPLES = CHUNK_LENGTH * SAMPLE_RATE MAX_LENGTH = CHUNK_LENGTH * 100 N_MELS = 80其中MAX_LENGTH = CHUNK_LENGTH * 100 = 2000对应 log-mel 频谱的时间帧数(即CHUNK_LENGTH / HOP_LENGTH * SAMPLE_RATE ≈ 20 / 160 * 16000 = 2000),N_MELS = 80对应频谱的梅尔维度。mel_filters()函数从 mel_80_filters.txt 读取80 × 201的梅尔滤波器矩阵,其中 201 等于N_FFT / 2 + 1;且源码中有assert n_mels in {80},即当前 Python Demo 只支持 n_mels=80,若导出时使用了 128,需要相应适配滤波器文件。
5.2 C++ Demo 中的对齐参数
在 examples/whisper/cpp/process.h 中定义了 C++ 侧的宏,其中两项在文档“Special Notes”中被明确要求按模型修改:
#define CHUNK_LENGTH 20 // 需与模型输入长度一致 #define ENCODER_OUTPUT_SIZE CHUNK_LENGTH * 50 * 512 // 384/512/1024 分别对应 tiny/base/mediumCHUNK_LENGTH:音频分块长度,必须与导出的模型输入长度一致(20 秒模型配 20,30 秒模型配 30);ENCODER_OUTPUT_SIZE:encoder 输出向量的总元素数,其公式为CHUNK_LENGTH * 50 * d_model。d_model 为 transformer 隐藏维度,tiny/base/medium分别对应384/512/1024,即 base 模型为20 * 50 * 512 = 512000,换成 medium 需改为20 * 50 * 1024。
同一文件中还定义了MAX_AUDIO_LENGTH = CHUNK_LENGTH * SAMPLE_RATE、ENCODER_INPUT_SIZE = CHUNK_LENGTH * 100、N_MELS 80、MELS_FILTERS_SIZE 201、MAX_TOKENS 12、VOCAB_NUM 51865等,共同约束着前处理与解码循环。
5.3 解码循环与 token 常量
在 C++ 的 process.cc 与 Python 的whisper.py中,解码采用自回归方式:从sot(50258)开始,拼接任务 token(en 为 50259、zh 为 50260)与timestamp_begin(50364)后反复推理,直至输出endoftext(50257)。中文字符在词表中以 base64 编码存放,因此 C++ 侧实现了base64_decode,Python 侧同样有对应的解码函数——这些逻辑与导出的 decoder 输入输出完全对应。
六、从 ONNX 到 RKNN:转换与 Demo 验证
6.1 获取/转换模型
仓库提供了官方转换脚本 examples/whisper/python/convert.py:
cd python python convert.py <onnx_model> <TARGET_PLATFORM> <dtype(optional)> <output_rknn_path(optional)> # 例如: python convert.py ../model/whisper_encoder_base_20s.onnx rk3588 python convert.py ../model/whisper_decoder_base_20s.onnx rk3588<onnx_model>:导出的 ONNX 模型路径;<TARGET_PLATFORM>:NPU 平台名,支持rk3562、rk3566、rk3568、rk3576、rk3588、rv1126b;<dtype>(可选):i8/u8表示量化,fp表示不量化,默认fp;<output_rknn_path>(可选):RKNN 模型保存路径,默认与 ONNX 同目录。
若想跳过手动导出,仓库 model/download_model.sh 直接提供了 base 20 秒模型的 ONNX 下载脚本:
cd model ./download_model.sh6.2 运行 Python Demo 验证
cd python # 使用 ONNX 模型推理 python whisper.py --encoder_model_path ../model/whisper_encoder_base_20s.onnx --decoder_model_path ../model/whisper_decoder_base_20s.onnx --task en --audio_path ../model/test_en.wav # 使用 RKNN 模型推理(需指定目标平台) python whisper.py --encoder_model_path ../model/whisper_encoder_base_20s.rknn --decoder_model_path ../model/whisper_decoder_base_20s.rknn --task en --audio_path ../model/test_en.wav --target rk3588--task支持en(英文识别)与zh(中文识别),--target指定 NPU 平台,--audio_path指向测试音频(仓库自带 test_en.wav 与 test_zh.wav)。运行后输出形如:
# TASK_FOR_EN Whisper output: Mr. Quilter is the apostle of the middle classes, and we are glad to welcome his gospel. # TASK_FOR_ZH Whisper output: 对我做了介绍,我想说的是大家如果对我的研究感兴趣不同平台、工具与驱动版本下结果可能略有差异。C++ Demo 的编译、推板与运行步骤(build-android.sh/build-linux.sh、./rknn_whisper_demo <encoder.rknn> <decoder.rknn> <task> <audio>)可参见 examples/whisper/README.md。
七、常见问题与排查要点
- 导出后模型无法在 Demo 上跑通:优先核对三处
CHUNK_LENGTH(官方audio.py、Pythonwhisper.py、C++process.h)是否一致,20 秒模型三处都应等于 20; - 换了模型规格(如 base → medium):C++ 侧
ENCODER_OUTPUT_SIZE必须同步从512维改为1024维,否则 decoder 输入尺寸不匹配; n_mels非默认值:当前 Python Demo 的mel_filters()有assert n_mels in {80}限制,使用其他梅尔数导出时需同步替换 mel_80_filters.txt 并放开断言;- 量化失败:如需部署到 RKNN 上做
i8量化,可在convert.py中传入i8,Whisper 属于较重的 Transformer 模型,量化精度受平台与工具版本影响,建议以实际识别结果为准; - 模型规格限制:
small/large未被官方导出脚本覆盖,如需使用需自行扩展export_onnx.py的导出逻辑。
八、总结
本指南完整覆盖了 export_onnx.md 的全部内容:20 秒模型的官方源码两处修改(audio.py的CHUNK_LENGTH与model.py的位置编码切片)、30 秒模型的一键导出、--model_type与--n_mels参数说明,以及导出后 Python/C++ Demo 中CHUNK_LENGTH与ENCODER_OUTPUT_SIZE的强制对齐要求。在此基础上,结合 export_onnx.py、convert.py、whisper.py 与 process.h 等仓库源码,进一步揭示了 encoder/decoder 的输入输出命名、opset 版本、onnxsim 精简、梅尔滤波器矩阵以及解码 token 常量等底层细节,帮助你从“能导出”走向“能对齐、能部署、能排错”。
- 示例工程
- 人工智能
- 嵌入式
- 边缘计算
- 计算机视觉
- 模型优化
【免费下载链接】rknn_model_zoo
相关推荐
rknn_model_zoo 的 PP-LiteSeg Paddle 转 ONNX 全流程指南:从 PaddleSeg 模型导出到 RKNN NPU 部署
rknn_model_zoo 的 PP LiteSeg Paddle 转 ONNX 全流程指南:从 PaddleSeg 模型导出到 RKNN NPU 部署 导读
示例工程人工智能嵌入式边缘计算计算机视觉模型优化sherpa-onnx Whisper 模型 RKNN 导出与 RK3588 板端部署实战
sherpa onnx Whisper 模型 RKNN 导出与 RK3588 板端部署实战 本文基于 sherpa onnx 仓库中 scripts/whisp
人工智能语音音频本地部署rknn_model_zoo 实战:PP-OCRv4 识别模型从 Paddle 到 ONNX 的转换与 RKNN 部署指南
rknn_model_zoo 实战:PP OCRv4 识别模型从 Paddle 到 ONNX 的转换与 RKNN 部署指南 本文面向在 Rockchip RKN
示例工程人工智能嵌入式边缘计算计算机视觉模型优化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考