☰
FullSubNet 在 Ascend 310B1 上的 .om 转换与部署实战:从 fused LSTM 时延超标到「静态展开 + origin 精度」方案
2026/10/14 20:24:25 网站建设 项目流程
  • 具身智能
  • 机器人
  • 人工智能
  • 大模型
  • 模型推理服务
  • 本地部署
  • 计算机视觉
  • ROS

【免费下载链接】IB_Robot

Save the code of IB-Robot, an AI robot execution framework developed by openEuler Embedded for embodied intelligence scenarios. It includes references to the forked version of tensormsg, references to lerobot fork, code references to the lerobot_ros2:ros2_ws branch, as well as some code related to development usability.

项目地址:https://gitcode.com/openeuler/IB_Robot
点击查看免费下载

导读

本文完整记录 FullSubNet cumulative 218epochs checkpoint 从 PyTorch 导出、经 ONNX 中转、最终转换为 Ascend 310B1.om并集成进models/fullsubnetbundle 的全过程。文章以 speech_direction 流水线的语音增强链路为背景,详细复盘了「fused LSTM 直接导出后 SB 时延超标」与「FP16 长序列递归状态漂移」两个工程问题的定位、归因与解法,最终落地为「静态展开 LSTM(unrolled)+ origin 精度模式」的最终方案。读者将掌握 stateful 模型在昇腾 310B 系列 NPU 上的固定 ABI 设计、ATC 编译参数选择、pyACL 8.3 API 差异、板端门控/时延/漂移验证方法,以及 bundle 化部署的完整套路。

关键结论:unrolled + origin 组合下,FB 2.78ms + SB 18.48ms ≈ 21.3ms(预算 32ms/block),16 帧门控 cos ≈ 1.0,FB 256 帧漂移有界(cos_min 0.99996),SB 64 帧漂移数值上可忽略(cos ≈ 1.0)。

一、背景:speech_direction 流水线中的 FullSubNet 与跨平台部署诉求

FullSubNet 是 speech_direction 流水线(docs 中对应的说明见 src/voice_asr_service/README.md)中的语音增强模型:四通道音频经过 SRP-PHAT 声源定向与 FullSubNet 增强后,输出 VAD 概率与 4ch 增强音频。仓库中该链路的契约与 shape 定义集中在 contract.py,对外暴露的服务语义为observation.audio_4ch → voice.vad_prob + voice.audio_enhanced_4ch。

本次部署任务的背景:

  • 310P 现状:310P 上已有 FB/SB 拆分 stateful OM(ascend_310pdeployment,CANN 8.1.RC1),生产链路已经跑通。
  • 310B1 新目标:310B1(openEuler Embedded aarch64 板,CANN 8.3.RC1)需要一套自己的 OM 对。
  • 共用 checkpoint:两侧共用同一个 cumulative 218epochs checkpoint,算法契约完全一致,仅平台后端不同。
  • 转换工作目录:models/_work/fullsubnet/onnx2om_310b/(ONNX、GT、板端脚本、报告均保留在此,bundle 只放最终 OM)。需要说明的是,models/目录下的资产(checkpoint、OM)不随仓库分发,需通过scripts/download_models.py --models fullsubnet或离线流程生成/获取(详见 verify_speech_direction_assets.py 的注释)。

从源码结构看,310P 与 310B 的差异被严格限制在「OM 资产 + deployment 声明」层面:算法契约、Host 处理逻辑、执行器协议全部共用,这正是固定 ABI 设计带来的可移植性红利。

二、模型结构与固定 ABI:为什么 Host 做 DSP、OM 只做推理

生产链路把 FullSubNet 拆成两个 stateful 子图(全频带 Full-Band 与子带 Sub-Band),Host 负责 STFT / 归一化 / OLA(overlap-add),OM 只做网络推理;LSTM hidden/cell 作为图的输入输出回环。

子图输入输出说明
FBframe[4,2,257]+hidden[2,4,512]+cell[2,4,512]output[4,2,257]+hidden_out+cell_outLSTM(257→512, 2层) + fc(512→257) + ReLU
SBframe[1028,2,32]+hidden[2,1028,384]+cell[2,1028,384]output[1028,2,2]+hidden_out+cell_outLSTM(32→384, 2层) + fc(384→2)

几个关键设计点:

  • 固定静态 shape:T=2、batch=4(FB)/ 4×257=1028(SB),全部输入输出 shape 写死在 ABI 里,不允许 dynamic shape。
  • Host ABI 全部 float32:ATC 在 fp16/origin 精度模式下,OM 的输入输出 dtype 仍是 float32,内部计算精度由--precision_mode_v2决定。因此 Host 侧无需感知图内精度,这也是后续「origin 模式下 OM IO 仍为 float32」能够成立的前提。
  • ABI shape 的单一事实来源(SSOT):这些 shape 并不是散落在脚本里的魔法数字,而是集中定义在两处源码中。算法级常量(N_FFT=512、STFT_HOP=256、BATCH=4、NUM_FREQS=257、FB_HIDDEN=512、SB_HIDDEN=384、NUM_LAYERS=2、SB_FEATURES=32、SB_BATCH=BATCH*NUM_FREQS、LOOK_AHEAD=2)定义在 fullsubnet_stateful_executor.py 的StatefulFullSubNetContract中;OM 输入输出 ABI 的 shape 与语义绑定定义在 contract.py(如FULLSUBNET_FB_FRAME_SHAPE = (4, 2, 257)、FULLSUBNET_SB_FRAME_SHAPE = (1028, 2, 32))。打包脚本、session、校验脚本统一读这里,避免一端改了 shape 另一端仍按旧值比对。
  • state 自循环:FB/SB 的hidden_out喂回hidden_in、cell_out喂回cell_in,属于 v3Deployment禁止的 device link 自循环,因此 state 一律走 session 内allocate_device_buffer+prepare_dataset_banks双 bank,在 Device 内 ping-pong,不声明任何DeviceLink。FB 的输出不直接喂给 SB,而是回到 Host 经 STFT 归一化、sub-band 滑窗组织后再进 SB,所以 FB→SB 也没有 device 直传,全部用host.命名空间。

从 fullsubnet_stateful_acl.py 的_StatefulAclStage实现可以看到双 bank 回环的落地方式:

  • 加载 OM 后校验必须恰好 3 输入 / 3 输出;
  • 分配两组 hidden/cell Device buffer(state0、state1),运行期不做 state memcpy——本轮输出直接映射为下一轮输入,通过 bank 切换完成「无拷贝」状态回环;
  • 调用AclModel.prepare_dataset_banks将两组 buffer 绑定为两套静态 dataset(见 model.py 中AclModel.prepare_dataset_banks与execute_bank的实现);
  • 每次run调用execute_bank(self.bank, ...)后翻转self.bank = 1 - self.bank;
  • reset()将两组 buffer 清零并复位 bank 指针,用于流切换或会话开始。

生产用 ACL dataset bank 双 buffer ping-pong,验证脚本用 host 回放,两者数值等价——这保证了「离线验证结论可直接迁移到生产运行」。

三、环境准备:转换机与板端

3.1 转换机(x86_64 Ubuntu)

  • CANN toolkit 8.3.RC1(/usr/local/Ascend/ascend-toolkit/8.3.RC1)。
  • ATC 需要 stub 库路径(转换机无本地 NPU,用 stub 代替真实 runtime):
source /usr/local/Ascend/ascend-toolkit/latest/bin/set_env.sh export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/8.3.RC1/runtime/lib64/stub/linux/x86_64:$LD_LIBRARY_PATH export PYTHONPATH=$ASCEND_TOOLKIT_HOME/python/site-packages:$ASCEND_TOOLKIT_HOME/opp/built-in/op_impl/ai_core/tbe:$PYTHONPATH
  • Python 侧用仓库 venv(source .shrc_local),依赖fullsubnetwheel(from fullsubnet.model import Model)、onnx、onnxruntime。该 wheel 是仓库内审计过的私有构建:上游 pinned 到e97448375cd1e883276ad583317b1828318910dc,通过 build_fullsubnet_wheel.sh 从 pinned revision 重建(产物ibrobot_fullsubnet-0.0.1+ibrobot.1-py3-none-any.whl,patch 清单见 third_party/patches/fullsubnet/e97448375)。
  • 静态 shape 导出时 ATC 提示This model is irrelevant to the host platform,即无需--host_env_cpu=aarch64(与 RAM/RAM++ 文档结论一致,只有 dynamic_axes 才嵌入 host 平台信息)。

3.2 板端(openEuler Embedded aarch64, Ascend 310B1)

  • CANN 8.3.RC1 + driver,NPU 为/dev/davinci0。
  • pyACL 位于/usr/local/Ascend/ascend-toolkit/latest/python/site-packages,配合/IB_Robot/venv/bin/python3(3.11)。
  • 运行验证脚本需要显式挂 CANN 与 driver 库路径(见第八节命令)。

3.3 pyACL 8.3 API 差异(踩坑记录)

板端 CANN 8.3 的 pyACL 与常见旧示例代码不同,逐一探测后确认(这也是任何迁移到 8.3 的项目都可能遇到的兼容点):

常见写法(旧)本板实际 API
acl.rt.create_context()ctx, ret = acl.rt.create_context(0)(必须传 device id,返回二元组)
acl.mdl.create_data_buffer(ptr, size)buf, ret = acl.create_data_buffer(ptr, size)(在顶层acl模块)
acl.mdl.dataset_add_dataset(ds, buf)acl.mdl.add_dataset_buffer(ds, buf)(返回(dataset, ret))
acl.mdl.get_input_size(desc, i)acl.mdl.get_input_size_by_index(desc, i)(output 同理)
acl.rt.destroy_context()acl.rt.destroy_context(ctx)(必须传 ctx)
—acl.mdl.get_desc(desc, model_id)返回裸 ret;get_num_inputs/outputs返回裸 int;get_input_dims返回(dims_dict, ret),shape 在dims['dims']
—acl.util.numpy_to_ptr已弃用,推荐acl.util.bytes_to_ptr

仓库中生产侧的 ACL 调用统一收敛在inference_service的 ascend 后端(AclRuntimeManager/AclModel),板端验证脚本走的是更底层的 pyACL——两套代码面对的 8.3 API 差异是一致的,后者需要更小心地探测每个调用的返回形状。

四、基准 GT 与第一版(fused LSTM)导出

4.1 冻结 Torch fp32 GT

export_stateful_onnx.py:用生产同款 wrapper(StatefulFullBandT2/StatefulSubBandT2,来自fullsubnet_stateful_torch.py,即 fullsubnet_stateful_torch.py)加载 checkpoint,固定种子(20260907)生成 16 帧随机序列(randn*0.5),从零态回放并保存逐帧frames / outputs / hiddens / cells:

  • gt_fb.npz(16 帧)、gt_sb.npz(16 帧):门控用。
  • gt_fb_long.npz(256 帧,含逐帧状态)、gt_sb_long.npz(64 帧,输出 + 末态):漂移分析用(gen_long_gt.py)。

所有 ONNX / OM 一律与这套 GT 对比,禁止跨目录拼装参照物。这是整个验证流程的「基准锚点」:固定种子保证可复现,统一 GT 保证候选之间的可比性。

4.2 fused ONNX 导出与门控

torch.onnx.export直接导出 wrapper(LSTM 保持融合算子),输入名frame/hidden/cell,输出名output/hidden_out/cell_out。Torch vs ONNXRuntime(CPU)逐帧回放对比:FB/SB 输出、hidden、cell 余弦全部 1.000000,max_abs ≤ 4.6e-5(FB)/ 6e-6(SB)。ONNX gate PASS。

这一步的意义在于:先把「Torch 实现 ↔ ONNX 图」的等价性钉死,之后 ATC 编译、板端执行引入的偏差才能被单独归因到 NPU 侧。

4.3 fused ATC 编译与板端首测

--precision_mode_v2=fp16编译两个 OM 成功(FB 7.88MB / SB 3.84MB)。板端回放结果:

指标FBSB
16 帧 out cos_min0.995270.9999994
execute 时延(100 次均值)1.61ms40.22ms

两个问题暴露:SB 40ms 严重超 32ms/block 预算;FB 余弦随帧数下滑(16 帧已到 0.995),提示递归状态漂移。

这里「预算 32ms/block」是 speech_direction 流式增强的实时约束:每个 512 samples 处理块必须在固定时间内完成,否则无法跟上音频输入节奏。

五、问题一:SB fused LSTM 时延 40ms

排查过程:

  1. --op_select_implmode=high_performance重编 SB:40.32ms,无效。
  2. batch_first=True语义确认无误(SB 为 T=2 / batch=1028,不存在 T=1028 的误解)。
  3. 根因判定:fused ONNX LSTM 算子在 310B1 上映射到低效 kernel(同样形状的 MatMul+Sigmoid+Tanh 组合远快于 LSTM 算子)。

解法:静态展开 LSTM(unrolled)

export_unrolled_onnx.py把 2 步 × 2 层 LSTM 手工展开成addmm门控 +sigmoid/tanh+ 逐元素更新的静态图(权重作为 buffer 固化)。等价性验证:

  • fp64 下与nn.LSTM逐元素最大差6.9e-15(数学等价)。
  • fp32 下 ≤ 3.4e-5(累加顺序噪声,量级正常)。
  • unrolled ONNX vs GT:cos = 1.000000,ONNX gate PASS。

板端结果(fp16):FB 0.95ms、SB10.27ms(fused 的 1/4),FB+SB ≈ 11.2ms 进入预算。

要点:unroll 不是「近似」,而是与nn.LSTM在 fp64 下逐位等价(6.9e-15 已是浮点舍入噪声级别),只是把融合算子的计算拆成了昇腾 kernel 更擅长的常规算子组合——这是解决「特定算子在特定 SoC 上 kernel 低效」问题的通用思路。

六、问题二:FB fp16 长序列漂移

时延达标后,用 256 帧(FB)/ 64 帧(SB)长序列回放考察漂移是否有界:

变体FB 256 帧 out cos_minFB state cos_min结论
unrolled fp160.79950.9453漂移无界,不可用
unrolled cube_fp16in_fp32out0.21460.9546更差
unrolled + hi/lo split matmul(cfio)0.25730.9559补偿无效

排查链路(关键推理):

  1. cfio 与 fp16 漂移幅度接近 → 瓶颈不在 cube 输入 cast。
  2. hi/lo split(a@a_hi + a_lo@a的精确补偿分解,恢复 cube 输入 ~22 bit 尾数)在 cfio 下依旧漂移 →剩余误差来自 vector 算子:LSTM 状态更新c = f*c + i*g、h = o*tanh(c)在 fp16/cfio 模式下都以 fp16 执行,每步 ~2^-11 的相对误差在递归中不断注入并累积。
  3. origin精度模式让 vector 算子保持 fp32;此前 fused 图上 origin 编译失败(LSTM 算子内部含 cube MatMul,310B1 cube 不支持 fp32),而unrolled 图没有 LSTM 融合算子,origin 可编译成功。

最终组合:unrolled + origin

指标数值
FB 16 帧门控 out cos_min0.99999999989(max_abs 8.1e-5)
FB 256 帧漂移 out cos_min0.99996(有界;state cos_min 0.99985)
SB 16 帧门控 out cos_min0.99999999999956(max_abs 7.0e-6)
SB 64 帧漂移 out cos_min≈ 1.0(末态 hidden cos 0.99999999999984)
FB / SB execute p502.76ms / 18.48ms,合计 ≈ 21.3ms

hi/lo split 版本(+0.00002 余弦收益、多一倍 matmul)与 origin 搭配收益边际,被放弃;最终 pair 即朴素 unrolled 图 +--precision_mode_v2=origin。

原理小结:RNN 的递归状态更新对精度衰减是「放大器」——每步 fp16 的相对误差都会注入下一帧的状态并持续累积。origin模式让 vector 算子(状态更新的乘加)保持在 fp32,把每步误差压到可忽略量级,漂移立即有界。而 unrolled 恰好为 origin 清除了障碍:fused LSTM 内部含 cube MatMul,310B1 cube 不支持 fp32;unrolled 图全是常规算子,origin 可编译,cube 自动降 fp16。

七、候选对比总表

候选精度模式FB 长序列 cos_minSB 长序列 cos_minFB+SB 时延结论
fused LSTMfp16(16 帧已 0.995,趋势恶化)0.99998(64f)1.6+40.2=41.8ms❌ SB 超预算
fused LSTMorigin编译失败(cube fp32)--❌
unrolledfp160.800.999980.95+10.3=11.2ms❌ FB 漂移
unrolledcube_fp16in_fp32out0.21≈1.01.2+16.9=18.1ms❌ FB 漂移更差
unrolled + splitcube_fp16in_fp32out0.26--❌ 补偿无效
unrolled + splitorigin0.99998-3.5+?ms收益边际,弃
unrolledorigin0.99996≈1.02.8+18.5=21.3ms✅ 采用

这张表清晰展示了「精度与算力之间存在交叉权衡」的完整搜索空间:fused 慢但精度的上限被融合算子约束;unrolled 快但 fp16 递归漂移;最终通过 origin 同时拿到时延与精度。值得注意的是,「unrolled + cube_fp16in_fp32out」反而比纯 fp16 更差(0.21 vs 0.80),说明精度策略的作用点(cube 输入 vs vector 计算)远比「看起来更精确」重要。

八、板端验证

8.1 门控 + 时延(board_om_verify.py)

# 板端(192.168.136.127) cd /root/fullsubnet_310b_verify export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/lib64:/usr/local/Ascend/driver/lib64/common:/usr/local/Ascend/driver/lib64/driver:$LD_LIBRARY_PATH PYTHONPATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages \ /IB_Robot/venv/bin/python3 board_om_verify.py \ --fb-om fullsubnet_cum_stateful_fb_b4_t2_310b_unrolled_origin.om \ --sb-om fullsubnet_cum_stateful_sb_b4_t2_310b_unrolled_origin.om
  • OM ABI 实测与 manifest 一致:3 in / 3 out,frame/hidden/cell,float32,字节数精确(FB 8224/16384/16384;SB 263168/3158016/3158016)。
  • 16 帧逐帧回放(状态回环)vs GT:FB/SB 输出、hidden、cell cos ≈ 1.0,GATE PASS。
  • 时延(100 次 execute):FB mean 2.78 / p95 2.91ms;SB mean 18.48 / p95 18.55ms。

字节数可以作为 ABI 是否对齐的快速粗校验:4*2*257*4 + 2*4*512*4 + 2*4*512*4 = 8224 + 16384 + 16384,与契约 shape 完全对应。

8.2 长序列漂移(board_drift.py)

... board_drift.py --om <fb.om> --gt gt_fb_long.npz --frame-shape 4,2,257 --state-shape 2,4,512 --output-shape 4,2,257 ... board_drift.py --om <sb.om> --gt gt_sb_long.npz --frame-shape 1028,2,32 --state-shape 2,1028,384 --output-shape 1028,2,2

结果 JSON 已归档:models/_work/fullsubnet/onnx2om_310b/{board_verify_result.json, fb_drift_origin_256.json, sb_drift_origin_64.json}。

长序列漂移测试是本部署的关键一环:16 帧门控全绿不代表长流水可用(下文经验第 4 条会再次强调),必须用 256 帧级的长序列确认递归误差有界。

九、Bundle 集成

最终 OM 复制入 bundle(改名去掉unrolled,ABI 与实现细节无关——这个细节很重要:OM 文件名不暴露实现策略,部署层按 deployment 名选择即可):

models/fullsubnet/artifacts/ascend_310b/fullsubnet/ ├── fullsubnet_cum_stateful_fb_b4_t2_310b_origin.om (15.4MB, sha256 3e1a0af0…42a1) └── fullsubnet_cum_stateful_sb_b4_t2_310b_origin.om (7.5MB, sha256 c1b920dd…c061)

代码与配置同步(git 跟踪部分):

  • package_fullsubnet_bundle.py:新增_ascend_310b_deployment(soc=Ascend310B1,runtime_abi=cann-8.3.RC1),OM 路径纳入 required 资产。该文件的模块 docstring 直接点名本次部署的两个技术决策:"fused ONNX LSTM maps to a slow 310B1 kernel, so these OMs come from the statically unrolled LSTM export;originprecision keeps the recurrent state update in fp32 — see docs/fullsubnet_310b_deployment.md"。从实现看,_ascend_310b_deployment与_ascend_deployment共享同一个_execution_contract()(含 hidden/cell 四条StateLink,分别指向fullsubnet_fb.bank与fullsubnet_sb.bank两个 state bank),差异只在DeploymentTarget(soc + runtime_abi)与 artifacts 路径;且打包器要求 OM 对必须成对出现(部分缺失直接报错而不是静默跳过)。
  • models/fullsubnet/inference_manifest.json:新增ascend_310bdeployment(revision 1;310p/torch 部署不变)。打包逻辑通过_deployment_revision对比全部声明字段,字段变化时自动递增 revision,保证 manifest 版本与资产内容严格绑定。
  • verify_speech_direction_assets.py:fullsubnet 的 deployment 清单加入ascend_310b。该脚本以 bundle 内inference_manifest.json的 deployments 字典为唯一事实来源,新 deployment 自动纳入校验,逐资产校验文件存在性与 SHA-256。
  • 测试:
    • test_audio_contract_validation.py(fake bundle 补 310B 资产,写入artifacts/ascend_310b/fullsubnet/下的两个_310b_origin.om);
    • test_model_sessions.py(参数化覆盖ascend_310b+cann-8.3.RC1,验证 manifest 选择逻辑)。
  • src/voice_asr_service/README.md:资产下载与 deployment 说明。

验证:

source .shrc_local python3 scripts/verify_speech_direction_assets.py --bundle fullsubnet # 全 deployment 结构 + SHA-256 OK python3 -m pytest src/voice_asr_service/test/test_audio_contract_validation.py \ src/voice_asr_service/test/speech_direction/test_model_sessions.py # 16 passed

launch 侧无需改动:ascend_310b是 bundle 内 deployment,通过customprofile +fullsubnet_deployment:=ascend_310b显式选择(默认ascend_310pprofile 不变;silero-vad 尚无 310B deployment,故未加整机ascend_310bprofile)。

这个「deployment 级而非 profile 级」的接入策略值得注意:customprofile 允许覆盖silero_vad_backend/silero_vad_deployment/fullsubnet_backend/fullsubnet_deployment四个平台字段(写入公共算法字段会被 launch 拒绝),且 launch 会用 manifest 校验 deployment 的 backend 与声明的平台后端一致,避免后端与 deployment 错配的半切换配置。仅ascend后端会在节点环境注入 CANN 库路径,ubuntu_cuda不注入无关环境。

十、复现命令(转换机)

source .shrc_local # 1. fused 导出 + GT(历史路径,用于对比) python3 models/_work/fullsubnet/onnx2om_310b/export_stateful_onnx.py python3 models/_work/fullsubnet/onnx2om_310b/compare_onnx_gt.py # 2. unrolled 导出(最终图源) python3 models/_work/fullsubnet/onnx2om_310b/export_unrolled_onnx.py # 3. 长序列 GT python3 models/_work/fullsubnet/onnx2om_310b/gen_long_gt.py # 4. ATC(310B1, origin) source /usr/local/Ascend/ascend-toolkit/latest/bin/set_env.sh export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/8.3.RC1/runtime/lib64/stub/linux/x86_64:$LD_LIBRARY_PATH export PYTHONPATH=$ASCEND_TOOLKIT_HOME/python/site-packages:$ASCEND_TOOLKIT_HOME/opp/built-in/op_impl/ai_core/tbe:$PYTHONPATH cd models/_work/fullsubnet/onnx2om_310b atc --framework=5 --model=fullsubnet_cum_stateful_fb_b4_t2_unrolled.onnx \ --output=fullsubnet_cum_stateful_fb_b4_t2_310b_unrolled_origin \ --soc_version=Ascend310B1 --precision_mode_v2=origin \ --input_shape="frame:4,2,257;hidden:2,4,512;cell:2,4,512" atc --framework=5 --model=fullsubnet_cum_stateful_sb_b4_t2_unrolled.onnx \ --output=fullsubnet_cum_stateful_sb_b4_t2_310b_unrolled_origin \ --soc_version=Ascend310B1 --precision_mode_v2=origin \ --input_shape="frame:1028,2,32;hidden:2,1028,384;cell:2,1028,384" # 5. bundle 集成(先切回 workspace 根目录;两条显式复制,禁止 brace expansion 误用) cd "$(git rev-parse --show-toplevel 2>/dev/null || echo .)" cp models/_work/fullsubnet/onnx2om_310b/fullsubnet_cum_stateful_fb_b4_t2_310b_unrolled_origin.om \ models/fullsubnet/artifacts/ascend_310b/fullsubnet/fullsubnet_cum_stateful_fb_b4_t2_310b_origin.om cp models/_work/fullsubnet/onnx2om_310b/fullsubnet_cum_stateful_sb_b4_t2_310b_unrolled_origin.om \ models/fullsubnet/artifacts/ascend_310b/fullsubnet/fullsubnet_cum_stateful_sb_b4_t2_310b_origin.om source .shrc_local && python3 -m voice_asr_service.package_fullsubnet_bundle --bundle-root models/fullsubnet

ATC 参数要点:

  • --framework=5:ONNX 模型。
  • --soc_version=Ascend310B1:目标 SoC,必须与板端实际 NPU 一致。
  • --precision_mode_v2=origin:最终方案的核心——vector 算子保持 fp32,cube 自动降 fp16。
  • --input_shape:三输入静态 shape,与 contract.py 中的固定 ABI 逐字对齐,不允许动态维度。

十一、文件清单

models/_work/fullsubnet/onnx2om_310b/(保留物):

文件说明
export_stateful_onnx.py/export_unrolled_onnx.py/gen_long_gt.py导出与 GT 冻结脚本
compare_onnx_gt.pyTorch vs ONNX 门控
board_om_verify.py/board_drift.py板端门控/时延/漂移脚本
fullsubnet_cum_stateful_{fb,sb}_b4_t2_unrolled.onnx最终图源
fullsubnet_cum_stateful_{fb,sb}_b4_t2_310b_unrolled_origin.om最终 OM(bundle 内同内容)
board_verify_result.json/fb_drift_origin_256.json/sb_drift_origin_64.json板端结果
reports/decisions.jsonl/reports/summary.md决策账本与汇总

GT npz 与被拒绝的候选 OM/ONNX 已清理——保留「每个决策的最终证据」(结果 JSON 与决策账本),清理「中间产物」(被淘汰的 OM 与临时 GT),是这套工作目录整洁与可追溯并存的关键。

十二、经验总结

  1. 310B1 fused LSTM 慢 → unroll:fused ONNX LSTM 在 310B1 上 SB 需 40ms;静态展开成常规 MatMul/elementwise 后 10~18ms。unroll 与nn.LSTM在 fp64 下逐位等价(6.9e-15),无语义风险。
  2. stateful RNN 的 fp16 漂移主因是 vector 算子,不只是 cube cast:cfio、hi/lo split 补偿都无效,因为状态更新c=f*c+i*g本身在跑 fp16;origin让 vector 算子保 fp32 后漂移立即有界(cos_min 0.99996)。
  3. origin 的可用性取决于图里有没有融合算子:fused LSTM 图上 origin 编译失败(内部 cube 要求 fp32);unrolled 图全是常规算子,origin 可编译,cube 自动降 fp16。
  4. 短门控不等于长流水可用:FB 16 帧门控 0.995 看似可用,256 帧跌到 0.80。stateful 模型必须做长序列漂移测试再下结论。
  5. pyACL 8.3 API 与旧示例差异大(见 3.3 表),板端脚本先小步探测每个调用的返回形状再写主流程。
  6. 方法论:固定种子冻结 GT → 本地 ONNX 门控 → ATC → 板端回放门控 + 长序列漂移 + 时延,所有候选对同一 GT 比较;决策与结果落reports/。

这套方法论在仓库中同样服务于其他昇腾部署(如 RAM/RAM++ 的 host 平台信息结论与 310P 部署路径),而本部署的独特价值在于同时覆盖了「算子级性能问题」与「递归精度漂移问题」两类典型故障,且最终方案(unrolled + origin)在 package_fullsubnet_bundle.py 与 contract.py 中作为文档化决策沉淀下来,形成可复用的工程资产。

  • 具身智能
  • 机器人
  • 人工智能
  • 大模型
  • 模型推理服务
  • 本地部署
  • 计算机视觉
  • ROS

【免费下载链接】IB_Robot

Save the code of IB-Robot, an AI robot execution framework developed by openEuler Embedded for embodied intelligence scenarios. It includes references to the forked version of tensormsg, references to lerobot fork, code references to the lerobot_ros2:ros2_ws branch, as well as some code related to development usability.

项目地址:https://gitcode.com/openeuler/IB_Robot
点击查看免费下载

相关推荐

上一篇:Dgraph跨云部署终极指南:如何实现高性能分布式图数据库的多云部署
下一篇:Mistral-7B-Instruct-v0.1_rai_1.7.1_npu_4K提示词工程:提升NPU推理效率的10个技巧

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询