生成
logits 对上之后(搬一个 LLM),还差把它们变成一段文字。transformers 把这些都放进 model.generate 和 tokenizer 里;candle 只给 forward、LogitsProcessor 和 apply_repeat_penalty,循环、停止条件、流式输出、对话模板都要自己写。这一部分用 SmolLM2-135M-Instruct 把这几件事两边逐个对一遍:贪心生成逐 token 相同,取样比分布,分词比 id 和字节。
- 生成循环:手写循环对
model.generate,三段对话 19、71、80 个 token 在 CPU 和 Metal 上逐个相同;eos 从哪来、不停会怎样、默认长度;汉字被拆在几个 token 里时TextStreamer和TokenOutputStream各怎么等。 - 取样:同一组 logits 上温度、top-k、top-p、重复惩罚的分布对上;
do_sample=True默认的top_k=50、top-k 加 top-p 的归一化、并列、惩罚范围这几处不同;同一个种子两边抽到不同 token 的原因。 - 分词与对话模板:Python 和 Rust 的
tokenizers在 28 条字符串上 id 全同;文本里的特殊 token;apply_chat_template在 Rust 侧没有,手拼的字符串和它逐字节相同。
flowchart TB
msgs["messages"] --> tpl["对话模板"]
tpl --> tok["tokenizer 编码"]
tok --> fwd["forward"]
fwd --> pick["argmax 或取样"]
pick --> stop{"eos 或上限"}
stop -->|否| fwd
stop -->|是| done["结束"]
pick --> dec["流式解码"]
candle 一侧取样的内部实现(哪些在设备上算、每次多少毫秒、Metal 的种子问题)见 candle 卷的取样。
💬 关联讨论
发起讨论加载中...