微调

• 63 min read • 12564 words
Tags: Deep Learning NLP
Categories: NLP

微调

1. 指令微调

a.a. 基本概念

指令微调是收集大量覆盖不同任务的 (指令, 输出) 数据对,然后用这些数据去微调一个已经预训练好的语言模型(LM)。

一个重要的发现是,我们可以利用一个非常强大的模型(如GPT-4)来生成大量的指令和回答,然后用这些生成的数据去微调一个规模小一些的开源模型。

对齐,“少即是多” (Less Is More for Alignment):对于指令微调来说,数据的质量和多样性可能比单纯的数量更加重要。

b.b. 指令微调的局限性

指令微调具有如下的局限性:

  1. 数据成本高昂:这是最直接的问题。制作高质量的(指令, 输出)数据对需要大量的人工。每个任务都需要人来精心设计指令,并写出高质量的、作为“标准答案”的输出。
  2. 微妙的局限性:这是更深层次的问题。指令微调的目标和我们真正想要的目标(满足人类偏好)之间仍然存在偏差。
    • 开放式、创造性任务没有唯一的“正确答案”
      • 如让模型“写一个关于一只小狗和它的宠物蚱蜢的故事”。世界上可以有无数个同样精彩、但内容完全不同的故事。在指令微调的框架下,我们只能提供一个故事作为“标准答案”。如果模型生成了另一个同样优秀但措辞不同的故事,训练算法仍然会因为它与“标准答案”在字词上不匹配而“惩罚”模型。这显然是不合理的。
    • 模型对所有错误的惩罚是平等的,但对人类来说,不同的错误的严重性有很大差别
      • 语言模型的训练目标是最小化预测错误,它在词元层面(token-level)上计算损失。这意味着,模型犯的任何错误,无论大小,惩罚可能都是一样的

c.c. 为人类偏好优化

为解决指令微调的微妙局限性,我们引入一个新的、更强大的范式,它的核心思想是:不再告诉模型什么是“对”的,而是告诉它什么是“更好”的。这个过程被称为 RLHF (Reinforcement Learning from Human Feedback),即“从人类反馈中进行强化学习”。

i.i. 基本概念与流程

我们引入奖励(Reward)概念:

  • 我们不再需要一个唯一的标准答案,而是让模型针对一个指令(Prompt)生成多个不同的输出。
  • 然后,我们评估这些输出,并给出一个分数 (reward),分数越高代表越好

这样,我们的目标函数变成了最大化模型输出的期望奖励 (Maximize the expected reward):

maxEs^pθ(s)[R(s^)]\max \mathbb{E}_{\hat{s} \sim p_{\theta}(s)}[R(\hat{s})]

RLHF 并不是要取代指令微调,而是建立在它的基础之上。我们首先需要一个经过指令微调的、已经具备基本能力的模型作为起点。然后,我们需要训练一个奖励模型 (Reward Model)来模拟人类的偏好,然后使用强化学习算法(如PPO)来根据这个奖励模型的打分,进一步优化语言模型本身

ii.ii. 策略梯度

我们的目标是调整语言模型的参数 θ\theta,来最大化模型输出的期望奖励 Es^pθ(s)[R(s^)]\mathbb{E}_{\hat{s} \sim p_{\theta}(s)}[R(\hat{s})]。最自然的想法是使用梯度上升 (gradient ascent)。即,我们计算期望奖励关于模型参数的梯度 Es^pθ(s)[R(s^)]∇ \mathbb{E}_{\hat{s} \sim p_{\theta}(s)}[R(\hat{s})] ,然后让参数 θ\theta 沿着这个梯度的方向更新,这样就能让期望奖励变大:

θt+1:=θt+αθtEs^pθt(s)[R(s^)]\theta_{t+1} := \theta_t + \alpha \nabla_{\theta_t} \mathbb{E}_{\hat{s} \sim p_{\theta_t}(s)}[R(\hat{s})]

我们将这个梯度式子展开:

θEs^pθ(s)[R(s^)]=θsR(s)pθ(s)=sR(s)θpθ(s)\nabla_{\theta} \mathbb{E}_{\hat{s} \sim p_{\theta}(s)}[R(\hat{s})] = \nabla_{\theta} \sum_{s} R(s) p_{\theta}(s) = \sum_{s} R(s) \nabla_{\theta} p_{\theta}(s)

这个式子有一个严重的问题:奖励函数 R(s)R(s) 来自人类的打分(或者一个模仿人类打分的模型),它是一个“黑箱”,我们无法对它进行求导,因此梯度 无法通过 R(s)R(s) 进行反向传播。

于是我们使用一个“对数导数技巧” (log-derivative trick):这是一个非常巧妙的数学变换:

θlogpθ(s)=1pθ(s)θpθ(s)θpθ(s)=pθ(s)θlogpθ(s)\nabla_{\theta} \log p_{\theta}(s) = \frac{1}{p_{\theta}(s)} \nabla_{\theta} p_{\theta}(s) \Rightarrow \nabla_{\theta} p_{\theta}(s) = p_{\theta}(s) \nabla_{\theta} \log p_{\theta}(s)

于是原来的式子变为:

sR(s)θpθ(s)=spθ(s)R(s)θlogpθ(s)\sum_{s} R(s) \nabla_{\theta} p_{\theta}(s) = \sum_{s} p_{\theta}(s) R(s) \nabla_{\theta} \log p_{\theta}(s)

这个新的求和形式,正好是另一个期望的定义:

E[R(s)logpθ(s)]\mathbb{E}[R(s) \nabla \log p_{\theta}(s)]

这样,我们成功地将 E[R(S)]∇ \mathbb{E}[R(S)] 变换成了 E[R(s)log(pθ(s))]\mathbb{E}[ R(s) ∇ \log(p_{\theta}(s))]。。梯度 现在只作用于 log(pθ(s))\log(p_{\theta}(s)) 这个模型的对数概率。这在神经网络中是完全可求导的。 而不可求导的奖励函数 R(s)R(s) 被分离了出来,只是一个普通的权重,不再参与求导。

接下来我们需要解决如何估计这个期望的问题。我们采用蒙特卡洛近似的方法:我们让模型生成 NN 个输出 s1,s2,...,sns_1, s_2, ..., s_n,并得到它们各自的奖励 r(s1)...r(sn)r(s_1)...r(s_n)。然后,我们用这些样本的平均值来近似整个期望:

Es^pθ(s)[R(s^)θlogpθ(s^)]1mi=1mR(si)θlogpθ(si)\mathbb{E}_{\hat{s} \sim p_{\theta}(s)}[R(\hat{s}) \nabla_{\theta} \log p_{\theta}(\hat{s})] \approx \frac{1}{m} \sum_{i=1}^{m} R(s_i) \nabla_{\theta} \log p_{\theta}(s_i)

这样,我们沿梯度方向更新的式子就变成了:

θt+1:=θt+α1mi=1mR(si)θtlogpθt(si)\theta_{t+1} := \theta_t + \alpha \frac{1}{m} \sum_{i=1}^{m} R(s_i) \nabla_{\theta_t} \log p_{\theta_t}(s_i)

在这个式子中,如果奖励 R(s)R(s) 是一个很大的正数 (+++):意味着模型生成的输出非常好,更新的方向就是 + log(p(s))∇\log(p(s)),这会增大 p(s)p(s) 的概率。反之亦然。

iii.iii. 奖励模型的训练

单纯让模型输出一个具体的奖励值有如下的问题:人类的绝对判断是既主观又不稳定的。对此,我们引入成对比较法:与其问“这个东西值多少分?”,不如问一个更简单、更客观的问题:“A和B这两个,你觉得哪个更好?”

这样,奖励模型的完整训练流程可以整理如下:

  1. 数据收集:
    • 针对同一个指令(Prompt),让语言模型生成多个不同的回答(如y1,y2,y3y_1, y_2, y_3)。
    • 将这些回答两两组合,让标注员进行比较,选出较好的回答。例如,标注员认为 y1>y2y_1 \gt y_2 并且 y1>y3y_1 \gt y_3。这些成对的比较结果就构成了我们的训练数据集。
  2. 奖励模型的训练目标:
    • 奖励模型 rψr_ψ 的工作是给任何输入的文本打一个分。它并不需要精确地拟合某个分数,而是需要让结果能够正确地反映人类的比较偏好。也就是说,如果人类认为 y1>y2y_1 \gt y_2,那么我们的奖励模型经过训练后,也应该满足 rψ(y1)>rψ(y2)r_ψ(y_1) > r_ψ(y_2)
  3. 损失函数 (Loss Function):损失函数的计算方式如下:
L=E[logσ(rψ(yw)rψ(yl))]L = -\mathbb{E}[\log σ(r_ψ(y_w) - r_ψ(y_l))]

其中 ywy_w 是“获胜”的样本,yly_l 是“落败”的样本。rψ(yw)rψ(yl)r_ψ(y_w) - r_ψ(y_l) 是奖励模型给出的分差。我们的目标是让这个分差尽可能地大

d.d. 完整的 RLFH 流程

根据先前的讨论,我们已经拥有了下面的组件:

  • 预训练好的基础语言模型 pπref(y)p_{π_{ref}}(y)
  • 奖励模型 rψ(y)r_ψ(y)
  • 策略梯度优化算法

于是完整的 RLFH(Reinforcement learning from human preferences) 流程如下:

  1. 我们复制一份基础模型 pπref(y)p_{π_{ref}}(y),得到一个我们将要优化的策略模型pπθp_{\pi_{\theta}}。训练开始时,θ\theta 参数和 refref 的完全一样。
  2. 我们使用强化学习算法来优化这个策略模型,但我们优化的目标不仅仅是奖励模型的分数,而是一个复合的奖励函数:
R(y)=rψ(y)βlogpπθ(y)pπref(y)R(y) = r_{\psi}(y) - \beta \log \frac{p_{\pi_{\theta}}(y)}{p_{\pi_{\text{ref}}}(y)}
  • rψ(y)r_ψ(y) 是激励项,它鼓励我们的策略模型 pπθ(y)p_{π_θ}(y) 生成能够从奖励模型 rψ(y)r_ψ(y) 那里获得高分的文本。
  • β(...)-\beta(...) 是惩罚项,防止我们的策略模型 pπθ(y)p_{π_θ}(y) 与最初的参考模型 pπref(y)p_{π_{ref}}(y) 偏离得太远。

原始参考模型中包含了海量的世界知识、语法结构和常识。惩罚项就像一个锚,将优化过程中的模型牢牢地拴在这些知识基础上,确保它在学习“讨人喜欢”的同时,不会忘记如何保持事实的连贯性和逻辑性。

2. InstructGPT & ChatGPT

InstructGPT是OpenAI在2022年发布的一个里程碑式的项目。这个项目首次完整且成功地将在数万个任务上进行的大规模RLHF流程公之于众,深刻地影响了整个领域。整个流程如下:

  1. 监督微调 (Supervised Finetuning, SFT):收集示范数据,训练一个监督策略。这步就是前面所说的指令微调。
  2. 训练奖励模型 (Reward Model, RM):收集比较数据,训练一个奖励模型。这步的具体做法如下:
    1. 拿第一步训练好的SFT模型,让它针对一批新的指令,生成多个不同的回答。
    2. 让标注员对这几个回答进行排序,根据他们的偏好给出排名。这种排序的方式,本质上就是我们之前讨论的成对比较法。
    3. 收集大量这种包含了人类偏好排序的数据。
  3. 使用PPO算法进行强化学习 (Reinforcement Learning with PPO):我们将第一步得到的SFT模型作为初始策略,然后进入一个强化学习的循环:
    1. 从指令库中随机取一个指令,让当前策略模型生成一个回答。
    2. 将这个回答喂给第二步训练好的奖励模型,得到一个奖励分数 rr
    3. rr 被用来计算一个梯度,然后通过PPO算法来更新策略模型的参数。

InstructGPT项目证明了,通过 “SFT → RM → RL”这套三步走的RLHF流程,可以有效地将一个庞大的、只懂语言知识的预训练模型,“对齐” (Align)到复杂、细致、多样的人类价值观和偏好上。

为了训练InstructGPT,OpenAI使用了下面的指令:

  1. 普通指令 :直接让标注员自由发挥,构思出任意类型的任务指令。
    • 这是为了获取最广泛、最多样化的指令。标注员需要确保他们提出的任务五花八门,覆盖尽 可能多的领域和类型,避免数据单一。这就像一个“头脑风暴”式的收集过程。
  2. 少样本指令 (Few-shot):让标注员不仅提供一个指令,还要为这个指令提供多个“提问/回答”的范例。
    • 这种格式旨在教会模型如何进行“上下文学习”或“少样本学习”。当模型看到一个包含范例的指令时,它能更好地理解任务的要求和期望的输出格式。例如,指令可能是“把句子翻译成法语”,后面会跟着几个“英语句子 -> 法语句子”的例子。
  3. 基于用户的指令:OpenAI分析了申请使用其API的用户的真实应用场景(Use-case)。然后,他们让标注员根据这些真实世界的需求来创造相应的指令。
    • 这是最重要的一种数据来源。它确保了训练数据与用户的实际需求高度相关,从而让训练出的模型在解决真实问题时更有用。

和InstructGPT不同,ChatGPT 的训练数据从单轮的“指令-回答”全面转向了多轮的“对话”。ChatGPT训练的第二步和第三步,即RM的训练和最终的RL优化和InstructGPT也有一些区别:

  • RM阶段的优化:与InstructGPT的不同,ChatGPT的比较和排序是针对对话中的某一步进行的,而不是像InstructGPT那样对一个指令的初始回复进行排序。这使得奖励模型能够更精细地学习到在对话的上下文中,什么样的回复是更好的
  • RL阶段的优化:ChatGPT会使用多次迭代 (Several iterations)来进行学习。这说明ChatGPT的训练不是一个线性的“SFT→RM→RL”过程,而是一个循环迭代、不断精进的过程

3. PPO

PPO 由 OpenAI 在 2017 年提出,它的核心目标是解决传统策略梯度(Policy Gradient)算法中的一个关键痛点:更新步长难以确定。

PPO 的核心思想是:在“信赖域”内稳定更新。它设计了一个新颖的、更容易优化的目标函数(Objective Function),通过裁剪(Clipping)的方式来间接限制策略更新的幅度。

PPO 最核心、最常用的部分就是它的裁剪代理目标函数(Clipped Surrogate Objective Function)。它的详细工作流程如下:

首先,我们定义一个比率 rt(θ)r_t(\theta)

rt(θ)=πθ(atst)πθold(atst)r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}

这个公式衡量的是新策略 πθ\pi_\theta 在状态 sts_t 下选择动作 ata_t 的概率,与旧策略 πθold\pi_{\theta_{old}} 的概率之比

  • 如果 rt>1r_t > 1,说明这个动作在新策略下变得更可能被选中。
  • 如果 rt<1r_t < 1,说明这个动作在新策略下变得更不可能被选中。

我们还需要一个概念叫做优势函数 A^t\hat{A}_t。它衡量在状态 sts_t 下,执行动作 ata_t 到底有多好。

  • 如果 A^t>0\hat{A}_t > 0,说明 ata_t 是一个好于平均的动作,我们应该鼓励它。
  • 如果 A^t<0\hat{A}_t < 0,说明 ata_t 是一个差于平均的动作,我们应该抑制它。

PPO-Clip 的目标函数如下:

LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1ϵ,1+ϵ)A^t)]L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min \left( r_t(\theta) \hat{A}_t, \quad \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]

这里的关键是 minminclipclip 这两个操作:

  • ϵ\epsilon 是一个很小的超参数,如 0.2。
  • clip(rt(θ),1ε,1+ε)\text{clip}(r_t(\theta), 1-ε, 1+ε) 的意思是把比率 rtr_t 强制“裁剪”并限制在 [1ε,1+ε][1-ε, 1+ε] 这个区间内。

这个目标函数是如何实现稳定更新的呢? 我们分两种情况讨论:

  1. 优势 A^t\hat{A}_t 为正(这是一个好动作)
  • 我们希望增加这个动作的概率,也就是增大 rtr_t
  • 此时目标函数变为 min(rtA^t,(1+ϵ)A^t)\min(r_t \hat{A}_t, (1+\epsilon)\hat{A}_t)
  • 因为 A^t\hat{A}_t 是正数,所以 rtr_t 越大, rtA^tr_t \hat{A}_t 也越大。
  • 但是 min 函数的存在给它加了一个“天花板”:目标函数的增长不会超过 (1+ϵ)A^t(1+\epsilon)\hat{A}_t。这就防止了我们因为一个特别好的动作,而让策略更新得过于“激动”,从而保证了稳定性。
  1. 优势 A^t\hat{A}_t 为负(这是一个坏动作)
  • 我们希望减小这个动作的概率,也就是减小 rtr_t
  • 此时目标函数变为 min(rtA^t,(1ϵ)A^t)\min(r_t \hat{A}_t, (1-\epsilon)\hat{A}_t)
  • 因为 A^t\hat{A}_t 是负数,所以这是一个“比谁更小”的游戏。
  • clipclip 函数会把 rtr_t 限制在 1ϵ1-\epsilon 以上。minmin 函数会选择两者中更小(更负)的值作为最终的目标。这相当于对策略施加了一个更大的惩罚,阻止其变化过大。这同样也给策略的更新加了一个“地板”,防止因为一个坏动作而过度惩罚,导致策略剧烈变化。

通过这种巧妙的“软限制”,PPO既允许策略向好的方向更新,又防止了更新幅度过大,从而实现了稳定、高效的训练。


PPO 通常在 Actor-Critic (演员-评论家) 框架下实现:

  1. 数据收集 (Sampling):使用当前的策略(Actor)与环境交互,收集一批数据(状态、动作、奖励等)。
  2. 计算优势 (Advantage Estimation):对于收集到的每一步,用评论家(Critic)网络计算优势函数 A^t\hat{A}_t 的估计值。
  3. 多次优化 (Optimization):将收集到的一批数据重复使用,用梯度上升法对我们上面提到的 LCLIP(θ)L^{CLIP}(\theta) 目标函数进行多轮(epochs)优化,更新策略网络(Actor)的参数。
  4. 重复:回到第一步,使用更新后的策略继续收集数据,循环往复,直到策略收敛。

数据复用是 PPO 相比于传统策略梯度算法的一个巨大优势,它显著提高了样本的利用效率。

4. DPO

DPO(Direct Preference Optimization)的理论基础是移除RLHF中的“RL”部分。我们知道RLHF的目标是最大化一个复合函数:scoreβKLpunishmentscore - β KL_{punishment}。这需要通过迭代式的强化学习来求解。而研究者发现,上述这个优化问题有一个理论上的封闭解:可以直接写出最优策略 pp^{*} 的数学表达式,而无需一步步地去迭代求解。

既然有了最优策略和奖励模型之间的直接关系,我们可以反过来,用下面的公式来定义奖励模型:

RM(x,y)βlogp(yx)pPT(yx)RM(x, y) ≈ β \log \frac {p(y|x)} {p^{PT}(y|x)}

这意味着,奖励分数可以被隐式地定义为“最优策略”和“原始策略”在同一个回答上的概率比。我们不再需要一个独立训练出来的、显式的奖励模型了。

我们从训练奖励模型时使用的偏好损失函数出发:

L=E[logσ(rψ(yw)rψ(yl))]L = -\mathbb{E}[\log σ(r_ψ(y_w) - r_ψ(y_l))]

然后我们将前面推导出的隐式奖励模型代入:

DPO=E[logσ(βlogpθ(ywx)pPT(ywx)βlogpθ(ylx)pPT(ylx))]\text{DPO} = -\mathbb{E} \left[ \log \sigma \left( \beta \log \frac{p_{\theta}(y_w|x)}{p^{PT}(y_w|x)} - \beta \log \frac{p_{\theta}(y_l|x)}{p^{PT}(y_l|x)} \right) \right]

这样,我们不再需要 RM 或 RL。这个损失函数直接关联了我们正在优化的模型 pθp_θ 和原始的参考模型 pPTp^{PT}

  • 它的目标是:调整参数 θθ,使得对于人类偏好的“获胜”回答 ywy_wpθp_θ给出的概率相对于 pPTp^{PT} 的增长幅度,要大于它在“落败”回答 yly_l 上的增长幅度。

本质上,这个式子是在直接利用偏好数据,通过一个简单的分类损失,来优化语言模型本身。

Comments

Total words: 12564