TT Lab
开始
学习 学习路径 课程

MiniMind — 亲手从头到尾训练一个小型语言模型

SFT 只教回答 — 损失掩码划出这条界线

在 TT Lab 中继续学习

一句话总结

预训练模型只会续写语料,不会回答问题。SFT(监督微调)把“问题–回答”对话做成 聊天格式 的一行,用同样的下一个令牌预测来训练,但 只在回答(assistant)的令牌上计算损失。划出这条界线的是 MiniMind 的 generate_labels。这个模块会把加了掩码的模型和去掉掩码的模型用同样的步数训练,用数字看提问令牌的损失和回答令牌的损失是怎样分开的。

为什么需要它

要教模型“收到问题就回答”,需要两样东西。一样是让模型能认出 到哪里为止是问题、从哪里开始是回答 的标记,另一样是 学什么 的选择。如果用整段对话来训练,模型连怎么写问题也会学——要把训练信号的相当一部分花在模仿用户可能说的话上,回答结束后甚至会自己编出下一个问题。我们想要的,是把问题 当作条件 再给出回答的模型。

工作原理

聊天格式。MiniMind 使用与 Qwen 系列相同的 ChatML 形式。

<|im_start|>user
가람 마을의 특산물은 뭐야?<|im_end|>
<|im_start|>assistant
가람 마을의 특산물은 인삼입니다.<|im_end|>

MiniMind 的实际模板会在每个 assistant 回合里放入一个空的 <think>\n\n</think>\n\n,并在训练时以 80% 的概率把它删掉(为了与思考模式对应的装置)。它有 20% 的概率还会在前面加上 system 提示。这门课程不处理思考模式,所以两者都去掉,只用上面的形式(mmkit.chat_text)。

标签掩码。generate_labels 在令牌序列中找 <|im_start|>assistant\n 的令牌片段,只把从其后直到 <|im_end|>\n 片段结束为止的部分,在标签里填入原来的令牌。其余全部是 -100。模型的 cross_entropy(..., ignore_index=-100) 会跳过这些位置。

labels = [-100] * len(input_ids)
# <|im_start|>assistant\n 을 찾으면 그 뒤부터 <|im_end|>\n 까지 labels[j] = input_ids[j]

把结束标记 <|im_end|> 也放进答案里很重要。这样模型才能学会 回答什么时候停下。在多轮对话中,每个 assistant 回合都会有这样一段。在我们的 SFT 数据上数一下,实际令牌中进入损失的只有 22% 多一点——其余 78% 只是条件。

超参数。MiniMind 的 train_full_sft.py 从预训练权重出发,以 lr 1e-5(预训练 5e-4 的五十分之一)跑两个轮次。这是为了不大幅扰动已经学到的语言能力。这门课程的小模型把它取得大得多,为 1e-3,其代价是会相当程度地忘掉续写预训练语料的能力——最后一个模块里会用困惑度来测量。

在现场相遇的样子

用公司内部的咨询记录做了 SFT,结果模型在回答之后接着写出以“客户:……”开头的假问题,那就是漏掉了掩码,或者把结束标记从答案里去掉了。反过来,如果没有放入结束标记,模型就停不下来,一直写到最大长度。这两种症状,只要打印出一行标签,马上就能看到——MiniMind 的 SFTDataset.__getitem__ 里以注释形式保留的调试输出(把输入令牌、下一个令牌和标签并排打印出来),用途正是这个。

还有一种常见的误解,是“用 SFT 灌入知识”的期待。SFT 教的主要是 格式和态度——收到问题后以什么样子回答、在哪里停下——而事实大多来自预训练。想用几千条 SFT 数据把数据里没有的事实灌进去,模型只会背下那些句子,换一种提问的形式去问,它就取不出来了。这门课程的最后一个模块里,会用数字来看这种现象。

本课程与 MiniMind 原版的不同之处

MiniMind 的 SFT 数据里混有工具调用对话(system 中放工具列表,assistant 用 <tool_call> 调用,tool 角色返回结果的形式),而 pre_processing_chat 对带工具的对话不做处理。模板把工具结果包在 user 回合里,所以那个位置也从标签中排除——模型不会模仿工具结果,只学会看到结果后接着回答。这门课程不处理工具调用,只使用一两轮的问答和问候语。最大长度也不是 MiniMind 推荐的 768,而是 64——我们的对话最长的也只有 40 多个令牌,所以 64 不会截掉任何东西,填充也少。反过来,用真实数据做的时候,必须先测对话长度的分布,再定最大长度。太短的话,回答的结尾和结束标记会被截掉,学不会怎么停下来。

下一项实验要做什么

把 SFT 数据的第一段对话转换成聊天格式,亲手做出只保留回答的标签。数出进入损失的令牌比例,并以基准预训练权重为起点,在相同条件下训练加了掩码的模型和去掉掩码的模型。在留出的对话上比较两个模型的回答损失和提问损失,再生成留出问题的回答看看。