1. 背景

在 SmolVLA 从 PyTorch 向 ONNX / TensorRT 迁移过程中,最初为了规避 ONNX opset 18 下 Conv(BF16) 无法加载的问题,将部署模型整体切换为 FP32。

虽然后续验证证明:

PyTorch FP32 -> ONNX FP32
54 / 54 PASS
max_abs = 4.7445e-05

说明 ONNX FP32 导出本身是数值等价的,但同时发现:

Official reference -> PyTorch FP32
past_key max_abs ≈ 0.304
action   max_abs ≈ 0.128

因此问题不在 ONNX exporter,而在于:

Official/native precision
        ↓
      FP32

改变了 SmolVLA 的实际推理数值路径。

本次排查目标是:

  1. 找出 SmolVLA 中对精度最敏感的模块;
  2. 区分 Prefix/VLM、KV Cache、Expert、Euler、Action 各部分的误差来源;
  3. 找到与 official reference 完全一致的 PyTorch mixed precision 策略;
  4. 为后续 ONNX / TensorRT mixed precision 迁移提供基准。

2. 验证对象

验证共比较 54 个输出:

  • prefix_pad_masks:1 项
  • past_key_0 ~ past_key_15:16 项
  • past_value_0 ~ past_value_15:16 项
  • step_00_velocity ~ step_09_velocity:10 项
  • step_00_x ~ step_09_x:10 项
  • normalized_action_chunk:1 项

总计:

54 tensors

其中主要分组:

Prefix / KV
├── prefix_pad_masks
├── past_key_0 ~ past_key_15
└── past_value_0 ~ past_value_15

Expert / Flow Matching
├── step_00_velocity ~ step_09_velocity
└── step_00_x ~ step_09_x

Final
└── normalized_action_chunk

默认严格数值门限:

atol = 1e-4
rtol = 1e-4

3. 第一阶段:全 FP32 推理

3.1 策略

将 SmolVLA 部署路径整体切换为 FP32:

Vision Encoder      FP32
Vision Connector    FP32
VLM                 FP32
KV Cache            FP32
Expert              FP32
Action modules      FP32
Euler               FP32
Output              FP32

3.2 结果

total = 54
pass  = 1
fail  = 53
pass_rate ≈ 1.85%

关键误差:

past_key max_abs   ≈ 0.303991
past_value max_abs ≈ 0.165426
velocity max_abs   ≈ 0.207983
action max_abs     ≈ 0.127735

其中:

past_key_7 max_abs ≈ 0.303991

说明误差在 Prefix / VLM 阶段已经产生。

Euler 状态误差还呈现明显累计趋势:

step_00_x ≈ 0.0160
step_01_x ≈ 0.0307
step_02_x ≈ 0.0501
...
step_08_x ≈ 0.1173
step_09_x ≈ 0.1277

3.3 结论

全 FP32 路径虽然有利于 ONNX opset 18 兼容,但与 official reference 数值不等价。

关键问题不是 ONNX 转换,而是:

BF16 / native
    ↓
FP32

改变了模型推理行为。


4. 第二阶段:验证 ONNX FP32 转换本身

将 ONNX FP32 与同精度 PyTorch FP32 直接比较:

PyTorch FP32
    ↓
ONNX FP32

结果:

total = 54
pass  = 54
fail  = 0
max_abs = 4.744529724121094e-05

分模块:

past_key   max_abs = 4.7445e-05
past_value max_abs = 3.3140e-05
velocity   max_abs = 1.7166e-05
euler_x    max_abs = 1.1683e-05
action     max_abs = 1.1683e-05

4.1 结论

可以明确排除以下部分作为主要误差来源:

  • ONNX exporter
  • ONNX Runtime CPU
  • ONNX KV graph
  • ONNX Euler loop
  • ONNX Action graph

即:

PyTorch FP32 -> ONNX FP32

本身是正确的。

真正的大误差来自:

Official/native -> PyTorch FP32

5. 第三阶段:初版 mixed precision

初始 mixed precision 策略:

Vision Encoder      FP32
Vision Connector    BF16
VLM                 BF16
State Projection    FP32
Expert              FP32
Action In           FP32
Action Time         FP32
Action Out          FP32
KV Cache            FP32
Euler               FP32
Output              FP32

结果:

total = 54
pass  = 1
fail  = 53
max_abs = 0.375

主要误差:

past_key max_abs   = 0.375
past_value max_abs = 0.1875
velocity max_abs   ≈ 0.205899
action max_abs     ≈ 0.126714

5.1 与全 FP32 对比

模块全 FP32初版 Mixed变化
past_key0.3039910.375000变差
past_value0.1654260.187500变差
velocity0.2079830.205899基本一致
action0.1277350.126714基本一致

5.2 结论

仅将 VLM 改回 BF16、但 Vision Encoder 保持 FP32,并不能恢复 official 行为。

主要嫌疑:

Image
 ↓
Vision FP32
 ↓
Connector BF16
 ↓
VLM BF16

在进入 VLM 之前,视觉特征已经与 official BF16 Vision 路径发生偏差。


6. 第四阶段:恢复 Prefix 为 BF16

修改为:

Vision Encoder      BF16
Vision Connector    BF16
VLM                 BF16
KV Cache            BF16

Expert              FP32
Action In           FP32
Action Time         FP32
Action Out          FP32

Euler               FP32
Output              FP32

结果:

total = 54
pass  = 42
fail  = 12
max_abs ≈ 0.055039

最关键的变化:

past_key:
16 / 16 PASS
max_abs = 0

past_value:
16 / 16 PASS
max_abs = 0

说明 Prefix 完全恢复。

剩余误差:

velocity:
10 / 10 FAIL
max_abs ≈ 0.055039

euler_x:
9 / 10 PASS
max_abs ≈ 0.007834

action:
FAIL
max_abs ≈ 0.007834

6.1 与全 FP32 对比

模块全 FP32Prefix BF16改善
past_key0.3039910.0完全消除
past_value0.1654260.0完全消除
velocity0.2079830.055039约下降 73.5%
action0.1277350.007834约下降 93.9%

6.2 结论

可确认:

Vision BF16
Connector BF16
VLM BF16
KV BF16

是 official Prefix 的正确精度策略。

Prefix 已经不再是问题。

剩余误差集中于:

Expert / denoise / action projection

7. 第五阶段:Expert / Action 恢复 native

保持 Prefix 不变:

Vision Encoder      BF16
Vision Connector    BF16
VLM                 BF16
KV Cache            BF16

将:

Expert
Action In
Action Time
Action Out

从强制 FP32 改为:

native

最终策略:

Vision Encoder      BF16
Vision Connector    BF16
VLM                 BF16
KV Cache            BF16

Expert              native
Action In           native
Action Time         native
Action Out          native

State Projection    FP32
Euler               FP32
Output              FP32

7.1 最终结果

total = 54
pass  = 54
fail  = 0
max_abs = 0.0

各模块:

action:
1 / 1 PASS
max_abs = 0

euler_x:
10 / 10 PASS
max_abs = 0

past_key:
16 / 16 PASS
max_abs = 0

past_value:
16 / 16 PASS
max_abs = 0

prefix_mask:
1 / 1 PASS

velocity:
10 / 10 PASS
max_abs = 0

这说明:

Official reference
        vs
PyTorch official-like

在当前测试样本下实现逐 Tensor 完全一致。


8. 最终 official-like precision policy

建议正式定义为:

official-like

具体配置:

{
  "vision_encoder": "bf16",
  "vision_connector": "bf16",
  "vlm": "bf16",
  "kv_cache": "bf16",

  "expert": "native",
  "action_in": "native",
  "action_time": "native",
  "action_out": "native",

  "state_proj": "fp32",
  "euler": "fp32",
  "output": "fp32"
}

逻辑结构:

Image
 ↓
Vision Encoder BF16
 ↓
Vision Connector BF16
 ↓
VLM BF16
 ↓
KV Cache BF16
 ↓
Expert native
 ↓
Velocity
 ↓
Euler FP32
 ↓
Action FP32

最终:

54 / 54 PASS
max_abs = 0

9. 精度误差来源总结

本次排查最终确认了两个主要误差源。

9.1 Vision / VLM 被改成 FP32

表现:

past_key max_abs ≈ 0.304 ~ 0.375
past_value max_abs ≈ 0.165 ~ 0.188

原因:

官方:
Vision BF16
 ↓
VLM BF16

修改后:
Vision FP32
 ↓
VLM FP32 / BF16

视觉特征在 Prefix 入口已经发生变化,随后被 Transformer 多层放大。


9.2 Expert 被强制改成 FP32

在 Prefix 已经完全对齐的情况下:

Expert FP32

仍会产生:

velocity max_abs ≈ 0.055
action max_abs   ≈ 0.0078

当恢复:

Expert native
Action modules native

后:

velocity max_abs = 0
action max_abs   = 0

说明 Expert 侧同样对 precision policy 敏感。


10. 实验结果汇总

策略PassFailKV max_absVelocity max_absAction max_abs
Full FP321530.3039910.2079830.127735
初版 Mixed1530.3750000.2058990.126714
Prefix BF16 + Expert FP3242120.00.0550390.007834
Prefix BF16 + Expert native5400.00.00.0

从结果可以看到,误差是按模块逐步被消除的:

Full FP32
    ↓
恢复 Vision/VLM/KV BF16
    ↓
KV exact match
    ↓
恢复 Expert / Action native
    ↓
全部 exact match

11. 验证 Gate 建议

后续迁移建议将验证拆成三层。

Gate A:Official 语义一致性

Official reference
       vs
PyTorch official-like

要求:

54 / 54 PASS
max_abs = 0

当前已经通过。


Gate B:ONNX 迁移一致性

PyTorch official-like
       vs
ONNX mixed precision

要求:

  • Prefix / KV 不发生不可解释偏差;
  • Velocity 在部署容差内;
  • Final Action 在部署容差内。

不能再直接使用:

Official BF16
    vs
ONNX FP32

否则会把:

precision change error

和:

conversion error

混在一起。


Gate C:TensorRT 迁移一致性

ONNX
  vs
TensorRT

重点验证:

KV Cache
Velocity
Euler
Final Action

最终还需要增加:

Task-level success rate

验证机器人真实闭环行为。


12. 对 ONNX 迁移的影响

PyTorch official-like 已经完全复现 official reference,但 ONNX 仍有一个核心问题:

Vision BF16
    ↓
ONNX opset 18
    ↓
Conv(BF16) invalid

因此后续不能再采用:

model.float()

这种全模型 FP32 转换方式。

推荐方向:

方案 A:支持 BF16 的 ONNX / TensorRT 路径

如果目标 TensorRT / ONNX opset / Runtime 能完整支持:

Vision Conv BF16
VLM BF16
KV BF16

优先保持 official-like 原始精度。


方案 B:Vision fake-BF16

如果 ONNX Conv 必须为 FP32:

BF16 semantics
+
FP32 storage

即:

FP32 tensor
 ↓
BF16 rounding
 ↓
FP32 Conv
 ↓
BF16 rounding
 ↓
FP32 storage

从而保证:

ONNX schema 合法

同时尽量接近:

Official BF16 Vision

这比直接将 Vision Encoder 全部改成 FP32 更合理。


13. 工程建议

建议在部署配置中正式增加:

--precision-preset official-like

并记录:

{
  "requested_precision": "...",
  "resolved_precision": "..."
}

尤其对:

expert = native
action_in = native
action_time = native
action_out = native

必须同时保存实际解析后的 dtype,例如:

torch.float32
torch.bfloat16

避免后续 TensorRT 构建阶段对 native 含义产生歧义。


14. 最终结论

本次 SmolVLA PyTorch 精度排查最终确认:

  1. ONNX FP32 转换本身是正确的

    PyTorch FP32 -> ONNX FP32
    54 / 54 PASS
    max_abs ≈ 4.74e-05
    
  2. 全 FP32 会改变 SmolVLA 的模型行为

    past_key max_abs ≈ 0.304
    action max_abs   ≈ 0.128
    
  3. Vision / Connector / VLM / KV 必须保持 BF16
    恢复后:

    past_key/value max_abs = 0
    
  4. Expert / Action 子模块必须保持 native
    强制 FP32 时:

    velocity max_abs ≈ 0.055
    action max_abs   ≈ 0.0078
    
  5. 最终 official-like 策略可完全复现 official reference

    54 / 54 PASS
    max_abs = 0
    

因此推荐将下述配置作为 SmolVLA 后续迁移的 PyTorch 数值基准:

Vision Encoder      BF16
Vision Connector    BF16
VLM                 BF16
KV Cache            BF16
Expert              native
Action In           native
Action Time         native
Action Out          native
State Projection    FP32
Euler               FP32
Output              FP32

最终结论:

SmolVLA 的 PyTorch official-like 精度策略已经完整复现 official reference。后续 ONNX / TensorRT 迁移应以该策略作为数值 Golden,不再通过全局 FP32 转换换取 ONNX 兼容性,而应仅针对 BF16 Vision Conv 等不兼容算子进行局部精度适配。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐