SmolVLA PyTorch `official-like` 精度排查过程与结论
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 的实际推理数值路径。
本次排查目标是:
- 找出 SmolVLA 中对精度最敏感的模块;
- 区分 Prefix/VLM、KV Cache、Expert、Euler、Action 各部分的误差来源;
- 找到与 official reference 完全一致的 PyTorch mixed precision 策略;
- 为后续 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_key | 0.303991 | 0.375000 | 变差 |
| past_value | 0.165426 | 0.187500 | 变差 |
| velocity | 0.207983 | 0.205899 | 基本一致 |
| action | 0.127735 | 0.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 对比
| 模块 | 全 FP32 | Prefix BF16 | 改善 |
|---|---|---|---|
| past_key | 0.303991 | 0.0 | 完全消除 |
| past_value | 0.165426 | 0.0 | 完全消除 |
| velocity | 0.207983 | 0.055039 | 约下降 73.5% |
| action | 0.127735 | 0.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. 实验结果汇总
| 策略 | Pass | Fail | KV max_abs | Velocity max_abs | Action max_abs |
|---|---|---|---|---|---|
| Full FP32 | 1 | 53 | 0.303991 | 0.207983 | 0.127735 |
| 初版 Mixed | 1 | 53 | 0.375000 | 0.205899 | 0.126714 |
| Prefix BF16 + Expert FP32 | 42 | 12 | 0.0 | 0.055039 | 0.007834 |
| Prefix BF16 + Expert native | 54 | 0 | 0.0 | 0.0 | 0.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 精度排查最终确认:
-
ONNX FP32 转换本身是正确的
PyTorch FP32 -> ONNX FP32 54 / 54 PASS max_abs ≈ 4.74e-05 -
全 FP32 会改变 SmolVLA 的模型行为
past_key max_abs ≈ 0.304 action max_abs ≈ 0.128 -
Vision / Connector / VLM / KV 必须保持 BF16
恢复后:past_key/value max_abs = 0 -
Expert / Action 子模块必须保持 native
强制 FP32 时:velocity max_abs ≈ 0.055 action max_abs ≈ 0.0078 -
最终 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 等不兼容算子进行局部精度适配。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)