目录

强化学习基础

强化学习是机器通过与环境交互来实现目标的一种计算方法。机器和环境的一轮交互是指,机器在环境的一个状态下做一个动作决策,把这个动作作用到环境当中,这个环境发生相应的改变并且将相应的奖励反馈和下一轮状态传回机器。这种交互是迭代进行的,机器的目标是最大化在多轮交互过程中获得的累积奖励的期望

image.png

如图,在每一轮交互中,智能体感知到环境目前所处的状态,经过自身的计算给出本轮的动作,将其作用到环境中;环境得到智能体的动作后,产生相应的即时奖励信号并发生相应的状态转移。智能体则在下一轮交互中感知到新的环境状态,依次类推。

需要注意的是,我们所处的环境是动态变化的,意思就是它会随着某些因素的变化而不断演变,例如一个微粒在水中的布朗运动可以由它的起始位置以及下一刻的位置相对当前位置的条件概率分布来刻画。对于一个随机过程,其最关键的要素就是状态以及状态转移的条件概率分布。如果在环境这样一个自身演变的随机过程中加入一个外来的干扰因素,即智能体的动作,那么环境的下一刻状态的概率分布将由当前状态和智能体的动作来共同决定,用最简单的数学公式表示则是:

下一状态P(当前状态, 智能体动作) \text{下一状态} \sim P(\cdot | \text{当前状态, 智能体动作})

下一状态是由当前环境和动作决定的一个概率分布。与面向决策任务的智能体进行交互的环境是一个动态的随机过程,其未来状态的分布由当前状态和智能体决策的动作来共同决定,并且每一轮状态转移都伴随着两方面的随机性:一是智能体决策的动作的随机性,二是环境基于当前状态和智能体动作来采样下一刻状态的随机性。

在讲强化学习目标之前,我们需要先明确三个概念:奖励回报以及价值

在上述动态环境下,智能体和环境每次进行交互时,环境会产生相应的奖励信号,其往往由实数标量来表示。这个奖励信号一般是诠释当前状态或动作的好坏的及时反馈信号,我们把每一次交互得到的信号 rtr_t 称为即时奖励 reward。整个交互过程的每一轮获得的奖励信号可以进行累加,形成智能体的整体回报 return,所以说回报是未来奖励的总和

Gt=Rt+1+γRt+2+γ2Rt+3+G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + \cdots

根据环境的动态性我们可以知道,即使环境和智能体策略不变,智能体的初始状态也不变,智能体和环境交互产生的结果也很可能是不同的,对应获得的回报也会不同,所以 GtG_t 是随机变量。因此,在强化学习中我们关注回报的期望,并将其定义为价值,这就是强化学习中智能体学习的优化目标。

在之前监督学习 sft 的过程中,我们的数据集是固定的。例如我们准备好 20k 的对话数据进行 sft,这过程中数据分布是不会变的。但是强化学习没有现成的数据集,只有在做出一个 action 的时候我们得到反馈,这时候才会有数据。比如训练一个玩游戏的AI:

  • AI 尝试向左走 → 撞墙了,得到数据"向左+撞墙"
  • AI 尝试向右走 → 吃到金币,得到数据"向右+金币"

然后就要说一个占用度量的概念,它指的是:在当前策略下,AI在整个游戏过程中,遇到某个"状态+动作"组合的概率是多少?比如策略 A,在遇到十字路口时向左转的概率为 20%,向右转的概率为 60%,回头的概率为20%,这就是策略 A 的占用度量。从这个例子我们也能看出,策略和占用度量为一一对应的关系,策略改变了占用度量也相应改变。

这里还有一个非常形象的比喻:

  1. sft 就像你把自行车固定在地面上骑车,你只要学会踩脚踏板就好了。
  2. rl 就像你在马路上骑自行车,每次你的骑法稍微变一下,地面的坡度和风向就跟着变。

前面我们提到,奖励信号是在特定环境下做出动作得到的反馈,所以它也是基于"状态+动作"给出的。因此一个策略能拿到的总奖励期望 = 在该策略的占用度量下,对所有"状态+动作"的奖励求加权平均。那么现在可以得到最终的结论了,强化学习的目的就是:在所有可能得数据分布(占用度量)中,找到价值最高的那个。比如当前在进行直走任务,直走能得到奖励,那就让模型学会一直直走,这个策略对应的占用度量就是直走概率为 100%,这样价值就最高。

概率论研究静态的随机:比如"抛一次硬币正面朝上的概率"。 而随机过程研究动态的随机:比如"天气每天怎么变化"。在随机过程中,某一时刻 tt 的随机状态是一个随机变量,它服从某个概率分布,而这个概率分布由过去的历史状态决:

P(St+1S1,S2,S3,,St) P(S_{t+1}\mid S_1,S_2,S_3,\dots,S_t)

我们可以这么理解:

历史状态:
  昨天: 晴
  前天: 晴
  大前天: 下雨
当前状态 X(今天) 的概率分布:
  P(今天=晴)  = 0.7
  P(今天=阴)  = 0.2
  P(今天=雨)  = 0.1

马尔可夫性质指的是某时刻的状态只取决于上一时刻的状态。如果一个随机过程被称为具有马尔可夫性质,那么可以知道:

P(St+1St)=P(St+1S1,S2,S3,,St) P(S_{t+1}\mid S_t) = P(S_{t+1}\mid S_1,S_2,S_3,\dots,S_t)

需要明确的是,具有马尔可夫性并不代表这个随机过程就和历史完全没有关系。因为上一时刻的状态还是由上上一次的状态决定,通过这种链式的关系,历史的信息被传递到了现在。但是马尔可夫性质能够帮助大大简化运算,因为只要当前状态可知,所有的历史信息都不再需要了,利用当前状态信息就可以决定未来。

马尔可夫过程就是满足马尔可夫性质的随机过程,也被称为马尔可夫链。我们通常用元组 (S,P)(S,P) 描述一个马尔可夫过程,其中 SS 是有限数量的状态集合,PP 是状态转移矩阵。例如在天气系统中,我们假设只有晴天和雨天,那么 S={晴天,雨天}S=\{\text{晴天},\text{雨天}\},状态转移矩阵类似:

当前状态 → 晴(概率) → 雨(概率)
0.7 0.3
0.4 0.6

这就是一个马尔可夫过程:今天的天气状况只取决于前一天的天气,不需要知道之前的天气状况。

在马尔可夫过程的基础上加入奖励函数 rr 和折扣因子 γ\gamma,就可以得到马尔可夫奖励过程 MRP。一个马尔可夫奖励过程由构成,各个组成元素如下:

  • SS 是有限状态的集合。
  • PP 是状态转移矩阵。
  • rr 是奖励函数,某个状态 ss 的奖励 r(s)r(s) 指转移到该状态时可以获得奖励的期望。
  • γ\gamma 是折扣因子,rr 的取值范围为 (0,1](0,1]

折扣因子的作用是:越远的未来,权重越小。今天给你100元和一年后给你100元,哪个更值?当然是今天的更值。γγ 就是表达这种"时间偏好"。

在一个马尔可夫奖励过程中,从第 tt 时刻状态开 SS 始,直到终止状态时,所有奖励的衰减之和称为回报 Return GtG_t,公式如下:

Gt=Rt+1+γRt+2+γ2Rt+3+=k=0γkRt+k+1 G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + \cdots = \sum_{k=0}^{\infty} \gamma^k R_{t+k+1}

回报 return 是未来奖励 reward 的折扣总和,但由于回报具有随机性,不同环境不同策略不同动作对应不同的奖励,所以我们关注回报的期望,也就是价值。在马尔可夫奖励过程中,一个状态的期望回报被称为这个状态的价值。所有状态的价值就组成了价值函数

V(s)=E[GtSt=s]=E[Rt+γRt+1+γ2Rt+2+St=s]=E[Rt+γ(Rt+1+γRt+2+)St=s]=E[Rt+γ(Gt+1)St=s]=E[Rt+γV(St+1)St=s] \begin{align} V(s) &= \mathbb{E}[G_t\mid S_t=s] \\ &= \mathbb{E}[R_{t} + \gamma R_{t+1} + \gamma^2 R_{t+2} + \cdots \mid S_t=s] \\ &= \mathbb{E}[R_{t} + \gamma (R_{t+1} + \gamma R_{t+2} + \cdots) \mid S_t=s] \\ &= \mathbb{E}[R_{t} + \gamma (G_{t+1}) \mid S_t=s] \\ &= \mathbb{E}[R_{t} + \gamma V(S_{t+1}) \mid S_t=s] \end{align}

把上式拆为两个部分,左侧即时奖励的期望就是奖励函数的输出。右侧部分 E[γV(St+1)St=s]\mathbb{E}[\gamma V(S_{t+1}) \mid S_t=s] 可以根据状态转移得到:

V(s)=r(s)+γsSp(ss)V(s) V(s)=r(s)+\gamma\sum_{s'\in S}p(s'\mid s)V(s')

上式就是马尔可夫奖励过程中非常有名的贝尔曼方程

之前提到的天气的变化是自然发生的,没有任何人能干预。但现实中的强化学习,智能体需要主动做决策。例如 AI 走迷宫,AI 会自己决定下一步往左还是往右走。在马尔可夫奖励过程(MRP)的基础上加入动作,就得到了马尔可夫决策过程(MDP)。马尔可夫决策过程由元组构成,其中:

  • SS 是状态的集合;
  • AA 是动作的集合;
  • γ\gamma 是折扣因子;
  • r(s,a)r(s,a) 是奖励函数,此时奖励可以同时取决于状态 ss 和动作 aa,在奖励函数只取决于状态 ss 时,则退化为 r(s)r(s)
  • P(ss,a)P(s'\mid s,a) 是状态转移函数,表示在状态 ss 执行动作 aa 之后到达状态 ss' 的概率。

这里明确一下,用大写的 SS 代表随机状态,用小写的 ss 代表一个特定的状态。假设当前状态为 ss,这是一个确定的状态,执行一个动作之后,下一个状态 SS 是随机的,P(ss,a)P(s' \mid s,a) 代表执行动作 aa 后状态变成 ss' 的概率,并且满足 sSs' \in S 。这也解释了为什么 E[GtSt=s]=E[V(St)St=s]\mathbb{E}[G_t\mid S_t=s]=\mathbb{E}[V(S_t)\mid S_t=s]

能体的策略(Policy)通常用字母表示。策略 π(as)=P(At=aSt=s)\pi(a\mid s)=P(A_t=a\mid S_t=s) 是一个函数,表示在输入状态 ss 情况下采取动作 aa 的概率。当一个策略是确定性策略时,它在每个状态时只输出一个确定性的动作,即只有该动作的概率为 1,其他动作的概率为 0;当一个策略是随机性策略时,它在每个状态时输出的是关于动作的概率分布,然后根据该分布进行采样就可以得到一个动作。

Vπ(s)V^{\pi}(s) 表示在 MDP 中基于策略的状态价值函数,定义为从状态 ss 出发遵循策略 π\pi 能获得的期望回报,数学表达为:

Vπ(s)=E[GtSt=s] V^{\pi}(s)=\mathbb{E}[G_t\mid S_t=s]

Qπ(s,a)Q^{\pi}(s,a) 表示:策略 π\pi 在状态 ss 时执行动作 aa 能获得的期望:

Qπ(s,a)=E[GtSt=s,At=a] Q^{\pi}(s,a)=\mathbb{E}[G_t\mid S_t=s,A_t=a]

这里我们需要和上面的状态价值函数区分一下,举个例子:假设我们是打野,目前局势很焦灼,暂且把当前的局面设为 ss。状态价值函数 V(s)V(s) 代表的是这个局面我们的平均胜率;假设接下来我们有两种选择,开大龙 a1a_1 和 开小龙 a2a_2(没有其他选择),那么 Q(s,a1)Q(s,a_1) 指的是当前这种局面下打大龙的平均胜率,Q(s,a2)Q(s,a_2) 指开小龙的平均胜率。通过这个例子也能很明显的看出,当前局面的平均胜率为开大龙的概率*开大龙的胜率+开小龙的概率*开小龙的胜率,公式为:

Vπ(s)=aAπ(as)Qπ(s,a) V^{\pi}(s) = \sum_{a\in A}\pi(a\mid s)Q^{\pi}(s,a)

我们把动作价值 QQ 和状态价值 VV 作差,就能得到这个动作是相对好还是相对较差。初次之外,每次执行动作都会导致当前状态 StS_t 以不同概率转换为下一个随机状态 St+1S_{t+1},所以能够得出动作价值 Q(s,a)Q(s,a) 等于当前状态 ss 下做动作 aa 的即时奖励 r(s,a)r(s,a) 加上下一个随机状态 St+1S_{t+1} 的状态价值期望:

Qπ(s,a)=r(s,a)+γE[Vπ(St+1)St=s,At=a] Q^{\pi}(s, a) = r(s, a) + \gamma \mathbb{E}[V^{\pi}(S_{t+1})\mid S_t=s,A_t=a]

以上就是 QQVV 的相互表达。

  • 状态价值函数:
Vπ(s)=Eπ[GtSt=s]=Eπ[Rt+γGt+1St=s]=Eπ[RtSt=s]+Eπ[γGt+1St=s]=aπ(as)r(s,a)+γaπ(as)sSp(ss,a)Vπ(s) \begin{aligned} V^{\pi}(s) &= \mathbb{E}_{\pi}\left[G_t \mid S_t = s\right] \\[6pt] &= \mathbb{E}_{\pi}\left[R_t + \gamma G_{t+1} \mid S_t = s\right] \\[6pt] &= \mathbb{E}_{\pi}\left[R_t \mid S_t = s\right] + \mathbb{E}_{\pi}\left[\gamma G_{t+1} \mid S_t = s\right] \\[6pt] &= \sum_{a} \pi(a \mid s)\cdot r(s,a) + \gamma \sum_{a} \pi(a\mid s)\sum_{s' \in S} p(s'\mid s, a)V^{\pi}(s') \end{aligned}
  • 动作价值函数:
Qπ(s,a)=Eπ[GtSt=s,A=a]=Eπ[Rt+γGt+1St=s,A=a]=Eπ[RtSt=s,A=a]+Eπ[γGt+1St=s,A=a]=r(s,a)+γsSp(ss,a)aAπ(as)Qπ(s,a) \begin{aligned} Q^{\pi}(s,a) &= \mathbb{E}_{\pi}\left[G_t \mid S_t = s, A=a\right] \\[6pt] &= \mathbb{E}_{\pi}\left[R_t + \gamma G_{t+1} \mid S_t = s,A=a\right] \\[6pt] &= \mathbb{E}_{\pi}\left[R_t \mid S_t = s, A=a\right] + \mathbb{E}_{\pi}\left[\gamma G_{t+1} \mid S_t = s, A=a\right] \\[6pt] &= r(s,a) + \gamma \sum_{s' \in S} p(s'\mid s, a) \sum_{a' \in A} \pi(a'\mid s') Q^{\pi}(s',a') \\[6pt] \end{aligned}

前面我们计算动作价值 QQ 和状态价值 VV,采用的都是期望的方式,因为是过程和动作都是随机的。强化学习中求期望就遗忘着我们需要遍历每一种可能性,例如大模型的 next token prediction 中我们需要遍历整个 vocab,计算每一个 token 下的情况,这是几乎不可能的。蒙特卡洛方法的思想就是 用"采样平均"代替"求期望"

假设我们想要求 π\pi 的具体值,我们可以在边长为 2 的正方形里面做一个圆,然后均匀打点,统计落在圆内的点数 NcircleN_\text{circle}​,圆的面积即为 NcircleNtotal×4\frac{N_\text{circle}}{N_\text{total}} \times 4,反推 π\pi 即可。我们没法知道圆的具体面积,所以用大量随机采样 + 统计平均 来近似求解难以直接计算的期望值。我们采样的样本越多,估计的结果就越准确。

回到我们之前的问题,状态价值 VV 是一个期望的形式,我们很难求出来。所以我们用策略 π\pi 在 MDP 上采样多条完整序列,形如:

s1 --(a1,r1)--> s2 --(a2,r2)--> s3 --(a3,r3)--> ... --> 终止

然后对每条序列,从后往前计算每个状态的回报 GG

Gt=Rt+γRt+1+γ2Rt+2+ G_t = R_t + \gamma R_{t+1} + \gamma^2 R_{t+2} + \ldots

因为回报是一个累加的形式,所以我们从后往前可以用递推 Gt=Rt+γGt+1G_t = R_t + \gamma G_{t+1}。然后把每条序列里从某个状态 ss 出发的累积回报 GG 平均起来,就得到该状态的价值 Vπ(s)V^\pi(s)

V(s)1N(s)i=1N(s)Gi(s) V(s) \approx \frac{1}{N(s)} \sum_{i=1}^{N(s)} G_i(s)

但这里还存在一个问题,假设有 nn 种状态,每个状态我们存 mm 种回报来进行蒙特卡洛估计,那么总共要存 n×mn\times m 个数据。所以这里采用的是增量更新,不用把所有 G 存起来再平均,可以每来一个新的 G 就更新一次:

V(s)V(s)+1N(s)(GV(s)) V(s) \leftarrow V(s) + \frac{1}{N(s)}\left(G - V(s)\right)

但是蒙特卡洛估计存在几个很严重的问题:

  1. 如果我们想采用蒙特卡洛估计,必须先有完整序列,才能从后往前算。也就是说整个序列生成完毕我们才能计算,很浪费时间。
  2. 蒙特卡洛估计需要大量采样才准确,样本效率低。

时序差分是一种用来估计一个策略的价值函数的方法,它结合了蒙特卡洛和动态规划算法的思想。时序差分方法和蒙特卡洛的相似之处在于可以从样本数据中学习,不需要事先知道环境;和动态规划的相似之处在于根据贝尔曼方程的思想,利用后续状态的价值估计来更新当前状态的价值估计。

说的这么复杂直接用一个例子代入一下:假如你在玩游戏,现在在状态 sts_t​,你估计从这里出发能拿 10分,即 V(st)=10V(s_t) = 10。然后你执行了一个动作,发生了两件事:

  • 立刻得到了奖励 Rt=2R_t = 2
  • 进入了下一个状态 st+1s_{t+1},你估计从那里出发还能拿 9分,即 V(st+1)=9V(s_{t+1}) = 9

所以走了这一步之后,你对"从 sts_t 出发总共能拿多少分"有了一个新的估计:

新估计=Rt+γV(st+1)=2+0.9×9=10.1 \text{新估计} = R_t + \gamma V(s_{t+1}) = 2 + 0.9 \times 9 = 10.1

但你原来的估计是 V(st)=10V(s_t) = 10,两者差了:

δt=10.110=0.1 \delta_t = 10.1 - 10 = 0.1

这个差值就是 TD 误差。它的思路走一步,用即时奖励 + 下一状态的估计值来纠正:

V(St)V(St)+α[Rt+γV(St+1)V(St)] V(S_t) \leftarrow V(S_t) + \alpha\left[R_t + \gamma V(S_{t+1}) - V(S_t)\right]

TD ERROR 和 蒙特卡洛估计本质区别就是:蒙特卡洛等到终点再回头纠错,TD 每走一步就纠错一次

在讲 GAE 之前,必须先清楚优势函数。前面提到,动作价值 QQ 和状态价值 VV 作差,就能得到这个动作是相对好还是相对较差,这就是优势函数:

Aπ(s,a)=Qπ(s,a)Vπ(s) A^\pi(s, a) = Q^\pi(s,a) - V^\pi(s)

通过贝尔曼方程,我们可以用状态价值 VV 表示动作价值 QQ

Qπ(s,a)=r(s,a)+γVπ(s) Q^\pi(s,a) = r(s,a) + \gamma V^\pi(s')

代入优势函数可以得到:

Aπ(s,a)=r(s,a)+γVπ(s)Vπ(s) A^\pi(s,a) = r(s,a) + \gamma V^\pi(s') - V^\pi(s)

接着会神奇的发现,他和 TD 误差的形式基本相同:

Aπ(st,at)δt=Rt+γV(St+1)V(St) A^\pi(s_t, a_t) \approx \delta_t = R_t + \gamma V(S_{t+1}) - V(S_t)
为什么不用动作价值Q表示状态价值V?

因为在优势函数的场景里,V 比 Q 更容易估计。回忆一下两者的输入:

函数 输入 含义
V(s)V(s) 只需要状态 s 这个状态值多少
Q(s,a)Q(s,a) 需要状态 s + 动作 a 这个状态下做动作a值多少

在实际中,动作空间可以非常大(甚至连续),对每一个 (s,a) 都维护一个 Q 值代价很高。而 V 只需要对每个状态维护一个值,简单得多。另外,优势函数的目的是衡量动作 a 相对于平均水平好多少,自然需要一个"基准"来做比较。V(s) 正是这个基准——它代表在状态 s 下按策略平均行动的价值。所以:

A(s,a)=Q(s,a)V(s)基准 A(s,a) = Q(s,a) - \underbrace{V(s)}_{\text{基准}}

用 V 表示 Q,再代入,就能把 Q 消掉,只剩 V,计算更简单。

贝尔曼方程代入之后,优势函数变成了只需要状态价值 VV,但是这个估计只用了一步的信息,精度有限。能不能用更多步?由此引出了多步估计:

1步估计:

A^t(1)=δt=Rt+γV(St+1)V(St)\hat{A}^{(1)}_t = \delta_t = R_t + \gamma V(S_{t+1}) - V(S_t)

2步估计:

A^t(2)=Rt+γRt+1+γ2V(St+2)V(St)\hat{A}^{(2)}_t = R_t + \gamma R_{t+1} + \gamma^2 V(S_{t+2}) - V(S_t)

3步估计:

A^t(3)=Rt+γRt+1+γ2Rt+2+γ3V(St+3)V(St)\hat{A}^{(3)}_t = R_t + \gamma R_{t+1} + \gamma^2 R_{t+2} + \gamma^3 V(S_{t+3}) - V(S_t)

k步估计:

A^t(k)=l=0k1γlRt+l+γkV(St+k)V(St)\hat{A}^{(k)}_t = \sum_{l=0}^{k-1} \gamma^l R_{t+l} + \gamma^k V(S_{t+k}) - V(S_t)

多步估计确实减小了误差提高了进度,但是随机变量越多,叠加在一起,整体的波动就越大,导致随着 k 增加方差越来越大。

步数 k 偏差 方差
小(1步) 大(过度依赖估计值)
大(∞步=MC) 小(接近真实回报)

前情提要说明完,现在可以讲 GAE 了。GAE 的思路是:对所有步数的估计取加权平均,步数越多权重越小。引入参数 λ[0,1]\lambda \in [0,1],权重是 (1λ)λk1(1-\lambda)\lambda^{k-1}

A^tGAE=(1λ)[A^t(1)+λA^t(2)+λ2A^t(3)+] \hat{A}^{\text{GAE}}_t = (1-\lambda)\left[\hat{A}^{(1)}_t + \lambda\hat{A}^{(2)}_t + \lambda^2\hat{A}^{(3)}_t + \ldots\right]

这个式子可以进一步化简得到:

A^tGAE=l=0(γλ)lδt+l=δt+γλδt+1+(γλ)2δt+2+=δt+γλA^t+1GAE \begin{align} \hat{A}^{\text{GAE}}_t &= \sum_{l=0}^{\infty}(\gamma\lambda)^l \delta_{t+l} \\ &= \delta_t + \gamma\lambda\delta_{t+1} + (\gamma\lambda)^2\delta_{t+2} + \ldots \\ &= \delta_t + \gamma\lambda \hat{A}^{\text{GAE}}_{t+1} \end{align}

这个形式非常像之前蒙特卡洛估计的递推式,我们同样可以从后往前计算。当取 λ=0\lambda=0 的时候,退化为 1 步 TD,也就是低方差高偏差;当取 λ=1\lambda=1 时,就是蒙特卡洛估计,高方差低偏差。所以 λ 是一个在偏差和方差之间的权衡,实践中通常取 λ=0.95\lambda = 0.95 左右,偏向低偏差。

GAE 貌似还没有解决 MC 需要生成一整个 trajectory 的问题啊?

我们提出 TD Error 的目的,就是解决蒙特卡洛估计需要得到完整 episode。但是 GAE 是对 1 到 ∞ 步的 TD Error 做加权平均,那不也需要等到整个 episode 生成结束吗?那不是又回到蒙特卡洛估计的问题了吗?

在实际工程实现中,我们不会等到 episode 结束,而是用 truncated rollout。例如 PPO 里面我们会在固定长度里进行 rollout 计算 GAE,此时公式就变成了:

A^t=l=0Tt1(γλ)lδt+l \hat{A}_t = \sum_{l=0}^{T-t-1} (\gamma \lambda)^l \delta_{t+l}

最后一个 state 的 A^t\hat{A}_t 会用 V(ST)V(S_T) 来代替,弥补截断带来的误差。其次我们假设 γ=0.99,λ=0.95\gamma=0.99, \lambda=0.95,那么 γλ=0.9405\gamma\lambda = 0.9405。50 步之后权重已经不到 5% 了,所以远处的 δ\delta 贡献几乎可以忽略,所以不需要真的算无穷步。

那为什么蒙特卡洛估计不能这么做呢?蒙特卡洛估计的公式为:

Gt=Rt+γRt+1+γ2Rt+2+ G_t = R_t + \gamma R_{t+1} + \gamma^2 R_{t+2} + \ldots

它的核心在于必须是“真实采样的未来 reward”,所以必须等到整个 trajectory 结束。


现在我们来梳理一下至今为止我们学了什么。

  1. 马尔科夫决策过程中,我们了解了动作价值 Q(s,a)Q(s,a) 和状态价值 V(s)V(s),期望意味着要对所有可能的未来求加权平均。现实中未来的可能性是无穷多的,根本算不出来。
  2. 通过蒙特卡洛估计,我们用采样平均代替期望,从而近似得到回报 GG 的期望也就是价值 VVQQ。它的问题在于必须先有完整序列,才能从后往前算,效率极低。
  3. 为了解决这个问题,我们了解了时序差分 TD Error,不用等到终点,走一步之后,用即时奖励 + 下一状态的估计值来更新当前状态的估计值:V(St)V(St)+α[Rt+γV(St+1)V(St)]TD误差V(S_t) \leftarrow V(S_t) + \alpha\underbrace{\left[R_t + \gamma V(S_{t+1}) - V(S_t)\right]}_{\text{TD误差}}
  4. 但是单步 TD 只看一步,太近视了。 当前奖励 RtR_t​ 只占整个未来的一小部分,估计偏差大的估计偏差比较大。我们又了解了多步 TD:A^t(k)=Rt+γRt+1++γk1Rt+k1+γkV(St+k)V(St)\hat{A}^{(k)}_t = R_t + \gamma R_{t+1} + \ldots + \gamma^{k-1}R_{t+k-1} + \gamma^k V(S_{t+k}) - V(S_t)。随着步数增加,偏差是减小了,但是用到的随机变量越多,结果方差大,导致梯度更新不稳定。
  5. 于是我们又学习了广义优势估计 GAE,它对所有步数的 TD Error 进行指数加权,实现了偏差和方差的权衡。

铺垫了那么长的基础知识,现在可以回到 Large Language Model 的正轨上来了。

大模型生成序列的过程可以看作一个 马尔可夫决策过程 (MDP)

  1. 模型本身就是强化学习中的策略 π\pi
  2. 模型训练的过程中在做 next token prediction
    1. 从给出 prompt 生成第一个 token 到生成结束就是一个 episode
    2. 每次生成 next token 的过程就是一个 step
    3. prompt 加上已经生成的 token 就是当前的 state
    4. 我们知道大模型生成的是一个概率分布,我们会通过不同的解码策略选择下一个 token,这个概率分布就是 π(as)\pi(a\mid s),选择词表中任一个 token 作为 next token 就是一个 action

RLHF 的目的就是找到一个策略(模型参数),使得所有样本 episode 中每个 step 奖励期望之和最大。

如果你从开始认真看到现在,可能会出现一个和我一样的疑惑:LLM 里面的 state 指的是当前的 context,那这个 state 怎么可能重复出现呢?例如某个句子里面出现了 “生成一个笑话。好的,接下来”,这个 state 在整个数据集应该只会出现一次吧?不像传统的强化学习训练,例如训练 AI 走迷宫,可能多次实验中都以同样的路径到达 P 点,这个 state 是重复出现的。

LLM 里面的 RL 用一个神经网络来近似 V(s)V(s),这个网络的输入是整个上下文(prompt + 已生成的 token),输出是一个数值,表示从这个上下文出发预期能得到多少奖励。它也是通过一个损失函数来进行梯度下降不断修正,使得神经网络泛化出可以预测状态价值的能力。也就是说不需要多次访问同一个 state,网络会在不同 state 之间泛化。

然后再举一个例子看看 LLM 里面,前面提到的 GtG_tr(s,a)r(s,a)V(s)V(s) 都是怎么得到的。

prompt: "帮我写一首诗"
         ↓ 生成
token1: "春"    → r1 = 0
token2: "风"    → r2 = 0
token3: "吹"    → r3 = 0
...
tokenT: "。"    → rT = 8.5  ← 奖励模型打分

在标准 RLHF 中,Reward Model 的输入通常是: prompt + 完整生成的 response,然后输出一个标量分数,它不会在生成过程中对每个 token 打即时分。假设我们采样蒙特卡洛估计,那么把末尾的奖励折扣传播就能得到 GtG_t

G_T   = 8.5
G_T-1 = 0 + γ × 8.5
G_T-2 = 0 + γ × G_T-1
...
G_1   = 0 + γ × G_2

假设用 GAE 计算优势 A^t\hat{A}_t

  1. 最后一个 token A^t=δt=RtV(st)\hat{A}_t = \delta_t = R_t - V(s_t)
  2. 然后从后往前计算 A^t=δt+γλA^t+1\hat{A}_t = \delta_t + \gamma\lambda\hat{A}_{t+1}

在 RLHF 中存在 Reward Hacking 这个概念,训练过程中模型可能会朝着 Reward 倾向的方向走捷径。比如 prompt 是 “今天的天气如何”,那么模型会生成天气情况的分析然后再告诉今天的天气,依次来获取更高的 reward。所以我们需要在 loss 中增加一项,避免模型过度学习,或者说让训练后的模型和原模型差距小一些。RLHF 中引入了一个 Ref Model,这个模型就是经过 SFT 训练后冻结的模型。我们用它和新模型计算 KL 散度,来衡量模型相较于原先的变化。KL 散度的计算公式为:

KL[q,p]=xq(x)logq(x)p(x)=Exq[logq(x)p(x)] \begin{array}{c} \text{KL}[q,p]=\sum_xq(x)\log\frac{q(x)}{p(x)}=\mathbb{E}_{x\sim q}\left[\log\frac{q(x)}{p(x)}\right] \notag \\ \end{array}

我们对估计的 KL 散度有两个要求,第一最好是无偏的,即估计值的期望与真实值相等,第二方差要尽可能小。最常见的对 KL 散度的估计,是直接按 KL 散度的定义,取 k1 估计:

k1=logq(x)p(x)=logr \begin{array}{c} k1=\log\frac{q(x)}{p(x)}=-\log r \notag \\ \end{array}

这里记 r=p(x)q(x)r=\frac{p(x)}{q(x)},我们将这个估计记作 k1。k1 的形式就是按定义来的,所以他显然是无偏的。但是,它其中有个 log  函数,当 q(x)p(x)<1\frac{q(x)}{p(x)}<1 时,它的值是负的,而我们知道 KL 散度一定是正的,所以说它的方差很大。因此 k1 这个估计不满足低方差的要求。


这里我们就引出了 KL 散度的第二种估计 k2 估计:

k2=12(logp(x)q(x))2=12(logr)2 \begin{array}{c} k2=\frac{1}{2}\left(\log\frac{p(x)}{q(x)}\right)^2=\frac{1}{2}(\log r)^2 \notag \\ \end{array}

k2 估计的它对 log\log 取了一个平方,这样子 KL 散度就只有正数使得方差显著减小。这个公式是从 KL 散度的更一般的 f 散度近似来的,f 散度是 KL 散度的一种推广:

Df(p,q)=Exq[f(p(x)q(x))] \begin{array}{c} D_f(p,q)=\mathbb{E}_{x\sim q}\left[f(\frac{p(x)}{q(x)})\right] \notag \\ \end{array}

KL 散度的 k1 估计就是取了 f(x)=log(x)f(x)=-\log(x) 的 f 散度,刚刚的 k2 估计是取了 f(x)=12(logx)2f(x)=\frac{1}{2}(\log x)^2 的 f 散度。

为什么不同 f 散度可以近似 KL?或者换句话说,为什么 k2 估计也能当做 KL 散度?

当 p 和 q 很接近时候,r=pq1+ϵr = \frac{p}{q} \approx 1 +\epsilon。而真实的 KL 散度很小接近于 0,所以我们可以用泰勒展开来近似估计它。我们用任意凸函数做泰勒展开:

f(r)=f(1+ϵ)f(1)+f(1)ϵ+12f(1)ϵ2+O(ϵ3) f(r) = f(1 + \epsilon) \approx f(1) + f'(1) \epsilon + \frac{1}{2} f''(1) \epsilon^2 + O(\epsilon^3)

由于所有的 f 散度都要求 f(1)=0f(1)=0,并且高阶 O(ϵ3)O(\epsilon^3) 可以忽略,所以我们可以简化为:

f(r)f(1)ϵ+12f(1)ϵ2 f(r) \approx f'(1)\epsilon + \frac{1}{2} f''(1) \epsilon^2

然后我们取期望:

Df(p,q)=Exq[f(r)]E[f(1)ϵ+12f(1)ϵ2]=f(1)E[ϵ]+12f(1)E[ϵ2] D_f(p, q) = \mathbb{E}_{x \sim q} [f(r)] \approx \mathbb{E} \left[ f'(1)\epsilon + \frac{1}{2} f''(1) \epsilon^2 \right] = f'(1) \cdot \mathbb{E}[\epsilon] + \frac{1}{2} f''(1) \cdot \mathbb{E}[\epsilon^2]

由于 E[ϵ]=E[r1]=E[pq1]=11=0\mathbb{E}[\epsilon]=\mathbb{E}[r - 1]=\mathbb{E}[\frac{p}{q} - 1]=1-1=0 所以一阶项就没了,然后 E[ϵ2]=Var(r)\mathbb{E}[\epsilon^2]=Var(r) 是同一个值,所以最终的期望就 只依赖于f’’(1)。只要 f’’(1) 相同,所有的 f 散度的二阶近似就完全一样,就可以近似当做 KL 散度。


k2 估计我们改变了 f(x)f(x) 导致这个估计是有偏的,但是平方项降低了 KL 散度的方差, k3 估计就是在无偏的基础上仍然保持了低方差。我们只需要在无偏估计 k1 的基础上,加上一些期望为 0 且与 k1 负相关的项,就可以保证无偏的同时,降低方差。而 r1=p(x)q(x)1r-1=\frac{p(x)}{q(x)}-1 就是一个期望为零的项:

Eq[r1]=Eq[p(x)q(x)1]=[p(x)q(x)1]q(x)dx=p(x)dxq(x)dx=11=0 \begin{aligned} \mathbb{E}_q[r-1]&=\mathbb{E}_q\left[\frac{p(x)}{q(x)}-1\right] \\ &=\int\left[\frac{p(x)}{q(x)}-1\right]q(x)dx \\ &=\int p(x)dx-\int q(x)dx \\ &=1-1=0 \end{aligned} \notag \\

所以,我们有对 KL 散度的第三种估计为:

k3=(r1)logr \begin{array}{c} k3=(r-1)-\log r \notag \\ \end{array}
为什么加上期望为 0 且与 k1 负相关的项就可以无偏且低方差?

我们假设加上的变量为 YY,并且满足 E[Y]=0E[Y]=0YYX=logrX=-\log r 负相关,那么新的估计量可以表示为:

Z=X+cY Z = X+cY

那么新的估计量的期望就是:

E[Z]=E[X+cY]=E[X]+cE[Y]=E[X] E[Z]=E[X+cY]=E[X]+cE[Y]=E[X]

可以看到,ZZ 依然是 XX 的无偏估计。根据方差的性质 Var(A+B)=Var(A)+Var(B)+2Cov(A,B)Var(A + B) = Var(A) + Var(B) + 2Cov(A, B),我们有:

Var(Z)=Var(X)+c2Var(Y)+2cCov(X,Y) Var(Z) = Var(X) + c^2 Var(Y) + 2c Cov(X, Y)

所以只要 XXYY 负相关且相关性的绝对值大于 c2Var(Y)c^2Var(Y),那么新估计量 ZZ 的方差就小于旧估计量 XX 的方差。


因为在 RLHF 里我们根本不可能真正对所有 x 求和,所以我们需要从 qq 分布中采样样本 x1,x2,qx_1,x_2,\dots\sim q,然后用蒙特卡洛方法对 KL 散度进行估计(也就是我们把一个 batch 里面的平均值近似当做它的期望):

Exq[f(x)]=MC1Ni=1Nf(xi)(xiq) \mathbb{E}_{x \sim q}[f(x)] \quad \overset{\text{MC}}{=} \quad \frac{1}{N}\sum_{i=1}^N f(x_i) \quad (x_i \sim q)

由于采样过程本身就是按概率抽样,所以蒙特卡洛估计采样求均值不用和求和一样乘一个系数。

KL=1nresponselogp(as)pref(as) \text{KL} = \frac{1}{n}\sum_{\text{response}}{log{\frac{p(a|s)}{p_{ref}(a|s)}}}

在 RLHF 的 PPO 中,我们喂一个 prompt 给 actor model,让它正常 generate 输出对应的 response。response 中每一个 token 都有它对应的概率分布,我们把它记为 log_probs。我们把 actor model 生成的"prompt + response" 以 Teacher-Forcing 的方式喂给 ref model,那么它同样能给出 response 中每个 token 的 log_prob 结果,我们记其为 ref_log_probs。把这两个概率分布作差,然后再求对数之和的平均值,就是 KL 散度了。

其次,KL 散度的标准定义应该是:对于单个 token 的 KL 散度是要对 vocab 上 每一个 token 的概率分布作差。但是在 RLHF 的实际实现中,KL 只针对 Actor 实际生成的 token 计算概率差,也就是计算 log p_actor(response[t]) − log p_ref(response[t])

相关内容