- 具身智能
- 机器人
- 人工智能
- 大模型
- 模型推理服务
- 本地部署
- 计算机视觉
- 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.
导读
本文完整记录 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 作为图的输入输出回环。
| 子图 | 输入 | 输出 | 说明 |
|---|---|---|---|
| FB | frame[4,2,257]+hidden[2,4,512]+cell[2,4,512] | output[4,2,257]+hidden_out+cell_out | LSTM(257→512, 2层) + fc(512→257) + ReLU |
| SB | frame[1028,2,32]+hidden[2,1028,384]+cell[2,1028,384] | output[1028,2,2]+hidden_out+cell_out | LSTM(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)。板端回放结果:
| 指标 | FB | SB |
|---|---|---|
| 16 帧 out cos_min | 0.99527 | 0.9999994 |
| execute 时延(100 次均值) | 1.61ms | 40.22ms |
两个问题暴露:SB 40ms 严重超 32ms/block 预算;FB 余弦随帧数下滑(16 帧已到 0.995),提示递归状态漂移。
这里「预算 32ms/block」是 speech_direction 流式增强的实时约束:每个 512 samples 处理块必须在固定时间内完成,否则无法跟上音频输入节奏。
五、问题一:SB fused LSTM 时延 40ms
排查过程:
--op_select_implmode=high_performance重编 SB:40.32ms,无效。batch_first=True语义确认无误(SB 为 T=2 / batch=1028,不存在 T=1028 的误解)。- 根因判定: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_min | FB state cos_min | 结论 |
|---|---|---|---|
| unrolled fp16 | 0.7995 | 0.9453 | 漂移无界,不可用 |
| unrolled cube_fp16in_fp32out | 0.2146 | 0.9546 | 更差 |
| unrolled + hi/lo split matmul(cfio) | 0.2573 | 0.9559 | 补偿无效 |
排查链路(关键推理):
- cfio 与 fp16 漂移幅度接近 → 瓶颈不在 cube 输入 cast。
- 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 的相对误差在递归中不断注入并累积。 origin精度模式让 vector 算子保持 fp32;此前 fused 图上 origin 编译失败(LSTM 算子内部含 cube MatMul,310B1 cube 不支持 fp32),而unrolled 图没有 LSTM 融合算子,origin 可编译成功。
最终组合:unrolled + origin
| 指标 | 数值 |
|---|---|
| FB 16 帧门控 out cos_min | 0.99999999989(max_abs 8.1e-5) |
| FB 256 帧漂移 out cos_min | 0.99996(有界;state cos_min 0.99985) |
| SB 16 帧门控 out cos_min | 0.99999999999956(max_abs 7.0e-6) |
| SB 64 帧漂移 out cos_min | ≈ 1.0(末态 hidden cos 0.99999999999984) |
| FB / SB execute p50 | 2.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_min | SB 长序列 cos_min | FB+SB 时延 | 结论 |
|---|---|---|---|---|---|
| fused LSTM | fp16 | (16 帧已 0.995,趋势恶化) | 0.99998(64f) | 1.6+40.2=41.8ms | ❌ SB 超预算 |
| fused LSTM | origin | 编译失败(cube fp32) | - | - | ❌ |
| unrolled | fp16 | 0.80 | 0.99998 | 0.95+10.3=11.2ms | ❌ FB 漂移 |
| unrolled | cube_fp16in_fp32out | 0.21 | ≈1.0 | 1.2+16.9=18.1ms | ❌ FB 漂移更差 |
| unrolled + split | cube_fp16in_fp32out | 0.26 | - | - | ❌ 补偿无效 |
| unrolled + split | origin | 0.99998 | - | 3.5+?ms | 收益边际,弃 |
| unrolled | origin | 0.99996 | ≈1.0 | 2.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 选择逻辑)。
- test_audio_contract_validation.py(fake bundle 补 310B 资产,写入
- 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 passedlaunch 侧无需改动: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/fullsubnetATC 参数要点:
--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.py | Torch 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),是这套工作目录整洁与可追溯并存的关键。
十二、经验总结
- 310B1 fused LSTM 慢 → unroll:fused ONNX LSTM 在 310B1 上 SB 需 40ms;静态展开成常规 MatMul/elementwise 后 10~18ms。unroll 与
nn.LSTM在 fp64 下逐位等价(6.9e-15),无语义风险。 - stateful RNN 的 fp16 漂移主因是 vector 算子,不只是 cube cast:cfio、hi/lo split 补偿都无效,因为状态更新
c=f*c+i*g本身在跑 fp16;origin让 vector 算子保 fp32 后漂移立即有界(cos_min 0.99996)。 - origin 的可用性取决于图里有没有融合算子:fused LSTM 图上 origin 编译失败(内部 cube 要求 fp32);unrolled 图全是常规算子,origin 可编译,cube 自动降 fp16。
- 短门控不等于长流水可用:FB 16 帧门控 0.995 看似可用,256 帧跌到 0.80。stateful 模型必须做长序列漂移测试再下结论。
- pyACL 8.3 API 与旧示例差异大(见 3.3 表),板端脚本先小步探测每个调用的返回形状再写主流程。
- 方法论:固定种子冻结 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.
相关推荐
IB_Robot 昇腾 OM 转换精度门禁:Torch vs ONNX 与 Torch vs Ascend OM 双数值门禁的完整规则与实践
IB_Robot 昇腾 OM 转换精度门禁:Torch vs ONNX 与 Torch vs Ascend OM 双数值门禁的完整规则与实践 本文基于 IB_R
具身智能机器人人工智能大模型模型推理服务本地部署计算机视觉ROSopenEulerOpenHarmony深度学习语音AI AgentAscendFigma MCP Server深色模式设计:创建符合用户偏好的界面
Figma MCP Server深色模式设计:创建符合用户偏好的界面 Figma MCP Server是一款功能强大的设计系统工具,它能够帮助开发者和设计师从代
AI 技能AI 插件Hoppscotch:免费开源 API 测试工具完整指南
Hoppscotch:免费开源 API 测试工具完整指南 调接口时最耗时的往往不是写请求本身,而是来回切换:改参数要翻文档,换 Token 要翻配置,发完请求还
开发工具接口测试前端后端CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考