【求助】K3 Pico-ITX 跑 EmbeddingGemma-2 本地语义检索:CPU EP 逐位一致,SpaceMIT EP 在注意力 QKᵀ 上算错,有详细实验测试

概括

在 K3 Pico-ITX 上用 ONNX Runtime 跑 EmbeddingGemma-2 文本嵌入,CPUExecutionProvider 可用(单条 182 ms,与本机 fp32 结果逐位一致);SpaceMITExecutionProvider 跑得动但结果错(cosine 0.6956),检索不可用。我用 EP 自带的张量导出功能把误差定位到了具体的算子:/model/layers.0/attn/MatMul_qk(注意力 Q·Kᵀ),而紧邻的 /model/layers.0/attn/qkv_proj/MatMul 是正常的。

社区的其他类似问题贴

一、环境

项 值
硬件 K3 Pico-ITX,16 GB LPDDR5;8 核 X100(cpu0-7 @2.2GHz)+ 8 个 AI 核(cpu8-15 @1.8GHz)
系统 Bianbu 4.0.1(codename resolute),kernel 6.18.3,riscv64 RVA23
ONNX Runtime onnxruntime 1.24.0+spacemit.a3(apt python3-spacemit-ort)
EP 库 apt spacemit-ort 2.0.7;另试官方 release spacemit-ort.riscv64.2.0.7
模型 EmbeddingGemma-2 文本骨干 ONNX fp32(1.08 GB,ai.onnx opset 21 + com.microsoft 1)
量化版 社区转换 int8(314 MB,onnx-community/embeddinggemma-2-ONNX)
Python 依赖 tokenizers(PyPI 的 riscv64 wheel,用 pip install --target 装入,未动系统 Python)

术语说明:K3 上没有 GPU,加速单元是 8 个 AI 核,EP 指的是 SpaceMITExecutionProvider。

二、复现步骤

  1. 把模型与 tokenizer 放到板子的 /root/models/eg2/。
  2. 按 ort.InferenceSession(path, providers=["CPUExecutionProvider"]) 建会话并跑,得到参考向量。
  3. 把 providers 换成 ["SpaceMITExecutionProvider"],其余不动。
  4. 用同一个句子的输入,比较两次输出的 cosine 相似度。

三、实测数据

配置 单条耗时 cosine vs fp32 参考 top-3 检索结果
fp32 + CPU EP 182 ms 1.00000 正确
int8 + CPU EP 298 ms 0.99995 与 fp32 完全一致
fp32 + SpaceMIT EP 132 ms 0.69561 全错
int8 + SpaceMIT EP 编不过 — —

板子上的其它基准:模型装载 1.4 s;64 个文本块按 4 批建索引 119.36 s(1865 ms/块);峰值内存 1505 MB。

四、定位过程

EP 提供了两个导出开关,我把中间结果全部导出后与本机 CPU EP 逐张量对拍。

  1. SPACEMIT_EP_DUMP_SUBGRAPHS=1 导出 EP 编译出的子图:145 个。
  2. SPACEMIT_EP_DUMP_TENSORS=<dir> 导出每层张量:533 个。
  3. 用一个只对齐输出名的探针模型,把两边同名的张量逐个比较。
  4. 每个张量同时算两个指标:顺序敏感误差与顺序无关误差(后者用于排除转置造成的布局假象)。
  5. 按图序找第一处真分歧。

结果:

图序 张量 顺序敏感误差 顺序无关误差 判定
#57 /model/embedding_projection/MatMul 5.6e-7 5.6e-7 正常
#67 /model/layers.0/attn/qkv_proj/MatMul 6.7e-7 6.7e-7 正常
#112 /model/layers.0/attn/MatMul_qk 1.00 0.33 算错

误差再逐层放大:layer 0 最大 1.5,layer 8 达 3.1,layer 16–20 的 MLP 到 5–17 倍。

为了排除 com.microsoft::RotaryEmbedding 的干扰,我还把它拆成 Slice/Mul/Add/Concat 等基础算子(拆完在 CPU EP 上与原图 cosine = 1.00000,即逐位一致)。拆完后 EP 不再报编译错、能跑整图,但 cosine 仍是 0.69561,与本机逐位相同 —— 说明误差不来自 RotaryEmbedding。

五、已排除的做法

做法 结果
SPACEMIT_EP_DENSE_ACCURACY_LEVEL = 0/1/2/3(int8/FP16/FP32 档) 输出逐位相同
SPACEMIT_EP_DISABLE_FLOAT16_EPILOGUE 开/关 逐位相同
换 int8 量化模型 EP 报同一个 RotaryEmbedding 错;CPU 上反而更慢(298 ms)
拆 RotaryEmbedding 图手术 见上,cos 不变
SPACEMIT_EP_DISABLE_PASSES_FILTER 关掉 MatMulTransposeFusion、PackedAttentionFusion、_FusePackedAttention 毫无效果,耗时与结果逐位相同(该旋钮看来未实现,也不在文档的 ProviderOption 列表里)
SPACEMIT_EP_DISABLE_OP_TYPE_FILTER="Attention" 无效果
SPACEMIT_EP_DISABLE_OP_NAME_FILTER 把 24 个 QKᵀ 踢回 CPU 生效(142→136 ms),但 cosine 仍 0.6815,精度不恢复
xslim fp32 精简后再跑 EP 仍报同一个 RotaryEmbedding 错;关掉 rotary 后 cosine 仍 0.69561
xslim --fp16 后再跑 xslim 产出的模型装不进 ORT(见下面第六节)

六、附带发现的三个小问题

  1. apt 的 EP 库与官方 release 不是同一份:apt 版 libspacemit_ep.so.2.0.7 的 md5 是 ac949dde…,官方 release 是 a16afbce…。apt 版加载即报 Attempt to use DefaultLogger but none has been registered.,反过来建 session 又直接段错误(官方自己的 resnet18.q.onnx 也是这样)。换成 release 版后 resnet18 int8 单条 2.95 ms,正好对上 modelzoo 标称的 2.90 ms。
  2. xslim 2.1.1 的两个问题:(a) 对 ai.onnx opset 21 的模型,默认的版本转换会崩(IndexError: invalid unordered_map key),要显式加 --opset 21 才能跑;(b) --fp16 产出的模型 ORT 装不进,报 /model/attention_bias/sliding/Where 与 /model/pooling/count/Max 的类型不匹配(fp32 与 fp16 混用),清掉 value_info 后还会在下一个节点复发。
  3. TCM 节点缺失:板子已经装了 spacemit-tcm 3.0.1,/dev/tcm 存在,但没有 /dev/tcm_sync_mem。llama.cpp 侧会报 open(/dev/tcm_sync_mem) failed 并退化到堆内存。

七、希望得到的答复

  1. SpaceMIT EP 在这类结构(24 层、rank-5 注意力、Q/K/V 由 Transpose + MatMul 组成)上是否已知有精度问题?有没有已修复的版本?
  2. 官方验证流程一般是先过 xslim 再上板。我已按这个流程做过,问题依旧 —— 还有没有其它必须的前置步骤?
  3. 是否考虑开源部分算子接口来提升社区解决方案的操作空间?