训练 · 预训练之后
- 之前
- Qwen3-1.7B-Base
- 之后
- Qwen3-1.7B Qwen 的后训练
- 样本
- 43 个词元 20 个参与训练
全部步骤
模型看到的是什么
模型从来看不到聊天气泡。聊天模板把对话变成一串词元,每个回合前后加上特殊词元:<|im_start|>、角色、正文、<|im_end|>。SFT 在大量这样的序列上训练;这一条共 43 个词元,其中 20 个属于回答。
43 个词元 · 回答占 20 个只给回答打分
训练时读入整条序列,在每个位置都让模型预测下一个词元,和预训练完全一样。只有回答部分的猜测被打分:它们的 −log p 加总进损失,提示词和模板被掩码。微调之前,Qwen3-1.7B-Base 每个回答词元要付出 2.47 nats。
loss = 20 个回答词元上 −log p 的平均它学会了做什么
在成千上万个这样的例子上训练之后会怎样:同一个提示词,训练前与训练后。基座模型把对话当成要续写的记录,自己写出说话人标签;微调后的模型直接回答。(Qwen 真实的后训练远不止 SFT。)
基座模型:续写文本 · 微调模型:回答它对自己的措辞很笃定
在回答上训练,也让模型对自己的措辞非常笃定。在为本页写的回答上,微调后的模型给大多数词元的概率接近 1,但本页的措辞一偏离它自己的说法,概率就跌到几乎为 0。每个词元,它在自己的回答上付出 0.12 nats,在这个回答上付出 4.12。
后训练让分布变尖锐它从没学过的词元
被掩码的词元得不到梯度,所以没有什么能让模型在那里的猜测保持稳定。后训练之后,这些词元大多变得更可能了,但有几个断崖式下跌:Qwen3 现在给 <|im_start|> 后面的 “assistant” 的概率低于 10⁻¹⁵。它永远不必预测它:模板由程序写出。
没有损失,就没有训练信号
代码
ids = tokenizer.apply_chat_template([{'role': 'user', 'content': q}, {'role': 'assistant', 'content': a}])
labels = ids.clone()
labels[:n_prompt] = -100 # prompt and template: masked
logits = model(ids[:-1]).logits
loss = F.cross_entropy(logits, labels[1:], ignore_index=-100) # the answer only
loss.backward(); opt.step()