Rust in Everything
基于 candle-core 0.11.0

模型

前几部分讲的是零件:张量和后端(candle-core)、层和缓存(candle-nn)、量化格式和 GGUF(candle-core/src/quantized)。这一部分讲 candle-transformers 怎么用这些零件搭出模型。candle-transformers 有 8.3 万行、124 个模型模块,本卷只读两个,正好对应推理主线的两条路:BERT 走 safetensors + VarBuilder,一次前向算完整个句子;量化 llama 走 GGUF + QMatMul,一个 token 一个 token 地生成,最后由取样决定下一个 token。

  1. BERT:bert.rs 一个结构体对一个 Python 类,没有 pooler;加载 bert-base-chinese 要把 LayerNorm 的 gamma / beta 改名,失败时报错里的名字是误导;mean pooling 给中文句子算向量;with_tracing 的 span 必须在建模型前装 subscriber,CPU 上量出 gelu_erf 占一半时间;CPU 与 Metal 的批处理时间。
  2. 量化 llama 的一次前向:from_gguf 只认 llama.* 元数据键,RoPE 约定按架构名选;forward 里 mask 何时建、按什么缓存;按 span 和逐个 op 拆开一个 decode 步;prefill 与 decode 的吞吐;index_pos 传错和不清缓存各会怎样。
  3. 取样:LogitsProcessor 的六种取法哪些在设备上、哪些读回主机;温度为 0 的报错;top-p 和 top-k-then-top-p 的真实语义;每次取样的耗时;种子复现什么、Gumbel 为什么不受种子控制、Metal 的 set_seed 为什么不起作用;重复惩罚。
flowchart TB
  st["safetensors"] --> vb["VarBuilder"]
  vb --> bert["BertModel"]
  bert --> pool["mean pooling 句向量"]
  gg["GGUF"] --> mw["ModelWeights from_gguf"]
  mw --> fwd["forward input pos"]
  fwd --> lp["LogitsProcessor"]
  lp -->|下一个 token| fwd

三页的实验都在 M2 Max、macOS 27、rustc 1.99.0 上跑,模型是本地的 bert-base-chinese 和 SmolLM2-135M-Instruct 的 GGUF。生成一个 token 的分段计时(读 GGUF、建模型、prefill、decode、冷热缓存)在生成一个 token,本部分不重复。

💬 关联讨论

发起讨论
加载中...