第 14 课把这个模型训到加减 99%,它认的格式是「三十四加十二等于?四十六」—— 一整句、没有角色、没有人在说话。 可你把这同一道题丢给任何一个真实的大模型,它多半会答得莫名其妙 —— 因为真实模型只认一种格式:对话。 这一课就把「一道题」改造成「一轮对话」,顺着把最关键的一件事补上:损失到底该盖在哪几个字上。
先说清楚「对话格式」到底是什么。它一点也不神秘:模型还是第 3 课那个「看前文、猜下一个字」的模型, 我们对它做的事情,只是把一次问答拼成一长串带路牌的文本 —— 路牌写着「现在是系统在说话」「现在是用户在说话」「现在轮到助手了」。 模型在这串文本上学,也在这串文本上生成。 下面这一站,我们把这道题逐字拆开给你看。
真实的对话模型只有三种角色:system(系统提示,相当于「人设说明书」)、user(用户的话)、assistant(模型自己的话)。 一轮一轮拼起来,每条消息都用 <|im_start|> 开头、<|im_end|> 收尾。 下面就是「三十四加十二」这道题被拼出来的样子 —— 精确到每一个 token。
<|im_start|> 和 <|im_end|> 各算 1 个 token。它们不是「一堆普通字符」—— 切词器把它们当整体认,这样模型学到的「轮次边界」才是干净的信号, 而不是被拆碎的一串标点。<|im_start|>…<|im_end|>、 Llama 用别的写法。喂错模板,模型会答得残缺甚至乱码 ——这是排查「模型怎么突然变笨了」时的第一嫌疑人。第 14 课定过一条规矩:题干屏蔽、答案算损失。 这一课它一个字都没改 —— 只是「题干」的范围变大了: 以前是「等于?」前面的题面,现在是 system 轮 + user 轮 + 助手的抬头。 下面三个开关默认都是开的(= 正确做法),试着关掉一个, 看模型会多学一些什么不该学的话。
看出来了吗:规矩是同一条,边界是一样的 —— 「递进去的不学,要说出来的才学」。 变的只是「递进去的」从一小段题面,变成了一整圈台子。 也正因为台子变大了,有效训练的比例从 25% 掉到 10.5% —— 这是对话格式的代价, 不是它的错误。
再提醒一个容易漏掉的细节:助手那一轮的结尾的 <|im_end|> 要算损失。 因为推理时是我们把「<|im_start|>assistant ⏎」递进去让它接着写, 抬起头我们替它说了;但什么时候停下得它自己学会 —— 不然它会一直往下编。这个「学会停」的能力, 就是靠让它在 <|im_end|> 上算损失训出来的。
格式讲完了,回到这一课标题的另一半 ——评测与调优。 第 9 课说过「评估必须诚实」,第 14 课说过「要分题型看」。 这里还有最后一把最容易被骗的尺子:逐字准确率。 它总是比「整题正确率」好看得多,而用户只认后者。
还有一层:「逐字准确率」也不能乱平均。 我们那个模型整体整题率 66.5%,可它是由加减的 99% 和乘除的 30% 混出来的 —— 一平均,两边的极端都被抹平了。这和第 9 课那句 「只看整体分数,你永远发现不了加减已经 99%、乘除还在 30%」是同一件事, 只是在「对话格式」这个大背景下又强调了一遍:报数之前,先问清楚这个数是在什么粒度上量的、在哪一批题上量的。
「对话格式」就是往一次问答里塞进一串角色路牌。一条 system / user / assistant 的对话,每条消息用 <|im_start|> 和 <|im_end|> 包住, 这两个标记各自只算 1 个 token;一轮固定 5 个模板 token。 模型要学的,从头到尾只有 assistant 那一轮的正文加结尾的 <|im_end|>—— 第 14 课那条「题干屏蔽、答案算损失」的规矩一个字没改, 只是「题干」从一段题面变成了一整圈台子,有效训练比例也从 25% 收到 10.5%。 而评测上,永远记住两把尺子:整题正确率 = 逐字正确率答案长度, 乘法那两把尺子差了 39.9 个百分点 ——报出去的、以及决定下一步改什么的,必须是整题那一把。
<|im_start|> / <|im_end|> 各算 1 个 —— 它们是切词器里的特殊格子,不按字符拆。