Rust in Everything
基于 candle-core 0.11.0

常见坑

每一章对着一个报错或一个现象:先用最小的程序把它复现出来(输出是 M2 Max、macOS 27、rustc 1.99.0 上 candle 0.11.0 的原文,CPU 和 Metal 都试过),再到源码里找它从哪一行来,最后给出修法和检查办法。机制在前面几部分讲过的,这里只链过去。有八个坑是上游的问题,会悄悄算错或在不该 panic 的地方 panic,章里写明在 0.11.0 上复现。CUDA 上的情况只读了源码,章里另外标出。

  1. shape mismatch in add:+ 不自动广播,用 broadcast_*;matmul 只比批维的乘积。
  2. device mismatch in add:CPU 和 Metal 混用;两次 new_metal(0) 是两个设备,报错里却印得一模一样。
  3. unsupported dtype BF16 for op matmul:CPU 上 BF16 matmul、accelerate 下 F16 matmul、Metal 上的整数和 F64、整数 exp 的 todo!。
  4. Invalid matmul arguments:Metal 和 accelerate 对步长有要求,softmax_last_dim 和 KvCache 要连续输入。
  5. unexpected rank, expected: 0, got: 2:to_scalar / to_vec1 的秩不对;取样时 logits 多了一维。
  6. 错误里看不出是哪一行:默认没有 backtrace,打开它的代价和 matches! 失效。
  7. RmsNorm 之后梯度是 None:融合算子都不记反向,训练悄悄停在一半参数上。
  8. 设了种子结果还是不一样:CPU 的 set_seed 报错;Metal 小于 2^32 的种子被忽略。
  9. Metal 第一次运行要好几秒:运行期编译整个 .metal 文件,macOS 的库缓存不一定命中;预热把代价挪到启动时。
  10. Metal 上计时几乎为零:不 synchronize 量到的只是编码,GPU 时间落在下一次等待上。
  11. 改了 copy 出来的张量,原张量也变了:Metal 上 copy() 共享缓冲区,用 affine(1.0, 0.0)。
  12. Metal 上批量 matmul 结果不对:批维步长不能合并时静默算错,Linear 遇到非连续的 4 维输入也中招。
  13. from_vec 元素个数不对也不报错:CPU 之后 panic,Metal 读到缓冲区外。
  14. CPU 上 BF16 softmax 加起来不是 1:半精度累加,先转 F32 再归约。
  15. inplace_op2 卡死:第二个参数和第一个共享存储,读写锁自己等自己。
  16. cannot find tensor:pp 前缀不对、配置少了字段,Llama::load 还会直接 panic。
  17. cannot find tensor embeddings.word_embeddings.weight:bert-base-chinese 的 gamma / beta,报出来的是另一个名字。
  18. index-select invalid index 512 with dim size 512:BertModel 不截断,Metal 上干脆不报错;分词器不转小写。
  19. cannot broadcast [13, 45] to [1, 9, 13, 13]:清了缓存却沿用旧的 index_pos;计数器在对话中途走错时静默乱码。
  20. A weight is negative, too large or not a valid number:from_sampling 里温度 0,要自己换成 ArgMax。
  21. 读 GGUF 头部要几十毫秒:BufReader<File> 上近十万次 lseek,读进内存再解析。
  22. attempt to divide by zero:general.alignment = 0 让读头部 panic;截断的文件到 tensor() 才报错。
  23. 同一个 GGUF,CPU 和 Metal 生成的文字不一样:CPU 先把输入量化成 Q8_0,CANDLE_DEQUANTIZE_ALL=1 能对齐。
  24. 量化后 NaN 变成了 0:Q8_0 遇到 NaN、inf、极小和极大值时的行为。

按出在哪里分:

出在哪里坑
张量运算的检查1 形状、2 设备、3 dtype、4 连续性、5 秩、13 元素个数
错误与调试6 backtrace、15 死锁
自动微分与随机数7 融合算子、8 种子
Metal9 首次编译、10 计时、11 copy、12 批量 matmul
数值14 BF16 softmax、23 CPU 与 Metal 量化、24 Q8_0 边界值
加载权重16 VarBuilder、17 BERT 参数名、22 GGUF 对齐
推理18 BERT 长度、19 index_pos、20 温度 0、21 GGUF 读取
flowchart TB
  q{"碰到了什么"}
  q -->|返回 Err| er{"原文"}
  er -->|"shape 或 rank"| p15["1 或 5"]
  er -->|"device mismatch"| p2["2 设备"]
  er -->|"unsupported dtype"| p3["3 dtype"]
  er -->|"striding 或 contiguous"| p4["4 连续性"]
  er -->|"cannot find tensor"| p1617["16 或 17"]
  er -->|"cannot broadcast"| p19["19 index_pos"]
  q -->|panic 或卡住| pn{"哪种"}
  pn -->|"divide by zero"| p22["22 GGUF 对齐"]
  pn -->|"进程不动了"| p15b["15 死锁"]
  pn -->|"todo 或 swap_remove"| p32["3 或 2"]
  q -->|结果不对但没报错| odd{"哪种"}
  odd -->|"只在 Metal 上错"| pm["11 12 13 18"]
  odd -->|"梯度是 None"| p7["7 融合算子"]
  odd -->|"每次不一样"| p8["8 种子"]
  odd -->|"概率和不是 1"| p14["14 BF16"]
  odd -->|"CPU 和 Metal 不同"| p23["23 量化"]
  q -->|慢| slow{"哪里慢"}
  slow -->|"第一次几秒"| p9["9 首次编译"]
  slow -->|"计时不可信"| p10["10 计时"]
  slow -->|"读 GGUF"| p21["21 GGUF 读取"]

💬 关联讨论

发起讨论
加载中...