Rust in Everything
基于 candle 0.11.0

模块

PyTorch 的模型是 nn.Module 的子类:__init__ 里把层赋给属性,__setattr__ 把它们登记下来,state_dict()、.to()、eval()、hook 都靠这份运行期登记表。candle 的模型只是一个持有张量的结构体,前向是 Module trait 的一个方法;权重在构造时由 VarBuilder 按名字从文件里取,名字是调用 pp 拼出来的字符串,和字段名无关。没有登记表,也就没有 named_parameters()、model.eval() 和 forward hook。

这一部分走推理主线的「建模块」一步,用 SmolLM2-135M 的真实权重:

  1. 定义一个模块:手搬 SmolLM2 一个解码层的后一半,nn.Module 对结构体 + Module + VarBuilder;pp 拼出的名字就是 state_dict 的键(Llama::load 要 272 个,比 state_dict 少一个绑定的 lm_head.weight);名字错了在构造时就报;seq() 的下标要跳过激活。
  2. 常用层:Linear、Embedding、LayerNorm、RmsNorm、Conv1d/2d 和 PyTorch 同参数顺序、同权重布局;x.gelu() 是 tanh 近似,F.gelu 要写 gelu_erf,用错了 BERT 的隐藏状态差 1.93e-2;hidden_act 字符串能解析哪些;Dropout 是 ModuleT,每次传 train。
  3. 逐层对数:forward hook 对「抄一份前向、每层存进 Vec」;30 层逐层比,绝对误差随残差流涨到 2.1e-2,相对误差不超过 4.91e-6;单层误差和累积误差;故意写错 eps、RoPE,第 0 层就超标。
flowchart TB
  cfg["config.json"] --> ld["load vb cfg"]
  st["safetensors"] --> vb["VarBuilder"]
  vb -->|"pp 和 get"| ld
  ld --> m["结构体"]
  m -->|"Module forward"| y["输出"]
  m -->|"自己存中间结果"| chk["逐层对数"]

VarBuilder 内部怎么拼名字、交给哪个后端,见 candle 卷的 VarBuilder;层的实现见 candle 卷的常用层。和 burn 的做法对照,见 burn 那一卷的模型。

💬 关联讨论

发起讨论
加载中...