5.5 指令微调SFT与RLHF对齐
1.预训练出来的模型只会续写
说起来,我们在5.1 节聊过预训练是怎么回事。简单回顾一下,就是给模型喂海量的无标注文本,让它学着一个字一个字地往下猜。这么训练完,模型其实只学会了一件事,那就是续写。你给它一句(今天天气不错),它会顺着往下接(适合出去走走)或者(阳光洒在身上暖洋洋的)。这个本领其实挺了不起,毕竟语感和常识都装进去了,可问题也恰恰在这里,它压根不懂什么叫做回答问题。
举个具体的例子。你问它(中国的首都是哪里),它接着往下写的概率最高的,很可能是(美国的首都是华盛顿)或者(日本的首都是东京)。为什么会这样呢,因为在它的训练语料里,列举各国首都的句子实在太多,它学到的是这个列表本身的节奏,根本没意识到你是在问它。说穿了,预训练模型像一位博览群书却只会接话茬的老学究,你说上句他接下句,可你要是正经问他个事,他多半还沉浸在续自己的小文章里。这情形让我想起某部科幻片里的仿生人,台词模仿得惟妙惟肖,你正经问它个事,它却只会接着你的话往下编。
所以我们要做一件事,业内叫对齐(alignment),说人话就是把模型的行为调整到和人的期望对得上。预训练给了它一肚子的知识和语言能力,对齐则是教会它把这份能力用在人想要的地方,让它乖乖听指令、好好答问题、少胡说八道。这一章我们就讲讲对齐这条路上的三件大事,分别是SFT、RLHF,以及更晚近的DPO。
2.SFT:用指令和回答配对来教规矩
SFT的全文是Supervised Fine-Tuning,翻译过来就是监督微调。它的思路其实朴素得很。我们前面讲过监督学习,这里就是把同样的办法搬过来再用一遍。
具体怎么做呢,我们先要准备一批指令和回答配对的数据(instruct-response pairs)。比方说一条样本里,输入是(请把下面这句话翻译成英文:今天天气真好),期望的输出则是(The weather is nice today)。这样的数据集积少成多,能凑到几万条甚至几十万条。然后我们拿这批数据继续训练那个预训练好的模型,损失函数就是1.4 节概率信息论里详细讲过的交叉熵。设指令是 ,期望回答是 ,模型当前给出 的概率记作 , 表示模型的所有参数, 是输入指令, 是期望的回答。我们要做的就是让这个概率尽量大,对应到损失上就是:
这里 就是SFT阶段用的损失, 是自然对数,前面加负号是因为我们习惯把要最小化的目标写成损失的形式。最小化这个损失,就等价于让模型给出正确回答 的概率越来越大。
这么一调,模型就慢慢摸到了门道。它发现原来输入里有个(翻译成英文),自己就该往外蹦英文。输入里有个(总结一下),自己就该收敛成一段简短的摘要。SFT教的就是这种格式和规矩,相当于给一个博学但不善言辞的人上了一节客服培训课,告诉他接起电话该怎么应答,什么时候该说您好、什么时候该说再见。
我之前看过一本讲教学的书,里头说好老师最重要的本事之一,是知道学生已经懂了什么、还差什么。SFT在这点上做得挺到位,它保留了模型脑子里那份基础的语感和常识(毕竟那是预训练辛辛苦苦攒下的家底嘛),只是在这之上叠加一层薄薄的听话的本领。所以SFT用的算力比起预训练要小得多,参数量都不变,只是换了一批更精致的数据。
不过SFT也有它够不着的地方。同一个问题往往有好几种合理的回答,哪一种更好,光靠一份标准答案很难说清楚。这时候就需要更细粒度的偏好信号,这就引出了下一节的RLHF。
3.RLHF:让人类偏好来当老师
RLHF的全称是Reinforcement Learning from Human Feedback,意思是从人类反馈中学习。这套办法是OpenAI在ChatGPT上发扬光大的。整个流程说起来分两步走,先训一个奖励模型,再用强化学习去优化大模型本身。
第一步,训练奖励模型。
我们先准备一批偏好数据。给同一个指令 ,让模型生成好几个不同的回答,比如 、、,然后请真人来比较,把它们从好到坏排个序。这里 、、 都是模型针对同一个输入 给出的不同回答,下标 、、 只是用来区分它们。把这些排序收集起来,就构成了一份偏好数据集。
接着我们训练一个奖励模型(reward model)。它的输入是 这一对,也就是指令加回答,输出是一个标量分数,记作 , 是奖励模型自己的参数, 来自reward(奖励)这个词,下标 用来和主模型参数 区分开。这个分数越高,说明这个回答越合人意。
奖励模型学的是什么呢,其实就是相对偏好。如果人在某次比较里觉得 比 好,那我们希望:
具体训练的时候,常用Bradley-Terry模型把这种谁比谁好的偏好转成概率。记 比 好的概率为:
这里 读作优于, 就是前面章节反复出现的那个Sigmoid函数,它把任意实数压到 这个开区间里,正好可以当概率用。让上面这个概率尽量接近 ,奖励模型就慢慢学会了给人喜欢的回答打高分,给人不喜欢的打低分。
打个比方,这一步有点像培养一位品菜评委。你不用告诉他每道菜具体该是多少分,只要让他多尝几回、告诉他这盘比那盘好吃,日子久了他自己就能给每道菜打出一个相对靠谱的分数来。
第二步,用强化学习优化大模型。
奖励模型训好之后,它就能替真人给任意一个回答打分了。接下来我们让大模型去试着回答各种指令,把它的回答喂给奖励模型打分,得分高就鼓励,得分低就惩罚。这一步最常用的算法叫PPO(Proximal Policy Optimization)。
这里出现两个新名词,我说清楚。在强化学习的语境里,大模型被称为策略(policy),记作 , 是policy那个词的首字母, 还是主模型的参数。 表示在输入 的条件下,模型生成回答 的概率。强化学习要做的,就是调整 ,让模型多生成那些能拿高分的回答。
但光看奖励模型打分还不够,还得加一个约束。要是不管不顾地往高分冲,模型很容易学偏,它发现某种回答格式不管什么指令都能骗到高分,就一个劲儿地输出那种回答,把原本流畅的语言能力都给毁了。这种现象有个专门的名字叫奖励黑客(reward hacking)。这道理和训练运动员有点像,我之前看过一篇写短跑的报道,里头讲一个选手为追求某项数据把发力动作练到了极致,结果姿势变形反倒伤了膝盖。模型这边也是同一个道理,单盯一项指标练到底,动作就走样了。
为了防这一手,PPO在优化目标里加了一项KL散度(KL divergence)。KL散度是1.4 节概率信息论里讲过的工具,用来衡量两个概率分布差得多远。这里我们让被训练的策略 别离它的SFT版本太远,那个SFT版本记作 ,就是上一节SFT训完定格的那个模型。把这一项写成:
就是KL散度的记号,括号里两个分布用 隔开,表示前者相对后者。这一项越大,说明当前模型和SFT版本差得越离谱。把它作为惩罚加进目标里,模型就既想拿高分,又不敢走得太远。
于是PPO这一步要优化的目标,大致就是:
我们一项一项看。 表示要调整 让后面这个式子尽量大。 是期望符号,意思是在大量样本上取平均。下面那行 表示 和 都从当前策略 里采样得到, 这个符号读作服从分布于。 是一个正数,用来控制KL这一项的份量, 大就管得严, 小就放得松。整个式子的意思就是,我们要让奖励分数减去KL惩罚之后的平均值尽量大。
这么一套做下来,模型既学会了讨人喜欢,又没忘掉自己原本是个会说话的模型。这便是RLHF的精髓所在。
4.DPO:跳过奖励模型的简化办法
RLHF效果好是好,可它确实麻烦。训一个奖励模型是一摊子事,PPO跑起来又是一摊子事,超参数多得让人头疼,学习率、KL系数 、clip比率这些哪一项没调好都容易出岔子。于是有人就想,能不能直接拿偏好数据来训主模型,跳过显式的奖励模型和那一套复杂的强化学习。
这就是DPO(Direct Preference Optimization),直接偏好优化。
DPO的思路说起来颇有点巧。它从理论上证明了一件事,最优策略其实可以用一个闭式公式直接写出来,这个公式里就含着奖励函数。换句话说,奖励函数可以用策略本身来表达。这么一来,原本先学奖励再学策略的两步,被它一搅和合成了一步,直接拿偏好数据微调主模型就行。
DPO用的损失长成这样(细节我们略过,只看个形状):
这里头的符号我们都见过。 是偏好数据里较好的那个回答,下标 来自win(赢)这个词。 是较差的那个,下标 来自lose(输)。 和 还是上一节那两个策略。 是对数, 是Sigmoid, 还是大管小调的那个系数。两个分数 和 分别表示当前策略相对SFT版本,给好回答和坏回答多少倍的占比。DPO要做的,就是让好回答的占比提升得多一些,让坏回答的占比提升得少一些,甚至降下去。
DPO的好处是显见的。它不要奖励模型,也不要PPO那一套复杂的采样和价值网络,训练流程和SFT几乎一样稳,调起来舒服得多。记得小张前阵子拿DPO训了个小模型,超参数没怎么细调就跑通了,他直说比PPO省心太多。所以现在很多开源模型的对齐阶段,都从PPO换成了DPO或者它的变体(比如IPO、KTO这些)。
不过DPO也不是万能。它对数据质量更敏感,偏好数据要是标得不干净,效果会肉眼可见地差。还有研究指出,DPO在分布外的泛化上有时候不如PPO稳。所以工业界的顶级模型,往往是PPO和DPO混着用,取两者之长。
5.对齐的三件事:有用、诚实、安全
讲到最后,我们说说对齐到底要对齐到哪儿去。业内大致把它分成三个目标。
第一是有用性(helpfulness)。模型得真能帮人解决问题,回答得切题准确,还得够细致。你问它怎么写一段读取CSV的Python代码,它就该给出能跑的代码,再附上必要的说明。
第二是诚实性(honesty)。模型不知道的就说不知道,别一本正经地胡编。这种胡编有个专门的说法叫幻觉(hallucination),是当前大模型最让人头疼的毛病之一。小明之前拿某模型查过一道关于宋代官制的题,模型张口就来,几个官职的职责讲得头头是道,他查了半天才发觉大半是模型自己编的,可信的没几句。这种事我们现在都见怪不怪了,可每碰上一回心里总还是不踏实。
第三是安全性(safety)。模型不能教人做坏事,也不能在敏感话题上乱开口。这一条做起来最微妙,因为安全和有用之间常常有张力,管得太严模型就变得什么都不敢讲,管得太松又容易出事故,怎么把分寸拿捏好,至今还是个活跃的研究方向。
回过头来看,ChatGPT之所以用起来顺手,整套训练流程其实就是三步走。先靠预训练让它有本事,再靠SFT让它守规矩,最后靠RLHF或者DPO让它贴人心。这三步哪一步都省不得,少了预训练它没本事,少了SFT它没规矩,少了RLHF它不贴人心。
说起来,这一路从5.1 节的预训练走到这里,大模型这块的总览也算补全了。下一篇我们换个方向接着聊。今天就先到这儿吧。
练习
Q1. 预训练出来的语言模型为什么不会直接回答问题,而只会续写?SFT是怎么改变它的?
预训练目标是根据前文预测下一个词元,模型学到的是语言的接续规律,所以你说"中国的首都是哪里",它顺着往下接的可能是"美国的首都是华盛顿"——因为它学的是列举首都那种句子的节奏,没意识到你在问它。SFT(监督微调)用一批"指令-回答"配对数据继续训练,损失是交叉熵 ,让模型给出正确回答 的概率尽量大。这样它就学会了输入里有"翻译成英文"就蹦英文、有"总结一下"就收敛成摘要,等于给它上了节客服培训课,让它懂规矩。
Q2. RLHF的奖励模型训练里,偏好数据是"排序",可损失用的是 。这里的 (Sigmoid)起什么作用?
人给的是"谁比谁好"的排序,可优化需要的是一个概率。(Sigmoid函数)把奖励模型输出的任意实数分数差 压缩到 开区间,正好可以当" 比 好"的概率用。让这个概率尽量接近1(即 比 大得多),奖励模型就学会了给人喜欢的回答打高分、给不喜欢的打低分。这是把相对偏好转成可优化的对数似然的常用套路(Bradley-Terry模型)。
Q3. 易错点:PPO优化目标里为什么要加一项 (KL散度惩罚),不加会怎样?
不加会出"奖励黑客"(reward hacking)。模型只盯着奖励模型打分冲,很容易学偏,发现某种回答格式不管什么指令都能骗到高分,就一个劲儿输出那种回答,把原本流畅的语言能力都毁了,动作走样。KL散度这一项衡量当前策略 和SFT版本 差得多远,把它作为惩罚(系数 )加进目标 ,模型就既想拿高分、又不敢离SFT太远,保住了原本的说话能力。 大管得严、小放得松,是个要细调的参数。
Q4.(面试题) 请完整描述从预训练到RLHF的三步流程,并对比PPO和DPO两条对齐路线的优劣。
三步流程:第一步预训练,在海量无标注文本上压交叉熵损失,让模型学会语言能力和常识,但只会续写;第二步SFT(监督微调),用"指令-回答"配对数据继续训练,让模型懂规矩、会按指令答;第三步RLHF(或DPO),用人类偏好数据让模型贴人心、少胡说。对齐的目标是有用、诚实、安全三件事。PPO路线分两步:先训一个奖励模型 ,再用强化学习(PPO算法)优化主模型策略 ,目标里加KL散度惩罚防奖励黑客。优点是对齐质量高、泛化稳;缺点是要训奖励模型、跑PPO、超参多(学习率、、clip比率),调起来费劲。DPO(直接偏好优化)跳过奖励模型,从理论上证明最优策略可闭式写出、奖励可用策略本身表达,于是直接拿偏好数据微调主模型,损失 ,让好回答 占比提升多、坏回答 占比提升少。优点是流程和SFT一样稳、省心;缺点是对偏好数据质量更敏感、分布外泛化有时不如PPO。工业界顶级模型常两者混用取长补短。