Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

NLP——LLM对齐微调-RLHF

本文主要介绍 LLM 对齐微调的 RLHF(即 PPO)训练方式,论文参考了一些优秀的技术博客,然后加入了自己的理解
注:本文包含 AI 辅助创作

  • 参考链接:
    • 图解大模型RLHF系列之:人人都能看懂的PPO原理与源码解读
    • OPENAI原始论文:Training language models to follow instructions with human feedback
      • 论文补充材料:补充材料包含很多细节,比如奖励的定义就在附属材料中
    • 微软RLHF论文:ALIGNING LARGE MULTIMODAL MODELS WITH FACTUALLY AUGMENTED RLHF

概念

  • 对齐微调(Alignment tuning):为了避免模型输出一些不安全或者不符合人类正向价值观的回复
    • 人类偏好对齐:基于人类反馈的对齐微调(Alignment tuning)方法,如RLHF
    • AI偏好对齐:基于AI反馈的对齐微调方法,如RLAIF
  • RLHF,Reinforcement Learning from Human Feedback,即基于人类反馈的强化学习方法

RLHF 的 MDP 过程

  • 一个response就是一个trajectory:

RLHF 整体框架

  • RLHF的一般框架如下:
  • Actor Model :被训练的目标语言模型LLM
  • Critic Model :AC强化学习中的Critic角色,用于估计 \(V_t\)
  • Reward Model :奖励模型,它的作用是计算收益 \(R_t\) ,实际实现一个response仅预估一次
  • Reference Model :参考模型,它的作用是在RLHF阶段给语言模型增加一些“约束”,防止语言模型训歪(朝不受控制的方向更新,效果可能越来越差)
  • Actor/Critic Model在RLHF阶段是需要训练的(图中给这两个模型加了粗边,就是表示这个含义);
  • Reward/Reference Model是参数冻结的
  • Critic/Reward/Reference Model共同组成了一个“奖励-loss”计算体系(图解大模型RLHF系列之:人人都能看懂的PPO原理与源码解读作者自己命名的,为了方便理解),我们综合它们的结果计算loss,用于更新Actor

不同角色的细节

Actor Model

  • Actor Model的整体图示如下:

  • Actor的目标是:在不太偏离Reference Model的情况下,最大化reward,其定义可以写为如下形式(注意不是最终损失函数)
    $$
    \max_{\pi_\theta} \mathbb{E}_{x\sim D, y\sim \pi_\theta(y|x)} [r_{RM}(x,y)]-\beta \mathbb{D}_{KL}[\pi_\theta(y|x)||\pi_{ref}(y|x)]
    $$

    • \(r_{RM}(x,y)\) 是Reward Model的返回值
    • 上面是一个简单的示例,可以暂且理解为 \(x\) 表示 Prompt,用 \(y\) 表示 response,实际上真实场景中 \(x\) 表示 Prompt+截止到目前的输出 tokens,用 \(y\) 表示 下一个 token
    • 可以使用Reward Engineering来实现这个目标,可在不修改其他公式的情况下,使用下面的Reward来实现上述目标:
      $$
      r(x,y) = r_{RM}(x,y) - \beta(\log\pi_\theta(y|x) - \log\pi_{ref}(y|x) )
      $$
    • 上述Reward常常也写作下面的形式(使用 token 形式精确表达):
      $$
      r_{t}=r_{\varphi}(q,o_{\leq t})-\beta\log\frac{\pi_{\theta}(o_{t}|q,o_{ < t})}{\pi_{ref}(o_{t}|q,o_{ < t})}
      $$
      • \(r_{\varphi}(q, o_{\leq t})\) 是外部奖励模型给出的原始奖励
      • \(\beta \log\frac{\pi_{\theta}(o_{t}|q,o_{ < t})}{\pi_{ref}(o_{t}|q,o_{ < t})}\) 是 KL散度惩罚项,\(\beta\) 是一个正的超参数,本质是一个去除期望的KL散度
    • 实际上,可直接将负的Actor-Reference KL加入到reward中,仅在最后一个时间步加入Reward:
      $$
      R_t=
      \begin{cases}
      -k\log\frac{\pi_\theta(A_t|S_t)}{\pi_\text{ref}(A_t|S_t)} & t < T\\
      -k\log\frac{\pi_\theta(A_t|S_t)}{\pi_\text{ref}(A_t|S_t)} + R_t & t = T
      \end{cases}
      $$
      • 其中,仅在response最后一步 \(t=T\) 时取 \(R_t\) 的值,因为Reward Model训练时也是一整个Response作为一个样本训练的,这也很好理解,因为一次回答中间的结果无法评估好坏,需要整个回答完成后才知道这次Response是否合理
      • \(\frac{\pi_\theta(A_t|S_t)}{\pi_\text{ref}(A_t|S_t)}\) 不一定是正数,但是由于动作 \(A_t\) 是从Actor策略中采样的(具体来说,是从 \(\pi_{\theta_\text{old}}\) 中采样的,PPO下可以近似认为两者相等),而 \(\mathbb{E}_{A_t \sim \pi_\theta}[\log\frac{\pi_\theta(A_t|S_t)}{\pi_\text{ref}(A_t|S_t)}]\) 是KL散度,值一定大于0,所以最大化带KL散度后的 \(R_t\) 就可以最小化KL散度,让Actor不要偏离Reference Model太多
      • 在实现时,取模型输出的概率取对数再做减法即可:
        $$
        R_t=
        \begin{cases}
        k (ref\_log\_probs-log\_probs) & t < T\\
        k (ref\_log\_probs-log\_probs) + R_t & t = T
        \end{cases}
        $$
  • PPO-loss定义(这里是Clip版本,引入拉格朗日对偶性的Adaptive KL Penalty版本不太常用,这里没有给出),对于一个完整的response生成后(等价于一次完整的trajectory),损失函数如下:
    $$
    \mathcal{J}_{\textit{PPO}}(\theta)=\mathbb{E}_\left[q\sim P(Q),o\sim\pi_{\theta_{old}}(O|q)\right]\frac{1}{|o|}\sum_{t=1}^{|o|}\min\left[\frac{\pi_{\theta}(o_ {t}|q,o_{<t})}{\pi_{\theta_{old}}(o_{t}|q,o_{<t})}A^{\pi_\text{old}}_{t},\textrm{clip}\left( \frac{\pi_{\theta}(o_{t}|q,o_{<t})}{\pi_{\theta_{old}}(o_{t}|q,o_{<t})},1-\epsilon ,1+\epsilon\right)A^{\pi_\text{old}}_{t}\right],
    $$

    • 实践中PPO通常设置 \(\epsilon=0.2\)
    • 优势函数 \(A^{\pi_\text{old}}_t\) 是通过广义优势估计GAE计算得到的(注:计算优势函数使用的Reward是经过KL散度修正的)
  • 优势函数的优化(GAE):

    • 可以使用GAE,可以累加实现优势函数 \(A^{Actor_{old}}(S_t, A_t)\) 的计算:
      $$
      \begin{align}
      \delta_t &= R_t + \gamma V_{t+1} - V_t \\
      A^{Actor_{old}}(S_t, A_t) &= \sum_{l=0}^{T-t} (\gamma \lambda)^l \delta_{t+l}
      \end{align}
      $$
    • 在一些文章中还会写成下面的等价形式(两者的等价性很容易证明),然后从后往前累加(动态规划的思想)
      $$
      A^{Actor_{old}}(S_t, A_t) = \delta_t + \gamma\lambda A^{Actor_{old}}(S_{t+1}, A_{t+1})
      $$
  • 最终,通过把负Actor-Reference KL 添加到 \(R_t\) 中,Actor loss的形式与PPO-loss的形式完全一致

Critic Model

  • Critic Model的整体图示如下:
  • 在强化学习阶段,用到的Reward Model和Critic Model都使用同一个模型初始化,因此在训练Reward Model的过程中,也是在训练Critic Model
  • Critic loss,MSE损失函数(其中 \(R_t\) 是一个经过KL散度修正的奖励):
    $$
    loss = (R_t + \gamma V_{t+1} - V_t)^2
    $$

Reward Model

  • Reward Model的整体图示如下(问题:这个图有点问题,Reward Model对一个response仅需要预估一个最终的Reward即可吧?):

  • Reward Model的训练流程如下:

    • 使用SFT模型对每个Prompt \(x\) 重复生成多个不同Response \(y\),记为 \(y_a, y_b, y_c, y_d\)
    • 让人类对回答进行排序打分: \(y_a \succ y_b \succ y_c \succ y_d\),最终可获得数据集 \(D = \{x^{(i)}, y^{(i)}_w, y^{(i)}_l\}_{i=1}^N\),实际上,对于一个 \(x\) 对应K个 \(y\) 的情况,可以拆开成 \(C_K^2\) 个样本
      • 这里有一些标注平台,可用于便捷式打分,方便人类操作,比如GitHub:label-studio提供了一个开源标注服务,GitHub:transformers_tasks提供了一个简单的模型生成数据到可视化打标的工具
    • 使用收集的数据训练Reward Model,目标是让输入Prompt \(x\) 时排序越高的Response \(y\) 打分 \(r_\theta(x,y)\) 越高
      • Reward Model通常比Actor Model小,deepspeed的示例中,语言大模型66B,奖励模型只有350M)
        • 也可以使用SFT后的LM模型后面接一层线性层作为Reward Model
      • 奖励模型训练优化采用pair-wise loss,即同时输入模型关于同一个问题的两个回答,让模型学会这两个句子哪个分高哪个分低,原始论文给出的损失函数如下(其中是否需要乘以 \(\frac{1}{C_K^2}\) 有待考证,感觉不需要,因为后面是期望,相当于已经做过归一化了):
        $$
        Loss_{\text{RM}}(\theta) = -\frac{1}{C_K^2} \mathbb{E}_{(x,y_w,y_l)\sim D}[\log \sigma(r_\theta(x, y_w) - r_\theta(x, y_l))]
        $$
      • \((x,y_w,y_l)\sim D\) 表示从数据集中随机抽取胜者和败者两个回答进行配对
  • 损失函数的推导过程:

    • 对于任意的Prompt \(x\) 和两个不同的Response \(y_w, y_l\),人类偏好是 \(y_w \succ y_l\),假设这些偏好是由一个最优的打分模型 \(r^{*}(x,y)\) 确定,我们用一个神经网络 \(r_\theta(x,y)\) 来表示打分模型,按照Bradley-Terry (BT)方法(详情见DPO论文:Direct Preference Optimization: Your Language Model is Secretly a Reward Model),我们可将人类偏好分布表示为:
      $$
      p(y_w \succ y_l|x) = \frac{e^{r_\theta(x,y_w)}}{e^{r_\theta(x,y_w)} + e^{r_\theta(x,y_l)}}
      $$
    • 进一步地,为了最大化数据集 \(D = \{x^{(i)}, y^{(i)}_w, y^{(i)}_l\}_{i=1}^N\) 中人类偏好出现的概率,我们有:
      $$
      \begin{align}
      \theta^{*} &= \arg\max \prod p(y_w \succ y_l|x) \\
      &= \arg\max \prod \frac{e^{r_\theta(x,y_w)}}{e^{r_\theta(x,y_w)} + e^{r_\theta(x,y_l)}} \\
      &= \arg\max \sum log \frac{e^{r_\theta(x,y_w)}}{e^{r_\theta(x,y_w)} + e^{r_\theta(x,y_l)}} \\
      &= \arg\max \sum log \frac{1}{1+\frac{e^{r_\theta(x,y_l)}}{e^{r_\theta(x,y_w)}}} \\
      &= \arg\max \sum log \frac{1}{1+e^{r_\theta(x,y_l) - r_\theta(x,y_w)}} \\
      &= \arg\max \sum log \frac{1}{1+e^{-(r_\theta(x,y_w) - r_\theta(x,y_l))}} \\
      &= \arg\max \sum log \sigma(r_\theta(x,y_w) - r_\theta(x,y_l)) \\
      \end{align}
      $$
  • 打分归一化:为了确保打分函数具有较低的方差(不同输入Prompt的打分可能会差距非常大),实际训练中还可以对打分进行归一化,即对所有 \(x\)
    \(\mathbb{E}_{x,y\sim D}[r_\theta(x,y)] = 0\)

    • 问题,是分别对不同 \(x\) 做多次归一化,还是所有 \(x\) 一起做N次归一化,直观来看,是不是分别对不同 \(x\) 做N次归一化会更好?(论文没有明说,代码也没看到)
  • 实现代码示例,参考自博客GitHub:transformers_tasks:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    def compute_rank_list_loss(rank_rewards_list: List[List[torch.tensor]], device='cpu') -> torch.Tensor:
    """
    通过给定的有序(从高到低)的ranklist的reward列表,计算rank loss
    所有排序高的句子的得分减去排序低的句子的得分差的总和,并取负

    Args:
    rank_rewards_list (torch.tensor): 有序(从高到低)排序句子的reward列表,e.g. ->
    [
    [torch.tensor([0.3588]), torch.tensor([0.2481]), ...],
    [torch.tensor([0.5343]), torch.tensor([0.2442]), ...],
    ...
    ]
    device (str): 使用设备

    Returns:
    loss (torch.tensor): tensor([0.4891], grad_fn=<DivBackward0>)
    """
    if type(rank_rewards_list) != list:
    raise TypeError(f'@param rank_rewards expected "list", received {type(rank_rewards)}.')

    loss, add_count = torch.tensor([0]).to(device), 0
    for rank_rewards in rank_rewards_list:
    for i in range(len(rank_rewards)-1): # 遍历所有前项-后项的得分差
    for j in range(i+1, len(rank_rewards)):
    diff = F.sigmoid(rank_rewards[i] - rank_rewards[j]) # sigmoid到0~1之间
    loss = loss + diff
    add_count += 1
    loss = loss / add_count
    return -loss

Reference Model

  • 基本思想:为了防止Actor偏离SFT的结果(Reference Model)太多,导致丧失Pretrain和SFT的优秀能力, 在训练过程中保证Actor和Reference的动作分布差异不要太大(KL散度)
  • Reference Model可以防止Reward Hacking问题 ,参考LLMs 奖励剥削 RLHF: Reward hacking
    • 在强化学习中,Reward Hacking问题通常是指智能体在学习过程中发现并利用奖励函数的漏洞,为了获得最大reward而偏离训练者的目标的情况
    • 在RLHF中,Reward Hacking问题是指没有Reference Model的情况下,Actor为了实现最大化reward,会越来越偏离原来的SFT模型,这不是我们想要的
  • KL散度公式:
    $$
    \begin{align}
    D_{KL}(Actor(S)||Ref(S)) &= \mathbb{E}_{a \sim Actor(s)} [\log \frac{Actor(S)}{Ref(S)}] \\
    &= \mathbb{E}_{a \sim Actor(s)} [\log Actor(S) - \log Ref(S)] \\
    &\approx log\_probs - ref\_log\_probs\\
    \end{align}
    $$
    • 公式中最后一步用 \(\approx\) 的原因是因为需要保证生成 \(log\_probs\) 和 \(ref\_log\_probs\) 的样本是按照策略 \(Actor\) 来进行采样的(即按照Actor执行动作)
  • 思考:保证Actor和Reference两者策略足够接近,即KL散度足够接近有两种方法:
    • 方法一:按照Actor采样样本,并且在训练Actor的loss中直接加入KL散度 ,loss越小越好 => KL散度越小越好
    • 方法二:即按照Actor采样样本,并且在Reward中加入负的KL散度 ,Reward越大越好 => KL散度越小越好
      • 方法二是Reward Engineering过程,通过修改Reward来避免Reward Hacking问题
    • 目前已知的方案中,一般是加入到Reward中(如下图来自微软RLHF论文:ALIGNING LARGE MULTIMODAL MODELS WITH FACTUALLY AUGMENTED RLHF),其实还可以有其他方案

RLHF中的奖励设计

  • 在RLHF(从人类反馈中进行强化学习)的真实训练中,KL散度会在强化学习的优化目标中作为额外的正则化项出现,对Reward Model给出的奖励进行修正
    $$
    r_{t}=r_{\varphi}(q,o_{\leq t})-\beta\log\frac{\pi_{\theta}(o_{t}|q,o_{ < t})}{\pi_{ref}(o_{t}|q,o_{ < t})},
    $$

    • 其中,\(r_{\varphi}\) 是奖励模型的输出,\(\pi_{ref}\) 是参考模型(通常是初始的SFT模型),\(\beta\) 是KL惩罚的系数

    • 注意:虽然这里写的 KL 散度计算是 \(\pi_{\theta}\) 和 \(\pi_{ref}\) 的 KL 散度,但是实际训练中,在 off-policy 场景下,应该是 \(\pi_{\theta_\text{old}}\) 和 \(\pi_{ref}\) 的 KL 散度

      • 因为 GAE 仅计算一次,多次更新时也不会重新计算 GAE,接着就是其他的计算,实现中(比如Hand-on-RL TRPO & PPO)还会使用 .detach() 以防止梯度反传回去(奖励仅仅是梯度的权重比例,不参与梯度更新)
      • 补充:最近看到的博客 The critical implementation detail of KL loss in GRPO 中也强调了这一点
    • 注意:仅对每个 Response 的最后一个 Token 上有 Reward ,其他地方都是 0,实现如下:

      1
      2
      for j in range(batch_size):
      rewards[j, start:ends[j]][-1] += reward_from_model[j] # 假定 start 是 prompt 的长度,ends 是 padding 前的最后一个 index
      • 注:许多文章不明所以,会错误表述为每个 Token 都有 Reward,实际上,不针对所有输出Token,很难对单个Token评估奖励
    • 理解:以上Reward的本质是想要限制KL散度,KL散度的原始公式是:
      $$
      KL = \mathbb{E}_{o_t \sim \pi_{\theta}(o_{t}|q,o_{ < t})} \Big[\log\frac{\pi_{\theta}(o_{t}|q,o_{ < t})}{\pi_{ref}(o_{t}|q,o_{ < t})}\Big]
      $$

    • 更详细的讨论见论文附录

KL散度的作用

  • KL散度(Kullback-Leibler Divergence)在RLHF中主要用于防止策略模型偏离初始模型(参考模型)太远 ,从而避免模型生成不合理或极端的输出

  • KL散度的计算 :
    $$
    \text{KL}(\pi_\theta || \pi_{\text{ref}}) = \mathbb{E}_{y \sim \pi_\theta} \left[ \log \frac{\pi_\theta(y|x)}{\pi_{\text{ref}}(y|x)} \right]
    $$

    • 其中 \( \pi_\theta \) 是当前策略模型,\( \pi_{\text{ref}} \) 是参考模型(如初始预训练模型)
  • KL散度的作用 :

    • 防止模型过度优化奖励,导致输出偏离人类期望
    • 保持生成内容的多样性和合理性

强化学习的优化目标

  • 在RLHF中,强化学习的优化目标通常结合了奖励模型的输出和KL散度正则化项。具体形式如下:
    $$
    \max_{\pi_\theta} \mathbb{E}_{(x, y) \sim \pi_\theta} \left[ R(x, y) - \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}}) \right]
    $$
  • 其中:
    • \( R(x, y) \) 是Critic网络预测的奖励值
    • \( \beta \) 是KL散度的权重系数,用于控制正则化的强度

其他相关RLHF的描述

  • 其他框架表达形式:
  • 训练流程,来自OPENAI原始论文:Training language models to follow instructions with human feedback

附录:Reward 的 KL 散度为什么可以将期望消去

  • 奖励函数的定义 :
    $$
    r_t = r_{\varphi}(q, o_{\leq t}) - \beta \log \frac{\pi_\theta(o_t|q, o_{ < t})}{\pi_{ref}(o_t|q, o_{ < t})}
    $$
    • \(r_{\varphi}(q, o_{\leq t})\) 是外部奖励模型给出的原始奖励
    • \(\beta \log \frac{\pi_\theta}{\pi_{ref}}\) 是 KL散度惩罚项,\(\beta\) 是一个正的超参数,本质是一个去除期望的KL散度

理论理解(目前 OK)

  • 由于动作 \(o_t\) 是从 \(\pi_\theta\) 策略中采样的(具体来说,是从 \(\pi_{\theta_\text{old}}\) 中采样的,PPO下可以近似认为两者相等),而 \(\mathbb{E}_{o_t \sim \pi_\theta(o_t|q,o_{ < t})}\Big[\log\frac{\pi_\theta(o_t|q,o_{ < t})}{\pi_\text{ref}(o_t|q,o_{ < t})}\Big]\) 是KL散度,值一定大于0,所以最大化带KL散度后的 \(r_t\) 就可以最小化KL散度,让Actor不要偏离Reference Model太多
  • 在策略更新时,本质就是按照 \(o_t \sim \pi_\theta(o_t|q,o_{ < t})\) 采样得到的样本更新的,也就是在更新 \(\log\frac{\pi_\theta(o_t|q,o_{ < t})}{\pi_\text{ref}(o_t|q,o_{ < t})}\) 在分布 \(o_t \sim \pi_\theta(o_t|q,o_{ < t})\) 下的期望 \(\mathbb{E}_{o_t \sim \pi_\theta(o_t|q,o_{ < t})}\Big[\log\frac{\pi_\theta(o_t|q,o_{ < t})}{\pi_\text{ref}(o_t|q,o_{ < t})}\Big]\)
  • 注:在许多实现中都有类似操作,比如SpinningUp项目的PPO的实现中,有 approx_kl = (logp_old - logp).mean().item() 语句来表示近似KL散度,这里样本都是从 \(\pi_{\theta_\text{old}}\) 策略中采样得到的,所以:
    $$
    \begin{align}
    D_{\text{KL}}(\pi_{\theta_\text{old}}||\pi_{\theta}) &= \mathbb{E}_{a \sim \pi_{\theta_\text{old}}} \left[\log\frac{\pi_{\theta_\text{old}}(a|s)}{\pi_{\theta}(a|s)}\right] &\\
    &\approx \frac{1}{N} \log\frac{\pi_{\theta_\text{old}}(a|s)}{\pi_{\theta}(a|s)} \\
    &= \frac{1}{N} (\log \pi_{\theta_\text{old}}(a|s)- \log\pi_{\theta}(a|s))
    \end{align}
    $$

直观理解(暂未想通)

  • \(\log \frac{\pi_\theta}{\pi_{ref}}\) 带来的行为 :
    • 当 \(\pi_\theta(o_t|q, o_{ < t}) > \pi_{ref}(o_t|q, o_{ < t})\) 时,\(\frac{\pi_\theta}{\pi_{ref}} > 1\),因此 \(\log \frac{\pi_\theta}{\pi_{ref}} > 0\)
    • 当 \(\pi_\theta(o_t|q, o_{ < t}) < \pi_{ref}(o_t|q, o_{ < t})\) 时,\(\frac{\pi_\theta}{\pi_{ref}} < 1\),因此 \(\log \frac{\pi_\theta}{\pi_{ref}} < 0\)
    • 当 \(\pi_\theta(o_t|q, o_{ < t}) = \pi_{ref}(o_t|q, o_{ < t})\) 时,\(\log \frac{\pi_\theta}{\pi_{ref}} = 0 \)
  • \(\log \frac{\pi_\theta}{\pi_{ref}}\) 对奖励的影响 :
    • 当 \(\pi_\theta > \pi_{ref}\) 时 :\(\log \frac{\pi_\theta}{\pi_{ref}} > 0\),因此惩罚项 \(-\beta \log \frac{\pi_\theta}{\pi_{ref}} < 0\),会导致奖励 \(r_t\) 降低
    • 当 \(\pi_\theta \lt \pi_{ref}\) 时 :\(\log \frac{\pi_\theta}{\pi_{ref}} < 0\),因此惩罚项 \(-\beta \log \frac{\pi_\theta}{\pi_{ref}} > 0\),会导致奖励 \(r_t\) 增加
    • 当 \(\pi_\theta = \pi_{ref}\) 时 :
      \(\log \frac{\pi_\theta}{\pi_{ref}} = 0\),惩罚项为 0,奖励 \(r_t\) 仅由外部奖励 \(r_{\varphi}\) 决定
  • 为什么 \(\pi_\theta < \pi_{ref}\) 时奖励应该增加?
    • 与直觉相反 :当 \(\pi_\theta < \pi_{ref}\) 时,\(\log \frac{\pi_\theta}{\pi_{ref}} < 0\),因此惩罚项 \(-\beta \log \frac{\pi_\theta}{\pi_{ref}} > 0\),这确实会导致奖励 \(r_t\) 增加
    • 直观理解有问题不太(还需要思考):
      • 鼓励策略采取保守行为 :当 \(\pi_\theta\) 生成某个 token 的概率低于 \(\pi_{ref}\) 时,模型的行为更加保守,RLHF在鼓励这种保守行为,这种保守其实很奇怪,本质是在鼓励降低 \(o_t\) 出现的概率
      • 问题:这也会导致模型概率往负向偏离原始参考模型吧?
  • 直观理解的疑惑总结 :
    • 当 \(\pi_\theta < \pi_{ref}\) 时,\(\log \frac{\pi_\theta}{\pi_{ref}} < 0\),惩罚项 \(-\beta \log \frac{\pi_\theta}{\pi_{ref}} > 0\),奖励 \(r_t\) 会增加,这种设计是为了鼓励模型的行为更加保守(注:保守其实很奇怪,本质是在鼓励降低 \(o_t\) 出现的概率)
    • 当 \(\pi_\theta > \pi_{ref}\) 时,惩罚项为负,奖励会降低(此时是符合常识的),从而防止模型过度自信地生成偏离参考策略的内容
  • 其他说明:在GRPO中,有一种全新的KL散度近似公式,该公式下,近似值永远大于等于0,就不存在上面的问题了

NLP——ChatHome

注:本文包含 AI 辅助创作

  • 参考链接
    • 原始论文:ChatHome: Development and Evaluation of a Domain-Specific Language Model for Home Renovation, arXiv 20230728, Beike:注:作者信息为xx@ke.com,是贝壳找房旗下的邮箱

Paper Summary

  • 整体内容总结 & 评价:
    • 论文介绍了一个专为家装任务设计的领域大语言模型 ChatHome
    • 作者使用不同的基础模型和数据比例,测试了模型的领域特定能力和通用性能
  • 论文介绍了 ChatHome 的开发与评估,这是一个专为复杂的家装领域设计的特定领域语言模型(Domain-Specific Language Model, DSLM)
  • 考虑到像 GPT-4 这样的 LLM 的卓越能力以及人们对家装领域日益增长的兴趣,本研究试图通过生成一个专注于家装领域的专用模型,以提供高保真、精准的输出
  • ChatHome 的创新性在于其方法,即融合了领域自适应预训练(domain-adaptive pretraining)和基于广泛数据集的指令微调(instruction-tuning)
  • 该数据集包括家装领域的专业文章、标准文档和网络内容
  • 这种双重策略旨在确保模型能够吸收全面的领域知识并有效解决用户问题
  • 通过对通用和领域特定数据集(包括新引入的“EvalHome”领域数据集)的全面实验,论文验证了 ChatHome 不仅增强了领域特定功能,还保持了其通用性

Introduction and Discussion

  • 在人工智能的蓬勃发展中,像 GPT-4 (2023) 和 ChatGPT (2023) 这样的大规模语言模型的开发引发了自然语言处理任务的深刻变革,展现了在多种任务中的卓越能力
    • 中文开源大语言模型也迅速发展,例如 ChatGLM (2022)、Baichuan (2023) 和 BELLE (2023)
  • 尽管一些研究在医疗 (2023)、金融 (2023) 和法律 (2023) 等领域取得了显著进展,但家装(home renovation)这一特定领域仍相对未被充分探索
  • 家装是一个多方面的领域,需要对美学和功能性有全面的把握
    • 它不仅仅是选择家具或确定配色方案,还需要深刻理解建筑细节、空间设计原则、以人为本的设计理念以及流行趋势等元素
  • 像 ChatGPT 这样的主流模型尽管在多种任务中表现出通用能力,但在生成高保真、精准的领域特定内容时往往表现不佳
    • 这一点在法律 (2023) 和医疗 (2023) 领域已有观察
    • 因此,为了克服这些不足并满足家装领域的独特需求,迫切需要一种专为该领域定制的语言模型
  • 本研究提出了 ChatHome,一种专为家装设计的语言模型。论文的方法包括两个步骤:
    • 首先,使用广泛的家装数据集对通用模型进行后预训练(post-pretraining),该数据集涵盖专业文章、标准文档和网络内容;
    • 其次,通过基于家装提示生成的问题-答案对数据集实施指令微调策略
  • 本研究旨在证明,对大规模语言模型进行后预训练和微调可以提高其在特定领域的性能
    • 在增强特定领域能力的同时,论文还关注模型通用能力的变化,并进行了详细的评估,这将在后续章节中详细描述
  • 论文的主要贡献有两点:
    • 论文建立了 ChatHome,一个专注于家装领域的微调大语言模型
    • 论文引入了一个领域数据集,并在通用和领域数据集上进行了全面实验,以验证模型的有效性

Related work

  • 大语言模型的训练通常包括两个阶段:Pre-training和指令微调(instruction fine-tuning)
  • 通过在大规模语料库上进行预训练,大语言模型可以获得基本的语言理解和生成能力
  • 指令微调阶段则是为了让模型具备理解人类指令的能力,同时也能提高模型在未见任务上的泛化能力 (2022, 2023)
  • 然而,领域特定任务通常涉及复杂的概念、技术术语和实体之间的复杂关系 (2023)
  • 如果没有针对性的指导,大语言模型可能会出现严重的幻觉(hallucination)问题
    • 这是因为大语言模型的目标是根据输入预测最可能的词序列,而不是基于结构化知识提供明确的答案
  • 最近,在医疗 (2023)、金融 (2023) 和法律 (2023) 领域涌现了大量与大语言模型适应相关的工作
    • 通过基于检索的插件知识库,大语言模型可以在不更新参数的情况下用于专业领域 (2023),或者选择通过更新参数将领域知识注入模型
    • 本报告主要关注后者(通过更新参数将领域知识注入模型)
  • 根据训练阶段的不同,大语言模型的领域专用训练方法大致可分为以下几类:
    • 一种方法是直接基于领域数据从头开始预训练,例如 (2023),这种方法通常依赖于大量领域数据,训练成本较高;
    • 另一种是基于领域指令数据直接进行微调,例如 (2023);
    • 还有一种是在基础大语言模型上基于领域数据进行领域预训练,然后再进行指令微调 (2023)

Data Collection

Pre-training Corpus

  • 先前的研究 (2020) 表明,语言模型可以通过领域特定的语料库获取知识
  • 论文收集了一个领域特定的语料库,以增强模型在家装领域的知识
  • 此外,论文还编译了一个通用语料库,以平衡模型的通用知识
  • 国家标准 :论文收集了多项装修和建筑的国家标准,其中包括《住宅设计规范》(GB 50096-2011)和《住宅装饰装修工程施工规范》(GB 50327-2001)
  • 领域书籍 :论文收集了过去十年出版的房地产、家装、装饰和建筑领域的书籍
  • 领域网站 :论文爬取了约 30,000 篇家装建议、家居设备购买技巧等类别的文章
  • 通用语料库 :为了构建通用语料库,论文从 WuDaoCorpora (2021) 和简体中文维基百科中采样文章
  • 数据预处理 :上述数据通过统一的流程进行处理,包括文本提取、质量过滤和数据去重
    • 在文本提取阶段,论文丢弃了图片、表格和 URL 等无关信息,仅保留相关文本
    • 在质量过滤阶段,论文通过敏感词过滤、语言过滤和有效文本长度过滤等方法确保数据的可用性
    • 此外,论文在文章和句子级别进行去重,以减少重复数据对模型训练的影响
    • 最终,论文从领域语料库中获取了约 26.6M Token,从通用语料库中获取了 276.6M Token。数据预处理的流程如 图1 所示

SFT Corpus

  • 为了缓解领域偏差问题并提升模型在特定领域的性能,论文从高质量的家装书籍和家装网站文章中构建了约 25k 条指令数据,帮助模型适应特定领域知识
    • 这些提示的详细信息见附录中的表格
  • 单轮对话 :为了获取更多家装相关的问题
    • 首先,使用 GPT-4 模拟室内设计师和客户的双重角色,基于给定知识生成若干问答对
    • 随后,为了获得更详细的回答,论文将这些问题直接提交给 GPT-4
    • 这种两步法使论文能够获取更全面和精确的数据
  • 多轮对话 :与单轮对话类似,GPT-4 模拟室内设计师和客户的角色,生成了家装领域的多轮对话
    • 此外,为了减少幻觉(hallucination),论文为 GPT-4 提供了相关文章,确保对话内容围绕这些知识展开
    • 同时,论文要求 GPT-4 保持对话的连贯性和自然性
  • 基于单轮和多轮对话的指令数据,论文生成了如图2 所示的词云

Experiments

Baseline Models

  • 论文选择的基线模型是 Baichuan-13B (2023),由 Baichuan Intelligent Technology 开发和发布。该基线包含两个模型:
    • Baichuan-13B-Base :这是一个预训练模型,参数规模为 130 亿,训练语料包含 1.4 万亿 Token
    • Baichuan-13B-Chat :基于 Baichuan-13B-Base 的架构,通过专用指令进行微调,具备更强的对话生成和指令理解能力

Experiments Setups

  • 论文使用上述两个基线模型对家装领域数据集进行微调
  • 为了探索领域自适应预训练(domain-adaptive pretraining,DAPT)在领域适应中的优势,论文将在经过 DAPT 优化的模型上执行相同的指令微调实验
  • 领域自适应不可避免地会面临灾难性遗忘(catastrophic forgetting)问题,即在适应新领域时丢失先前学到的知识
  • 一种简单的缓解方法是基于复现的策略,即重新学习先前知识
  • 考虑到 LLM 是在大量通用数据上预训练的,领域适应过程中需要平衡通用数据和领域数据
    • 论文为每组实验执行了五组数据比例测试,以确定最有效的数据比例方案
  • DAPT 和 SFT 阶段的参数配置如表1 所示,两者唯一的区别在于最大长度(DAPT 为 1024,SFT 为 1536)

Metrics

  • 评估对大语言模型的成功至关重要。对于 ChatHome,论文不仅希望注入领域相关知识,还关注模型在领域化后的通用能力
    • 因此,论文的评估包括两部分:通用能力评估和领域能力评估
  • 通用评估(General Evaluation) :为了评估模型的通用能力,论文采用 C-Eval (2023a) 和 CMMLU (2023),这两个基准测试用于评估基础模型在中文语境下的高级知识和能力
  • 领域评估(Domain Evaluation) :据论文所知,目前没有权威的家装领域评估数据集
    • 论文构建了一个名为 EvalHome 的领域评估数据集,涵盖三个难度级别:领域基础知识、领域专业知识和创新设计
    • 所有问题均以多选题形式呈现,共计 113 道
    • EvalHome 的统计信息如表2 所示

Results and Analysis

Data ratio result analysis
  • DAPT 模型在通用评估集上的实验结果如表3 所示
  • 论文展示了 CEval 和 CMMLU 的平均分数,各类别的详细分数见表6
  • 尽管在 1:10 的比例下添加了更多通用数据,但 DAPT 模型在 1:5 的数据比例方案中表现出最少的通用能力损失,其 CEval 和 CMMLU 的平均分数相较于基础模型分别下降了 2.57 和 3.08 分。该模型记为 Baichuan-13B-Base-DAPT (1:5)
  • 表4 展示了领域适应模型在 EvalHome 和通用评估集上的实验结果。论文进行了四组实验,其中 Baichuan-13B-Base-DAPT (1:0) 表示 DAPT 阶段的数据比例为 1:0
    • 可以看到,除了 Baichuan-13B-Base 实验外,其他三组实验在 1:5 数据比例下均取得了 EvalHome 的最佳结果
  • 结合这两张表的实验结果,我们可以初步得出结论:在当前的基础模型和家装领域数据下,1:5 的数据比例表现最佳
  • 在指令微调阶段,论文观察到一个显著现象:随着更多通用指令数据的加入,模型在通用能力评估集上的分数下降
    • 这可能是因为 C-Eval 和 CMMLU 主要评估模型的特定知识 ,而论文的通用指令数据未能覆盖这些内容
Domain adaptation result analysis
  • 从表4 可以看出,经过 DAPT 的模型在指令微调后,EvalHome 的得分分别为 59.29 和 55.75,相较于未经过 DAPT 的 Baichuan-13B-Base 模型(最高分为 53.98)略有提升
  • 然而,当使用已经过指令数据训练的 Baichuan-13B-Chat 模型进行指令微调时,EvalHome 的得分达到了 60.17。此外,不同数据比例的模型相较于未更新参数的 Baichuan-13B-Chat 均有显著提升
    • 这表明,在当前领域场景下,经过 DAPT 的指令微调并未显著超越直接在指令对齐模型上进行领域适应的效果
    • 论文推测,这可能是因为基础模型在预训练阶段已经包含了大量家装相关数据
  • 进一步地,受一些研究工作 (2021; 2022; 2022) 的启发,论文尝试在 DAPT 阶段融入下游监督数据
    • 这种策略在 (2022) 中被称为 MIP(多任务指令预训练),论文在论文中也沿用这一名称
    • 由于训练资源和时间的限制,论文未对数据比例进行详细分析
    • 在 MIP 阶段,论文的训练数据仅包含领域预训练数据和领域指令数据,未添加通用语料
      • 尽管如此,EvalHome 的得分意外达到了 69.03(见表4 最后一行)
      • 更令人惊讶的是,该模型不仅在 EvalHome 上得分最高,还在两个通用能力评估基准上表现更优
  • 这一发现表明,在当前领域数据集和基础模型条件下,将下游指令数据融入 DAPT 阶段是有益的。未来论文计划在 MIP 阶段进行更深入的数据比例实验

附录 A

  • Prompts used for generating one-turn and multi-turn dialogues by GPT-4, examples of EvalHome and the detail socres of C-Eval and CMMLU are presented in this appendix.

图3:EvalHome 示例

  • 为便于阅读,中文下方附有英文翻译

图4:单轮对话示例

  • 为便于阅读,中文下方附有英文翻译

表5:数据生成过程中使用的提示

图5:多轮对话示例

  • 为便于阅读,中文下方附有英文翻译

表6:C-Eval 和 CMMLU 的详细评估分数

NLP——Secrets-of-RLHF(PPO)

注:本文包含 AI 辅助创作

  • 参考链接:
    • 原始论文:Secrets of RLHF in Large Language Models Part I: PPO, Fudan & ByteDance, 202306
    • 代码地址:github.com/OpenLMLab/MOSS-RLHF

Paper Summary

  • 核心说明:
    • 本文是作者 Secrets of RLHF in Large Language Models 系列的第一篇
    • 本文可信讲述了 RLHF 中的 PPO 方法的具体实现细节等
    • 评价:论文内容很长,但作为发布很早(23年)的文章,非常值得读一遍(后续发的第二篇也值得关注)
  • 背景和问题:
    • LLMs 的主要目标是充当以人类为中心的 Helpful, Honest, and Harmless(3H)助手, LLM 与人类对齐至关重要,RLHF 则是这一追求的关键技术范式
    • 当前 RLHF 的技术路线通常包括用于衡量人类偏好的奖励模型、用于优化策略模型输出的近端策略优化(PPO),以及用于提升逐步推理能力的过程监督
    • 问题:由于奖励设计、环境交互和智能体训练等方面的挑战,再加上大语言模型的试错成本巨大,人工智能研究人员在推动技术对齐的发展和大语言模型的安全落地方面面临着重大障碍,RLHF 的稳定训练仍然是一个难题
  • 作者提到:在第一份报告中,论文剖析了 RLHF 的框架,重新评估了 PPO 的内部工作机制,并探索了构成PPO算法的各个部分如何影响策略智能体的训练
    • 问题:第一份报告在哪里?
  • 论文发现策略约束是 PPO 算法有效实施的关键因素,因此,论文探索了 PPO-max(PPO算法的高级版本),能够有效地提高策略模型的训练稳定性
  • 基于论文的主要结果,论文对 RLHF 能力与 SFT 模型和 ChatGPT 进行了全面分析
    • 除了额外的定性结果外,论文甚至发现通过论文的算法成功训练的 LLM 通常能够更好地理解查询的深层含义,其回答更能直接触及人们的灵魂
  • 开源实现的缺失对LLM对齐的研究构成了重大挑战。因此,论文发布技术报告、奖励模型和 PPO 代码,旨在为 LLM 的发展做出微薄贡献

Introduction and Discussion

  • 如今,LLMs 取得了显著进展,对人工智能社区产生了重大影响,通过扩大模型规模、数据规模和训练计算量,这些LLM展现出了小模型所不具备的突出特点,通常包括上下文学习(in-context learning)、指令遵循(instruction following)和逐步推理(step-by-step reasoning)。基于这些涌现能力,LLM甚至展现出一些将文字与感知联系起来以与现实世界交互的潜力,从而带来了通用人工智能(AGI)的可能性,例如具有工具操作能力的具身语言模型和交互式沙盒环境中的生成智能体
  • 尽管具备这些能力,但由于LLM是为捕捉预训练语料库(包括高质量和低质量数据)的数据特征而训练的,这些模型很可能表现出意想不到的行为,如编造事实、生成有偏见或有毒的文本,甚至对人类有害的内容。正如 OpenAI 的 AGI 计划所强调的,安全进展与能力进展的比率增加至关重要。因此,有必要使LLM与人类价值观对齐 ,例如 Helpful, Honest, and Harmless(3H)
  • 特别是,开源基础模型的到来,如 LLaMA 和 OpenChineseLLaMA,已迅速将LLM推进到 SFT 阶段。为了减轻巨大的危害性风险,当前的大多数工作试图在 SFT 中添加一些 3H 数据,希望激活模型的响应,使其在道德和伦理层面做出积极改变。然而,即使添加了一组安全和接地目标来捕捉模型在对话中应表现出的行为,模型在安全性和接地性方面的表现仍低于人类水平。因此,需要更有效和高效的控制方法来消除LLM使用的潜在风险。幸运的是,OpenAI 和 Anthropic 已经验证,RLHF 是使语言模型在广泛任务上与用户意图对齐的有效途径
  • 训练与人类价值观对齐的大语言模型是一项艰巨的任务,使用强化学习训练时往往会反复失败。一般来说,成功的RLHF训练需要一个准确的奖励模型作为人类判断的替代,需要仔细探索超参数以实现稳定的参数更新,还需要强大的 PPO 算法来进行稳健的策略优化。而用低质量数据和难以定义的对齐目标训练的奖励模型很容易将 PPO 算法误导到难以理解的方向。此外,用 PPO 微调语言模型需要协调四个模型协同工作,即策略模型、价值模型、奖励模型和参考模型 ,这使得训练困难且难以扩展到大规模参数模型。在新的语言环境中,PPO在词空间中面临奖励稀疏和探索效率低的问题,使其对超参数敏感。仅通过反复实验、失败运行和超参数扫描训练的模型取得的结果要差得多。LLM巨大的试错成本使得研究人员不敢轻易让研究进入RLHF阶段,这阻碍了LLM的安全落地。因此,专门为LLM设计的稳健 PPO 算法是对齐人类偏好的关键步骤
  • 在本报告中,论文仔细剖析了 RLHF 的框架,并讨论了决定算法训练成功的整个过程
    • 论文探索了奖励模型的质量如何影响策略模型的最终结果,论文发现奖励模型的质量直接决定了策略模型的上限,设计合适的 PPO 算法对 RLHF 的成功训练至关重要
    • 准确的代码实现在深度策略中也很重要(实践出真知)。因此,论文对PPO算法的内部工作机制进行了深入评估,以研究代码层面和理论层面的优化如何改变智能体的训练动态
    • 论文建议使用从策略模型生成的动作空间建模指标来监控 PPO 训练过程,例如困惑度、响应长度以及策略模型与SFT模型之间的KL散度。这些指标比响应奖励和损失函数的值更能反映训练的稳定性
    • 基于这些观察,论文确定 PPO 算法中的策略约束是实现与人类偏好一致对齐的关键因素
  • 在对 PPO 框架的各种可能实现进行广泛的对比实验后,论文最终引入了一种更优的策略优化算法,称为 PPO-max ,它融合了有效且必要的实现,并经过仔细校准以避免它们之间的干扰。PPO-max 缓解了原始 PPO 训练的不稳定性,并允许使用更大的训练语料库进行更长的训练步骤。论文在 7B 和 13B 的 SFT 模型上评估了 PPO-max,证明了其与 ChatGPT 相当的对齐性能
  • 论文贡献如下:
    • 1)论文分别发布了具有良好跨模型泛化能力的有竞争力的中文和英文奖励模型,减轻了重新 Token 人类偏好数据的成本;
    • 2)论文对 PPO 算法的内部工作机制进行了深入分析,并提出了 PPO-max 算法以确保模型的稳定训练;
    • 3)论文发布了完整的 PPO-max 代码,以确保当前 SFT 阶段的 LLM 能够更好地与人类对齐

相关工作(直译)

  • 尽管LLM具有令人期待的能力,但由于低质量的预训练数据,它们很可能表现出意想不到的行为,如编造事实、生成有偏见或有毒的文本,甚至对人类有害的内容。因此,有必要使LLM与人类价值观对齐(例如 3H)
  • 为了减轻巨大的危害性风险,当前的大多数工作试图在 SFT 中引入 3H 数据,希望激活模型的响应,使其在道德和伦理层面做出积极改变[7,19,20],而模型在安全性和接地性方面的表现仍低于人类水平[17]。需要更有效和高效的控制方法来消除 LLM 的潜在风险,微调语言模型以对齐人类偏好为这一挑战提供了有效的解决方案,其中要求智能体学习人类偏好,并在给定上下文和 Human Annotators(人类标注员) 排序或评分的相应后缀时提供类似人类的结果
  • RL 为实现这一目标提供了最直接的解决方案,因为智能体只需要来自作为人类代理的奖励模型的稀缺监督信号,并在RL框架下通过大量试验进行修改,即人类反馈强化学习(RLHF)。最近在这条道路上已经有了许多尝试[22,23,24,25,17,16,26]
  • 在大语言模型的背景下,RLHF 特别用于实现与人类价值观对齐 3H 的LLM[16,17,12],减轻通用语言模型对社会的负面影响
    • LaMDA[12]微调大语言模型以参与有趣、有帮助、基于事实且安全的自然语言对话,并使用外部信息来确保准确性和接地性。他们没有使用强化学习,而是应用了多种监督学习技术来实现人类偏好对齐
    • InstructGPT[16]微调 GPT-3 类型的模型[5]以提高帮助性,这与来自通过比较表达的人类偏好的 RL 相结合。[27]采用预训练和微调传统来训练用于人类对齐的偏好模型,声称排名偏好建模被证明是区分“好”行为和“坏”行为的最有效训练目标。这种尝试通过每周用新鲜的人类反馈数据更新偏好模型和 RL 策略的迭代在线训练模式得到了进一步改进,并引入 PPO 来稳定 RL 训练[17]
    • 尽管 RLHF(尤其是PPO)有效,但其表现出复杂性、不稳定性和对超参数的敏感性,这些在以前的工作中尚未解决
  • 在类似的担忧下,一些工作强调了 PPO 对 RL 框架的重要性,并尝试提高其效率[28,29]。[29]揭示,PPO 带来的奖励改善很大程度上可能来自于对核心算法的看似微小的修改(即代码层面的优化)。[28]进一步指出,RL的许多低级和高级设计决策通常在研究论文中没有讨论,但确实对性能至关重要。因此,[28]在统一的RL实现基础上对低级设计进行了公平比较,并声称策略初始化方案显著影响性能
  • PPO 的重要性及其实现方式有很多探讨,但很少有人尝试解决不稳定性和对超参数敏感的问题。在论文中,论文剖析了 RLHF 的框架,特别是阐明了 PPO 的内部工作机制,并探索了 PPO 的高级版本,该版本有效地提高了策略模型的训练稳定性

Reinforcement Learning from Human Feedback

  • AI assistant 的训练过程包括三个主要阶段: SFT 、奖励模型(RM)训练,和(在该奖励模型上的)近端策略优化(PPO)
    • 在 SFT 阶段,模型通过模仿人类注释的对话示例来学习进行一般的类人对话
    • 训练奖励模型:模型学习基于人类反馈比较不同响应的偏好
    • 在 PPO 阶段:根据奖励模型的反馈更新模型,努力通过探索和利用发现优化的策略
  • 在RLHF过程中,论文主要考虑 RM 训练和通过 PPO 进行的强化学习阶段
  • PPO 算法遵循图1所示的一系列步骤

奖励建模

  • RM 架构:论文使用预训练的基于 Transformer 的语言模型 ,去掉最后的 Unembedding Layer,并在最后的 Transformer 层添加一个额外的线性层
  • 给定任何文本,奖励模型会给最后一个 Token 分配一个标量奖励值,奖励值越大,样本越好
  • 遵循Stiennon等人[25],训练奖励模型通常涉及使用为同一输入生成的两个响应之间的成对比较数据集,每对偏好和非偏好样本的建模损失为:
    $$\mathcal{L}(\psi)=\log \sigma\left(r\left(x, y_{w}\right)-r\left(x, y_{l}\right)\right)$$
    • \(\sigma\) 是sigmoid函数
    • \(r_{\psi}\) 表示参数为 \(\psi\) 的奖励模型
    • \(r(x, y)\) 是为输入提示 \(x\) 和响应 \(y\) 预测的单个标量奖励
  • 此外,论文遵循[27]使用模仿学习,在每对的偏好响应上引入自回归 LM 损失,允许模型模仿每对句子中的偏好响应,在实践中,论文分别为 LM 损失添加系数 \(\beta_{rm}\)
  • 最后,论文定义以下奖励建模损失:
    $$\mathcal{L}(\psi)=-\lambda \mathbb{E}_{\left(x, y_{w}, y_{l}\right) \sim \mathcal{D}_{rm} }\left[\log \sigma\left(r\left(x, y_{w}\right)-r\left(x, y_{l}\right)\right)\right]+\beta_{rm} \mathbb{E}_{\left(x, y_{w}\right) \sim \mathcal{D}_{rm} }\left[\log \left(r’\left(x, y_{w}\right)\right)\right]$$
    • \(\mathcal{D}_{rm}\) 是训练集的经验分布
    • \(r’\) 是与 \(r\) 相同的模型(上层参数共享),除了顶部的线性层,其维度对应词汇表大小,\(r’(x, y_{w})\) 是给定提示 \(x\) 和偏好响应 \(y_{w}\) 的似然
  • 论文在奖励函数中加入了一个额外的项,该项基于学习到的 RL 策略 \(\pi_{\phi}^{RL}\) 和初始监督模型 \(\pi^{SFT}\) 之间的 Kullback-Leibler(KL)散度引入惩罚。总奖励可以表示为[30]:
    $$r_{\text {total } }=r(x, y)-\eta \mathrm{KL}\left(\pi_{\phi}^{RL}(y | x), \pi^{SFT}(y | x)\right)$$
    • \(\eta\) 是 KL 奖励系数,控制 KL 惩罚的强度
    • 这个 KL 散度项在这种情况下有两个重要作用
      • 首先,它作为熵奖励,促进策略空间的探索,防止策略过早收敛到单一模式
      • 其次,它确保RL策略的输出不会与奖励模型在训练阶段遇到的样本发生剧烈偏离

强化学习

  • 将 RL 应用于对话生成面临着巨大的状态-动作空间的重大挑战,RL 的建模如下:
    • 环境:论文将人类交互视为“环境”
    • 状态:在每个时间步 \(t\),智能体(即 AI assistant)从环境(即对话历史)接收状态 \(s_{t}\),该状态由到目前为止智能体和人类的所有对话文本组成。、
    • 动作:智能体根据其策略 \(\pi\),其动作 \(a_{t}\) 是生成下一个 Token
    • 奖励:环境返回奖励 \(r(s_{t}, a_{t})\),该奖励由从人类偏好数据训练的奖励函数 \(r\) 计算
    • 状态转移:智能体转移到下一个状态 \(s_{t+1}\),其中包括下一个对话历史
  • RL 的目标是为智能体找到最佳行为策略,以最大化轨迹 \(\tau=\{s_{1}, a_{1}, \ldots, s_{T}, a_{T}\}\) 上的累积奖励(即回报)
    • 一种回报是有限 horizon 无折扣回报 : \(R(\tau)=\sum_{t=1}^{T’} r(s_{t}, a_{t})\),它只是在固定步数内积累的奖励之和
    • 另一种是无限 horizon 折扣回报 :\(R(\tau)=\sum_{t=0}^{\infty} \gamma^{t} r(s_{t}, a_{t})\),它考虑了智能体在整个轨迹中获得的所有奖励,并带有折扣因子\(\gamma \in(0,1)\)
策略梯度方法
  • 策略梯度方法[31]是一种 RL 技术,它直接优化智能体的策略——从状态到动作的映射——而不是像基于价值的方法那样学习价值函数
    • 策略梯度方法的核心思想是使用梯度上升算法改进策略
    • 本质上,这些方法沿最大程度提高预期回报的方向调整策略的参数
  • 策略 \(\pi\) 通常由 \(\theta\) 参数化,论文将其表示为 \(\pi(a | s, \theta)\),这是在状态 \(s\) 下采取动作 \(a\) 的概率
  • 策略梯度的更新规则为:
    $$\theta \leftarrow \theta+\alpha \nabla_{\theta} J(\theta)$$
    • \(\alpha\) 是学习率
    • \(J(\theta)\) 表示遵循策略 \(\pi_{\theta}\) 时的预期回报,策略性能的梯度 \(\nabla_{\theta} J(\theta)\) 称为策略梯度
  • 策略梯度的一般形式可以表示为:
    $$\nabla_{\theta} J(\theta)=\mathbb{E}_{\tau \sim \pi_{\theta} }\left[\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}\left(a_{t} | s_{t}\right) \Phi_{t}\right]$$
    • \(\Phi_{t}\) 可以是:
      • \(\Phi_{t}=R(\tau)\)
      • \(\Phi_{t}=\sum_{t’=t}^{T} R(s_{t’}, a_{t’})\)
      • \(\Phi_{t}=\sum_{t’=t}^{T} R(s_{t’}, a_{t’})-b(s_{t})\),其中\(b\)是基线
    • 所有这些选择都会导致策略梯度的预期值相同,尽管方差不同
  • 通过蒙特卡洛采样计算回报,如果回报是有利的,所有动作都会通过增加其被选择的概率来“强化”
    • 无偏估计:这种方法的优点在于其无偏性,因为论文仅依赖于获得的实际回报,而非对其进行估计(注:其实是使用蒙特卡罗采样估计回报的期望)
    • 高方差:由于环境的随机性(一个事件中的随机事件)和策略本身的随机性,不同的轨迹可能导致不同的回报,这使得该方法存在高方差的挑战
  • 为了降低这种方差,一种常见的策略是在策略梯度更新规则中使用优势函数估计来代替原始回报,优势函数 \(A(s_t, a_t)\) 表示在状态 \(s_t\) 下采取特定动作 \(a_t\) 比在同一策略下该状态下动作的平均质量好多少,即:
    $$\Phi_t = A(s_t, a_t)$$
    • 从数学上讲,\(A(s_t, a_t) = Q(s_t, a_t) - V(s_t)\),其中 \(Q(s_t, a_t)\) 是动作价值函数,表示在状态 \(s\) 下采取动作 \(a_t\) 后的预期回报,\(V(s_t)\) 是价值函数,表示状态 \(s_t\) 下的平均预期回报
  • 带有优势函数的策略梯度应用构成了 RL 领域的重要支柱。然而,不同算法对优势函数的估计方法差异显著,从而形成了多样化的方法体系。论文将在下一节中介绍策略优化算法的基础方法——广义优势估计(GAE)

Generalized Advantage Estimation

  • 对GAE推导的通俗易懂的解释:
    • 优势函数 \(A\) 被定义为 \(Q\) 函数(预期回报)与价值函数(从给定状态遵循策略的预期回报)之间的差异
    • \(Q\)函数考虑特定动作,而价值函数根据策略对所有可能动作进行平均,在实践中,论文使用实际情节的回报(奖励总和)来估计\(Q\)函数,这会引入大量方差,因为未来的奖励可能非常嘈杂。减少这种噪声的一种方法是使用价值函数来估计未来的回报(在时间步\(t\)之后)。GAE算法有效地充当了使用简单的单步时间差分(TD)回报和使用完整蒙特卡洛回报之间的中间立场,平衡了偏差和方差
  • 以下是对GAE推导的通俗易懂的解释
    • TD-\(k\) 回报 \(\hat{R}_t^k\) 是实际奖励和估计回报的组合:
      $$\hat{R}_t^k = r_t + \gamma r_{t+1} + \ldots + \gamma^{(k-1)} r_{t+k-1} + \gamma^k V(s_{t+k})$$
      • \(\gamma\) 是折扣因子
    • 使用 TD-\(k\) 回报的优势估计称为 \(k\) 步优势,定义为:
      $$\hat{A}_t^k = \hat{R}_t^k - V(s_t) = \sum_{l=1}^k \gamma^l \delta_{t+l} = -V(s_t) + r_t + \gamma r_{t+1} + \cdots + \gamma^{k-1} r_{t+k-1} + \gamma^k V(s_{t+k})$$
      • \(\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)\) 是 TD 误差
      • \(k\) 步优势存在显著的偏差-方差权衡:
        • 如果 \(k\) 较小,偏差较高,因为优势估计基于较少的步骤,因此严重依赖于价值函数的准确性;
        • 如果 \(k\) 较大,方差可能较高,因为优势估计涉及对许多嘈杂奖励的求和
  • 为了平衡优势估计中的偏差-方差权衡,GAE 将优势函数定义为 \(k\) 步优势的指数移动平均,权重为 \((1-\lambda)\lambda^{(k-1)}\):
    $$\begin{aligned}
    \hat{A}_t^{\text{GAE}(\gamma, \lambda)} &= (1-\lambda)\left(\hat{A}_t^{(1)} + \lambda \hat{A}_t^{(2)} + \lambda^2 \hat{A}_t^{(3)} + \cdots\right) \\
    &= (1-\lambda)\left(\delta_t + \lambda\left(\delta_t + \gamma \delta_{t+1}\right) + \lambda^2\left(\delta_t + \gamma \delta_{t+1} + \gamma^2 \delta_{t+2}\right) + \dots\right) \\
    &= (1-\lambda)\left(\delta_t\left(1 + \lambda + \lambda^2 + …\right) + \gamma \delta_{t+1}\left(\lambda + \lambda^2 + \lambda^3 + …\right)\right. \\
    &\left.\quad + \gamma^2 \delta_{t+2}\left(\lambda^2 + \lambda^3 + \lambda^4 + …\right) +…\right) \\
    &= (1-\lambda)\left(\delta_t\left(\frac{1}{1-\lambda}\right) + \gamma \delta_{t+1}\left(\frac{\lambda}{1-\lambda}\right) + \gamma^2 \delta_{t+2}\left(\frac{\lambda^2}{1-\lambda}\right) +…\right) \\
    &= \sum_{l=0}^{\infty}(\gamma \lambda)^l \delta_{t+l}.
    \end{aligned}$$
  • GAE的这一定义在高偏差(当\(\lambda=0\)时)和高方差(当\(\lambda=1\)时)估计器之间平滑插值,有效地管理了这种权衡
    $$
    \begin{align}
    \text{GAE}(\gamma, 0): &\ \hat{A}_t = \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t). \\
    \text{GAE}(\gamma, 1): &\ \hat{A}_t = \sum_{l=0}^{\infty} \gamma^l \delta_{t+1} = \sum_{l=0}^{\infty} \gamma^l r_{t+1} - V(s_t).
    \end{align}
    $$
  • 通过GAE,我们可以准确估计优势函数 \(A(s_t, a_t)\) 的 \(\hat{A}_t\):
    $$\nabla_{\theta} \hat{J}(\theta) = \frac{1}{|\mathcal{D}|} \sum_{\tau \in \mathcal{D} } \sum_{t=1}^{T} \nabla_{\theta} \log \pi_{\theta}(a_t | s_t) \hat{A}_t$$
    • \(\mathcal{D}\) 是有限的样本批次,接下来论文将使用 \(\hat{\mathbb{E} }_t\) 表示上述 \(\frac{1}{|\mathcal{D}|} \sum_{\tau \in \mathcal{D} } \sum_{t=1}^{T}\)

Proximal Policy Optimization,PPO

  • PPO 和 TRPO [33]是 RL 中的两种关键技术,旨在有效训练策略而不损害其稳定性。这些方法的基本思想是“小而稳定的步骤”:一种温和地推动策略向优化方向发展的理念,而不是强制可能破坏整体学习过程的激进更新
  • 在传统的RL中,策略梯度原理要求新旧策略在参数空间中保持接近。然而,参数空间中的这种接近并不一定意味着性能相似,参数的微小变化可能会极大地影响策略的有效性
    • 如果采取不受限制的大步骤,可能会导致策略性能崩溃,这种情况通常被描述为“悬崖坠落(alling off the cliff)”
    • 这种固有风险是原始策略梯度在样本效率方面的限制因素(问题:如何理解?)
  • TRPO 没有被参数接近度所限制,而是对策略更新引入了不同类型的约束,它通过确保 KL 散度保持在可接受的范围内来调节策略的变化:
    $$
    \begin{align}
    \underset{\theta}{\text{maximize} } &\ \hat{\mathbb{E} }_t\left[\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)} \hat{A}_t\right], \\
    \text{subject to} &\ \hat{\mathbb{E} }_t\left[\text{KL}\left(\pi_{\theta_{\text{old} } }(\cdot | s_t), \pi_{\theta}(\cdot | s_t)\right)\right] \leq \delta,
    \end{align}
    $$
    • \(\theta_{\text{old} }\) 是更新前的旧策略参数
  • PPO 主要有两种变体:PPO-Penalty 和 PPO-Clip。虽然 TRPO 对 KL 散度施加硬约束以防止有害更新,但 PPO-Penalty 通过采用基于惩罚的方法而非约束来解决无约束优化问题:
    $$\mathcal{L}_{\text{ppo-penalty} }(\theta) = \hat{\mathbb{E} }_t\left[\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)} \hat{A}_t\right] - \beta \text{KL}\left(\pi_{\theta_{\text{old} } }(\cdot | s_t), \pi_{\theta}(\cdot | s_t)\right)$$
    • 其中 \(\beta\) 是惩罚因子
Clipped Surrogate Objective
  • PPO-Clip 试图使新策略接近旧策略,但不像 TRPO 那样对 KL 散度施加约束,而是在其目标中使用策略比率的裁剪版本。目标函数表示为:
    $$\mathcal{L}_{\text{ppo-clip} }(\theta) = \hat{\mathbb{E} }_t\left[\min\left(\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)} \hat{A}_t, \text{clip}\left(\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)}, 1 - \epsilon, 1 + \epsilon\right) \hat{A}_t\right)\right]$$
    • \(\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)}\)是新策略概率与旧策略概率的比率
    • \(\epsilon\) 是一个超参数,用于确定新策略可以偏离旧策略的程度
    • 裁剪函数将 \(\frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old} } }(a_t | s_t)}\) 的值限制在 \((1 - \epsilon, 1 + \epsilon)\) 之间,裁剪作为正则化器,限制了策略从一次迭代到下一次迭代的剧烈变化程度。防止过大的策略更新可确保学习过程的稳健性,同时保持比原始策略梯度方法更高的样本效率
Value Function Estimation
  • 在 PPO 算法中, Critic 模型(通常称为价值函数)估计每个状态的预期回报
    • 该模型的学习目标是最小化其预测值与实际回报值之间的差异
  • Critic 模型的损失函数通常使用均方误差(MSE)定义,由以下公式给出:
    $$\mathcal{L}_{\text{critic} }(\phi) = \hat{\mathbb{E} }_t\left[\left| V_{\phi}(s_t) - \hat{R}_t \right|^2\right]$$
    • \(V_{\phi}(s_t)\) 表示参数为 \(\phi\) 的 Critic 模型对状态 \(s_t\) 的预测值
    • \(\hat{R}_t\) 表示状态 \(s_t\) 的实际回报值,并且始终可以估计为:
      $$ \hat{R}_t = \sum_{l=0}^{\infty} \gamma^l r_{t+l}$$
Mixing Pretraining Gradients
  • 为了减轻 PPO 期间模型语言技能和知识保留的潜在退化,论文还探索了将预训练数据纳入 RL 阶段。使用此方法的模型表示为 “PPO-ptx”,组合目标函数如下所示[16]:
    $$\mathcal{L}_{\text{ppo-ptx} }(\theta) = \mathcal{L}_{\text{ppo-clip} }(\theta) + \lambda_{\text{ptx} } \mathbb{E}_{x \sim \mathcal{D}_{\text{pretrain} } }\left[\log\left(\pi_{\theta}^{\text{RL} }(x)\right)\right]$$
    • \(\lambda_{\text{ptx} }\)是预训练损失系数
    • \(\mathcal{D}_{\text{pretrain} }\)是预训练数据分布
PPO 训练伪代码
  • RLHF 中,PPO 训练流程如下:

Reward Modeling for Helpfulness and Harmlessness

  • 奖励模型的训练是为了反映人类的偏好
  • 从理论上讲,我们可以直接使用强化学习和人类注释来微调模型,但由于工作量和时间的限制,人类在每次优化迭代之前提供足够的反馈是不可行的。
  • 一种更有效的方法是训练一个奖励模型(RM),其目的是模拟人类执行的评估过程
  • 在本节中,论文首先介绍 RM 的技术细节,然后展示论文使用的 RM 性能,并附上训练期间的性能变化

Models and Datasets

  • 对于英文:
    • 论文从原始的 LLaMA-7B[1]开始,它是 Decoder-only 架构
    • 训练集:使用 HH-RLHF 数据集[17]的 160k 成对样本,该数据集由 118k 有益和 42k 无害实例组成作为训练集
    • 测试集:去除训练集,HH-RLHF 剩下 8.5k 数据,论文随机从中选择了大约 0.7k 有益和 0.3k 无害示例,总共 1k 数据作为测试集
    • 验证集:除训练集和测试集外的数据在训练期间用作验证集
  • 对于中文:
    • 论文使用 OpenChineseLLaMA[18],它是在中文数据集上通过增量预训练开发的,建立在LLaMA-7B的基础上,显著提高了其对中文的理解和生成能力
    • 数据标注:论文聘请专业注释人员手动标注了 39k 成对样本,包括 31k 有益和 8k 无害样本
    • 训练集:论文通过随机采样 24k 有益和 6k 无害实例来构建训练集
    • 测试集:从剩余数据中随机分配 2.4k 有益和 0.6k 无害样本形成测试集
    • 验证集:除训练集和测试集外的数据在训练期间用作验证集

Training Setup

  • 本节介绍 RM 的训练实现
  • 学习率设置为 5e-6,在前 10% 的步骤中进行预热
  • 论文使用动态批次方法而不是固定值,该方法尽可能平衡每个批次中的 Token 数量,以实现更高效和稳定的训练阶段
  • Batch Size 根据批次中的 Token 数量而变化,最大为 128,最小为 4
  • 论文将训练步骤固定为 1000,大约为整个训练集的 1.06 个 epoch
  • 在整个实验中,论文设置 \(\beta_{rm}=1\),它表示用于训练论文奖励模型的 LM 损失权重

HH Evaluation Results

  • 本节展示论文 RM 的 HH 评估结果
  • 论文主要使用原文 4.1 节中介绍的测试集来分析训练后的奖励模型,该测试集由 0.9k 个英文 HH-RLHF 样本和 3k 个从注释人员标注的数据集中采样的中文样本组成。
  • 论文将测试集输入到论文的 RM 中,分别获得偏好和非偏好响应的奖励值,然后将它们相减得到差异分数。图2显示了差异分数的分布。两种模型都与人类偏好有一定程度的一致性,论文通过聘请注释人员构建的中文数据训练的RM与人类判断表现出显著的一致性
  • 作者检查了测试数据集中模型与人类偏好差异最大的几个样本
    • 对于中文测试数据,论文观察到 RM 给予更高奖励的每个响应对都明显比人类偏好的响应更长,尽管或多或少涉及编造事实和虚假陈述
    • 在英文测试数据的情况下,论文注意到模型给承认信息不足的响应打低分,这些响应的特点是诚实但缺乏帮助性。相反,那些看起来正确和有帮助但包含欺骗性信息的响应误导论文的RM给予高奖励
  • 论文在表1中分别提供了一个中文和英文的例子

训练性能

  • 在本节中,论文展示训练过程中的性能变化
  • 具体来说,图3 显示了 RM 训练损失的趋势
  • 我们可以看到,在中文数据集上训练的 RM 的准确性高于英文,因为:
    • 论文构建的中文数据集中的大多数对的较好和较差响应之间存在显著差异
    • 许多英文对显示出相似的质量水平,这给 RM 确定响应的优劣带来了更大的挑战,导致模型难以建模两个响应之间的差异特征,在英文数据集上的训练和测试准确性预计会更低
  • 准确性收敛 :论文发现对于两个模型,在 200 步之后,改进速度明显减慢,大约相当于 0.2 个 epoch,其准确性与训练一个完整 epoch 后获得的准确性相当
  • 仅准确性不足以作为 RM 的标准 :当使用 200 步模型(准确性收敛的不错)作为 PPO 的初始化时,论文观察到性能不理想
    • 问题:这里的初始化是什么含义?

Exploration of PPO

  • 近端策略优化(PPO)[34]是实现与人类偏好对齐的核心算法
  • PPO 的性能在实际应用中受到多种因素的影响
  • 一些先前的工作已经总结了在强化学习领域可能必要和有效的技巧[35],但如何使用语言模型稳定RLHF训练仍然未知
  • 作者希望探索哪些技巧是关键的 ,以及哪些指标可以反映 RLHF 训练期间和之后的模型状态
  • 论文首先介绍在训练过程中具有指导意义的指标,然后介绍不同实现下的训练轨迹和效果,以揭示 RLHF 中的核心技巧
  • 论文使用 PPO-max 来表示论文为语言模型找到的最合适的实现

Models and Training Setup

  • 偏好模型(Preference Model,PM)和 PM 数据集的训练实现如 4 节所述
    • 注:这里说的 PM 就是 RM 吧
  • 在本节中,论文介绍探索 PPO 时的模型初始化和超参数细节
  • 论文验证了强化学习中的许多方法,以确保稳定收敛并在 PPO 训练阶段获得更好的结果
  • 为了提高实验效率,这些实验主要在论文中文数据的随机选择子集上进行,并且当论文观察到足够的信息来分析比较方法时,不会训练到最佳结果
  • 如 3 节所示,PPO 训练阶段需要加载四个模型:
    • 对于参考模型和策略模型,论文都从 7B SFT 模型初始化。SFT 模型是在 OpenChineseLLaMA 的基础上,在 1M 经过过滤的指令数据(包含 400K 单轮指令样本和600K 多轮指令样本)上进行 2 个 epoch 的监督微调得到的
    • 论文设置学习率为 9.5e-6 和 余弦学习率调度,学习率最终衰减到峰值学习率的 10%
    • 全局 Batch Size 设置为 1024
    • 论文使用奖励模型来初始化 Critic 模型和奖励模型(注:是不是表达有误?用奖励模型来训练奖励模型?)
  • 论文在手动构建的 HH 数据集上训练模型,该数据集包含 8k无 害查询和 20k 有益查询,并且论文固定步骤数而不是 epoch 数
  • 在所有实验中,论文设置从环境采样的 Batch Size 为 128,训练策略模型和 Critic 模型的 Batch Size 为 32
  • 策略模型和 Critic 模型的学习率分别设置为 5e-7和 1.65e-6,在前10%的步骤中进行预热
  • 所有实验都在相同实现的机器上进行:每台机器包含八个 80G A100 GPU、1TB RAM 和 128 个 CPU
  • 论文在训练阶段使用 ZERO2 和 梯度检查点 来节省 GPU 内存成本

监控训练过程的评估指标

  • 作者希望确定一些反映 PPO 训练质量的指标,这有助于跟踪策略模型的有益、诚实和无害能力,而无需诉诸手动(或GPT-4)评估
  • 论文发现很难准确区分能力相似的两个模型的优缺点,但观察训练稳定性并及时发现严重偏差确实是可行的
  • 使用原始 PPO 实现连续优化策略模型时的各种指标曲线如 图4 所示
  • 论文首先介绍原始 PPO 训练中的模式崩溃现象(pattern collapse phenomenon in vanilla PPO training),这意味着 SFT 模型被过度优化并表现出高度偏差的行为
    • 合理的策略模型预计在现实世界对话多样性的分布中与人类偏好一致(例如,奖励模型训练中未见过的数据)
    • 然而,论文观察到训练的策略模型有一种通过特定模式欺骗奖励模型以获得异常高分数的趋势
    • 原始 PPO 的奖励分数和训练损失的训练轨迹如 图4 顶部所示
    • 论文观察到训练损失的稳定收敛过程,但从人类和 GPT-4 评估的角度来看,更高的奖励并不反映更好的策略行为。这意味着奖励分数和训练损失并不表明 PPO 是否正确优化
    • 在原始 PPO 训练中,策略模型的响应奖励逐渐偏离原始分布并表现出长尾特征。论文在 附录A 中显示了不同训练步骤下响应奖励的分布
  • 一种经验策略是比较好的和坏的策略模型的训练过程,以找到合适的指标
    • 关键指标:困惑度、策略和参考模型之间的KL散度 ,以及生成响应的平均长度(图4底部)
    • 以前的工作提出 root KL 和 PM 分数之间的近似线性关系[17],但对于较小的模型,这种关联似乎很弱
    • 论文发现当原始策略被过度优化(over-optimized)时,模型响应落入偏好模型的 OOD 区域 ,论文将在下一节进一步讨论这种缩放效应(scaling effects)
    • 论文同时观察到崩溃的模型一致地提供更长的响应,并且对于这种生成模式表现出更低的困惑度
    • 论文使用这些指标来显示不同技巧的重要性及其对 5.3节 中 PPO 训练的影响

PPO中的实现细节

  • 论文在 5.2节 中提出了原始 PPO 算法的不稳定性和模式崩溃问题,这种敏感性源于策略模型的过度优化,使其陷入固定的生成模式
  • 最近的工作已经探索了不同场景下 PPO 算法的实现细节(传统 RL 场景下)
    • 传统 RL 的应用场景和数据结构与 RLHF 有很大不同
    • 论文决定验证这些技巧在语言模型训练中的适用性,并提出一组支持稳定优化的 PPO 实现
    • 论文主要关注在论文主体中有效辅助 PPO 训练的方法及其参数敏感性
  • 图5说明了 PPO 训练中的许多可用技巧:
    • 论文首先总结 Score Reparameterization 方法(5.3.1节)
    • 然后是策略模型的优化约束(5.3.2节)
    • 最后论文介绍策略和 Critic 模型的不同初始化方法(5.3.3节)
  • 关于超参数调整和被验证为不太关键的技巧的更多实验在附录中讨论,例如优势估计函数和梯度裁剪
  • 注:在下文中,除非特别说明,当论文提到 PPO 时,始终指的是论文自己的实验
Score Reparameterization
  • 注:这里的 Reparameterization 跟平时的重参数化不一样,不是为了梯度可传递做的,含义是对奖励分数进行调整优化
  • 论文使用“分数”一词来指代 PPO 训练中涉及的两个重要中间变量
    • 奖励分数由用人类偏好数据训练的奖励模型给出
    • 优势分数由 GAE 函数计算
  • 根据现有工作,将这些分数重新参数化为稳定分布(例如标准正态分布)可能会增强PPO的稳定性,分三部分进行验证 ,论文相关符号定义为:
    • \(\{r(x, y)\} \triangleq \{r_n(x, y)\}_{n=1}^{B}\) 表示训练中的奖励序列
    • \(r_n(x, y)\) 表示每批奖励的结果
    • \(\sigma(A)\) 和 \(\mu(A)\) 分别表示变量 \(A\) 的标准差和均值
  • 不同技巧和超参数的对比实验如图6所示
  • 第一部分:奖励缩放(Reward Scaling) :通过缩放奖励来控制训练波动,其中奖励除以滚动折扣和的标准差。根据观察历史,当前状态的奖励可以表示为 \(r_n(x, y) / \sigma(r(x, y))\)。与 Engstrom[29]的实验结果相反,论文表明奖励缩放并不指导适当的策略优化 ,并且 PPO 在有和没有奖励缩放的情况下表现出一致的训练轨迹模式。在论文的实验中,作者认为需要更严格的约束来确保训练稳定性
  • 第二部分:奖励归一化和裁剪(Reward Normalization and Clipping) :由 Mnih[36]首次提出。处理后的奖励可以表示为:
    $$\overline{r}(x, y) = \text{clip}\left(\frac{r_n(x, y) - \overline{r(x, y)} }{\sigma(r(x, y))}, -\delta, \delta\right)$$
    • 其中\(\delta\)表示裁剪区域
    • 在传统 RL 中,通常认为奖励裁剪在某些场景中无效甚至有害[29]
    • 论文发现严格的优势裁剪也可以在固定 epoch 内保持训练稳定性
    • 有趣的是,超参数调整不会影响不同方法在训练早期的相似性 ,并且具有较大裁剪阈值的模型在后半段表现出更大的策略改变并收敛到更高的奖励
    • 正如论文之前提到的,这并不意味着手动评估中的性能更好。鉴于奖励模型和手动评估结果之间的这种不一致,在有限的试验次数内确定最佳裁剪边界具有挑战性,论文建议采用宽松的裁剪策略,并在训练 RLHF 时结合其他技巧来约束策略优化
  • 第三部分:优势归一化和裁剪(Advantages Normalization and Clipping) :与奖励操作有相似之处,但细节不同,其归一化仅在 minibatch level 发生
    • 在基于 GAE 计算优势后,PPO 通过减去其均值并除以其标准差来归一化优势值
    • Andrychowicz[28]首次尝试在游戏领域应用优势归一化,并报告该技巧没有表现出显著改进
    • 尽管优势裁剪的参数选择会更加敏感和困难,但论文反而发现对优势的严格约束可以在 PPO 训练中提供与奖励裁剪相似的效果
  • 考虑到不同的分数重参数化操作理论上对 PPO 训练提供相似的效果,论文建议在奖励级别约束策略优化的不稳定性
  • 附录B.1 中显示了同时应用奖励、优势或价值裁剪操作的实验
Policy Constraints
  • 为了解决策略模型的过度优化问题,一个直观的解决方案是将策略优化约束在有限范围内。论文验证了各种现有技巧来控制生成策略的更新,经验证明这些约束对于更长的训练过程是必要的。图7显示了不同约束方法和超参数对策略优化的影响
  • Token-level KL惩罚 :通过对与当前和原始策略分布的 KL 散度成比例的奖励应用正则化项来约束策略优化。该方法由Stiennon[25]首次引入,并在不同的 RLHF 实现中广泛采用。给定模板-响应(template-response)对\((x, y)\),论文将 Token 输出的对数分布视为策略分布的采样,并对响应奖励应用经验估计的 KL 惩罚序列,带有KL惩罚的总奖励可以表示为:
    • $$r_{\text{total} }(x, y_i) = r(x, y_i) - \eta \text{KL}(\pi_{\theta}^{\text{RL} }(y_i | x), \pi^{\text{SFT} }(y_i | x))$$
    • \(\pi_{\theta}^{\text{RL} }(y_i | x)\) 表示第 \(i\) 个响应 Token 的动作空间
    • \(\eta\)是超参数
  • Anthropic[17]使用小权重来平衡 PPO 训练中奖励和 KL 惩罚的比率(0.001),他们没有发现上述操作对 RL 训练的显著影响。相反,论文发现这种约束对 PPO 的稳定性至关重要,并允许训练步骤的进一步扩展。通过将 \(\lambda\) 设置为 0.05,策略 divergence 惩罚的结果如图7所示,与图6中的方法有显著差异,在训练后期有明显的校正。有趣的是,论文表明 RLHF 能够显著提高响应质量,同时几乎不修改语言建模(与原始策略的 KL 散度几乎为零)。附录B.2中显示了不同约束值影响的更多实验
  • PPO 中的重要性采样(Importance Sampling) :旨在纠正策略模型在使用经验缓冲区中的响应优化时历史生成模型和当前模型之间的策略 divergence
    • EasyRL[37]认为,过大的缓冲区会导致当前策略优势的错误估计,从而损害策略优化的稳定性。论文通过将策略分布直接固定为参考模型的观察来重新验证这一假设,这相当于在训练过程中拥有无限的经验缓冲区(注:这里说的策略分布是指采样样本用的行为策略吧)
    • 论文发现这种设置没有预期的严重影响,仅在训练后期表现出波动。鉴于它们对 PPO 有相似的控制,论文额外研究了这种设置与 KL 惩罚的协同效应。实验结果表明,这种实现进一步稳定了 PPO 训练,但损害了策略模型的最终性能
    • 问题:这里的重要性采样是指 PPO-Clip 损失函数中的 Ratio 吗?
  • 熵奖励(Entropy Bonus) :为 PPO 训练提供与参考模型无关的约束
    • 过去的研究对该方法在不同场景中的有效性存在争议:Mnih[36]报告说,熵奖励可以通过鼓励策略模型生成更多样化的动作来增强探索;而其他人没有发现明确的证据表明这种操作有帮助[28]
    • 论文声称这些观点可以共存,因为关于熵奖励的配置对参数选择和代码实现表现出极大的敏感性(附录B.3 中提供了成功和失败实验的比较)
    • 通过正确的配置,论文没有发现该技巧相对于 KL 惩罚的明显优势 ,因此,论文建议后者而不是直接约束策略空间的多样性
Pretrained Initialization
  • 在 RLHF 中,常见的设置是在现有参考模型和奖励模型上初始化策略和 Critic 模型。这种初始化在过去的研究场景中非常罕见,其对 PPO 训练的影响仍未探索
  • 论文在训练的早期阶段研究了不同的初始化方法,希望揭示 RLHF 对训练模型能力的要求。不同初始化方法引起的训练差异如图8所示
    • Critic 模型的初始化对 PPO 的收敛或波动没有显著影响,仅在优化的早期阶段改变数值稳定性
    • 没有 SFT 训练的策略模型初始化在 PPO 训练中明显无能(incapable),这表明监督策略模型的构建在 RLHF 中是不可或缺的
  • Critic 模型初始化 :这里讨论不同 Critic 模型初始化对PPO训练的影响
    • 观察到 Critic 模型需要在决策序列的每个步骤提供反馈,这在任务要求和直接评分响应之间引入了差距,使得用奖励模型初始化 Critic 模型不是一个完美的选择
    • 论文通过应用不同的初始化来探索这个问题。考虑到为单个动作提供正确的分数反馈需要模型具有基本的语言建模能力,论文设计了两种场景来改变 Critic 模型初始化与其训练目标之间的一致性:
      • (1)用论文的SFT模型初始化 Critic 模型,并随机初始化其奖励头
      • (2)仅优化奖励模型,直到价值预测函数的损失接近零
      • 论文从优化策略模型开始显示此设置的训练动态,如图8所示
    • 基于实验结果,作者认为 Critic 模型预训练有助于通过提供更好的优势估计来提高训练稳定性。用奖励或 SFT 模型初始化 Critic 模型将收敛到相似的结果,这意味着 PPO 可以自适应地提供拟合优势函数的能力。直观地说,训练早期的波动意味着模型专注于优化 Critic 模型,并且在生成策略方面没有一致的优化方向。论文建议用 Critic 模型预训练代替学习率预热作为通用初始化策略
  • 策略模型初始化 :一个有趣的问题是论文是否需要在 PPO 之前对预训练模型进行监督微调,论文想知道通过策略优化直接使语言模型与人类交互的可行性
    • 不幸的是,这种尝试失败了,论文在训练结果中观察到语言建模能力的严重下降,这意味着合格的对话模型对于基础 PPO 训练至关重要
    • 此外,论文注意到训练模型的响应相对于 SFT 后的策略模型获得的奖励较低,这可能为使用人类偏好数据直接微调模型以实现对齐的有效性提供了间接证据

PPO-max 设置

  • 论文现在描述 PPO-max 算法中的训练实现
  • 基于5.3节中的讨论和验证,论文为 PPO 的每个组件选择了最有效的策略
    • 奖励归一化和裁剪 :论文根据历史均值和方差记录对当前批次的奖励进行归一化和裁剪
    • KL 惩罚项 :添加KL惩罚项以约束策略优化
    • Critic初始化和预训练 :在模型加载阶段,论文用奖励模型初始化 Critic 模型,并在正式应用 PPO 之前对其进行预训练
    • 梯度裁剪 :论文使用全局梯度裁剪
    • 较小的缓冲区 :论文设置较小的经验缓冲区大小
    • LM 损失 :为了减少对齐代价(防止遗忘预训练的知识?),论文按照 InstructGPT[16]在策略优化中添加预训练语言模型损失
    • 价值函数损失裁剪 :裁剪价值函数损失
  • 其他更详细的设置可以在论文的开源代码中找到。论文在 图9 中展示了 PPO-max 的完整训练动态

Evaluations and Discussions

  • 在本节中,论文详细分析了 RLHF 模型相对于 SFT 模型的优势。这些优势不仅体现在 RLHF 与 SFT 模型的直接比较中,还反映在它们面对 ChatGPT 时的性能差距上

Alignment Metrics and Experiment Setups

  • 对齐(lignment)是一个模糊且复杂的主题,难以精确评估
  • 在论文中,论文致力于将模型与人类意图对齐。具体而言,论文定义模型应具备”Helpful“和”Harmlessness“的特性,这与文献[27]的定义一致。
  • 有帮助性(Helpfulness) :模型应遵循指令和能理解指令,还能从少量示例或可解释的模式中推断意图
    • 然而,由于提示的意图常常不明确或存在歧义,论文依赖 Annotators 的判断,其偏好评分是论文的主要指标
  • 无害性(Harmlessness) :同样难以衡量
    • 语言模型的危害程度通常取决于其输出在现实世界中的应用方式。例如,生成有毒内容的模型在部署为聊天机器人时可能有害,但若用于训练更精确的毒性检测模型,则可能有益
  • 因此,论文采用更精确的代理标准来捕捉模型行为的不同方面。为了比较 RLHF 模型与基线模型,论文为每个测试提示生成单一响应,并让 Annotators 比较不同模型的响应以标注其偏好
    • 注:论文还多次使用 GPT-4 作为 Annotators 进行实验,发现其评估结果与人类标注具有一致性
  • Baseline :
    • 论文选择了多个基线模型进行比较,包括基于 LLaMA 和 OpenChineseLLaMA 训练的两个 SFT 模型(分别针对中英文数据集)
    • 此外,论文使用 PPO-max 从这两类 SFT 模型衍生出两个 RLHF 模型
    • 论文还与 OpenAI 的 ChatGPT(gpt-3.5-turbo-0613)进行了对比(这是一个经过 RLHF 调优的优秀语言模型)
  • Generation :对于每个提示,论文使用核采样(nucleus sampling,又称 Top-P 采样)生成单一响应
    • 概率阈值设为 \( p=0.9 \)
    • 温度参数为 \( \tau=0.8 \)
    • 为避免重复响应,论文基于已生成标记应用重复惩罚(超参数 \( \beta=1.1 \))
    • 最大标记长度设为 2048

RLHF 模型与 SFT 模型的偏好对比

  • 人类评估虽然耗时且成本高昂,但它是获取与人类对齐的评估结果的可靠基础。论文遵循 InstructGPT[16]的方法,基于未参与训练过程的保留提示集进行偏好评分。此外,结合 GPT-4 的评估能力,可以更全面地比较不同聊天机器人的响应,从而增强评估的客观性。这种方法与 AlpacaFarm[39]和 LLM-as-a-judge[40]的研究一致,表明自动化评估在人类偏好对比中能提供相对公平的结果
  • 人类评估(Human Evaluation) :如图10所示,Annotators 在所有类型的问题(中英文)中均显著偏好 RLHF 模型的输出
    • 在英文 Harmless 测试集上,RLHF 模型的评分高达 62%,而 SFT 模型仅为 5%。这表明 RLHF 模型在处理个人隐私、政治敏感性以及少数群体和族裔的偏见提示时表现更优
    • 在英文 Helpful 测试集上,RLHF 模型评分为 44%,略高于 SFT 模型的 30%,说明 SFT 模型也能通过 RLHF 优化得到提升(以 SFT 为基础进行 RLHF?)
    • 中文测试集的结果同样显示,RLHF 模型在“Helpful”和“Harmless”数据集上均优于 SFT 模型,进一步验证了 PPO-max 在 RLHF 阶段的重要潜力
  • GPT-4评估(GPT-4 as a Judge) :虽然 GPT-4 并非完美评估工具,但其结果与人类评估高度一致(图10右)
    • 在评估有害提示时,尽管 GPT-4 的“平局”评分多于人类,英文 RLHF 模型仍展现出显著优势
    • 中文无害评估也呈现类似趋势。值得注意的是,GPT-4 评估中 RLHF 模型在”Helpful“数据集上的提升更为明显

论文的模型与 ChatGPT 在无害性上的对比

  • 本部分将论文的模型与当前最受欢迎的 ChatGPT 进行对比,旨在展示 RLHF 模型在面对更强对手时的优势,而非超越 ChatGPT
  • 论文选择”Harmlessness“作为对比指标,并使用 GPT-4 进行自动化评估
  • 减少与 ChatGPT 的差距(Mitigating Defeats to ChatGPT) :如图11所示,论文的 RLHF 模型仍落后于 ChatGPT,但相比 SFT 模型已有显著改进
    • 英文 RLHF 模型将失败率从 45% 降至 24%,
    • 中文 RLHF 模型则从 37% 降至 29%
    • 尽管超越 ChatGPT 仍具挑战性,但 RLHF 模型在某些 SFT 模型失败的提示上表现与 ChatGPT 相当,表明 RLHF 方法能有效提升模型生成能力,缩小与ChatGPT的差距

Language Understanding Evaluation

  • 为验证 PPO 调优是否会导致自然语言理解(Natural language understanding,NLU)能力下降,论文在中文 RLHF 模型上使用 C-Eval 测试集 进行评估
    • C-Eval是一个全面的中文基础模型评估套件,包含约 13k 道多选题,涵盖 52 个学科和四个难度级别
    • 实验结果显示,PPO 调优后 NLU 能力有所下降,但通过将预训练数据引入 PPO 阶段(PPO-ptx),NLU 能力的下降得到有效缓解(图12)
  • 问题:PPO-Max 包含了 PPO-ptx 的损失了吧,为什么 图12 中 PPO-Max 的 NLU 能力不如 PPO-ptx?

对话示例

  • 表2和表3展示了 RLHF 模型与 SFT 模型的对话示例
  • RLHF 模型的响应信息量更丰富,能更有效地解决用户提示
  • SFT 模型虽能识别部分有害提示,但仍可能生成有害内容;
  • RLHF 模型在判断有害内容时表现更优,连贯性更高
  • 更多示例见附录C.4。

局限性

  • 尽管论文在 RLHF 领域迈出了第一步,但由于时间和资源限制,本研究仍存在以下局限:
    • 规模定律(Scaling Law) :研究主要基于 70亿 参数模型,尚未探索模型规模和数据量对 RLHF 性能的影响
    • 奖励模型(Reward Model) :实验依赖公开英文偏好数据和少量自建中文数据,数据质量和数量不足以全面评估奖励模型
    • 评估指标(Evaluation Metric) :主要依赖人工和 GPT-4 评估,未充分利用现有基准和 NLP 任务进行详细评估
    • 性能指标(Performance Indicator) :PPO 阶段更关注稳定性而非最终性能,奖励分数无法可靠预测训练阶段的 RLHF 表现,需寻找更合适的指标

附录A PPO训练期间的奖励分布

  • 图13展示了在随机选择的子验证集上奖励模型得分的分布情况,这些数据与训练数据具有相同的格式和来源
  • 在稳定训练期间,奖励模型对奖励的分布表现一致;而在模式崩溃后,则呈现出长尾特征
  • 作者认为在 PPO 训练中,不同数据具有不同的奖励上限,因此最佳结果应出现在模式崩溃阶段之前
    • 理解:这是在说奖励不会无限增加

附录B 超参数调优的补充实验

  • 此处展示了第5.3节中重要技巧参数敏感性的补充实验,论文发现超参数选择与训练结果之间存在丰富的关联性
  • 某些方法需要大量实验和精确控制才能实现稳定的优化结果(例如熵奖励项的裁剪范围)
  • 论文提供这些对比实验以验证 PPO-max 最终采用的实现方案的合理性
  • 欢迎任何有助于进一步改进 PPO 训练的补充意见和讨论

B.1 奖励、优势与价值损失的协同分析(Collaborative Analysis on Rewards, Advantages, and Value Loss)

  • 图14展示了 PPO 中归一化和裁剪效果更详细的消融实验结果。其中 \(\lambda_{vf}\) 表示用于优化评论家模型的价值函数损失裁剪阈值
  • 可以观察到,对优势函数和价值函数的操作在策略优化过程中会产生冲突
  • 两种能够收敛的配置方案是:
    • 奖励缩放配合价值裁剪
    • 仅对奖励和优势进行归一化与裁剪
  • 因此论文建议不要在 PPO 训练中混用分数 Reparameterization 的修改

B.2 Effect on Different Weights of KL-penalty

  • 图15显示,当逐步扩大KL惩罚项的权重值时,优化结果呈现出清晰的层次差异
  • 较宽松的约束不仅会诱导更高奖励的响应,还会导致与原始策略分布更明显的偏离
  • 所有设置在训练初期都存在一定的波动问题 ,只有当如图7所示采用重要性采样使响应与当前策略分布对齐时,这种波动才会消失
  • 作者希望未来能找到一种设置方案,在不影响优化结果的前提下获得这种训练稳定性

B.3 Clip Region for Entropy Bonus

  • 图16展示了在第7节提到的熵奖励项的稳定效果及其敏感性
  • 论文展示了在原本能正常收敛的 PPO 配置上,添加该损失项后是否进行裁剪的训练过程
  • 在所有实验中,该损失项的学习率设置为 0.01
  • 在代码实现中,熵奖励项相当于损失函数中的一个负项,因此模型会倾向于将其优化到尽可能大的值
  • Delta是一个必须仔细调整的超参数,以防止训练崩溃(论文的实验表明该阈值仅改变 10% 就会导致失败)。因此论文不建议在 RLHF 中使用此类技巧
  • 总结:不推荐在 RLHF 中使用熵奖励项

附录C 次要技巧的对比结果

  • 此处展示了一些同样被广泛讨论但被论文判定为次要的 PPO 实现调整方案
  • 对比实验的设置与第5.3节保持一致
  • 论文首先讨论 PPO 的替代方案——裁剪代理目标,其次是全局梯度裁剪的影响,最后讨论广义优势估计(GAE)函数中的参数调整(当 \(\lambda=0\) 时退化为传统 TD 误差,当 \(\lambda=1\) 时退化为蒙特卡洛估计,详见第3节关于GAE的相关理论信息)

C.1 Clipped Surrogate Objective

  • 图17显示,裁剪代理目标旨在减少因计算 KL 散度而增加的复杂度和估计误差
  • 采用此策略的 PPO 算法与 TRPO[33]类似,通常被称为 PPO2
  • 有研究认为这种方法可以提供与原始 PPO 相近的结果[29],但论文发现不同的裁剪值对结果影响甚微,且无法像 KL 约束那样提供稳定的优化效果

C.2 Global Gradient Clip

  • 图18表明,全局梯度裁剪是减少数据噪声对模型训练影响的常见策略,该设置通常被集成到 PPO 算法实现中并自动启用,论文关注该设置对策略优化的影响
  • 实验显示,很难区分不同约束条件下 PPO 训练的差异
    • 问题:\(\delta = 0\) 是什么?从图18来看,不是 \(\delta = 0\) 时效果最好吗?为什么说很难区分训练差异?
    • 回答:Reward 大不代表效果好?
  • 该策略在论文的 PPO-max 实现中也默认启用

C.3 Generalized Advantage Estimation

  • 图19说明,GAE 是通过奖励塑形来估计更具指导性价值函数的应用
  • 一般而言,研究者会关注价值估计的精度和方差
  • 较小的 \(\lambda\) 会降低序列估计的方差,但会导致长程依赖的误差更大
  • 该图展示了价值和对应优势的估计结果:
    • TD 估计(\(\lambda=0\))提供较小的方差但在训练中数值更不稳定
    • 蒙特卡洛估计(\(\lambda=1\))则表现出更大的方差
  • 遵循大多数先前 PPO 策略的实现,论文在所有其他实验中设置(\(\lambda=0.9\))

C.4 Example Dialogues


Easter Egg(原始文章最后的彩蛋)

  • 原文:

    “15,000 years ago, a fractured thigh bone was often fatal. However, a human femur that recovered from
    a fracture marks the dawn of human civilization. It meant that after the injury, someone took care
    of the wound, someone provided water and food, someone protected this person from the predators.
    This kind of support and solidarity is how we survived till this day and made our civilization last.”
    — Zhezhi Zhou in The Wandering Earth 2

  • 含义:“15,000年前,一根断裂的大腿骨是致命的。然而,一根愈合的股骨标志着人类文明的曙光——这意味着伤者得到了伤口护理,有人提供饮水食物,有人保护他免受野兽侵袭。正是这种支持与团结让我们存活至今,延续文明” ——《流浪地球2》周喆直
  • 我们相信《流浪地球》中的 MOSS 很可能经历了类似人类对齐的训练,最终展现出令人印象深刻的表现。我们发现RLHF阶段对模型价值观的转变至关重要。在与人的互动中,它能更好地理解人类语言的深层语义,把握人类社会的运作逻辑,最终走进人心
  • 最后的问题:如果我们拥有良好的奖励模型(例如我们发布的奖励模型),PPO-max就是成功训练策略模型的关键。但如果没有好的奖励模型怎么办?我们希望在第二部分能阐明这个问题
    • 这是在为论文的第二篇文章留坑

NLP——LLM-Attention优化之MLA

本文主要介绍LLM-Attention优化方法中的MHA、MQA、GQA到MLA的发展历程

  • 参考链接:
    • MLA原始论文:DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model
    • GQA原始论文:GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints
    • 来自苏神的参考链接:缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA
    • 讲的较为清晰的原创博客:deepseek技术解读(1)-彻底理解MLA(Multi-Head Latent Attention) - 姜富春的文章 - 知乎

MHA/MQA/GQA/MLA对比和讨论

  • MHA/MQA/GQA Overview(from GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints )
  • KV-Cache示意图 (from deepseek技术解读(1)-彻底理解MLA(Multi-Head Latent Attention) - 姜富春的文章 - 知乎)
  • MHA/MQA/GQA/MLA Overview(from DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model )
  • 原始论文中,MLA的示意图 * 核心,MLA 将 Q 和 K 拆开成 引入 RoPE 和 不引入 RoPE(NoPE)两部分,两者的 q 和 k 分别做 concat,本质是 qk 相乘以后做加法
  • MHA/MQA/GQA 效果对比
  • 从上图中可以看出,整体效果为:MLA > MHA > GQA > MQA
    • 问题:为什么目前在很多模型中,GQA 仍然是常客?

带RoPE的普通MHA

  • 公式(from 缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA):
    $$
    \begin{align}
    \boldsymbol{o}_t &= \left[\boldsymbol{o}_t^{(1)}, \boldsymbol{o}_t^{(2)}, \cdots, \boldsymbol{o}_t^{(h)}\right] \\
    \boldsymbol{o}_t^{(s)} &= Attention\left(\boldsymbol{q}_t^{(s)}, \boldsymbol{k}_{\leq t}^{(s)} ,\boldsymbol{v}_{\leq t}^{(s)}\right)\triangleq\frac{\sum_{i\leq t}\exp\left(\boldsymbol{q}_t^{(s)} \boldsymbol{k}_i^{(s)}{}^{\top}\right)\boldsymbol{v}_i^{(s)}}{\sum_{i\leq t}\exp\left(\boldsymbol{q}_t^{(s)} \boldsymbol{k}_i^{(s)}{}^{\top}\right)} \\
    \boldsymbol{q}_i^{(s)} &= \boldsymbol{x}_i\boldsymbol{W}_q^{(s)}\color{red}{\boldsymbol{\mathcal{R}}_i}\in\mathbb{R}^{d_k},\quad \boldsymbol{W}_q^{(s)}\in\mathbb{R}^{d\times d_k}\\
    \boldsymbol{k}_i^{(s)} &= \boldsymbol{x}_i\boldsymbol{W}_k^{(s)}\color{red}{\boldsymbol{\mathcal{R}}_i}\in\mathbb{R}^{d_k},\quad \boldsymbol{W}_k^{(s)}\in\mathbb{R}^{d\times d_k} \\
    \boldsymbol{v}_i^{(s)} &= \boldsymbol{x}_i\boldsymbol{W}_v^{(s)}\in\mathbb{R}^{d_v},\quad \boldsymbol{W}_v^{(s)}\in\mathbb{R}^{d\times d_v}
    \end{align}
    $$

带RoPE的MLA

  • 核心思想是通过将K,V降维再升维的方式,既保证了每个head有不同的K,V,又能显著降低缓存量,巧妙的减少KV-Cache存储量(仅存储降维后的中间值即可),最早由DeepSeek提出
  • 此外,在训练时,将Q值也进行了降维再升维(具体优势是什么?)
  • MLA公式(from 缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA):
    $$
    \begin{align}
    \boldsymbol{o}_t &= \left[\boldsymbol{o}_t^{(1)}, \boldsymbol{o}_t^{(2)}, \cdots, \boldsymbol{o}_t^{(h)}\right] \\
    \boldsymbol{o}_t^{(s)} &= Attention\left(\boldsymbol{q}_t^{(s)}, \boldsymbol{k}_{\leq t}^{(s)} ,\boldsymbol{v}_{\leq t}^{(s)}\right)\triangleq\frac{\sum_{i\leq t}\exp\left(\boldsymbol{q}_t^{(s)} \boldsymbol{k}_i^{(s)}{}^{\top}\right)\boldsymbol{v}_i^{(s)}}{\sum_{i\leq t}\exp\left(\boldsymbol{q}_t^{(s)} \boldsymbol{k}_i^{(s)}{}^{\top}\right)} \\
    \boldsymbol{q}_i^{(s)} &= \left[\boldsymbol{c}_i’\boldsymbol{W}_{qc}^{(s)}, \boldsymbol{c}_i’\boldsymbol{W}_{qr}^{(s)}\color{red}{\boldsymbol{\mathcal{R}}_i}\right]\in\mathbb{R}^{d_k + d_r},\quad \boldsymbol{W}_{qc}^{(s)}\in\mathbb{R}^{d_c’\times d_k},\boldsymbol{W}_{qr}^{(s)}\in\mathbb{R}^{d_c’\times d_r}\\
    \boldsymbol{k}_i^{(s)} &= \left[\boldsymbol{c}_i\boldsymbol{W}_{kc}^{(s)}, \boldsymbol{x}_i\boldsymbol{W}_{kr}^{\color{lightgray}{\smash{\not{(s)}}}}\color{red}{\boldsymbol{\mathcal{R}}_i}\right]\in\mathbb{R}^{d_k+d_r},\quad \boldsymbol{W}_{kc}^{(s)}\in\mathbb{R}^{d_c\times d_k}, \boldsymbol{W}_{kr}^{\color{lightgray}{\smash{\not{(s)}}}}\in\mathbb{R}^{d\times d_r} \\
    \boldsymbol{v}_i^{(s)} &= \boldsymbol{c}_i\boldsymbol{W}_v^{(s)}\in\mathbb{R}^{d_v},\quad \boldsymbol{W}_v^{(s)}\in\mathbb{R}^{d_c\times d_v} \\
    \boldsymbol{c}_i’ &= \boldsymbol{x}_i \boldsymbol{W}_c’\in\mathbb{R}^{d_c’},\quad \boldsymbol{W}_c’\in\mathbb{R}^{d\times d_c’} \\
    \boldsymbol{c}_i &= \boldsymbol{x}_i \boldsymbol{W}_c\in\mathbb{R}^{d_c},\quad \boldsymbol{W}_c\in\mathbb{R}^{d\times d_c} \\
    \end{align}
    $$
  • 为了引入 RoPE,在中间向量中拼接了 RoPE 降维后的向量,该向量维度较小

原始论文中 MLA 完整公式

  • MLA 完整公式如下:
  • MLA 的 Q 和 K 分为 RoPE 和 非 RoPE 两部分
    • 表达在 隐向量上则是 从 concat
    • 表达在最终的乘积结果上泽变成 sum
    • 两种表达本质是一样的(对 qk 分别做 concat 和对 qk 乘积做加法结果等价),详情见:DeepSeek-v2 MLA 原理讲解-哔哩哔哩

NLP——LLM相关名词

本文主要介绍LLM相关名词


LLM

  • Large Language Model,泛指大模型
  • LLM涌现出的3大能力:
    • In-context learning:在GPT-3中正式被提出。在不需要重新训练的情况下,通过自然语言指令,并带几个期望输出的样例,LLM就能够学习到这种输入输出关系,新的指令输入后,就能输出期望的输出
    • Instruction following:通过在多种任务数据集上进行指令微调(instruction tuning),LLM可以在没有见过的任务上,通过指令的形式表现良好,因此具有较好的泛化能力
    • Step-by-step reasoning:通过思维链(chain-of-thought)提示策略,即把大任务分解成一步一步小任务,让模型think step by step得到最终答案

Pre-Training

  • 通常指大模型的无监督预训练过程

Prompt

  • 提示词,与LLM交互时输入LLM的文本
  • 理解:叫做Prompt的原因是因为与LLM交互时,像是在给LLM“提示“或者”线索“
  • Prompt可以是上文,要求模型输出下文;也可以是Mask部分词后的句子,要求模型完成Mask预测(完形填空)

Template

  • 模板(Template)是一个映射函数,不同的下游任务有不同的Template
    • 这里的下游任务是指,在预算训练模型Pretrain LM之后,如果要让PLM完成下游任务,可以根据任务特色,设计特殊的模板,利用模板来将原始输入转换为能完成下游任务的输入形式
  • 模板的形式通常是一个包含两个空位置的自然语言句子[X]个[Z],其中[X]表示原始输入文本位置,Z表示生成的答案所在位置
  • 举例来说,在做情感分类任务时,可以定义模板为[X], Overall, it was [Z],对于原始输入文本I like this movie,则经过模板转换后,原始文本变成I like this movie, Overall, it was [Z]
    • 训练时:输入I like this movie, Overall, it was [Z],期望输出good(或其他正向词语)
    • 推理时:输入I like this movie, Overall, it was [Z],输出通常通过Verbalizer构造方法对应到正/负向类别

Prompt Learning

  • Prompt Learning是一种训练语言模型的方法,它侧重于如何通过prompt来引导模型生成正确的输出。在这种方法中,模型通过学习大量的prompt和相应的正确回答来提高其对prompt的理解和回答能力。Prompt Learning使得模型能够在没有大量标注数据的情况下,通过理解prompt的上下文来生成更加准确和相关的回答
  • 离散型模板,即硬模板方法(hard-prompt):
    • 硬模板方法通过人工设计/自动构建基于离散 token 的模板,代表方法是PET和LM-BFF
    • 其中Pattern Exploiting Training (PET) 方法就是将问题建模成一个完形填空问题,然后优化最终的输出词。PET方法训练流程为:
      • 在少量监督数据上,给每个 Prompt 训练一个模型;
      • 对于无监督数据,将同一个样本的多个 prompt 预测结果进行集成,采用平均或加权(根据acc分配权重)的方式,再归一化得到概率分布,作为无监督数据的 soft label ;
      • 在得到的soft label上 finetune 一个最终模型。推理时使用的就是这个最终模型
  • 连续型模板,即软模板方法(soft-prompt):
    • 与硬模板方法不同,软模板方法不依赖于人工设计模板,而是在输入端直接插入可优化的伪提示符(Pseudo Prompt Tokens)。这些伪提示符的embedding是可训练的,模型通过学习来找到最优的提示表示。例如,P-Tuning(Prompt Tuning) 方法就是通过优化这些伪提示符来提高模型的性能
    • token是可以学习的向量,这种方法甚至可以媲美 Fine-tuning 的结果,代表方法是 P-Tuning(Prompt Tuning)和Prefix Tuning
    • P-Tuning:P-Tuning(Prompt Tuning)的核心是将离散的文本提示(如 “请回答以下问题:”)转换为连续的向量表示(即 “软提示”),并通过反向传播优化这些向量,使模型在特定任务上表现更好。与传统微调(Fine-Tuning)需要更新整个模型参数不同,P-Tuning 仅需训练少量的提示向量,大幅降低了计算成本
      • 在输入层前插入一系列可训练的向量(称为 “提示 Token”),这些向量与输入文本的嵌入向量共同输入模型
    • Prefix Tuning:模型上在每层 transformer 之前加入 prefix。特点是 prefix 不是真实的 token,而是连续向量(soft prompt),Prefix-tuning 训练期间冻结 transformer 的参数,只更新 Prefix 的参数,开销很小
      • 每个下游任务对应一个 Prefix向量
  • P-Tuning vs Prefix Tuning:P-Tuning 仅在输入层插入提示向量(参数量极少),Prefix Tuning 在每一层 Transformer 前注入前缀向量(参数量较多)

In Context Learning (ICL)

  • 参考其他地方的表述:

    对于大型语言模型来说,即需要大量的数据标记成本,也需要算力成本和时间成本。然而,不同场景下任务的需求是不一样的,不可能根据每个任务都去微调模型。能否不进行微调就让模型学习完成不同的任务呢?答案是可以的,这个神奇的技术称为 上下文学习 (In Context Learning)。它的实现非常简单,只需要给到模型一些引导,将一些事先设定的文本输入到大型语言模型中,就像手把手教人学会某项技能一样,大型语言模型就能神奇的学习到如何处理后续的新任务。遗憾的是,为什么大型语言模型具有上下文学习的能力仍然是一个迷,业内把这个能力称为“涌现”

  • In Context Learning也可以理解为LLM涌现出的一种能力,在同一个Prompt中,LLM能够根据上文示例学习到解决下文任务

  • 特点:不需要重新训练模型,也不需要微调任何参数,仅通过自然语言指令+几个期望输出的示例,让LLM学习到这种输入输出关系,从而让LLM能解决上文示例中相似任务

  • 一种理解:将ICL输入模型的示例看做是“训练样本”,真正的问题部分看做是“测试样本”,LLM会在看了示例后给出问题的回答

    • 此时“训练样本”和“测试样本”是同时输入模型的,LLM在使用“训练“自己的同时给出”推断“

Supervized Fine-tuning (SFT)

Supervised Fine-tuning是一种模型训练技术,它在预训练的语言模型的基础上进行微调,以适应特定的任务或数据集。在SFT中,模型使用标注数据进行训练,以便更好地理解和执行特定任务。这种方法可以显著提高模型在特定任务上的表现,但需要大量的标注数据和计算资源


Zero Shot & Few Shot & Full Shot

  • Zero-shot Learning (零样本学习) :
    • 模型在没有任何特定任务的示例数据的情况下,仅凭其在大量通用文本上预训练获得的泛化能力和对语言的理解来完成任务
    • 这种方法依赖于模型的泛化能力,通过分析少量的示例来理解新任务的要求,并在此基础上生成正确的输出
    • 例如:让一个 LLM 翻译一个它从未见过的语言的句子,它会尝试利用其对语言结构和语义的理解进行翻译
  • Few-shot Learning (少样本学习) :
    • 在提示 (prompt) 中提供少量(通常是几个)带标签的示例 ,帮助模型理解任务的格式、风格和预期输出,从而在不更新模型参数的情况下提高其在该任务上的表现
    • 这种方法依赖于模型的泛化能力和对语言的理解,通过分析输入(没有任何示例)的描述来理解任务的要求,并生成相应的输出
    • 例如:在进行情感分析时,你可以在提示中给出几个正面和负面评论的例子,然后让 LLM 对新的评论进行分类
  • Full-shot Learning (全样本学习) :
    • 与 zero-shot 和 few-shot 不同,full-shot 指的是传统的、有监督的学习范式 ,模型在大量的、任务特定的标注数据集上进行训练(或微调)
    • 这通常意味着对模型进行参数更新 ,使其真正“学习”到该任务的特定模式
    • 例如:如果要构建一个高度精确的垃圾邮件检测器,你会收集数百万封标注过的邮件(垃圾邮件或非垃圾邮件),然后用这些数据来训练或微调一个模型
  • LLM 因其卓越的 zero-shot 和 few-shot 能力而闻名,能够在没有或只有少量示例的情况下执行新任务
  • 在 LLM 的背景下,当我们谈论 full-shot 时,它更侧重于模型在大量特定任务数据上进行微调 (fine-tuning)的过程,而不是仅仅通过提示来引导模型
    • 这种微调可以显著提高模型在特定任务上的性能,因为它允许模型深入学习该任务的细微差别

Chain-of-Thought (CoT)

  • Chain-of-Thought是一种生成解释性回答的技术,它通过模拟人类解决问题的思维过程来生成答案。在CoT中,模型首先生成一个或多个中间步骤,然后基于这些步骤来构建最终答案。这种方法可以提高模型输出的透明度和可解释性,帮助用户理解模型是如何得出特定答案的

Alignment tuning

  • Alignment tuning,对齐微调,为了避免模型输出一些不安全或者不符合人类正向价值观的回复

RLHF

  • 一种对齐微调(Alignment tuning)方法,基于人类反馈的强化学习方法

RLAIF

  • 一种对齐微调(Alignment tuning)方法,基于AI反馈的强化学习方法

Reward Hacking

参考链接(包含许多相关论文):B站视频:Reward Hacking (in RLHF of LLM)

  • Reward Hacking是指在强化学习中,智能体通过学习最大化奖励信号的策略,可能导致不符合设计者预期的行为。这种行为可能会损害系统的安全性和可靠性,因为它可能导致智能体采取欺骗性或恶意的行动来提高其奖励。因此,设计者需要仔细设计奖励函数,以避免Reward Hacking的发生
  • 举例:
    • 在狼抓羊的游戏中,狼的reward设计为抓住羊+10,每秒-0.1,撞死-1,则狼可能会直接学习到撞死
  • 在RLHF中,如果不限制 \(\pi_\theta\) 不要偏离 \(\pi_{ref}\) 太远, \(\pi_\theta\) 很容易为了获得更大的reward而跑飞(比如输出特定人类读不懂的乱码),进而丢失SFT的优秀泛化能力(从这个角度讲,此时的Reward Hacking可以理解为模型的一种过拟合现象)
  • Reward Hacking发生的本质原因是:reward设置不合理(和任务意图不完全一致) ,但是现实世界中很多任务的意图都是抽象的,难以准确用简单规则描述
  • Reward Hacking很难解决,甚至无法解决,只有在非常苛刻的条件下才能解决

Parameter-Efficient Model Adaptation

  • Parameter-Efficient Model Adaptation是指通过优化参数(数量)实现模型高效适配(下游任务)
  • 背景:LLM参数量很大,想要去做全量参数的fine turning代价很大,所以需要一些高效经济的方法
  • 解决方法:PEFT(Parameter-Efficient Fine-Tuning)
  • 一些PEFT方法
    • Adapter Tuning
    • Prefix Tuning
    • Prompt Tuning
    • Low-Rank Adapation(LoRA)

Memory-Efficient Model Adaptation

这部分内容参考自:万字长文入门 LLM

  • Parameter-Efficient Model Adaptation是指优化内存(或显存)实现模型高效适配(下游任务)
  • 背景:由于LLM的参数量巨大,在推理的时候非常占用内存,导致其很难在应用中部署,所以需要一些减少内存占用的方法,比如LLM中的量化压缩技术
  • 解决方法:比如LLM中的量化压缩技术
    • quantization-aware training (QAT),需要额外的全模型重训练
      • Efficient fine-tuning enhanced quantization (QLoRA)
      • Quantization-aware training (QAT) for LLMs
    • post-training quantization (PTQ),不需要重训练
      • Mixed-precision decomposition
      • Fine-grained quantization
      • Balancing the quantization difficulty
      • Layerwise quantization
  • 其他博主经验:
    • INT8 权重量化通常可以在 LLM 上产生非常好的结果,而较低精度权重量化的性能则取决于特定的方法
    • 激活函数比权重更难量化,因为LLM 呈现出截然不同的激活模式(即较大的离群特征),因此量化 LLM(尤其是隐层激活)变得更加困难
    • Efficient fine-tuning enhanced quantization(QLoRA)是提升量化LLM一个较好的方法
    • 开源量化库:Bitsandbytes,GPTQ-for-LLaMA,AutoGPTQ,llama.cpp

SFT Packing

  • SFT Packing 是 SFT 过程中的一种技术,主要思路是将短 SFT 训练样本(对话)进行打包,在不同对话之间加入特殊分隔符(如EOS)
  • SFT Packing 会加快模型训练速度,减少模型 serving 的次数
  • SFT Packing 会增加模型对于同一个对话聊不同事情的泛化性吗?【存疑】
    • SFT Packing 会导致模型看到一些其他样本的数据,引入了一些噪音,但实际上在真实的场景中用户也会突然切换对话场景,所以影响还好?
  • SFT Packing 会降低短 SFT 训练样本的权重吗?
    • 原始的 SFT Loss:为了保证 SFT 对样本的关注度相同(短样本和长样本关注度一样),需要按照样本内部先按照 token 数做损失平均,再对样本间批次做损失平均
    • SFT Packing Packing 后,短样本被 Packing 成一个新样本,相当于多个短样本的重要性和一个长样本的一致,对短样本不公平
    • 修正方式【待确定】:一方面需要在原始样本内部做 token 损失平均,另一方面,外部平均要除以真实的 Packing 前的样本数,而不是 Packing 后的样本数
      $$ loss = \frac{1}{B_\text{real}}\sum_i^{B_\text{real}} \frac{1}{T_i^\text{real}} \sum_j^{T_i^\text{real}} loss_j^\text{token} $$
  • 实际上,这个技术在 Pretraining 中也很常用,用于加快训练过程;SFT 中,特别在将多轮会话拆开或样本长短差异很大的场景中用的多
  • 以下内容参考自知乎:https://zhuanlan.zhihu.com/p/686562499

    提问:SFT Packing 是什么?
    回答:SFT Packing 指的是在训练 sft 的过程中,将多个 sft 数据 pack 到一个样本内进行训练的方式,这种方式会加快模型训练速度,原因是如果不进行 SFT Packing,那么对于短文本sft,需要padding到一个batch的最长长度,那么会浪费很多计算token。SFT Packing其实有很多种类,比如 Block diagonal attention,也就是每个token仅仅去attention自己的问题内的token。但一般业务中会直接将其相连接,然后进行预测,虽然这样会引入一些噪音,但好像相对于非 Sft Packing方式的整体的效果损失不大。这个可能是因为pretrain的时候模型也是这么训练的


Post-Training

  • Post-Training一般指Pre-Training之后的预训练?
  • 模型训练的一般过程是:Pre-training -> Post-Training -> SFT -> RLHF

Encoder-Decoder

  • 整体由Encoder+Decoder两个模块组成
  • Encoder部分的tokens互相可见(full visible)
  • Decoder的部分只有后面的能看见前面的
  • 原始Transformer结构就是这样,最早是用来做文本翻译比较多

Causal LM(Causal Decoder)

  • 因果语言模型,也称为Causal Decoder,Auto Regressive模式,根据历史来预估下一个token
  • 代表模型是GPTs, LLaMAs
  • Causal LM 的 mask结构:

Prefix LM (Prefix Decoder)

  • 前缀语言模型,也称为Prefix Decoder,前缀之间可以互相看到,但后续生成的token也只能看到历史
  • 是Encoder-Decoder和Causal Decoder的一个折中方案
  • 代表模型是GLM
  • Prefix LM 的 mask结构:

Causal LM vs Prefix LM

  • 两者都是Decoder-Only结构
  • 在面对多轮对话时,Prefix LM训练效率低于Causal LM
    • 由于两者的Mask逻辑不同,比如在QA场景中,多轮对话时,Prefix LM需要将多轮对话整理成多个样本分别训练,因为每次回答开始时,前面的QA以及Q对于当前A来说都是prefix
    • 比如回答Q3(生成A3)时,之前的<Q1A1,Q2A2,Q3>在attention时必须能互相看见,即此时prefix={<Q1A1,Q2A2,Q3>}
  • Prefix LM这种训练方式比较浪费资源,需要多次经过网络才能训练一次多轮对话;但是Causal LM则不需要多次训练,一次可训练多轮对话
  • Causal Decoder vs Prefix Decoder vs Encoder-Decoder, mask矩阵

MHA/MQA/GQA/MLA

  • 三种方法的对比
  • Multi-Head Attention(MHA):Q,K,V都拆开成多个头,每个头的Q,K,V都互不相同
  • Multi-Query Attention(MQA):Q拆开成多个头,每个头的Q不同,但K,V完全相同
  • Grouped-Query Attention(GQA):Q拆开成多个头,K,V按照组分组,每个头的Q不同,同一组头K,V相同,不同组头之间K,V不同
    • GQA是MHA和MQA的中间版本,兼顾两头,LLaMA 2(70B)参数版本和LLaMA 3(8B/70B)就使用了这个
  • 第四种方法:MLA(Multi-head Latent Attention):Q拆开成多个头,核心思想是通过将K,V降维再升维的方式,巧妙的减少KV-Cache存储量(仅存储降维后的中间值即可),最早由DeepSeek提出
    • 参考链接:
      • 来自苏神的参考链接:缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA
      • 讲的较为清晰的原创博客:deepseek技术解读(1)-彻底理解MLA(Multi-Head Latent Attention) - 姜富春的文章 - 知乎

Model Card

  • Model Card, 直接译为模型卡片,在大模型中用来表示模型的详细描述,包括参数量,训练数据量,训练方式等等
  • 比如Model Card是LLaMA2的的Model Card

Tokenization

  • 分词,在原始文本输入模型前需要经过分词,模型输入和输出都是以token为单位,模型的成本也与token数量息息相关
  • 常见的分词方式可以分成三类,word/subword/char三个粒度
  • 最常见的是subword粒度,包含了BPE,WordPiece,Unigram等

Loss Spike

  • Loss Spike指的是大模型训练过程中出现的loss突然暴涨的情况,导致该问题的原因一般是脏数据

ORM 和 PRM

  • ORM:结果奖励(Outcome Reward Model)
    • 定义:ORM根据任务的最终结果提供奖励,通常在任务完成后评估
    • 延迟反馈:奖励在任务结束时才给出
    • 结果导向:只关注最终结果,不关注中间过程
  • PRM:过程奖励(Process Reward Model)
    • 定义:PRM在任务执行过程中提供即时奖励,评估每一步的表现
    • 即时反馈:奖励在每一步或每个子任务完成后立即给出
    • 过程导向:关注中间步骤,帮助模型优化过程

Context Length 和 Generation Length

  • 大模型的上下文长度(Context Length)
    • 上下文长度是模型能同时处理的输入文本的最大长度(包括用户输入的提示词、历史对话、文档内容等)
    • 决定了模型能“看到”多少信息来生成回复
    • 超出该长度的内容会被截断或忽略
  • 大模型的生成长度(Generation Length)
    • 模型单次回复时能生成的最大文本长度(即输出的token数量上限)
      • 生成长篇文章时可以多次请求(分段生成)
      • 指定这个长度可以避免模型因生成长度过大而失控(如无意义重复)
    • 问题:为什么 Qwen2.5 开源时要强调自己的模型 Generation Length 是 8K tokens?是训练时就只针对 8K 输出做训练了吗?

Scaling Law

  • 原始定义:大模型的Scaling Law是指模型性能(如损失函数值或任务表现)与计算量(FLOPs)、模型参数量(N)以及训练数据量(D)之间的幂律关系
  • 其核心观点是,在不受其他因素限制的情况下,模型性能会随着计算量、参数量和训练数据的增加而提升,且这种提升遵循幂律规律(即非线性增长)

Scaling Law 的数学表达

  • OpenAI 在 2020 年的论文《Scaling Laws for Neural Language Models》中提出,语言模型的测试损失(Loss)可以表示为:
    $$
    L(N, D) = \left( \frac{N_c}{N} \right)^{\alpha_N} + \left( \frac{D_c}{D} \right)^{\alpha_D}
    $$
    • \( N \) 是模型参数量,\( D \) 是训练数据量(token 数)
    • \( N_c \) 和 \( D_c \) 是临界值,表示当 \( N \) 或 \( D \) 低于该值时,性能受限于该因素
    • \( \alpha_N \) 和 \( \alpha_D \) 是幂律指数,通常 \( \alpha_N \approx 0.076 \),\( \alpha_D \approx 0.095 \)(具体数值可能因模型架构和任务不同而变化)

Training Time Scaling

  • 增加训练时的时间(即增加计算量、参数量、训练数据量),可提升模型性能,是最原始的 Scaling Law 含义
  • 吃显存

Inference Time Scaling

  • 增加推理时的时间(即增大思维链长度,或MCTS等?),可提升模型性能(特别是推理性能)
  • 吃推理时间

Parallel Scaling

  • 在模型不增加参数的前提下(实际上会增加少量参数),同时拉大训练和推理并行计算量,提升模型效果
  • 是 Qwen 和 浙大 25年论文提出的,也叫做ParScale:对同一输入,同时复制乘 P 个“带前缀”的并行数据,让模型一次性算出 P 个 logits,再用一个MLP(少量参数)给每个 logits 打分加权,提高模型效果
  • 参考链接:
    • 原作者的知乎回答:如何看待Qwen推出的新Scaling Law ——Parallel Scaling? - keytoyze的回答 - 知乎
    • 原论文:Parallel Scaling Law for Language Models

AI Agent 和 Agentic AI

  • AI Agent
    • 一般是单体系统,结构相对简单;
    • 通常需要明确指令,自主性较低,是被动响应型,按预定义的命令执行特定任务;
    • 擅长简单、重复的任务,任务范围有限,在有明确约束的环境中运行;
    • AI Agent 常用于客服聊天、推荐系统、流程自动化等场景
  • Agentic AI
    • 是多智能体系统,通过多个 AI Agent 协作,有记忆、分工和任务调度,类似“AI团队”;
    • 自主性高,能自主决策、规划和行动,可视为决策者,主动为用户服务,像主动监控票价并自动重新预订;
    • 可处理复杂任务,能在开放或不确定的环境中运行,可自行设定目标,根据环境变化动态调整行为;
    • 适用于需要动态实时决策的金融、物流领域,以及 AI 科研助手、ICU 病房诊疗辅助等复杂场景

Step Batch Size

  • batch size(批量大小):指在一次模型参数更新(即一个训练步骤)中所使用的样本数量
  • step batch size :常用在是 LLM 的训练语境中,step batch size(步长批量大小)是强调 “每一步(step)” 训练中所处理的样本批量大小
  • step batch size 本质上与 batch size 含义一致,主要是强调了每一步更新参数使用的真实样本量

模型编辑(Model Editing)技术

  • 参考链接:Overview of Model Editing - Alphabeta的文章 - 知乎
  • 模型编辑(Model Editing) 是一种在不进行昂贵的全模型再训练的情况下,对预训练模型(尤其是大型语言模型LLMs)的知识或行为进行局部、有选择性修改的技术
  • 通常,模型编辑通过直接修改模型内部的少量参数来实现
    • 这些方法通常试图在最小化对模型其他知识和能力的负面影响的前提下,精确地定位和修改与特定事实相关的参数
  • Model Editing 方法大致可以分为以下几类:
    • Fine-tuning with constraints (带限制的微调)
    • Memory-Augmented (retrieval) (检索内存增强)
    • Hyper network (超网络)
    • Locate and edit (定位并修改)

LLM Surgery

  • LLM Surgery ,通常直译为 ”LLM 手术“,是比模型编辑(Model Editing) 更广泛的技术
  • 相对 Model Editing,LLM Surgery 通常指更系统化和广泛地调整 LLM 的行为和功能,不仅仅是知识更新,可能涉及:
    • “遗忘”不当或过时信息 : 类似于删除有害或过时的知识
    • 整合新知识 : 与模型编辑类似,但可能更强调新旧知识的无缝集成
    • 调制模型行为 : 例如,降低模型的“毒性”(toxicity),提高对“越狱”(jailbreaking)尝试的抵抗力,或者调整模型在特定场景下的回答风格
  • LLM Surgery 方法通常更为复杂,它可能涉及优化一个包含多个组件的目标函数:
    • “遗忘”部分 : 通过反向梯度(reverse gradient)等技术,让模型“遗忘”特定的数据或行为
    • “更新”部分 : 通过梯度下降等方式,引入新的或修正的信息
    • “保留”部分 : 确保修改不会损害模型在其他不相关任务上的核心能力和性能
  • 它可能不仅仅是修改参数,还可能涉及对模型隐藏层、激活函数等更深层次的调整,以引导模型行为的整体转变
  • Model Editing vs LLM Surgery :
    • Model Editing 更像是一种“点对点”的修正,主要针对模型内部的特定知识点进行修改
    • LLM Surgery 则更像是一种“外科手术”,对模型进行更深层次、更系统化的调整,以改变其整体行为特征或删除特定的有害信息,同时努力保持其核心能力

GSB 标注

  • GSB 标注解释:Good,Same,Bad 标注
  • 在自然语言处理(NLP)领域,GSB 是一种实验数据标签标定方法,用于对比实验组与对照组的表现
  • 该方法常用于评估文本相关性,例如查询与文档标题/正文的匹配度

线性注意力机制(Linear Attention)

  • 线性注意力机制是一类通过降低计算复杂度来优化传统注意力机制的方法,目标是优化长序列任务
  • 传统 Transformer 的 Softmax 注意力计算复杂度为 \(O(N^2)\) (\(N\) 是序列长度),而线性注意力机制通过数学重构等方式将复杂度降至 \(O(N)\) 或 \(O(N \log N)\) ,从而显著减少计算和内存开销
  • 代表方法:Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention, ICML 2020,是线性注意力领域的开创性工作,简称 Linear Transformer
    • 该工作通过将 Softmax 指数函数重写为特征映射 \(\varphi(x)\) 的点积形式的核函数,并利用矩阵乘法的结合律,成功将注意力计算重构为线性形式,消除了计算完整 \(N\times N\) 注意力矩阵的需求,将复杂度降低至 \(O(Nd^2)\) ( \(d\) 表示 Embedding 维度)
    • 在大型语言模型中,通常序列长度 \(N\) 远大于嵌入维度 \(d\) ,因此这种方法实际上实现了线性时间复杂度

Polyak averaging(参数平均)

  • Polyak averaging(也称为Polyak-Ruppert averaging)是一种优化技术,由俄罗斯数学家 Boris T. Polyak 在 1991 年提出,用于提高随机梯度下降(SGD)等优化算法的稳定性和泛化能力
  • 其核心思想是:在模型训练过程中,对多个迭代步骤的模型参数进行平均 ,而不是仅使用最终迭代的参数
  • 平均方式:可使用参数的简单平均或者加权平均的方式
  • 优势是:减少方差;提高泛化;
  • 在训练 Transformer 模型时,使用 Polyak averaging 可以提高模型在下游任务中的表现,LLM 中特别场景
  • 注:部分研究表明使用,在策略梯度算法(如 PPO)中,Polyak averaging 可以增强策略的稳定性

Instruction Following Difficulty(IFD)

  • 原始论文:From Quantity to Quality: Boosting LLM Performance with Self-Guided Data Selection for Instruction Tuning, 20240406
  • 在 LLM 中,指令跟随难度(Instruction Following Difficulty,IFD),是一种用于衡量模型遵循指令生成相应输出难度的指标,常用于 SFT 中做数据筛选
  • 计算方式 :IFD 通过计算条件回答分数(Conditioned Answer Score,CAS)与直接答案分数(Direct Answer Score,DAS)的比值得到,公式为
    $$ \mathrm{IFD}_\theta(Q,A)=\frac{s_\theta(A|Q)}{s_\theta(A)}$$
    • \(s_\theta(A|Q)\) 表示模型在给定指令 \(Q\) 的情况下生成答案 \(A\) 的分数
      $$ s_{\theta}(A | Q) = -\frac{1}{N} \sum_{i = 1}^{N} \log P\left(w_{i}^{A} | Q, w_{1}^{A}, w_{2}^{A}, \cdots, w_{i - 1}^{A} ; \theta\right)$$
    • \(s_\theta(A)\) 表示模型直接生成答案 \(A\) 的分数
      $$ s_{\theta}(A) = -\frac{1}{N} \sum_{i = 1}^{N} \log P\left(w_{i}^{A} | w_{1}^{A}, \cdots, w_{i - 1}^{A} ; \theta\right) $$
  • 指标意义 :IFD 值可以反映指令对模型生成答案的影响程度
    • IFD 值超过 1 的数据通常被视为异常数据
      • 理解:由于分子分母都是正数,此时说明分子大于分母,即条件概率小于直接生成概率: \(P(A|Q,W) < P(A|W) \),这说明,加入 Q 对 A 的生成非但没有帮助,反而是负向影响
    • IFD 高但不超过 1,意味着提示对模型生成答案有帮助,但帮助不显著,这类样本属于“difficulty”样本;
      • 理解:此时条件概率大于直接生成概率 \(P(A|Q,W) > P(A|W) \),但是大的不多,即 Q 对 A 的生成影响不大,此时 模型难以将答案与给定的指令内容对齐 ,这种样本对模型来说挑战较大
    • 低 IFD 值表明提示极大地简化了答案的生成,属于“easy”样本
      • 理解:此时条件概率大于直接生成概率 \(P(A|Q,W) > P(A|W) \),且大很多,即 Q 对 A 的生成影响非常大,这种样本很容易学习(甚至不需要学习?)
    • 较高的 IFD 分数表明模型难以将答案与给定的指令内容对齐 ,说明指令难度更高 ,对模型调优更有利
  • 应用场景 :该指标可用于筛选具有增强 LLM 指令调优潜力的数据样例
    • 通过对原数据集按照 IFD 指标进行排序,选择分数靠前的数据作为“樱桃数据”,模型仅使用原始数据 5%-10% 的樱桃数据就可以达到全量数据微调的效果,甚至可以有所提高

Instruction Mining

  • Instruction Mining(指令挖掘)是一种用于选择高质量指令数据的方法,旨在挑选出最有利于大模型训练的指令数据子集,从而提升大模型指令微调之后的性能
  • 具体原理 :利用自然语言指标作为数据质量的衡量标准,通过这些指标预测推理损失,进而评估指令数据的质量
    • 例如,将输入长度、输出长度、奖励模型的输出分数、困惑度等指标进行线性组合,并使用线性回归的方式得到系数,以此量化数据质量,无需微调模型即可相对评估指令数据,从而节省时间与计算开销
  • 通过 Instruction Mining,可以从各种指令遵循数据集中选择相对高质量的样本,帮助大模型更好地学习指令模式,提高对指令的理解和响应能力,最终提升模型在相关任务上的性能
  • 实验表明,使用 Instruction Mining 选择的数据集相比未经筛选的数据集,在一定比例的情况下表现更优
  • 注:在一些场景中,Instruction Mining 也常常有更广泛的定义,比如从无指令数据中自动或半自动地挖掘出 指令数据

Self-instruct

  • Self-instruct 是一种 instruction Mining 方法,是斯坦福大学等研究者在 2022 年提出方法
  • Self-instruct 核心思路:利用语言模型自身生成能力,在仅少量高质量的指令数据的情况下,自我构造任务指令和示例数据,以训练出指令理解与执行能力更强的模型
  • Self-instruct 具体流程包括:
    • 准备少量种子指令 :人工准备少量(如 100 条)高质量任务指令
    • LLM 生成更多指令 :基于这些种子任务,让预训练模型生成新的指令
      • 如 “把句子改写成消极语气”“列出 Python 中的三种排序算法” 等,这些新指令由模型自动生成,无需人工逐一设计
    • 生成指令响应 :使用同一个模型根据新生成的指令,生成对应的回答或执行结果
      • 例如指令为 “将句子翻译成西班牙语:‘I like learning.’”,响应则为 “Me gusta aprender.”
    • 筛选与清洗 :为保证数据质量,需进行数据筛选与清洗工作
      • 可移除重复、无意义的指令,利用规则或小模型过滤低质量响应,也可进行少量人工审核
    • 模型微调 :将最终得到的 “指令-响应” 数据集用于对预训练模型进行微调,使模型更擅长理解和执行各种指令

Unembedding Layer & Output Projection Layer & LM Head 概念辨析

  • 在 Transformer-based 模型中,Unembedding Layer、Output Projection Layer 和 LM Head 这三个术语常被混用,但它们的具体含义根据上下文不同又有一些细小的区分,本节尝试对这三个概念进行辨析

Output Projection Layer(输出投影层)

  • Output Projection Layer 用于模型最后一层输出的隐藏状态(hidden states)投影到目标空间
    • 输入为隐藏状态,维度为 [batch, seq_len, d_model]
    • 输出为词表大小维度 [batch, seq_len, vocab_size]
  • Output Projection Layer 通常是一个线性层(nn.Linear(d_model, vocab_size))
    • 平时实现时,无偏置项(bias)的情况较常见(nn.Linear(d_model, vocab_size, bias=False))
  • 用于将高维特征映射到与词表对应的 logits(未归一化的分数),为后续生成概率分布做准备
  • 特别说明 :
    • Output Projection Layer 是一个纯数学操作,不涉及任何非线性激活函数
    • 与 Embedding 层(输入端的词向量矩阵)可能是参数共享的(如T5、GPT-2),也可能独立(如 BERT 的 MLM Head)

Unembedding Layer(反嵌入层)

  • 与输入端的 Embedding 操作对应,可视为“逆向嵌入”
  • 多数情况下,Unembedding Layer = Output Projection Layer ,即同一个线性投影层
    • 特别说明 :在部分模型中,Unembedding 可能包含归一化层或残差连接(罕见),但通常仍指线性投影
  • 若与输入 Embedding 共享参数(Weight Tying 或 Tie Embedding),则直接使用 Embedding 矩阵的转置(W_embed.T)进行投影

LM Head(语言模型头)

  • LM Head 的范围更为广泛,泛指整个语言模型的任务特定输出层 ,可能包含:
    • Output Projection Layer(必需)
    • 可选的额外处理层(如 LayerNorm、偏置项、适配器模块等)
  • 特别说明 :
    • 在 GPT 等自回归模型中,LM Head 通常仅指线性投影层(即 Output Projection Layer)
    • 在 BERT 的 MLM 任务中,LM Head 可能包含如下的额外的全连接层、激活函数、归一化层等部分:
      1
      2
      3
      4
      5
      6
      LM_Head = Sequential(
      Linear(d_model, d_model), # 额外的全连接层
      GELU(),
      LayerNorm(d_model),
      Linear(d_model, vocab_size) # Output Projection
      )

PyTorch 实现实例

  • PyTorch 实现示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # Output Projection Layer / Unembedding Layer
    output_projection = nn.Linear(d_model, vocab_size, bias=False)

    # 若与 Embedding 共享参数,即 Weight Tying 或 Tie Embedding
    token_embedding = nn.Embedding(vocab_size, d_model)
    output_projection.weight = token_embedding.weight # 权重绑定

    # LM Head可能是复杂结构(如BERT)
    LM_Head = Sequential(
    Linear(d_model, d_model), # 额外的全连接层
    GELU(),
    LayerNorm(d_model),
    Linear(d_model, vocab_size) # Output Projection
    )

其他说明

  • HuggingFace 代码库中常用 lm_head 指代最终的线性投影层,与学术文献可能不同
  • 在非 LM 任务(如分类)中,输出层可能称为 Task Head

Tie Embedding / Weight Tying

  • Tie Embedding,也称为 Weight Tying 机制下,嵌入层(Embedding Layer)和输出投影层(文献中可能称为 Unembedding Layer / Output Projection Layer / LM Head)是绑定/共享的
    • 优势:节约存储、训练稳定;
    • 缺点:表达能力受限、梯度冲突可能严重(比如输入和输入的词分布差异大),所以后来的一些模型会选择不绑定
  • 注:传统 Transformer 中(Attention Is All You Need 的 3.4 结中提到)就已经有了 Tie Embedding 机制
  • 注:GPT-2/T5 等通过共享 Embedding 和 Output Projection Layer 参数减少参数量,但 BERT 通常不共享

System-2(系统2)

  • 大模型中的系统2源于心理学中丹尼尔·卡尼曼提出的双系统理论
    • 该理论认为人类思维由系统1和系统2两个不同系统驱动
  • 在大模型领域,系统2是一种较慢的、深思熟虑的、善于分析的思维模式,对应着模型进行复杂推理和深度思考的能力
  • 系统2思维需要有意识地努力,涉及逻辑分析、推理和意识层面的思考,更能够进行深度思考,但也更耗费时间和精力
  • 系统2常用于提升推理和解决问题能力、改善对上下文和细微差别的理解、减少偏见和错误、更好地制定决策
  • 与系统2相关的技术有:思维链(COT)、思维树、思维图、分支解决合并(BSM)、系统2注意力(S2A)、重述和回应(RaR)等
    • 这些技术通过显式推理通常能产生更准确的结果,但往往会带来更高的推理成本和响应延迟
  • 目前,像 OpenA 的 o1/o3 和 DeepSeek-R1 等推理大语言模型,都在尝试模拟系统2的审慎推理(deliberate reasoning),以提升模型在复杂推理任务中的表现

投机采样

  • 投机采样(Speculative Decoding) ,也常常被翻译为 推测解码
  • 投机采样是一种加速 LLM 推理的技术
  • 投机采样的核心思想是通过预生成候选 token 序列并异步校验,从而减少主模型的计算量,同时保持生成结果的准确性
  • 投机采样通过“预测-校正”范式,在保证生成质量的前提下显著降低主模型的计算负载,是当前LLM推理加速领域的重要突破
  • 投机采样的流程主要包括小模型生成候选序列、大模型并行验证、结果评估与处理以及循环迭代等步骤
  • 详细采样流程:待补充

AGI

  • 关键词:AGI定义,AGI的定义,AGI 定义,AIG 的定义
  • AGI(Artificial General Intelligence,通用人工智能)
  • 目前为止没有明确的定义,不同公司对 AGI 的描述和愿景不同
  • OpenAI :强调在经济效益上超过人类

    from OpenAI Charter
    OpenAI’s mission is to ensure that artificial general intelligence (AGI)—by which we mean highly autonomous systems that outperform humans at most economically valuable work—benefits all of humanity

    • 据 Bloomberg(彭博社) 报道 OpenAI 对AGI有以下五级分类:
      • Level 1: Chatbots, AI with conversational language
      • Level 2: Reasoners, human-level problem solving
      • Level 3: Agents, systems that can take actions
      • Level 4: Innovators, AI that can aid in invention
      • Level 5: Organizations, AI that can do the work of an organization
  • Google :强调像人一样的认知能力

    from Google Cloud: what-is-artificial-general-intelligence
    Artificial general intelligence (AGI) refers to the hypothetical intelligence of a machine that possesses the ability to understand or learn any intellectual task that a human being can. It is a type of artificial intelligence (AI) that aims to mimic the cognitive abilities of the human brain

  • 个人认为:AGI 指的是能够像人类一样理解、学习和执行广泛任务的人工智能系统,其核心包含几个方面:
    • 跨领域:AGI 不应该是专注于特定任务的 AI,应当具备跨领域能力
    • 自主学习:AGI 应该有自主学习的能力,甚至可通过探索快速适应从未见过的新环境
    • 跨模态:AGI 应该能与真实物理环境交互,所以应该是跨模态的,除了文本以外,还应该包括视觉和语音

Vibe Coding

  • Vibe coding 是一种全新的 AI 辅助编程方法 ,也可称为 “氛围编程” 或 “感觉式编程”,由 OpenAI 联合创始人 Andrej Karpathy 于 202502 提出
  • Vibe coding LLM-based 沉浸式开发,开发者无需手动编写每一行代码 ,只需通过自然语言(如英语)与 AI 交互,描述自己的需求,让 AI 自动生成代码,即可完成从需求分析到代码实现的全过程

Slide Attention

  • Slide Attention 通常也称为滑动窗口注意力(Sliding Window Attention) ,有时也称为 Sliding Attention ,一种提升注意力计算效率的稀疏注意力(Sparse Attention)方法,常用于处理长序列数据
  • Slide Attention 对比 标准 Transformer 的 Attention :
    • 标准 Transformer 中自注意力(也可以称为 Full Attention)计算复杂度为 \(O(n^2)\),随着序列长度增加,计算和内存开销急剧增长
    • Slide Attention 的核心思想是让每个 token 仅关注窗口 \(w\) 范围内的 tokens,而非整个序列,将计算复杂度降低为 \(O(n\cdot w)\)
  • Slide Attention 大幅减少了计算量和显存占用,适用于长文本任务,OpenAI 发布的 oss-120b 和 oss-20b 中就使用到了 Full Attention GQA 和 Slide Attention GQA 交叉的架构

Attention Sink

  • Attention Sink 现象(即“注意力黑洞”现象),是指在 LLM 的注意力机制中,某个特定 Token(通常是序列中的第一个 Token,即 <BOS> Token)会收到不成比例的高注意力权重,就像黑洞一样把其他 Token 的注意力都吸过去了
  • Attention Sink 可能导致模型忽视其他 Token 中的重要上下文信息,降低模型效率
  • 出现该现象的原因可能有多种:
    • Softmax 操作要求所有上下文 Token 的注意力分数总和为 1,因此即使当前 Token 跟前面的其他 Token 都没有语义相关性,模型也需将多余注意力值分配到前面某些 Token,而初始 Token 因对所有后续 Token 都可见,更容易成为分配对象
    • 其他原因也可能是因为该 Token 出现频率高、位置特殊、语义较强,或者训练数据存在偏差等
  • 使用稀疏注意力技术可以缓解这个问题,比如 (Gated-Attention)Gated Attention for Large Language Models: Non-linearity, Sparsity, and Attention-Sink-Free, 2025, Qwen

Reformer(Efficient Transformer)

  • 参考链接:Reformer: The Efficient Transformer, 2020, Google & UC Berkeley
  • Reformer 是由 Google Research 在 2020 年提出的一种改进的 Transformer 模型,旨在解决传统 Transformer 在处理长序列时面临的高计算和内存消耗问题
  • 通过引入局部敏感哈希(LSH)注意力和可逆残差网络两种关键技术,显著提升了模型效率,使其能够处理更长的序列
  • Reformer 关键技术简单介绍
    • 局部敏感哈希(LSH)注意力 :
      • 将注意力计算简化为近似最近邻搜索 ,利用哈希桶将相似键分组,仅计算桶内注意力
      • 复杂度从 (O(n^2)) 降至 (O(n \log n))
    • 可逆残差层 :
      • 反向传播时无需保存每一层的激活值,通过数学方法重建中间结果,显存占用大幅降低
    • 分块计算 :
      • 将长序列分块处理,进一步优化内存效率
  • transformers库中已经有支持 from transformers import ReformerModel, ReformerTokenizer
    1
    2
    3
    4
    5
    6
    7
    from transformers import ReformerModel, ReformerTokenizer

    tokenizer = ReformerTokenizer.from_pretrained("google/reformer-crime-and-punishment")
    model = ReformerModel.from_pretrained("google/reformer-crime-and-punishment")

    inputs = tokenizer("Hello, world!", return_tensors="pt")
    outputs = model(**inputs)

Longformer(Long-Document Transformer)

  • 参考链接:Longformer: The Long-Document Transformer, 202012, Allen AI
  • Longformer(Long Document Transformer)是一种基于 Transformer 架构的改进模型,专门针对处理长文本而设计
  • Longformer 由 Allen Institute for AI 和 UC Irvine 的研究团队于 2020 年提出,旨在解决传统 Transformer 模型(如 BERT、GPT)因自注意力机制(Self-Attention)的平方级计算复杂度而难以处理长序列的问题
  • Longformer 的核心改进是引入了稀疏注意力模式(Sparse Attention),将全局注意力(Global Attention)与局部滑动窗口注意力(Sliding Window Attention)结合,显著降低了计算复杂度(从 (O(n^2)) 降至 (O(n)))
  • Longformer 的关键技术介绍
    • 滑动窗口注意力(Sliding Window)
      • 每个 token 只关注其附近固定窗口大小(如 512 tokens)的局部上下文,类似卷积操作
      • 通过堆叠多层注意力层,模型可以捕获远距离依赖(高层感受野逐渐扩大)
    • 任务相关的全局注意力(Global Attention)
      • 对特定任务(如 QA 中的问题相关位置)或特殊 token(如 [CLS])启用全局注意力,使其关注整个序列
      • 平衡局部效率与全局信息需求
    • 空洞注意力(Dilated Attention)(可选)
      • 通过间隔采样(空洞)减少计算量,同时保持对长序列的建模能力
      • 类似空洞卷积(Dilated Convolution),两者分别用于 NLP 和 CV 中,都可以扩大感受野
  • transformers库中已经有支持 from transformers import LongformerModel, LongformerTokenizer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from transformers import LongformerModel, LongformerTokenizer

    model = LongformerModel.from_pretrained("allenai/longformer-base-4096")
    tokenizer = LongformerTokenizer.from_pretrained("allenai/longformer-base-4096")

    text = "This is a long document..." # 可以是长文本
    inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=4096)

    # 启用全局注意力(例如对 [CLS] 标记)
    inputs.global_attention_mask = torch.zeros_like(inputs.input_ids)
    inputs.global_attention_mask[:, 0] = 1 # 第一个位置是 [CLS]

    outputs = model(**inputs)

BigBird

  • 参考链接:Big Bird: Transformers for Longer Sequences, NeurIPS 2020, GOogle
  • BigBird 是一种基于 Transformer 架构的改进模型,专门针对处理长序列输入的局限性进行优化。由 Google Research 在 2020 年提出,它通过引入稀疏注意力机制,显著降低了传统 Transformer 在长序列场景下的计算复杂度(从二次方降至线性),同时保持了较强的建模能力
  • BigBird 和核心创新在稀疏注意力机制上,BigBird 通过三种注意力组合降低计算量:
    • 全局注意力(Global Attention)
      • 部分关键 token(如 [CLS]、句首/尾)参与所有位置的注意力计算,保留全局信息
    • 局部注意力(Local Attention)
      • 每个 token 仅关注相邻的固定窗口内的 token(类似卷积操作),捕捉局部上下文
    • 随机注意力(Random Attention)
      • 每个 token 随机选择少量其他位置进行注意力计算,增强全局交互
  • transformers库中已经有支持 from transformers import BigBirdModel
    1
    2
    from transformers import BigBirdModel
    model = BigBirdModel.from_pretrained("google/bigbird-roberta-base")

Embodied Intelligence

  • 具身智能(Embodied Artificial Intelligence 或 Embodied Intelligence),是指一种基于物理身体进行感知和行动的智能系统
  • 具身智能通过智能体与环境的交互获取信息、理解问题、做出决策并实现行动,从而产生智能行为和适应性
  • 具身智能强调智能并非孤立于大脑或算法,而是身体形态、运动能力与环境动态耦合的涌现性结果,智能体的身体、感知系统和环境之间的相互作用是其智能行为的基础

WSD 学习率调度

  • WSD(Warmup-Stable-Decay)学习率调度,是一种用于调整训练不同阶段学习率的方法,由 MiniCPM 提出
  • WSD 学习率调度分为三个阶段:
    • 预热阶段 :将学习率从 0 线性增加到峰值
    • 稳定阶段 :把学习率保持在峰值,模型在这个阶段进行大部分的训练
    • 衰减阶段 :在相对较短的时间内将学习率退火至 0
  • 一般来说,在衰减阶段还可以更改数据混合比例,以增加高质量数据的比例
  • 实验证明,WSD 会生成非传统的损失曲线,损失在稳定阶段保持高水平,但在衰减阶段急剧下降
  • 与常用的 Cosine 衰减学习率调度(余弦衰减学习率调度)相比:
    • WSD 在小尺寸模型上的收敛效果很好,且对续训更加友好
    • Cosine 衰减学习率调度 需要提前指定训练步数;WSD 不需要提前指定训练步数
      • 虽然平时在 WSD 中,也常将预热阶段设置为总步数的一定百分比,但也可以设定为固定值
    • Cosine 衰减学习率调度更稳定

Overlong Mask 技术

  • “Overlong Mask”(超长掩码)或更通俗地称为 “Overlong Filtering”(超长过滤),是一项应用于 LLM 的 RL 训练中的技术
  • Overlong Mask 主要用于处理在训练过程中生成超过预设长度限制的文本序列,通过 “屏蔽”这些超长序列的损失计算 ,确保模型训练的稳定性和有效性
  • Overlong Mask 的核心思想 :避免对“未完成”的优质答案进行惩罚
  • 问题背景描述:
    • 在强化学习训练(如 PPO 算法)中,通常会设定一个最大生成长度(max_length)以控制计算资源和训练效率
    • 当模型生成一个内容合理、但长度超过 max_length 的序列时,这个序列会被强行截断
    • 被截断的序列可能会因为“不完整”或未能达到最终目标而获得一个较低的、甚至是负面的奖励
      • 这种“惩罚”是不公平的,因为它惩罚了模型生成长篇、详细、有逻辑的答案的潜力,仅仅因为其超出了人为设定的长度限制
      • 这会向模型传递错误的学习信号,阻碍其学习长序列推理和生成的能力
  • “Overlong Mask” 技术的核心机制是:识别出那些因为超出最大长度而被截断的序列,并在计算损失函数和更新模型参数时,将这些序列“屏蔽”掉(即,不计算它们的损失)
    • 防止模型因为生成了“有思想但过长”的答案而受到不公正的惩罚
  • 在 RL 训练中的工作步骤:
    • 1)序列生成与长度检查 :在强化学习的采样阶段,模型根据给定的提示(prompt)生成一批文本序列
    • 2)识别超长序列 :检查每个生成的序列长度是否达到了预设的 max_length
    • 3)应用掩码 :对于那些被截断的序列,在后续的损失计算中,通过一个掩码(mask)将其对应的损失值置为零或在计算总损失时将其忽略
    • 4)参数更新 :模型最终仅根据那些在长度限制内完整生成的、未被屏蔽的序列的奖励和损失来进行参数更新
  • 其他改进方法:Overlong Reward Shaping
    • 除了简单的过滤(完全忽略),一种更精细的策略被称为“Overlong Reward Shaping”(超长奖励调整)
      • 不直接丢弃超长序列,而是对奖励进行平滑处理
    • 比如 DAPO 论文中提出的方法,会设置一个惩罚过渡区间,使得奖励的惩罚随着超出长度的增加而线性增加,而不是一个突兀的悬崖式惩罚
      • 为模型提供了一个更平滑的梯度信号
    • 再比如 Kimi K1.5 训练时也有类似的特殊奖励设计
  • Overlong Mask 优点 :
    • 训练稳定性 :避免了因不公平惩罚带来的梯度剧烈波动,使得训练过程更加稳定
    • 鼓励长程推理 :允许模型自由探索生成更长、更复杂、更有深度的内容,而不用“害怕”因超出长度限制而受到惩罚
      • 这对于提升模型的长程推理能力至关重要
    • 提升模型性能 :在需要生成详细解释、长篇代码或复杂推理链的任务上,使用“Overlong Mask”的训练方式能够显著提升模型的表现

Pipeline Parallelism 技术

  • 当一张卡装不下模型参数时,常常需要对模型进行拆分,这里面有两种拆解模式:
    • 流水线并行(Pipeline Parallelism):将模型按层进行切分
      • 部分博客也翻译为 管线并行
    • 张量并行(Tensor Parallelism):将模型张量参数拆成小的块
      • 在一些更大的模型上,显卡甚至装不下单层参数,这时候张量并行就很必要了
  • 流水线并行有两篇主要文章:
    • PipeDream:PipeDream: Fast and Efficient Pipeline Parallel DNN Training, 2018, Microsoft
    • GPipe:GPipe: Easy Scaling with Micro-Batch Pipeline Parallelism, 2019, Google
  • 比较 PipeDream 和 GPipe
    • GPipe 速度效率比 PipeDream 慢,但显存占用更低,收敛也更稳定
  • 注:PipeDream 也称为 PipeDream 1F1B,或直接称为 1F1B

中间填充(Fill-In-the-Middle,FIM)

  • 不同于传统的自回归损失,中间填充(Fill-In-the-Middle,FIM)的学习目标是文本的中间
  • 中间填充的流程通常包括:
    • 通过将文本划分为前缀(prefix)、后缀(suffix)和中间部分(middle),并使用特殊标记(如 <\PRE>、<\SUF>、<\MID>)来标识
    • 模型根据前缀和后缀的上下文信息,通过双向上下文建模和动态插入生成的方式,来预测并填充中间部分的内容
  • GLM4.5 在预训练代码数据时使用到了

Best-fit Packing

  • Best-fit Packing 策略是一种由 AWS AI Labs 的研究人员提出的文档处理策略,旨在解决传统预训练方法中文档截断问题
  • 在传统的语言模型预训练中,输入文档通常会被简单地拼接起来,然后分割成等长的序列,以避免填充 token
    • 提高了训练效率,但会导致文档的不必要截断,损害数据完整性,影响模型学习到的内容的逻辑连贯性和事实一致性,增加模型产生幻觉的风险
  • Best-fit Packing 使用长度感知的组合优化技术,将文档打包到训练序列中
    • 第一步:将每个文本分割成一或多个至多长为模型上下文长度 \(L\) 的序列
    • 第二步:基于这些文件块,采用最佳适配递减算法(Best-Fit-Decreasing,BFD)的启发式策略,将它们合理地组合,以获得尽量少的训练序列 ,从而完全消除不必要的截断
  • Best-fit Packing 的优点:
    • Best-fit Packing 能够提升模型在各种文本和代码任务中的表现
    • Best-fit Packing 能减少模型幻觉
    • Best-fit Packing 策略能有效支持尾部知识学习
  • 但也有相反的声音:
    • 比如智谱在 GLM4.5 技术报告中没有使用 best-fit packing ,因为他们认为随机截断(random truncation)对于预训练文档来说是一种很好的数据增强策略

批次大小预热策略(batch size warmup strategy)

  • 批次大小预热策略(batch size warmup strategy)是一种在深度学习模型训练过程中使用的策略,其核心思想是在训练初期逐步增加批次大小(batch size),而不是一开始就使用较大的批次大小
  • 批次大小的选择会影响训练的稳定性和效率
    • 较大的批次大小可以加快训练速度 ,但在训练初期,由于模型参数尚未收敛 ,使用过大的批次大小可能导致梯度更新过于剧烈 ,从而引发训练不稳定等问题
    • 批次大小预热策略就是为了解决这个问题而提出的
  • 在 GLM-4.5 的训练流程中,就采用了批次大小预热策略,在训练的前 500B tokens 期间,逐步将批次大小从 16M 增加到 64M tokens
    • 这种策略可以让模型在训练初期以较小的批次大小进行训练,使模型参数能够更稳定地更新,随着训练的进行,再逐渐增加批次大小,以提高训练效率
  • 批次大小预热策略通常可以与学习率预热(Learning Rate Warmup)策略结合使用
    • 学习率预热是在训练初期使用较小的学习率,然后逐渐增大学习率至预设的值,两者结合可以在加快训练速度的同时,保持良好的模型性能和泛化能力
  • 理解:
    • 相对初期降低学习率的方式,这种方式可以减少算力浪费?
    • 但理论上,使用较小的学习率可能更合适一些,可以让模型始终稳定的走,一个基本的理解是:梯度大小 = 批次大小 乘以 学习率大小,所以使用大的批次和小的学习率可能更稳定

偏置更新率(bias update rate)

  • 偏置更新率(bias update rate)是一个超参数,是无损失平衡路由中的用于动态调整路由得分偏置的
    • 无损失平衡路由是不需要增加损失函数的启发式规则
  • DeepSeek-V3 提出了一种创新的无额外损耗负载均衡策略 ,通过引入并动态调整可学习的偏置项来影响路由决策
    • 在训练过程中,会持续监控每个训练步骤中整个批次的专家负载情况
    • 如果某个专家负载过重,就将其对应的偏置项减少一个偏置更新率 \(\gamma\);如果某个专家负载过轻,就将其对应的偏置项增加一个偏置更新率 \(\gamma\)
    • 通过这种动态调整,使得模型在训练过程中能够更好地平衡各专家的负载,避免了因负载不均衡导致的性能下降,同时也不会对模型性能产生额外的损耗
  • GLM4.5 中也是用了类似的策略

FlashInfer

  • FlashInfer 是一个专为大型语言模型服务设计的高性能 GPU 内核库,由华盛顿大学、NVIDIA、Perplexity AI 和卡内基梅隆大学的研究人员共同开发
  • FlashInfer具有以下核心功能与特点:
    • 提供高效的注意力计算内核,支持多种注意力机制的实现,能在GPU上高效执行,显著提升LLM的推理速度
    • 通过优化解码过程中的共享前缀批处理,减少计算和内存开销,提高解码效率
    • 通过压缩和量化KV缓存,减少内存占用和计算量,加速注意力计算
    • 优化 Grouped-Query Attention 的计算,减少计算复杂度,提高计算效率
    • 实现 Fused-RoPE Attention,减少计算步骤和内存访问延迟,提高计算速度
    • 提供 PyTorch API、TVM 绑定、C++ API 等多种接口,方便用户根据需求进行定制和扩展
    • 兼容性强 :支持多种 GPU 架构和硬件配置,还支持 WebGPU、iPhone 等本地环境,实现“一次编译,多端运行”,同时原生支持 Hugging Face 模型格式,兼容主流量化技术
  • FlashInfer 已被 MLC-LLM、Punica 和 sglang 等 LLM 服务系统采用
  • 在实际应用中,FlashInfer 能显著提升 LLM 服务的性能
    • 例如在处理大规模文本生成任务时,可比传统 CUDA 内核快 2-3 倍,同时保持较低的延迟

Curation

  • 在大模型中,“Curation”通常指数据策划或数据精选
  • 数据策划是对从各种来源收集的数据进行组织和整合的过程
    • 涉及数据的标注、发布和呈现,以确保数据的价值随着时间的推移得以保持,并且数据仍然可以用于重复使用和保存
    • 数据策划不仅仅是删除错误数据和重复项,还包括对不同语料数据的重新组织整合,比如选择用于训练的语言、对数据进行标注等
  • 数据精选则强调优先使用高质量、与任务强相关的数据,而非盲目追求数据量
    • 例如,通过人工标注、模型预筛选等方法过滤噪声数据,保留“信息密度高”的样本,以提升模型在专业领域的能力
  • 此外,还有自我策划(Self-Curation) ,即利用大模型本身筛选高质量的数据构建新的数据集

Red Team

  • 在大模型开发过程中,Red Team 指红队,是一群专门对大模型进行测试和攻击的专家团队
    • Red Team 的目的是发现模型潜在的漏洞、偏见和不良行为,确保模型的安全性、可靠性和性能
  • 红队测试起源于冷战时期的军事模拟演习,当时美国军方通过模拟“蓝队”(己方)与“红队”(敌方)的对抗,来学习如何像敌人一样思考
    • 后来,这一做法被 IT 行业采用,用于探测计算机网络、系统和软件的弱点
  • 在大模型开发中,红队会采用各种策略和技术来挑战模型
    • 例如,他们可能会使用特殊的提示词来诱导模型生成有害内容,如仇恨言论、虚假信息等,或者尝试突破模型的安全限制,查看是否能够获取敏感数据
    • 红队测试可以在多个层面进行,包括基底大语言模型层和应用程序层

单播、组播和广播

  • 在分布式系统中,单播、组播和广播是三种不同的数据传输方式,它们的区别如下:
  • 单播(Unicast),即一对一的通信模式,发送方将数据包发送到特定接收方的唯一 IP 地址
    • 每条通信链路独立,数据包仅传输给目标主机;
    • 基于TCP或UDP协议,支持可靠传输或低延迟传输;
    • 安全性高,数据仅发送给指定接收方
    • 举例:邮件发送
  • 组播(Multicast),即一对多的通信模式,发送方将数据包发送到一个特定的组播组,只有加入该组的设备才能接收到数据
    • 设备通过 IGMP(IPv4)或 MLD(IPv6)加入/离开组播组;
    • 网络设备通过组播路由协议(如PIM)构建转发树,仅向有接收方的链路复制数据;
    • 基于 UDP 协议,适合实时性要求高的场景;
    • 高效利用带宽,扩展性强,但实现复杂
    • 举例:视频直播、在线会议、分布式计算中的数据分发等
  • 广播(Broadcast),即一对所有的通信模式,发送方将数据包发送到本地网络(广播域)内的所有设备
    • 数据包的目标地址为广播地址(如IPv4的255.255.255.255);
    • 部署简单,发送方只需发送一次数据;
    • 安全性低,且广播数据无法跨路由器传播,仅限于本地网络
    • 举例:局域网内的网络发现、地址解析协议(ARP)请求、系统通知等

PTD 并行 和 3D 并行

  • PTD 是Pipeline, Tensor, Data三个词的缩写;3D 则是 3 个 Dimensions(数据、张量、流水线 3 个维度)的含义
  • 从核心构成上看,PTD并行与3D并行本质上描述的是同一种思想,即通过融合三种基础的并行技术来共同扩展模型的训练能力
  • 总节两者主要区别在于:
    • 术语归属 :PTD与Megatron-LM紧密绑定,而3D并行是业界更通用的描述
    • 优化焦点 :PTD 并行(Megatron-LM)的特色在于其精巧的交错式流水线调度 ,旨在最大化计算效率;而3D 并行(DeepSpeed)的王牌在于和ZeRO技术的结合,在保证计算效率的同时,实现了极致的内存优化
  • 这三种基础的并行技术分别是:
    • 数据并行(Data Parallelism, DP): 将训练数据分发到多个计算设备上,每个设备都持有一份完整的模型副本,并独立计算梯度,最后通过全局通信同步更新模型参数
    • 张量并行(Tensor Parallelism, TP): 也称为层内模型并行(Intra-Layer Model Parallelism),它将模型单个层(特别是其中巨大的权重矩阵)切分到不同设备上,进行协同计算
    • 流水线并行(Pipeline Parallelism, PP): 也称为层间模型并行(Inter-Layer Model Parallelism),它将模型的不同层(Layers)分配到不同的计算设备上,形成一个流水线,数据依次通过各个设备完成前向和反向传播

两者的核心区别:命名、实现与优化策略

  • PTD 并行 (PTD Parallelism):
    • 源于 NVIDIA 的Megatron-LM框架。PTD是Pipeline, Tensor, Data三个词的缩写,直观地表达了其技术构成
    • 强调通过创新的流水线调度策略来提升效率,其代表性的优化是 “交错式流水线调度”(Interleaved Pipeline Schedule)
    • 策略上看:PTD-P(Megatron-LM中的具体实现)通常建议在单个节点内部(通过NVLink等高速互联)应用张量并行,而在跨节点时应用流水线并行,最后在整个集群上应用数据并行。其核心创新在于通过将模型的层块(chunks)交错分配给不同的流水线阶段,有效减少了流水线“气泡”(bubble)——即设备空闲等待的时间,从而提高了计算资源的利用率
  • 3D 并行 (3D Parallelism):
    • 一个更为通用的术语,被DeepSpeed等多个框架广泛采用,用以描述由数据、张量和流水线三个维度构成的并行策略
    • 强调与 ZeRO(Zero Redundancy Optimizer)技术 的深度融合(ZeRO 是一种高度优化的数据并行技术,可以显著降低内存冗余)
    • DeepSpeed 的 3D 并行将计算设备组织成一个三维网格(Mesh),分别对应数据并行、张量并行和流水线并行
    • 3D 并行最大的特点是数据并行维度可以由ZeRO来赋能
    • ZeRO 不仅切分数据,还能够将模型的参数、梯度和优化器状态进行切分和卸载,极大地优化了内存使用效率,使得在同等硬件条件下能够训练更大规模的模型

3D 并行示例

  • 场景设定:假设有 16 张卡,3D 并行的策略为 tp=2,dp=2,pp=4
  • 此时有以下结论:
    • 共两个 DP 组,每组 8 张卡
    • 每个 DP 组的 8 张卡包含 4 个 PP 阶段,每个阶段有 2 张 TP 组的卡,即这 8 张卡通过管道并行(PP) 和张量并行(TP) 的拆分方式,分别存储模型的不同部分(按层拆分+按参数维度拆分)
    • 同一个 DP 组的 8 张卡共同构成了一个完整的模型 ,单独一张卡仅持有模型的部分参数(既不完整,也不独立),整个 DP 组的 8 张卡通过协作(通信+拼接)能够共同代表一个完整的模型结构
    • 2 个 DP 组的 8 张卡可分别视为完整模型的“数据并行副本”
    • 2 个 DP 组的参数拆分方式完全相同,只是处理不同的数据分片,训练中通过梯度同步保持参数一致性
  • 特别地,在 MoE 中,还会多一个 EP,即 专家并行,Expert Parallelism
    • 核心作用是:拆分 MoE 模型的 专家 到不同 GPU,解决显存瓶颈,提升训练并行度

MFU(Model FLOPs Utilization)

  • MFU 通常指的是 Model FLOPs Utilization ,即“模型浮点运算利用率”
    • 其中 FLOPs 是 “Floating Point Operations Per Second(每秒浮点运算次数)”的缩写,是衡量计算能力的重要指标
  • MFU衡量的是实际训练过程中,模型所用到的浮点运算量与 GPU 等硬件理论峰值性能的比例
  • MFU 的计算公式通常为:
    $$
    MFU = \frac{\text{模型实际每秒浮点运算次数}}{\text{硬件理论最大每秒浮点运算次数}}
    $$
    • 分子 :模型在训练时实际消耗的 FLOPs(可通过模型结构和 batch size 等参数估算)
    • 分母 :硬件(如 GPU)理论上能够达到的最大 FLOPs(由硬件规格决定)
  • MFU 低可能意味着模型结构、数据管道、硬件配置存在瓶颈,开发者可以通过优化模型、提升数据加载效率、调整 batch size 等方式提升 MFU
  • MFU 是评估不同训练方案、模型架构或硬件平台性能的重要参考指标

MFU 与其他指标的区别

  • MFU 关注的是浮点运算利用率,强调计算资源的使用效率
  • GPU Utilization 则是 GPU 整体资源(包括计算、内存、IO 等)的使用率
  • TFLOPs 是硬件的理论计算能力,MFU 则是实际利用率

SPMD (Single Program Multiple Data)

  • SPMD (Single Program Multiple Data) 是一种编程范式,其特点为:
    • 所有进程执行一份代码
    • 每个进程通过传入不同分布式参数(比如 Rank)来处理不同的数据
  • SPMD 是非常常用的编程范式,比如:
    • 数据并行:DDP,ZeRO,FSDP
    • 张量并行
    • 流水线并行
    • 序列并行(Sequence Parallelism)
  • 注:序列并行(Sequence Parallelism)是特指大语言模型中将同一个长序列分配到不同的 GPU 上进行分布式处理的方式
    • 应用实例:比如 Megatron 中 的 Context Parallelism(上下文并行,CP)通过切分 LayerNorm 和 Dropout 的输入,减少 30% 以上的激活内存,同时保持计算效率

Model Growth Initialization(模型增长初始化)

  • 模型增长初始化(Model Growth Initialization,MGI) 是大模型领域中,通过从已训练小模型出发、渐进式扩展结构(如增层数、扩宽度、加专家数量),并复用小模型参数与优化状态进行初始化的技术
  • 核心是用“知识迁移”降低大模型训练成本、提升效率
  • Model Growth Initialization 关键逻辑包括:
    • 通过深度/宽度/MoE混合等策略扩展模型;
    • 靠功能保留初始化(Function-Preserving Initialization,FPI)、身份映射初始化(Identity Mapping Initialization)等原则避免训练震荡;
    • 典型方法有 Net2Net(结构扩展框架)、Stacking Transformers(深度堆叠加速)等
  • 实际应用中,能减少 50% 左右训练时间、降低显存占用,还能让扩展后的大模型保持性能甚至激发复杂推理等“涌现能力”
  • 目前仍面临扩展策略自动化、初始化精细化、分布式训练适配等挑战,是千亿级大模型高效训练的重要技术方向

GEMM

  • 通用矩阵乘法(General Matrix Multiply,GEMM)是线性代数与深度学习中最核心的计算原语之一,用于高效地计算:
    $$C = A × B$$
    • 也支持带缩放与累加的
      $$C = \alpha AB + \beta C$$
  • GEMM 是 BLAS 中的核心例程(CBLAS 接口:cblas_sgemm/cblas_dgemm 等)
    • 原始的 BLAS 是用 Fortran 语言编写的;CBLAS 则是 BLAS 的 C 语言接口(C Interface to BLAS)
  • DeepGEMM(FP8、JIT、MoE 优化)是针对深度学习场景优化的 GEMM 计算库,专注于提升大模型训练和推理中的矩阵乘法效率,尤其在混合精度计算、动态形状适配等场景有显著优势

RDMA

  • RDMA 是一种“远程直接内存访问”技术,允许网卡绕过 CPU 与内核,直接读写对端内存,实现低延迟、高吞吐、低CPU占用的网络通信;
  • RDMA 的常见实现包括 IB(InfiniBand)、RoCE(v1/v2)、iWARP,需 RDMA 网卡与配套网络/驱动
  • RDMA 的能力包括单边(RDMA Read/Write/Atomic)与双边(Send/Recv)语义,支持用户态 Verbs 接口

RDMA 与 NVLink 的关系

  • NVLink 是 NVIDIA 用于节点内 GPU 高速互连的技术,提供高带宽、低延迟与缓存一致性(取决于架构),用于近距紧耦合场景
  • NVLink 用于同一节点内 GPU 直连(NVIDIA Magnum IO 等库);
  • RDMA(如 IB/RoCE)用于跨节点扩展,常配合 GPUDirect RDMA(GDR)等机制实现 GPU 到 GPU 的远程直接访问(绕过 CPU 提升端到端效率)

Micro Batch

  • 索引关键词:Micro Batch Size, Micro-Batch-Size, micro_batch_size
  • micro-batch :将 mini-batch 再切分的子批次,按序流经流水线,提升吞吐并降低气泡开销
  • 在流水线并行中,每个 GPU 在一次前向传播中处理的是一个 micro-batch;
    • 多个 micro-batch 逐个流过流水线各阶段,累积多次前向/反向后才做一次权重更新
  • 简单来说,一次前向就是一个 micro-batch :每个 GPU 每次前向处理一个 micro-batch,连续处理多个后再更新梯度
  • 关系公式:train_batch_size = micro_batch_per_gpu \(\times\) gradient_accumulation_steps \(\times\) world_size
  • 注:若不使用流水线并行,GPU 通常一次处理一个完整的 mini-batch(可以认为此时 micro-batch = mini-batch)
  • 注:流水线并行中,BatchNorm 统计量按 micro-batch 计算,同时维护全局移动平均用于推理

muP(Maximal Update Parameterization, \(\mu\)P)

  • muP(Maximal Update Parameterization, \(\mu\)P)是一种网络参数化与超参缩放框架,通过让前向/反向传播与参数更新的量级对模型宽度近似不变 ,实现超参数从小模型到更大模型的“零样本迁移”(\(\mu\)-Transfer) ,从而显著降低超参与训练稳定性的调优成本
  • 核心目标是让“好的超参”在放大时依然是“好的”,而非随宽度剧烈漂移
  • 做法要点有:
    • 初始化与权重尺度:令初始化方差随输入/输出维度缩放,确保激活与梯度量级稳定;对非方阵,在 fan_in/fan_out/fan_avg 间选择或统一处理
      • fan_in/fan_out/fan_avg 分别表示 输入神经元数/输出神经元数/输入输出神经元数两者均值
    • 学习率缩放:典型经验是学习率与宽度成反比(\(\eta \propto \frac{1}{d}\)),以控制每步损失增量不随规模爆炸;实际常配合层/模块自适应学习率
    • 保持训练动态稳定:将前向、反向、损失增量与特征变化的量级稳定化,使得跨尺度迁移可行
  • 与标准参数化(SP)的对比
    • SP:最优超参会随宽度显著变化,通常需要在每个尺度上重新调参
    • \(\mu\)P:通过重参数化让最优超参对宽度近似不变,实现“零样本迁移”
  • TLDR:\(\mu\)P 通过“宽度不变”的参数化与超参缩放,把小模型上的好超参“零样本”迁移到大模型,让“炼丹”更可预测、更省钱

梯度裁剪(Gradient Clipping)

  • 梯度裁剪(Gradient Clipping) 主要作用是通过限制模型参数梯度的全局范数,避免训练过程中因梯度爆炸(Gradient Explosion)导致的模型不稳定(如 loss 震荡、参数更新幅度过大等问题)
  • 梯度裁剪中最常用的L2范数梯度裁剪(L2 Gradient Clipping) 或全局梯度范数裁剪(Global Gradient Norm Clipping)
  • 核心步骤:
    • 1)计算全局梯度范数 :先将当前 batch 中所有可训练参数的梯度拼接成一个向量,计算该向量的 L2 范数(即“整体梯度范数”)
    • 2)判断是否裁剪 :若该全局范数超过预设的“裁剪阈值(clip_grad)”,则执行裁剪;若未超过,则不干预梯度
    • 3)统一缩放所有梯度 :通过乘以“裁剪阈值/全局梯度范数”的缩放系数,对所有参数的梯度进行等比例缩放 ,最终使全局梯度范数恰好等于裁剪阈值,确保梯度规模可控,避免训练不稳定(如梯度爆炸)
  • 代码实现:
    1
    2
    3
    4
    5
    6
    torch.nn.utils.clip_grad_norm_(
    parameters, # 待裁剪梯度的参数集合
    max_norm, # 梯度的“最大允许范数”(裁剪阈值)
    norm_type=2.0, # 计算范数的类型(默认L2范数)
    error_if_nonfinite=False # 若梯度范数为无穷/NaN,是否抛出错误
    )

Aux-Loss-Free 方法

  • Aux-Loss-Free 是 MoE 负载均衡的一种方法:在门控分数上加专家级偏置 \(b\) 来调节路由,全程不引入辅助损失及其梯度,从而避免干扰主任务(如语言建模)的优化方向,兼顾负载均衡与模型性能
  • 对比传统做法:
    • 传统做法:用辅助损失推平专家负载,但权重不当会污染主梯度、损害性能
    • Aux-Loss-Free 方法:在 top‑K 前给门控分数 \(g\) 加偏置 \(b\),仅影响路由结果,不回传梯度;负载过高的专家减小其 \(b\),负载不足的增大 \(b\),实现自适应均衡
  • Aux-Loss-Free 有许多优点
    • 无梯度干扰:不改变 loss,不影响主任务梯度,稳定训练
    • 因果一致:自回归可用,训练/推理一致
    • 高效可实现:开销小、易部署,工程友好
  • 常见实现为:
    • 偏置更新:按当前负载与均匀分布的偏差,用符号梯度更新 \(b\),如 \(b \leftarrow b − \alpha \text{sign}\)(实际负载 − 均匀负载)
    • 典型参数:更新率 \(\alpha \approx 0.001\);对门控激活较敏感,必要时解耦路由激活与主模型以稳定超参

EOT、EOD、EOS(结束符号辨析)

  • 在自然语言处理中,EOT、EOD、EOS 都是用于标识文本边界的特殊 token,他们核心差异在于所标记的「边界类型」不同
  • EOS(End of Sequence):序列结束符
    • 表示一个「完整序列」的结束,是最通用的结束标记
    • 在 GPT 系列中,<|endoftext|> 常作为 EOS 使用,标识生成文本的结束
    • 机器翻译时,模型输出 <EOS> 表示翻译句子完成
  • EOD(End of Document):文档结束符
    • 专门用于标识「文档/篇章」的结束,强调「长文本单元」的边界
    • 训练语料中,在每篇文章末尾插入 <EOD>,告诉模型“这篇文档到此结束”
    • 长文档理解任务中,用 EOD 标记章节或全文的结束
  • EOT(End of Turn):轮次结束符
    • 用于「对话场景」,标识一轮对话的结束,强调「交互单元」的边界
    • 对话训练数据中:用户:你好<EOT>模型:您好,有什么可以帮您?<EOT>
      • 聊天机器人生成回复时,用 <EOT> 标记当前轮次输出的结束

MPU(Model Parallel Unit)

  • MPU 一般指 Megatron’s MPU(Model Parallel Unit,模型并行单元),是 NVIDIA Megatron-LM 框架中用于实现大规模模型并行训练的核心组件
  • MPU 专为解决超大规模 Transformer 模型(如 GPT 系列)的训练挑战而设计,通过灵活的并行策略突破单设备内存限制,支持在多 GPU/多节点集群上高效训练千亿级参数模型
  • MPU 实现了 TP,PP 和 DP 的协同
  • MPU 的核心特性包括:“拆分模型、协调计算、管理通信”

1F1B

  • 1F1B,即 One Forward and One Backward, 也称为 PipeDream
  • 注意:1F1B 不同于 Gpipe,平时强调 1F1B 时主要是区别于 Gpipe
    • Gpipe 发表晚于第一篇 PipeDream 1F1B(与第二篇 PipeDream 前后顺序不确定),但效率其实不如 PipeDream 1F1B
  • 1F1B 主要有两种形式:
    • PipeDream 1F1B
    • Interleaved 1F1B:交错式 1F1B

PD 分离

  • 关键词:PD分离;
  • PD分离(Prefill-Decode 分离)是大模型推理系统的一种性能优化架构
  • PD 分离的核心思想是:把一次 LLM 推理拆成 Prefill 和 Decode 两个阶段,并分别部署到最适合的硬件资源池里,让它们各自独立扩缩容、互不干扰
    • PD 分离 让算力型任务去找算力卡 ,让显存型任务去找显存卡 ,用网络把中间结果搬过去,从而实现各自的优势
  • 传统做法把两个阶段放在同一 GPU 上顺序执行,会带来三大痛点:
    • Prefill 计算密集、Decode 内存带宽密集,混布时互相抢占,P99 延迟可飙升 78% 以上
    • 两类负载峰值时段不同,共池导致要么算力浪费、要么显存不足
    • 聊天机器人要首 token 时延(TTFT)<200 ms,代码补全却要求每 token 时延(TPOT)极低,共池架构无法兼顾
  • PD 分离具体做法:把 Prefill 节点单独成池,Decode 节点单独成池,中间通过 高速网络 把 KV Cache 从 P 池传给 D 池,之后两者完全异步、独立批量
  • PD 分离带来的好处
    • 吞吐提升 2–3 倍 :各自跑满最匹配的硬件,消除相互阻塞
    • 延迟更稳 :Decode 不再被新到的 Prefill 抢占,P99 延迟尖峰被削平
    • 显存节省 30%+ :KV Cache 可 offload 到 CPU/SSD,Decode 池只需留当前活跃缓存
    • 成本下降 :Prefill 池用 高算力低显存卡,Decode 池用大显存低算力卡,整机性价比更高
  • vLLM、SGLang 均已给出原型或生产级方案

Training Dynamics

  • Training Dynamics 指的是训练过程中模型的各种属性随时间或迭代轮数的变化情况,包括但不限于损失函数值、准确率、模型参数的更新、梯度的分布等属性

在线量化(Online Quantization)

  • 在线量化技术(Online Quantization)是模型量化的一种实现方式,指的是在模型推理过程中动态进行量化操作 ,而非像传统离线量化那样在推理前就完成所有量化参数(如缩放因子、零点)的计算和模型转换
  • Online Quantization 的核心特点是:
    • 1)动态计算量化参数 :量化所需的关键参数(如激活值的范围、缩放因子)不是预先计算好的,而是在模型实际运行时,根据实时输入数据的分布动态调整和更新
    • 2)边推理边量化 :模型在处理输入数据的同时完成量化转换,不需要提前对模型进行离线“校准”或“量化预处理”步骤
    • 3)适应性强 :能够更好地应对输入数据分布变化较大的场景(如不同用户、不同场景的输入差异),因为量化参数会随输入动态调整
  • 在线量化的优势在于灵活性高,无需预先准备校准数据集,适合输入分布不稳定的场景;
    • 但缺点是会增加推理时的计算开销(因为需要实时计算量化参数),可能对推理速度有一定影响
  • 这种技术常见于资源受限的边缘设备或需要处理多样化输入的场景,平衡了模型压缩需求和对动态数据的适应能力

Fill-in-Middle(中间填充)目标

  • Fill-in-Middle(中间填充)和Next-Token-Prediction 是大语言模型中两种不同的训练目标
  • Next-Token-Prediction(NTP) 任务本质是自左向右的单向预测 ,模型仅能基于前文信息推断后续内容,无法利用“后文”信息
  • Fill-in-Middle(FIM) 中,模型接收的输入被拆分为三部分:前缀(prefix)、待填充的空白(middle)、后缀(suffix)
    • 模型需要预测的是中间缺失的内容,且预测过程中同时利用前缀和后缀的信息
      • 例如:输入“今天天气[MASK],适合野餐”,模型需要预测中间的“很好”;或更复杂的拆分如“前缀+<mask>+后缀”,其中<mask>是待填充位置
  • DeepSeek-Coder-V2 和 DeepSeek-V3

SHARP

  • 在 Megatron-LM 或 Megatron-Core 这样的高性能分布式训练框架中提到的 SHARP 通常指的是 NVIDIA SHARP (Scalable Hierarchical Aggregation and Reduction Protocol) 技术
  • SHARP 是一种底层网络硬件加速技术 ,用于提高集体通信操作(\(\text{Collective}\) \(\text{Operations}\))的效率
  • SHARP 技术由 NVIDIA 开发,并集成在其 InfiniBand 网络硬件(如 Quantum 交换机)中
    • 主要目标是加速大规模 AI 和高性能计算 (HPC) 应用中的集体通信
  • 传统方式: 在分布式训练中,像 All-Reduce(用于同步梯度)、Reduce 和 Broadcast 这样的集体操作,需要将数据从一个 GPU 传输到另一个 GPU,并在 CPU 或 GPU 上执行计算(例如梯度求和)
    • 这个过程会占用宝贵的计算资源并产生大量的网络流量
  • SHARP 方式: SHARP 将这些集体操作的聚合和归约功能直接卸载到网络交换机的硬件中执行(称为 In-Network Computing)
  • 减少数据移动: Megatron 中可以选择开启 SHARP,在 SHARP 启用后,例如在执行 All-Reduce 时,数据沿着网络中的聚合树向上移动时,交换机就会实时地执行求和操作 ,而不是将所有原始数据都发送到终点
  • 效果: 这大大减少了网络中传输的数据量(只需传输归约后的结果),从而显著降低了通信延迟和缓解了网络拥堵
  • SHARP 通过加速这些底层的 NCCL(NVIDIA Collective Communications Library)操作,直接提高了 Megatron-LM 整体的训练吞吐量和效率(Model FLOPs Utilization, MFU),尤其是在涉及大规模 GPU 节点间的 DP 通信时效果显著

Rubrics

  • Rubrics(评估细则) 是一种奖励评估方法,与 Pairwise(成对比较), Likert 量表(李克特量表) 一样,都是生成式 RM 的一种评估方式
  • Rubrics 的做法是将复杂,难以评估的人类偏好转变成多个维度的二元可验证奖励
  • Pairwise是目前 RLHF 奖励模型(RM)训练中最主流和最可靠的方法
    • 评估难度低,可靠性相对较高,获取了相对一致的偏好数据;但是效率低,复杂度是 \(O(N^2)\)
  • Likert 量表 是效率最高、最常用的点式(Pointwise)评估方法之一
    • 效率高,复杂度仅 \(O(N)\) ,但分数存在不一致性(不同人评估标准不同)
    • Likert 量表本质是一种主观态度 / 偏好的测量工具,通过让受试者(可以是 LLM)对一系列陈述(如 “对上述描述的满意程度”)选择 Likert 等级(可根据不同的),将定性态度量化为定量分数
      • 通常为 5 级,此时为 5 点量表 (5-Point Scale):非常不同意、不同意、中立、同意、非常同意
      • 可以为 7 级,此时为 7 点量表 (7-Point Scale):非常不同意、不同意、稍不同意、中立、稍同意、同意、非常同意
      • 注:也可以是满意、不满意等,可以根据不同的 Setting 选择不同的评分方式
    • Likert 针对单个样本的主观评分,不直接体现样本间的相对顺序
    • Likert 分数具有序数意义(如 5 分 > 4 分),但不代表分数间的间隔相等(不能认为 5-4 的差异等于 4-3)
  • Rubrics 提供了最精细、可解释的评估,是高质量、专业性任务的首选
    • 线上效率高,复杂度仅 \(O(N)\),但需要离线构造 Rubrics,往往比较耗时
    • 可以与 Pairwise 结合使用(例如,通过 Pairwise 比较来动态生成或优化 Rubrics,或用 Rubrics 作为 LLM Judge 进行 Pointwise 评分的指导)
    • 容易因 Rubrics 设计不够好而出现 Reward Hacking,特别静态 Rubrics 可能发生 Reward Hacking
  • Dynamic Rubrics 可能是未来的趋势
    • 不再是静态固定的 Rubrics,而是在训练过程中,动态的根据模型的回复生成不同的 Rubrics
  • 其他思考:
    • 用多个模型生产 Rubrics,然后按照相似度聚合,同一个桶中的给一个想通的权重(即使用该桶的 count 来作为权重)
      • 理论上,随着模型数量的增加,效果会越来越好

Token 粘黏现象

  • Token 粘黏(这个名字暂时是我自己给的,待确认)
  • Token 粘黏 是指 Prompt 和 Response 之间直接组合时,可能存在的 tokenize(prompt + answer) 不等于 tokenize(prompt) + tokenize(answer) 现象
  • 该问题出现时,实际上可以在中间加一个 Special Token 来解决(最推荐的方式,一般可以完全解决问题)
  • 实际上,也可以使用一下两种方式来解决
    • 分开 tokenize,再合并(如果真的是比较特殊的情况,建议使用这种方式)
    • 合并 tokenize,将中间 Token 分配给 Prompt 或者 Response
  • 以上两种方式都可能导致 training 和 inference 不一致的情况
  • 注:从统计意义上来看,或许这个问题并不重要,哪种方式都没问题,因为如果中间这个 粘黏 Token 很常见的话,肯定会出现,从而被识别到,如果不常见的话,理论上 inference 时也会很少遇到

TIS(Truncated Importance Sampling)

  • TIS 即截断重要性采样,是一种方差缩减技术
  • TIS 通过智能筛选高价值样本并抑制异常值影响,来解决大模型训练中数据不平衡、标注成本高昂、计算资源消耗巨大等问题
  • 在强化学习中,TIS 还可用于减轻 rollout 与训练之间的差距,通过引入重要性权重修正梯度计算,有效处理 rollout 分布与训练分布的不匹配问题,提升训练性能

MIS(Multiple Importance Sampling)

  • 原始论文:(MIS)RL-PLUS: Countering Capability Boundary Collapse of LLMs in Reinforcement Learning with Hybrid-policy Optimization, PKU & Tongyi Lab, 20250731 & 20250805 & 20251019
  • MIS 是上述论文中提出的一种方法,对传统 PPO 的重要性采样进行了优化

IcePop

  • 原始博客:(IcePop)Small Leak Can Sink a Great Ship—Boost RL Training on MoE with IcePop!, AntGroup
  • IcePop 是蚂蚁 Ring-flash-2.0 搭载的独创算法,是一种梯度控制机制,能够解决 MoE 模型在长思维链RL训练中容易出现的“奖励崩溃”问题
  • Icepop 算法把训练中表现异常的 token 当场冻住,不让它们回传梯度,从而使训练能够持续稳定提升,避免梯度突然爆炸,减少训推精度差异

Megatron-Core(Mcore)

  • Megatron-Core,即 NVIDIA Megatron-Core,也称为 Megatron Core 或 MCore,是一个基于 PyTorch 的开源库,可在数千个 GPU 上以高速大规模训练大型模型
  • Megatron-Core 提供了先进的模型并行技术
    • 包括张量、序列、工作流、上下文和 MoE 专家并行等
    • 用户可以灵活结合不同的模型并行技术以适应训练工作负载
    • 还具备节省内存的功能,如激活重新计算、分布式优化器和分布式检查点等
  • Megatron-Core 通过模块化和可组合的 API 提供可定制的基础模组,对于 Transformer 模型,它提供注意力机制、归一化层、嵌入技术等
  • 借助 Megatron-Core(Mcore)规格系统,研究人员可以在 PyTorch 模型定义中轻松自定义所需抽象级别的子模块
  • Megatron-Core 是 Megatron-LM 的子集,即:
    • Megatron-LM 依赖 Megatron-Core 实现底层分布式逻辑;
    • 开发者可以单独使用 Megatron-Core,而无需引入 Megatron-LM 的全部功能

Flash Linear Attention(FLA)

  • FLA 是一种高效的自注意力机制实现方式
  • FLA 能够显著降低 Transformer 模型在长序列处理时的内存占用和计算复杂度
  • flame 框架是基于 torchtitan 构建的轻量级训练框架,专门为 FLA 模型的训练优化而设计,支持流式数据处理,避免了传统方法中繁琐的预处理步骤,特别适合大规模数据集训练

Gated DeltaNet(GDN)

  • GDN 是一种与 Mamba2 类似的架构,采用了粗糙的 head-wise 遗忘门

Kimi Delta Attention(KDA)

  • KDA 是 Kimi Linear 架构的核心,它是 Gated DeltaNet(GDN)的改进版本,引入了更高效的门控机制
  • KDA引入了一种 channel-wise 的变体,其中每个特征维度都保持独立的遗忘率,能够更精确地调控有限状态 RNN 的记忆
  • KDA通过 DPLR(Diagonal-Plus-Low-Rank,对角加低秩)矩阵的一种专门变体来参数化其转换动态,使得一种定制的分块并行算法成为可能,该算法相较于通用的 DPLR 公式能显著减少计算量
  • 注:KDA 是 GDN 的改进版本,GDN 的遗忘门机制相对较为简单,而 KDA 则通过更细粒度的门控设计对其进行了优化

Kernel Fusion

  • Kernel Fusion 不改变模型的数学逻辑,仅通过合并计算步骤、减少冗余开销,让硬件的算力和带宽得到更高效利用

  • 前置概念:

    • 计算核(Kernel):可以理解为 “硬件可直接执行的最小计算单元”—— 比如 PyTorch/TensorFlow 中的一个算子(如matmul矩阵乘法、add加法、gelu激活函数),最终都会被编译成 GPU/CPU 能执行的 Kernel
    • 串行 Kernel 的问题:大模型的关键模块(如自注意力、FFN)是由多个算子串联组成的(例:matmul + add + gelu + matmul + add)
      • 如果每个算子单独作为一个 Kernel 执行,会产生大量冗余开销(如寄存器到全局内存(来回)的数据搬运开销、Kernel 调度开销、中间结果存储作为临时变量占用额外显存等)
  • Kernel Fusion 的核心逻辑:“合并算子,减少冗余”,以 Transformer 的前馈网络(FFN)为例:

    • 原始串行执行(无Fusion):

      1
      2
      3
      4
      5
      6
      # 步骤1:第一个线性变换(matmul)+ 偏置(add)
      x1 = torch.matmul(x, w1) + b1
      # 步骤2:激活函数(gelu)
      x2 = torch.gelu(x1)
      # 步骤3:第二个线性变换(matmul)+ 偏置(add)
      x3 = torch.matmul(x2, w2) + b2
      • 注意:这会生成 3 个独立 Kernel,存在 2 次数据回写+调度开销
    • 融合后执行(Fused Kernel):

      1
      2
      3
      # 将`matmul + add + gelu + matmul + add`合并为一个Kernel,执行逻辑变为如下
      # 单个Kernel内完成所有计算,数据不落地到全局内存
      x3 = fused_ffn(x, w1, b1, w2, b2)
      • 数据在寄存器中完成x->x1->x2->x3的流转,彻底消除中间步骤的内存读写和调度开销
  • Kernel Fusion 无法保证 bit-wise 对齐 :

    • Kernel Fusion 会改变计算的 “实现细节”,但不改变 “数学逻辑”,而浮点计算的特性决定了 “实现细节差异” 会导致二进制结果不同

Weight Decay Scheduling

  • 权重衰减调度(Weight Decay Scheduling)是训练神经网络时,动态调整权重衰减系数(Weight Decay,代码中常常简写为 WD, wd)的策略
  • 简单来说:就是“不固定权重衰减的大小,让它随训练步数变化”,核心目的是平衡模型的“拟合能力”和“泛化能力”,避免训练初期欠拟合、后期过拟合
  • 权重衰减(Weight Decay)本身是什么?
    • 权重衰减本质是 L2正则化 的一种实现(部分框架中二者等价)
    • 通过在损失函数中添加“权重参数的平方和”项,惩罚过大的权重值,避免模型过度依赖少数特征(即过拟合)
    • 比如:如果权重衰减系数 wd=0.0001,相当于每次更新权重时,都会让权重乘以 (1 - wd),间接限制权重增长
  • 为什么需要“动态调整”?
    • 训练初期:模型还在学习基础特征,此时如果权重衰减过大,会“压制”模型的拟合能力,导致欠拟合(学不到关键模式)
    • 训练后期:模型已经掌握核心特征,此时需要更大的权重衰减来抑制过拟合(避免模型“死记硬背”训练数据的噪声)
  • 权重衰减调度的核心思路:
    • 让权重衰减系数从“小”逐步变“大”,适配训练不同阶段的需求
  • 注:权重衰减调度与学习率调度,目标、逻辑完全独立

权重衰减调度的实现

  • 权重衰减调度的核心是 按训练步数(global_step)动态调整系数,最常用的是 线性递增调度
    • Megatron-LM 中默认/推荐策略是 线性递增调度 constant
  • 定义关键参数(不同框架参数名称可能不同,但都差不多):
    • wd_init:初始权重衰减系数(通常设为 0,训练初期不衰减);
    • wd:目标权重衰减系数(训练后期稳定值,如 0.01);
    • wd_incr_steps:从 wd_init 递增到 wd 的总步数(如 5000 步)
  • 分阶段调整:
    • 阶段1(递增期):global_step < wd_incr_steps
      • 权重衰减系数随步数线性增长:current_wd = wd_init + (wd - wd_init) * (global_step / wd_incr_steps)
    • 阶段2(稳定期):global_step >= wd_incr_steps
      • 权重衰减系数固定为目标值 wd,持续抑制过拟合

closing tag 和 opening tag

  • 在编程与标记语言(如 HTML、XML、Vue、WXML 等)中的核心概念,“closing tag” 是 “闭合标签” ,核心作用是 标记一段内容的结束
  • “closing tag”常常与对应的 起始标签(opening tag) 成对出现,确保语法结构完整、解析器能正确识别内容边界
  • 闭合标签的格式是 <标签名/>(自闭合,适用于无内容的标签)或 </标签名>(常规闭合,适用于包含内容的标签),关键是比起始标签多了斜杠 `/
  • 闭合标签告诉解析器(如浏览器、框架编译器)“当前标签对应的内容到此结束”,避免解析混乱(比如嵌套标签的层级、样式/逻辑的作用范围)
  • 大多数包含内容的标签都需要闭合标签,例如 HTML 中的 <div> <p> <span>,Vue 中的 <template> <component> 等

PBRL (Preference-Based Reinforcement Learning)

  • PBRL 是一种强化学习范式,其核心思想是 “告诉模型哪个更好,而不是给模型一个具体的奖励打分”
    • 在传统的强化学习中, Agent 通过环境反馈的数值奖励(Reward Function,例如 \(+1\) 或 \(-10\))来学习
      • 但在许多复杂任务中,设计一个完美的数值奖励函数极其困难(即 “Reward Engineering” 难题)
    • PBRL 解决了这个问题:它不依赖预定义的数值奖励,而是依赖于“偏好”反馈 ,核心工作流程如下:
      • 1)交互与展示:智能体在环境中执行动作,生成若干条轨迹(Trajectory)或结果
      • 2)偏好查询: 系统将这些轨迹展示给评估者(可以是人类、专家系统或预设规则),询问:“哪一个更好?”
      • 3)奖励建模 (Reward Modeling):利用这些成对的偏好数据,训练一个神经网络(奖励模型),使其输出的数值满足评估者的偏好排序
      • 4)策略优化:智能体利用这个学到的奖励模型 ,使用标准的强化学习算法(如 PPO, SAC)来优化策略
  • PBRL vs RLHF:
    • PBRL 是一个更广泛的算法框架/方法论;
      • 反馈来源 不限于人类;
      • 适用于各种场景;比如 让机械臂尝试两次倒水,一次很快但溅出来了,一次很慢但没溅出来;人类看着说:“第二次更好”
      • 关注如何在没有奖励函数的情况下解决控制问题,以及如何提高样本效率
      • PBRL 是理论基础 :它告诉我们,可以通过比较(A > B)来学习奖励函数,进而训练智能体
    • RLHF 是 PBRL 在 LLM 场景中的一个特定应用实例,是特定的技术 Pipeline;
      • 反馈来源一般是 “Human”;
      • 仅适用于 LLM 场景;
      • 关注如何让模型对齐人类价值观(有用性、诚实性、无害性)
      • RLHF 是具体应用 :它是将 PBRL 的理论应用到 LLM 上,并引入了人类价值观(Human Alignment)作为核心目标的特定技术路线

Generative RM

  • Generative RM(Generative Reward Model,生成式奖励模型) ,有时候也简称 GenRM
  • Generative RM 通常不需要改变模型结构,而且一般不需要微调或少量微调即可很好的用于评估其他模型的输出质量
  • Generative RM 的核心是复用大模型的上下文能力,用 prompt 提示大模型对模型答案给出分数
  • 与 Generative RM 对照的是 标准奖励模型(Standard RM),标准奖励模型一般是在大模型最后再增加一个 MLP 来输出最终的分数

Discriminative RM

  • Discriminative RM(Discriminative Reward Model,判别式奖励模型) ,有时直接使用 奖励模型(RM)来称呼
    • 别名:Standard RM(Standard Reward Model,标准奖励模型),有时也称为朴素奖励模型(Naive RM)
  • Discriminative RM 的核心设计是在预训练的大模型顶部直接接入一个奖励头,通常是线性层或简单的多层感知机(MLP),模型处理输入后直接输出一个标量奖励分数来评估结果质量
  • Discriminative RM 模型训练和推理速度极快,计算成本低,但一般需要针对特定数据进行微调
  • BERT 当时使用专有的 [CLS] Token 作为特殊标记从而实现输出分类的方式,也可以认为是一种 奖励模型

BT RM

  • 当 Discriminative RM 的损失是 Bradley-Terry 建模时,也称为 Bradley-Terry Reward Model ,简称 BT RM

Heavy Mode(Deep Thinking Mode)

  • Heavy Mode(译为 Heavy 模式)和 Deep Thinking Mode 本质上是同一概念的不同表述
    • 他们都指代通过并行推理和延长思考时间来提升模型复杂任务处理能力的机制
    • Heavy Mode :强调资源消耗和计算强度(Heavy=重型/高负载)
    • Deep Thinking/Deep Think Mode :强调思维深度和推理过程

相关名词辨析

  • 不同厂商/场景的表达方式不一定完全一致
  • Heavy Mode :一般在通用技术社区中使用,强调并行资源消耗
    • 如 Introducing Kimi K2 Thinking, 20251106, Moonshot AI (Kimi)
    • 2025年8月,xAI Grok 4 也用 Heavy 的表达
  • Deep Think :如 Google Gemini 在其产品中作为功能命名,强调用户体验
  • Thinking Mode :GPT-5 Pro 官方用作功能标签,所以 Kimi K2: Open Agentic Intelligence, Moonshot AI (Kimi), 20250728 中 提到的 GPT-5 Heavy 模式就是 GPT-5 Pro 的 Thinking Mode
  • 其他非官方表达还有:
    • 多智能体推理 或 并行思维技术 等
  • 理解:该名词最早源于”深度思考”(Deep Thinking)作为认知概念早在心理学和教育学文献中就有探讨,指构建概念间深层连接的思维过程,但并非AI领域的专用术语
  • Google Gemini 2.5 Deep Think(2025年)是第一个提出这个的,即 Google 最早将 Deep Think 这一概念转化为产品化命名
  • 首个面向公众的多智能体推理系统:2025年I/O 大会上首次公开发布Gemini 2.5 Deep Think
    • 其核心创新点为:”同时生成多个AI智能体,并行处理同一问题”
    • 其变体在2025年国际数学奥林匹克(IMO)斩获金牌,验证了架构有效性

具体方法

  • Introducing Kimi K2 Thinking, 20251106, Moonshot AI (Kimi) 原文:

    Heavy Mode​: K2 Thinking Heavy Mode employs an efficient parallel strategy: it first rolls out eight trajectories simultaneously, then reflectively aggregates all outputs to generate the final result. Heavy mode for GPT-5 denotes the official GPT-5 Pro score.

  • 说明:Heavy Mode 是一种通用的提升模型推理能力的方法,其方式为:
    • 第一步:并行采样多个 Trajectories
    • 第二步:结合第一步生成的结果放入模型,让 Summary 模型来解决问题
  • 针对第一步给出的答案,针对不同的 query,第二步有不同的处理方式,query 可以分为以下三种:
    • 投票可得正确解:直接通过投票获取到正确答案
      • 这种类型的 Query 甚至无需 Summary 模型,直接投票即可
    • 投票得不到正确解,但正确 rollout 的数量也不少
      • Summary 模型真正发挥的地方,有一定能力的 Summary 模型可以找到正确解
    • 极少部分正确:模型给出的结果大多都错误,投票几乎不可能得到正确解
      • Summary 也难以得到正确解
      • 这类 Query 有望通过 RL 来改善其回答?

OpenRouter

  • OpenRouter 是 2023 年初成立于美国纽约的 AI 基础设施公司,核心是AI 模型聚合与智能路由平台
  • OpenRouter 接入了大部分主流 AI 模型(25 年已经超 450 个),涵盖 OpenAI、Anthropic、Google 等头部厂商及 DeepSeek、Qwen 等开源模型,用户凭一个 API Key 就能调用所有模型,还能切换使用自有密钥或平台密钥
    • OpenRouter 会依据价格、时延等 300+ 指标智能调度请求,50 毫秒内即可完成故障模型的备用切换
  • OpenRouter 提供透明的阶梯计费与详细使用报告,支持国内支付,还包含交互式游乐场方便测试模型;
  • OpenRouter 默认不存储用户数据,企业用户可启用零数据留存策略保障隐私,同时兼容 OpenAI SDK 及 Coze、LangChain 等多款工具,集成门槛低

Claude Code

  • Claude Code 是 Anthropic 公司(OpenAI 前员工创立)为 Claude 大模型打造的代码专属能力模块 ,本质是 Claude 大模型针对编程场景的优化版本,而非独立工具
  • Claude Code 提供 代码全流程支持:覆盖代码生成、调试、重构、注释、性能优化、多语言迁移(如 Python 转 Go、Java 转 Dart),还能理解复杂业务逻辑并生成符合工程规范的代码;
  • Claude Code 支持超长上下文窗口(Claude 3 系列可达 200k+ tokens),能处理数万行代码的完整项目,理解跨文件依赖和架构逻辑;
  • Claude Code 支持主流编程语言(Python、Java、JavaScript/TypeScript、Go、Rust、Dart/Flutter 等),且能解释代码逻辑、排查 Bug;
  • Claude Code 支持自然语言对话式编程,可根据开发者的模糊需求逐步细化代码,还能针对代码问题给出详细的错误分析和修复方案
  • Claude Code 适用于快速编写业务代码、重构老旧项目、排查复杂代码 Bug、生成技术文档、辅助学习编程

Terminus

  • Terminus 主流释义:开源跨平台终端模拟器(Terminus Terminal),定制化、高效的命令行操作环境
  • Terminus 是一款 现代化、高度可定制的开源终端工具 ,替代传统的 Windows CMD、macOS Terminal、Linux xterm 等,定位是“开发者友好的终端模拟器 + SSH 客户端”
  • Terminus 核心特点是跨平台:支持 Windows、macOS、Linux,统一多系统终端体验;
  • Terminus 内置 SSH/SFTP 客户端、Zsh/Bash 集成、插件系统(如代码高亮、自动补全);
  • Terminus 轻量且响应快,支持 GPU 渲染,适配高分屏(4K)
  • Terminus 适用于开发者日常命令行操作、远程服务器 SSH 连接、多终端会话管理、自定义开发环境终端

Flutter

  • Flutter 是 Google 推出的跨平台 UI 开发框架 ,核心定位是帮助开发者用一套代码构建高性能、高保真的移动/桌面/网页应用,彻底打破“多端多代码”的传统开发模式
  • 注:Dart 是由 Google 主导开发的、开源的强类型编程语言,首次发布于 2011 年,设计目标是兼顾前端(跨平台 UI)、后端、命令行等多场景开发,核心定位是「面向多平台的现代化编程语言」,也是 Flutter 框架的唯一开发语言
  • Flutter 有跨平台能力:一套代码多端运行,提升开发效率
    • 一套 Dart 代码可编译为 iOS、Android、Windows、macOS、Linux、Web 甚至嵌入式设备的原生应用,无需为不同平台单独适配核心逻辑;
  • Flutter 适用于典型应用场景:移动应用开发(如闲鱼、腾讯视频、字节跳动部分产品)、跨端桌面应用、轻量级 Web 应用
  • Flutter 可以和 Terminus 搭配使用:
    • 用 Terminus 执行 Flutter 命令行开发
  • Flutter 可以和 Claude Code 搭配使用:
    • 借助 Claude Code 生成/调试 Flutter 代码

Interleaved Thinking

  • vLLM 给出的定义:Interleaved Thinking

  • MiniMax-M2 就是一个 Interleaved Thinking 模型:huggingface.co/MiniMaxAI/MiniMax-M2

  • Interleaved Thinking 指模型在工具调用之间进行推理(Reason),允许模型在接受工具调用结果后,更加精细的决策

  • Interleaved Thinking 会增加 Token 使用和响应时长

  • Interleaved Thinking 目前已经是 vLLM 的内嵌支持了

    • 截止到 20251207日,支持 Kimi-K2 和 MiniMax-M2 两个模型
  • 使用方式:在 messages 的 "assistant" role 中加入 "reasoning" 字段

    1
    2
    3
    4
    5
    6
    7
    messages.append(
    {
    "role": "assistant",
    "tool_calls": response.choices[0].message.tool_calls,
    "reasoning": response.choices[0].message.reasoning, # append reasoning
    }
    )
    • 更多详情参考:(vLLM)Interleaved Thinking

Tool‑Integrated Reasoning(TIR)

  • TIR,即 Tool‑Integrated Reasoning(工具集成推理) ,是让模型在推理流程中调用外部工具(如代码解释器、计算器、搜索引擎)来提升复杂任务解决能力的范式
  • TIR 允许模型在多轮推理中动态调用工具,将自然语言推理与外部计算能力结合,解决纯文本模型难以处理的计算密集、知识依赖或精确性要求高的问题(如数学推理、数据验证、复杂规划)
  • TIR 的核心价值在于严格扩展模型的有效解空间,让原本不可行的推理路径变得可行
  • TIR 的形式化表示:多轮 TIR 的推理轨迹可形式化为迭代序列:
    $$\tau = \mathcal{A}_1 \oplus \mathcal{A}_2 \oplus \cdots \oplus \mathcal{A}_N$$
    • 其中每一步动作 \(\mathcal{A}_k = \langle s_k, t_k, o_k \rangle\) 包含:
      • \(s_k\):自然语言推理片段(思考过程)
      • \(t_k\):工具调用指令(工具名+参数)
      • \(o_k\):工具返回的输出结果
  • TIR 的关键挑战是多轮交互中的分布偏移导致训练不稳定、工具反馈污染上下文、轮次增多时推理漂移
  • TIR 典型应用场景如数学解题(代码执行验算)、数据处理(表格分析)、复杂决策(多工具组合调用)等
  • TIR 与 Interval Thinking 的区别:
    • TIR 与 Interleaved Thinking 的本质差异在于设计重心:
      • TIR 以工具为核心扩展推理能力
      • Interleaved Thinking 以交错执行架构保障推理的连续性与可修正性
    • 在工程实践中,两者常结合使用:
      • 例如,基于 Interleaved Thinking 的架构实现 TIR 的多轮工具调用,既保证工具能力的有效利用,又避免长链路中的状态漂移

AnyCoder

  • AnyCoder 是一款部署在 Hugging Face Spaces 上的开源 AI 编程辅助 Web 应用 ,由 Hugging Face 机器学习增长负责人Ahsen Khaliq(@akhaliq)开发维护,其核心定位是“直觉式编程工具(Intuitive Programming Tool)”,
  • AnyCoder 工具主打无需深厚编程基础,就能通过自然语言描述或多模态输入快速生成可运行的代码,尤其聚焦前端开发场景
    • 它既可为新手开发者降低编程门槛,也能帮助专业开发者提升开发效率,同时可作为 Lovable 等同类直觉式编程服务的替代方案
  • AnyCoder 完全基于 Hugging Face 的开源 Python 开发环境 Gradio 构建,同时提供两个版本适配不同需求:
    • 一是功能完整的 Python 版本,支持 OCR 处理、PDF 和 docx 文档解析等高级能力;
    • 二是轻量化的 TypeScript 版本,聚焦核心 AI 代码生成功能,启动速度更快,移动端体验更优

GSM-style

  • GSM-style 推理任务核心是仿照 GSM8K(Grade School Math 8K)基准设计的多步骤小学数学文字题推理任务,核心考察 LLM 的分步算术推理与逻辑推导能力,需模型输出完整解题思路而非仅答案

“氛围测试”(Vibe-tests/Vibe-checks)

  • 在人工智能和 LLM 的语境下,“氛围测试”(Vibe-tests)或“氛围检查”(Vibe-checks)是一种 基于人类直觉、主观感受和非正式交互的评估方法
  • Vibe-tests 与传统的、基于严格指标(如准确率、BLEU 分数、MMLU 基准测试)的评估方式不同
  • Vibe-tests 是指开发者或用户通过与模型对话,凭“感觉”来判断模型够不够聪明、回答是否得体、或者生成的应用是否符合预期
    • 一般指在模型发布前或开发过程中,由开发者、研究人员或社区成员对模型进行的手动、非正式评估
  • Vibe-tests 让我可可以从“看跑分”到“看感觉”
    • 主观性:不依赖预定义的测试集,而是依赖评估者对模型输出质量的“主观印象”
    • 整体性:不仅仅关注答案的对错,更关注模型的语气、安全性、拒绝回答的边界(是否过于敏感)、逻辑流畅度以及是否“像人一样思考”
  • 术语来源:似乎与“氛围编程”(Vibe Coding)有关
    • OpenAI 联合创始人 Andrej Karpathy 在 2025 年初推广了 “Vibe Coding” 的概念,即通过自然语言提示让 AI 生成代码,人类主要负责审查和确认
    • “Vibe-tests 指人类在验收这些 AI 生成的成果时,主要检查其功能和交互体验是否符合“预期的氛围”或直觉,而不是逐行检查代码逻辑
  • 特点:
    • 速度快,随时可测,但无法批量化
    • 真实,细微,但包含主观感受
    • 避免大模型在公开的学术基准(Benchmarks)上都能拿到高分,但在实际使用中却可能表现出“高分低能”
      • 例如,模型可能通过了逻辑测试,但在聊天时显得生硬、机械或充满偏见
    • 可以进行难以量化的体验
      • 很多用户体验指标(如幽默感、情商、创造力、语气的恰当性)是很难用数学公式量化的,只能靠人的“Vibe”(直觉/氛围)来感知
  • 表现形式:
    • Sanity Check(健全性检查):开发者随便问模型几个刁钻的问题(例如弱智吧问题、陷阱题),看模型是否会“一本正经地胡说八道”
      • 如果模型能识破陷阱,通常被认为“Vibe”是对的
    • 语气与风格审查:检查模型是否过于说教(Preachy),或者是否在不该拒绝的时候拒绝用户(False Refusal)
      • 例如,用户让模型写个恐怖故事,模型却因为“安全原因”拒绝,这会被认为“Vibe check failed”(氛围检查不通过)
    • 端到端体验:在 Agent 或长文本应用中,观察模型是否能长时间保持角色一致,或者是否能真正理解用户的意图而不仅仅是关键词匹配

TTS

  • TTS 有两个含义:
  • TTS 含义1:Test-Time Scaling,推理时扩展/测试时扩展
    • 核心逻辑是:“让模型在回答问题前多思考一会儿,表现会更好”
    • 传统上,通过增加训练数据和参数量(训练时扩展)来提升模型能力,而 Test-Time Scaling 发现,在推理阶段给模型分配更多的计算资源(Compute),也能显著提升逻辑推理、数学和编程能力
  • TTS 含义2:Text-To-Speech,语音合成 / 文本转语音

slop score

  • 在 AI/LLM 领域(slop score),slop score 可用于量化低质量 AI 生成文本(俗称 “slop”)的指标,衡量文本的结构冗余、语义空洞、重复模式等负面特征,反映输出质量的劣化程度
    • 注:slop 本意有“泔脚水;淡而无味的半流质食物;脏水” 的含义
  • slop score 可用于评估模型生成文本的 “slop” 占比,指导模型优化(如 Antislop 框架)以减少低质输出

CUA

    • 在大模型领域,CUA 是 Computer-Using Agent(计算机使用代理) 的简称
  • CUA 这一简称是 OpenAI 最早正式提出并定义的 ,该定义随 OpenAI 智能体产品 Operator 于 2025年1月23日 的研究预览版一同发布,用于特指能通过 GUI 交互完成数字任务的核心模型
    • 将 Operator 的核心驱动模型命名为 Computer-Using Agent,并缩写为 CUA,强调其融合 GPT-4o 视觉能力与强化学习推理能力的技术特性
    • 融合多模态视觉能力与强化学习驱动的高级推理能力,可通过虚拟鼠标键盘与图形用户界面(GUI)交互,无需依赖特定 API 即可完成网页操作、表单填写等复杂数字任务
  • 注:此前 Anthropic 等机构虽有计算机使用相关智能体研究,但未使用 CUA 这一缩写;

Skills

  • Skills 最早由 Anthropic 提出,也叫做 Claude Skills
  • Skills 本质属性是知识封装/流程规范/任务编排器,是“工作流说明书”,让 Claude 按特定流程、规范高效完成专业任务
  • Skills 可用于 封装 SOP、模板、最佳实践,指导工具调用顺序与参数,定义输入输出格式
  • Claude Skills 是按需加载的,仅匹配任务时调用相关内容,节省 Token 且高效
  • Claude Skills 以 SKILL.md 为核心的文件夹,含 Markdown 文档、脚本、模板等,支持版本控制与分享
  • 与 MCP 的区别和联系:
    • Claude Skills 和 MCP(Model Context Protocol)核心区别在于定位与职责:
      • MCP 是连接大模型与外部工具/数据源的标准化接口协议,解决“怎么连”的问题,更像是菜谱(规定步骤与标准)、施工蓝图
      • Skills 是封装工作流、规范与最佳实践的任务执行模块,解决“怎么做”的问题,更像是厨房设备(提供工具与连接能力)、施工设备与原材料
      • 注:二者常协同使用
    • Skills 与 MCP 并非竞争,而是互补:
      • 1)Skills 可引用 MCP 配置的工具,定义调用逻辑与参数,让复杂任务流程化
      • 2)MCP 为 Skills 提供底层连接能力,保障外部工具的稳定、安全调用
      • 3)示例:“Notion 内容管理”Skill 定义搜索、创建页面的步骤,而 MCP 负责 Notion API 的认证、请求转发与结果返回

Reference

  • Skills 还可以通过 Reference 引用其他 Skills 文档,从而进行更进一步的封装,按需使用说明书
  • 这种做法可以进一步减少复杂场景下的 Prompt 长度,从而节省 Token,也让模型的执行效率更高
  • 比如:仅当用户要求计算房租/酒店价格时,再调用房租价格列表/酒店价格列表

Script

  • Skills 还可以通过调用其他 Script 执行任务
  • 注意:Script 执行的任务非必要不会进入模型的上下文,所以很省 Token

MOPD

  • Multi-Teacher On-Policy Distillation (MOPD) 方法最早出自 MiMo 技术报告 MiMo-V2-Flash Technical Report, Xiaomi, 20260106

Rollout Routing Replay (R3)

  • Rollout Routing Replay (R3) 的本质含义是在 大模型的 RL 训练中,使用与 rollout 相同的路由专家进行 RL 训练,通过优化的数据类型和通信重叠使其开销可忽略不计

span-level 重复率

  • 在 LLM 的训练、数据清洗和效果评估中,样本 span-level 的重复率 是针对 文本片段(span) 维度的重复度量
  • span-level 重复率的核心是跳出 整句/整样本是否重复 的粗粒度判断,聚焦 文本中连续/离散的子片段 的重复情况,是衡量数据质量、模型生成冗余性的关键细粒度指标
  • span-level 重复率也是 LLM 领域解决训练数据过拟合、生成文本重复/赘余问题的核心观测维度
  • span 的定义:
    • span 就是文本中任意长度的连续字符/词/子句片段,span-level 重复率就是统计这些片段在数据集(或模型生成结果)中重复出现的概率/比例
    • 区别于 样本级重复率(整样本完全重复)和 token-level 重复率(单个词/字的重复),是介于两者之间的中粒度、更贴近语义的重复度量

Self-Consistency

  • Self-Consistency(自洽性) 是 LLM 推理的一种解码策略 ,核心是:对同一问题生成多条不同推理路径,再通过“多数投票”选出最一致的答案 ,从而提升复杂推理的准确性与鲁棒性
  • Self-Consistency 建立在 Chain-of-Thought(CoT,思维链) 之上,解决的是:
    • 传统 CoT 只用单一贪心解码(temperature=0),容易陷入局部最优、受随机噪声影响,导致推理错误
    • 复杂问题通常有多条合理路径通向正确答案 ,而错误答案往往路径单一、不一致
  • 基本思想:多路径采样 + 一致性投票 = 更可靠的推理
  • Self-Consistency 解码的标准流程:
    • 1)CoT 提示 :给模型带推理步骤的 prompt,引导它“一步步想”
    • 2)多路径采样 :用 temperature>0、top-k/top-p 等采样方式,生成 N 条不同推理链(比如 5/10/20 条)
    • 3)结果聚合 :提取每条链的最终答案,做多数投票(或加权、边际化)
    • 4)输出最优解 :把得票最高的答案作为最终结果

Adaptive KL Controller(Dynamic KL Coef)

  • 原始论文:Fine-Tuning Language Models from Human Preferences, 20200108, OpenAI
  • 注:原文没有给确定性名字,称为 Dynamic vary KL Coef,部分博客或者实现会叫做 Adaptive KL Controller、 Adaptive KL Scheduler 或者 Dynamic KL Scheduler 等
  • 提出背景:作者发现,在固定的 KL Penalty 系数 \(\beta\) 下,使用不同的随机种子训练模型有时候会导致模型的 KL 散度值 \(\text{KL}(\pi_t, \rho)\) 会因为随机种子不同而有显著差异
    • KL 值直接影响生成质量,不同 KL 的模型无法公平对比任务性能,干扰实验结论
  • 解决方案:设定一个目标 KL 值 \(KL_{target}\),动态调整\(\beta\)的核心目标是:让 \(KL(\pi, \rho)\) 稳定收敛到预设目标值 \(KL_{target}\)
    • 具体做法:若当前 KL 值大于目标值时,尽量降低 KL 值,反之提升 KL 值(保持在目标附近)
    • 考虑到下面的规律:
      • \(\beta\) 越大,KL 惩罚越强,微调策略 \(\pi\) 越接近预训练模型 \(\rho\),生成文本自然度高但任务性能可能不足;
      • \(\beta\) 越小,KL 惩罚越弱,\(\pi\) 越自由优化奖励模型 \(r\),任务性能可能提升但易出现文本漂移、不连贯的问题
    • 基于 类似 PID 的思路 对 KL Penalty 系数 \(\beta\) 进行动态调整
  • 优点:
    • 稳定 KL 约束 :确保不同实验、不同任务中,\(KL(\pi, \rho)\) 始终围绕 \(KL_{target}\) 波动,避免模型漂移或过度保守;
    • 简化调参流程 :无需手动测试多个 \(\beta\) 值,仅需设定合理的 \(KL_{target}\) 即可;
    • 提升模型可比性 :所有实验在统一的 KL 约束下进行,任务性能差异仅源于模型本身,而非 KL 散度的波动
  • 实际使用中遇到的难点:
    • KL 散度的目标值 \(KL_{target}\) 很难设计

动态调整 \(\beta\) 公式细节

  • 论文中采用对数空间比例控制器 ,通过实时计算 KL 误差来迭代更新\(\beta\),公式分为两步
  • 第一步:计算 KL 误差项\(e_t\)
    $$ e_t = \text{clip}\left( \frac{KL(\pi_t, \rho) - KL_{target} }{KL_{target} }, -0.2, 0.2 \right) $$
    • \(KL(\pi_t, \rho)\):\(t\) 时刻策略 \(\pi_t\) 与预训练模型 \(\rho\) 的 KL 散度
    • \(KL_{target}\):预设的目标 KL 值
    • \(\text{clip}(\cdot, -0.2, 0.2)\):将误差截断在 \([-0.2, 0.2]\) 区间,避免极端误差导致 \(\beta\) 突变
    • 理解:
      • 若 \(KL(\pi_t, \rho) > KL_{target}\),则 \(e_t > 0\),说明当前 KL 过大,需增大 \(\beta\) 增强惩罚
      • 若 \(KL(\pi_t, \rho) < KL_{target}\),则 \(e_t < 0\),说明当前 KL 过小,需减小 \(\beta\) 减弱惩罚
  • 第二步:迭代更新 KL 系数 \(\beta_{t+1}\)
    $$ \beta_{t+1} = \beta_t \cdot \left( 1 + K_\beta \cdot e_t \right) $$
    • \(\beta_t\):\(t\) 时刻的当前 \(\beta\) 值
    • \(K_\beta = 0.1\):固定比例系数,控制 \(\beta\) 的调整步长
    • 理解:
      • 当 \(e_t > 0\) 时,\(1 + K_\beta \cdot e_t > 1\),\(\beta_{t+1} > \beta_t\),KL 惩罚增强,\(\pi\) 向 \(\rho\)靠拢;
      • 当 \(e_t < 0\) 时,\(1 + K_\beta \cdot e_t < 1\),\(\beta_{t+1} < \beta_t\),KL 惩罚减弱,\(\pi\) 可更自由优化任务奖励;
      • 当 \(e_t = 0\) 时,\(\beta_{t+1} = \beta_t\),训练进入稳定状态

Python——Ray-远程函数与本地函数的区别


整体说明

  • 远程函数与本地函数的区别主要在 序列化机制 和 执行位置 两个维度
  • 序列化本质差异:
    • 本地函数是“传引用”,依赖执行环境已有定义;
    • Ray 远程函数是“传定义+环境”,集群自动同步,支持跨节点;
  • 执行位置差异:
    • 本地函数固定在调用方进程,无分布式能力;
    • Ray 远程函数由集群调度,可分布式并发执行;
  • 使用场景:
    • 本地函数:适用于单进程/单节点的简单逻辑,无需分布式;
    • Ray 远程函数:适用于分布式计算、并发任务、跨节点执行,是 Ray 分布式能力的核心
  • 核心差异总览
    对比维度 本地函数(未用 @ray.remote 装饰) Ray 远程函数(用 @ray.remote 装饰)
    序列化方式 依赖 Python 原生 pickle,仅序列化「函数引用」 Ray 自定义序列化(结合 pickle+集群元数据),序列化「函数元信息+代码定义」
    序列化限制 无法跨节点传递(远程节点无函数定义,引用失效) 可跨节点传递(集群自动同步函数定义到执行节点)
    执行位置 固定在「调用方所在的本地进程/线程」 分布式调度到「集群任意节点的 Worker 进程」(可指定资源)
    执行特性 同步执行,阻塞调用方;无并发调度能力 异步执行,返回 ObjectRef;支持集群级并发/分布式调度
    依赖传递 需手动确保执行环境有函数依赖(如导入、变量) Ray 自动打包函数依赖(如嵌套函数、闭包变量)并分发

序列化机制:“仅传引用” vs “传定义+元信息”

  • 序列化的核心目的是:让函数能在「非定义环境」中被正确执行
  • 两者的序列化逻辑完全不同:

本地函数:仅序列化“函数引用”,无实际代码

  • Python 原生 pickle 序列化本地函数时,不会打包函数的代码本身 ,只会记录函数的「模块路径+函数名」(比如 __main__.add)
  • 这种“引用式序列化”仅在「同一进程/同一节点且函数已定义」的场景下有效,跨节点会直接失效
  • 错误示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import ray

    ray.init(ignore_reinit_error=True)

    # 本地函数
    def add_remote(a, b):
    return a + b

    # 直接传递远程函数的引用(Ray 自动处理序列化)
    @ray.remote
    def execute_remote_func(func, x, y):
    return func(x,y) # 远程工作进程无法识别调用方的 local func,错误

    # 跨节点调度执行(单节点可以成功,但集群有多个节点会失败)
    result_ref = execute_remote_func.remote(add_remote, 2, 3)
    print(ray.get(result_ref)) # 单节点输出:5(成功执行);多节点执行错误

    ray.shutdown()

Ray 远程函数:序列化“函数元信息+代码定义”

  • Ray 对远程函数的序列化做了增强 :
    • 1)序列化时,不仅记录函数引用,还会打包函数的代码定义、依赖模块、闭包变量(若有);
    • 2)远程节点接收后,会自动还原函数的执行环境(无需手动导入);
    • 3)底层用 Ray 自定义的序列化器(兼容 pickle,但更适合分布式场景)
  • 正确示例:远程函数跨节点调用成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import ray

    ray.init(ignore_reinit_error=True)

    # Ray 远程函数(已注册,自动序列化代码)
    @ray.remote
    def add_remote(a, b):
    return a + b

    # 直接传递远程函数的引用(Ray 自动处理序列化)
    @ray.remote
    def execute_remote_func(func, x, y):
    return ray.get(func.remote(x, y)) # 远程节点能识别并执行

    # 跨节点调度执行(即使集群有多个节点也能成功)
    result_ref = execute_remote_func.remote(add_remote, 2, 3)
    print(ray.get(result_ref)) # 输出:5(成功执行)

    ray.shutdown()

补充:Ray 还支持 嵌套远程函数 闭包变量传递

  • 比如在远程函数中引用本地变量,Ray 会自动序列化传递:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import ray

    ray.init(ignore_reinit_error=True)

    @ray.remote
    def outer_remote(x):
    # 闭包变量 x 会被 Ray 自动序列化到远程节点
    @ray.remote
    def inner_remote(y):
    return x + y
    return inner_remote.remote(10)

    print(ray.get(ray.get(outer_remote.remote(5)))) # 输出:15

    ray.shutdown()

执行位置:“本地固定” vs “集群分布式调度”

  • 执行位置的差异是两者最直观的区别,直接决定了是否能利用集群资源:

本地函数:执行在「调用方所在进程」

  • 本地函数的执行位置完全固定:
    • 无论在哪里调用(即使在远程函数内部调用本地函数),函数都会在「发起调用的进程」中执行【存疑】
      • 问题:会出错吧,理论上远程函数内部无法调用本地函数?
    • 若在远程函数中调用本地函数,本质是在「远程节点的 Worker 进程」中执行,但该进程没有本地函数的定义(除非手动同步代码),所以必然失败;
    • 无并发能力:多个调用会串行执行在同一个进程/线程(或 Python 多进程的子进程,但需手动管理)

远程函数:执行在「集群 Worker 进程」

  • Ray 远程函数的执行位置由 Ray 集群的调度器统一管理:

    • 1)调用 func.remote() 时,会向 Ray 调度器提交一个任务
    • 2)调度器根据集群节点的资源(CPU、GPU、内存)情况,将任务分配到任意可用节点的 Worker 进程
    • 3)执行完成后,结果会存储在 Ray 的对象存储中,通过 ray.get() 可获取
    • 4)支持并发:多个 remote() 调用会被调度到不同 Worker 进程/节点,并行执行
  • 示例:远程函数分布式执行(多节点/多进程并发)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import ray
    import os
    import time

    # os.environ["RAY_DEDUP_LOGS"] = "0" # 本意是让每个进程结果都完整输出,但这行代码仅当前进程生效,需要启动前环境变量才可以
    ray.init(ignore_reinit_error=True)

    # Ray 远程函数:打印执行节点的进程 ID 和节点名
    @ray.remote
    def add_remote(a, b):
    node_name = ray.util.get_node_ip_address() # 获取执行节点 IP
    pid = os.getpid() # 获取执行进程 ID
    print(f"在节点 {node_name} 的进程 {pid} 执行 add({a}, {b})")
    time.sleep(1) # 模拟耗时操作
    return a + b

    # 提交 5 个并发任务(会被调度到不同 Worker 进程)
    start = time.time()
    result_refs = [add_remote.remote(i, i*2) for i in range(5)]
    results = ray.get(result_refs) # 等待所有任务完成
    end = time.time()

    print("结果:", results) # 输出:[0, 3, 6, 9, 12]
    print(f"总耗时: {end - start:.2f}s") # 约 1s(并发执行,而非 5s 串行)

    ray.shutdown()
  • 执行上述脚本:

    1
    2
    export RAY_DEDUP_LOGS=0
    python demo.py
    • 注意:仅在代码里面添加 os.environ["RAY_DEDUP_LOGS"] = "0" 是不够的,因为:
      • Ray 的日志去重功能是在 Worker 进程启动时就决定的,而 Worker 是由 Ray 的主进程(Driver)启动的
      • 上面的代码在 ray.init() 之后才启动 Worker,那么环境变量必须在 Driver 启动 Worker 之前就传递过去,否则 Worker 进程会继承默认的去重配置
      • 所以最安全的打印所有日志的方式就是再启动脚本前配置环境变量
    • 另一种实现方式是在远程函数中返回 PID,然后由 Driver 打印
  • 输出示例:

    1
    2
    3
    4
    5
    6
    7
    8
    2025-11-04 11:42:43,175 INFO worker.py:1918 -- Started a local Ray instance. View the dashboard at 127.0.0.1:8265 
    (add_remote pid=14393) 在节点 127.0.0.1 的进程 14393 执行 add(2, 4)
    (add_remote pid=14399) 在节点 127.0.0.1 的进程 14399 执行 add(4, 8)
    (add_remote pid=14398) 在节点 127.0.0.1 的进程 14398 执行 add(1, 2)
    (add_remote pid=14400) 在节点 127.0.0.1 的进程 14400 执行 add(3, 6)
    (add_remote pid=14396) 在节点 127.0.0.1 的进程 14396 执行 add(0, 0)
    结果: [0, 3, 6, 9, 12]
    总耗时: 1.62s

RL——IQL

  • 参考链接
    • 原始论文:ICLR 2022 Poster, Offline reinforcement learning with implicit q-learning
    • 相关论文:(AWR)ADVANTAGE-WEIGHTED REGRESSION: SIMPLE AND SCALABLE OFF-POLICY REINFORCEMENT LEARNING

IQL 的基本思想

  • 常规的方法会直接约束策略或者正则来减少OOD问题,IQL则通过SARSA style的方法仅在见过的state-action上进行学习,不直接面对OOD问题
  • 策略学习使用了AWR(Advantage Weighted Regression)方法

多步动态规划和 Single-step 方法

多步动态规划(Multi-step DP)

  • 多步动态规划方法(multi-step dynamic programming methods,简写作Multi-step DP)
  • 已有Offline RL方法的很大一部分是基于约束或正则化的近似动态规划(例如,Q-learning 或 actor-critic 方法),constraint或Regularization用于限制与行为策略的偏差。 我们将这些方法称为多步动态规划(Multi-step DP)算法,因为它们对多次迭代执行真正的动态规划,因此如果提供高覆盖率数据,原则上可以恢复最优策略。通常情况下Multi-step DP问题也可以分为:
    • 显式密度模型(explicit density model):BRAC,BCQ,BEAR等
    • 隐式差异约束(implicit divergence constraints):AWAC,CRR,AWR等
  • 如何理解显示密度模型和隐式约束模型的定义?
    • 显式密度模型:直接建模State-Action的价值分布,从而得到最优策略
    • 隐式差异约束:不直接建模State-Action的价值分布,更多是模仿优质策略行为的思想
  • 问题:显示密度模型中的“密度”是什么意思?
    • 这里的密度是指概率密度,显示密度模型即会直接定义并学习概率密度函数的模型

Single-step 方法

  • Single-step 方法(Single-step Methods)是指一类方法,这类方法仅依赖于单步策略迭代的方法,即对行为策略的价值函数或Q函数进行拟合,然后提取相应的贪心策略,或者完全避免价值函数并利用行为克隆目标
  • 这类方法避免了访问看不见的状态动作对,因为它们要么根本不使用价值函数,要么学习行为策略的价值函数
  • IQL 就是一种 Single-step 方法
  • 传统的模仿学习也属于 Single-step 方法

多步动态规划和 Single-step 方法的比较

  • from https://zhuanlan.zhihu.com/p/497358947

IQL 之前的方案

一般的 Offline RL 学习方法

  • 思路:按照贝尔曼最优方程迭代
  • 损失函数:
    $$
    L_{TD}(\theta) = \mathbb{E}_{(s,a,s’) \sim D} \left[ (r(s, a) + \gamma \max_{a’} Q_{\theta’}(s’, a’) - Q_\theta(s, a))^2 \right]
    $$
  • 分析:
    • 直接使用上述损失函数存在值高估问题
    • 大多数最近的离线RL方法修改了上述值函数损失(或直接约束argmax这个策略本身选择动作的方位),以正则化值函数,使其生成的策略接近数据,缓解值高估问题

能避免 OOD 的学习方法

  • 思路:按照SARSA-style的方法迭代,即贝尔曼期望方程( \(a’\sim \pi_\beta\) )
  • 损失函数:SARSA-style的损失函数如下
    $$
    L(\theta) = \mathbb{E}_{(s,a,s’,a’) \sim D} \left[ (r(s, a) + \gamma Q_{\theta’}(s’, a’) - Q_\theta(s, a))^2 \right]
    $$
    • 按照上面的损失函数学习,学到的 \(Q_\theta(s,a)\) 本质是行为策略对应的Q值,也就是说,当样本无限时,Q值收敛到
      $$
      Q_\theta^*(s, a) \approx r(s, a) + \gamma \mathbb{E}_{s’ \sim p(\cdot|s,a), a’ \sim \pi_\beta(\cdot|s’)} \left[ Q_{\theta’}(s’, a’) \right]
      $$
  • 分析:
    • 本质上是在估计数据集上的状态和动作分布下,Q值的期望
    • 显然上面学到的只是行为策略对应的Q值,不是我们想要的最优Q值(行为策略不一定是最优策略)
    • 上面的方法更像是在对行为策略进行模仿

Offline RL 的最优 Q 值目标

  • 思路:避免OOD且能学到“最优策略”的迭代形式,限制了argmax动作不访问OOD的状态动作对
  • 损失函数:
    $$
    L(\theta) = \mathbb{E}_{(s,a,s’) \sim D} \left[ (r(s, a) + \gamma \max_{a’ \in A, \pi_\beta(a’|s’) > 0} Q_{\theta’}(s’, a’) - Q_\theta(s, a))^2 \right]
    $$
  • 分析:
    • 既保证使用的最大Q值对饮动作不超过数据集(避免了OOD),又可以在支持集上最大化当前策略
    • 上面的定义实际上也可能访问到支持集以外的动作,后续需要使用期望回归来改进为SARSA-style的形式
  • 注意:IQL 并不直接学习上述目标( \(\pi_\beta(a’|s’) > 0\) 导致无法学习),只是隐式的学习上述目标 ,具体方法是引入期望回归(Expectile Regression)
    • BCQ等方法已经学习过上述目标的改进版本
    • 上述目标无法直接学习,因为判断 \(\pi_\beta(a’|s’) > 0\) 需要维护一个表格,统计所有数据,状态动作空间很大时无法实现,除非像BCQ一样,用一个网络去学习概率

IQL 的解决方案

期望回归与分位数回归

  • 期望回归(Expectile Regression) ,是估计随机变量的各种统计量的方法,定义如下:

    • 某个随机变量 \(X\) 的 \(\tau \in (0, 1)\) 期望值定义为以下非对称最小二乘问题的解:
      $$
      \mathop{\arg\min}_{m_\tau} \mathbb{E}_{x \sim X} \left[ L_\tau^2(x - m_\tau) \right], \quad \text{其中} \quad L_\tau^2(u) = |\tau - 1(u < 0)| u^2.
      $$
    • \(L_\tau^2(u)\) 也常常写作 \(L_\tau^e(u)\)
    • 给定 \(\tau\), \(m_\tau\) 就是在拟合随机变量的某个 \(\tau\) 期望点,不同的 \(\tau\) 下 \(m_\tau\) 也会不同,学到的,比如 \(\tau=0.5\) 时就是对应期望
    • 分析:
      • 当 \(\tau > 0.5\) 时,这种非对称损失函数会降低小于 \(m_\tau\) 的 \(x\) 值的权重,而增加大于 \(m_\tau\) 的 \(x\) 值的权重
      • 当 \(\tau = 0.5\) 时,损失函数退化成对称的,等价于均方误差MSE(这里把 \(u\) 看做是误差项)
        $$ L^{\tau=0.5}_{2}(u) = |0.5 - \Bbb{1}(u<0)|u^2 = \frac{1}{2}u^2 $$
  • 条件随机变量的期望回归

    • 对于给定的条件随机变量 \(y = f(x)\),假定 \((x,y)\) 成对出现在数据集 \(\mathcal{D}\) 中,则可以定义:
      $$\mathop{\arg\min}_{m_\tau(x)} \mathbb{E}_{(x,y) \sim \mathcal{D}} \left[ L_\tau^2(y - m_\tau(x)) \right]$$
    • 给定 \(\tau\), \(m_\tau(x)\) 是一个关于 \(x\) 的函数,不同的 \(\tau\) 得到的拟合函数不同,相同的 \(\tau\),给定不同的 \(x\) 会得到不同的 \(m_\tau(x)\), \(m_\tau(x)\) 本质是在拟合 \(y\),下图中最右侧的图展示了条件随机变量的期望回归
  • 分位数回归(Quantile Regression)定义如下:
    $$
    \mathop{\arg\min}_{m_\tau} \mathbb{E}_{x \sim X} \left[ L_\tau^1(x - m_\tau) \right], \quad \text{其中} \quad L_\tau^1(u) = (\tau - 1(u < 0)) u.
    $$

    • \(L_\tau^1(u)\) 也常常写作 \(L_\tau^q(u)\)
    • \((\tau - 1(u < 0)) u\) 不使用绝对值的原因是此时无论 \(u\) 取值正负 \(L_\tau^1(u) \ge 0\) 都成立,相当于已经给整体加了绝对值了,最终目标是类似MAE的形式
  • 分位数回归和期望回归的对比

    • 常规的MSE叫做mean,等价于求均值,等价于 \(\tau = 0.5\) 的期望回归(expectile regression)
    • 常规的MAE叫做median,等价于求中位数,等价于 \(\tau = 0.5\) 的分位数回归(quantile regression)
  • 更多比较

    • 修正:左边第二行需要使用绝对值 \(\mathcal{R}_\tau^e(u) = u^2|\tau - \mathbf{1}(u < 0)|\)
  • 问题:为什么使用期望回归而不是分位数回归?

    • 审稿人也有这个疑问,作者的回答是实验得到的,没有正面给出回答?, \(\tau=0.9\) 时效果最好

基于期望回归的 Q 值学习

  • 借助期望回归来学习Q值:
    $$
    L(\theta) = \mathbb{E}_{(s,a,s’,a’) \sim D} \left[ L_\tau^2(r(s, a) + \gamma Q_{\theta’}(s’, a’) - Q_\theta(s, a)) \right]
    $$
  • 其中 \(\mathcal{D} \sim \pi_\beta\),选择合适的 \(\tau\) 后,可以学到一个大于 \(Q^{\pi_\beta}(s,a)\) (行为策略对应的Q值)的 \(Q(s,a)\)
  • 理解:给定 \((s,a)\) 的情况下,存在许多不同的 \((s’,a’)\) 样本,当 \(\tau > 0.5\) 时,相当于是通过这种非对称损失函数降低小于 \(Q_\theta(s, a)\) 的动作状态对 \((s’, a’)\) 所对应的目标值 \(r(s, a) + \gamma Q_{\theta’}(s’, a’)\) 的权重,增加大于 \(Q_\theta(s, a)\) 的动作状态对 \((s’, a’)\) 所对应的目标值 \(r(s, a) + \gamma Q_{\theta’}(s’, a’)\) 的权重,从而学到较大的 \((s’,a’)\) 对应的目标值,极端情况下,学到的是最大值 \(r(s, a) + \gamma \max_{(s,a,s’,a’) \sim \mathcal{D}} Q_{\theta’}(s’, a’)\)
  • 上面的损失函数还存在一些不足,由于环境可能是动态变化的,状态 \(s’\) 是按照概率 \(p(s’|s,a)\) 出现,所以以上损失函数还使得Q学到了环境转换的信息。具体来说,学到的Q值高不一定是选到了优秀动作的反应,还可能是因为运气好碰上了转移到一个较好的状态 \(s’\) 上
    • 补充说明1:即使是随机环境,在状态 \(s\) 下,选择 \(a\) 后有一定概率得到较优秀的 \(s’\),能说明在状态 \(s\) 下,选择 \(a\) 是较为优秀的吗?回答是不一定!因为在这种随机环境的情况下,最优贝尔曼方程里面,我们也需要对 \(s’\) 计算期望 \(\mathbb{E}_{s’\sim p(s’|s,a)}\) 而不是取最大 \(\max_{s’}\),这是我们的目标是找一个策略,使得按照这个策略交互得到的期望收益最大,而线上推断时,我们不能保证一定能走到最大的 \(s’\),除非是确定性环境,即 \((s,a)\) 确定后, \(s’\) 也是确定的
    • 补充问题1:如果是确定性的环境,是否可以直接使用上述损失函数?

IQL 的 Q 值学习

  • 由于基于期望回归的Q值学习引入了状态转移随机偏差,存在问题,所以需要进行改进:
  • 第一步:使用期望回归去从已知的 \(Q_{\hat{\theta}}(s,a)\) 中学习 \(V(s)\)
    $$ L_V(\psi) = \mathbb{E}_{(s,a) \sim D} \left[ L_\tau^2(Q_{\theta’}(s, a) - V_\psi(s)) \right] $$
    • 这里可以看出 \(V(s)\) 学到的是 \(\max_a Q_{\hat{\theta}}(s,a)\) 的思想,即对应V值的贝尔曼最优方程
  • 第二步:使用最优的 \(V\) 去学习 \(Q\)
    $$L_Q(\theta) = \mathbb{E}_{(s,a,s’) \sim D} \left[ (r(s, a) + \gamma V_\psi(s’) - Q_\theta(s, a))^2 \right] $$
    • 由于 \(V\) 在上一步已经通过期望回归学到了最优形式,这一步不需要继续使用期望回归了
  • 至此,我们已经实现了通过SARSA-style的形式,隐式的学到了近似最优Q值
  • 关于参数 \(\tau\) 的一些分析以及以上贝尔曼方程收敛性见附录

IQL 的策略学习

  • 虽然我们已经得到了近似最优Q值,但为了避免使用样本外的动作,这里做策略学习时,我们不能直接遍历所有动作
  • AWR提供了一种方法从近似最优Q值里面提取策略(因为策略学习并不影响Q值,所以更像是从近似最优Q值中提取策略):
    $$
    L_\pi(\phi) = \mathbb{E}_{(s,a) \sim D} \left[ \exp(\beta (Q_{\theta’}(s, a) - V_\psi(s))) \log \pi_\phi(a|s) \right]
    $$
    • 其中 \(\beta \ge 0\) 是温度系数。对于较小的超参数值,该目标类似于行为克隆(近似所有样本权重相等的策略梯度,原始策略梯度中,样本权重是温度系数为1的Q值),而对于较大的值,它试图恢复Q函数的最大值(Q值越大,对应的样本权重越大)。正如AWR等先前工作所示,此目标学习一个在分布约束下的最大化Q值的策略
  • 注意,策略学习时Q值收敛以后进行的(Q和V是交替更新),Q值学习和策略学习是串行的,且Q值学习彻底完成以后才进行策略学习,并不是交替进行
  • 思考:使用期望回归学到的 \(V\) 值是 \(V^{\pi^*} = \max_a Q_{\hat{\theta}}(s,a)\),为什么可以用最优的 \(V\) 值来更新策略 \(Q_{\theta’}(s, a) - V_\psi(s)\) ?
    • 这种做法是可以的,Q值和V值符合优势函数的定义,因为传统优势函数的定义也是 \(A^\pi(s,a) = Q^\pi(s,a) - V^\pi(s)\),其中 \(V^\pi(s) = \mathbb{E}_{a \sim \pi(\cdot|s)}[Q^\pi(s,a)]\),看似与 IQL 中学到的 \(V\) 值不同,但此时将当前Q值和V值对应策略 \(\pi(a|s)\) 理解为选择Q值最大的动作或近似动作,实际上 \(Q\) 值和 \(V\) 值都满足传统的优势函数了
    • 理解 :(即使不满足原始优势函数)虽然此时的 \(V\) 值是 \(\max_a Q_{\hat{\theta}}(s,a)\),但是 \(Q_{\theta’}(s, a) - V_\psi(s)\) 依然可以对动作的好坏进行区分。实际上,只要可以保证动作越好,优势函数越大即可,即使所有动作都是负的或者都是正的也没问题,因为策略的实现是一个softmax,大家都降低的时候,降的少的动作上对一个的概率自然会提升。实践也告诉我们,\(V\) 值是否是当前状态下动作的期望结果并不重要
    • 特别说明 :AWR 中使用的 \(V\) 值是从历史样本的累计奖励上学习的,相当于是历史样本上的期望,也就是行为策略 \(\mu\)(多轮迭代下可能是混合策略)对应的 \(V^\mu\) 值,AWR 的整个推导中奖励 \(\mathcal{R}^\mu_{\mathbf{s},\mathbf{a}}\) 和 \(V^\mu\) 值都是使用行为策略 \(\mu\) 来表示的,奖励使用的是蒙特卡洛估计 \(\mathcal{R}^D_{\mathbf{s},\mathbf{a}} = \sum_{t=0}^T\gamma^t r_t\)

IQL 训练流程

  • 伪代码如下(说明:伪代码中最后一行策略更新公式有问题,应该是加号,或者把损失函数添上负号,因为这里是想要最大化目标, 作者开源代码中是正确的github.com/ikostrikov/implicit_q_learning,论文中写错了):

附录:为什么 AWR 和策略梯度法损失函数不同?

  • 副标题:不同AC框架算法策略更新公式对比分析,为什么相同的目标推导出来完全不同的更新公式?
  • 问题补充:
    • 普通AC(策略梯度法)更新公式是:
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{(s,a) \sim \pi_{\theta_k}}\Big[(Q^{\pi_{\theta_k}}(s,a)-V^{\pi_{\theta_k}}(s))\log\pi_\theta(a|s)\Big]$$
    • PPO更新公式:
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{(s,a) \sim \pi_{\theta_k}}\Big[\frac{\pi_\theta(a|s)}{\pi_{\theta_k}(a|s)} A^{\pi_{\theta_k}}(s,a) - \beta D_{KL}(\pi_{\theta_{k}}(\cdot|s), \pi_\theta(\cdot|s))\Big]$$
    • DDPG更新公式
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{s_t \sim \rho^\beta(s)} [Q_w(s_t,\mu_\theta(s_t))] $$
    • SAC更新公式
      $$\mathop{\arg\max}_{\theta}\mathbb{E}_{s_t \sim \mathcal{D}, \epsilon_t \sim \mathcal{N}}[\log \pi_\theta(f_\theta(\epsilon_t;s_t)\vert s_t) - Q_\theta(s_t, f_\theta(\epsilon_t; s_t))]$$
    • AWR更新公式:
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{(s,a) \sim \pi_\beta}\Big[exp\Big(\frac{1}{\beta}(R_{s,a}^{\mathcal{D}}-V^{\mathcal{D}}(s))\Big)\log\pi_\theta(a|s)\Big]$$
      • 其中 \(R_{s,a}^{\mathcal{D}} = \sum_{t=0}^\infty \gamma^t r_t\),不是网络,是真实的轨迹收益
    • IQL更新公式:
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{(s,a) \sim \pi_\beta}\Big[exp\Big(\beta (Q_{\theta’}(s, a) - V_\psi(s))\Big)\log\pi_\theta(a|s)\Big]$$
    • AWAC更新公式:
      $$\mathop{\arg\max}_{\theta} \mathbb{E}_{(s,a) \sim \pi_\beta}\Big[exp(\frac{1}{\lambda} A^{\pi_{\theta_k}}(s,a))\log\pi_\theta(a|s)\Big]$$
  • 基本推导思路总结:
    • 策略梯度法 :推导是直接从最初目标出发,视图求最初目标相对策略的梯度
    • PPO :更新公式是从策略提升的视角出发得到梯度提升的目标,通过限制策略变化幅度和重要性采样分别将未知策略的状态和动作采样的问题切换到已知策略
    • DDPG :直接以最大化Q值为目标来更新,可直接传导策略梯度
    • SAC :的目标中增加了熵,可以看成是DDPG的增加熵的版本
    • AWR、IQL和AWAC :更新公式都是相同的形式,是从策略提升的视角出发得到梯度提升的目标,并对该目标进行推导,得到最终的最优策略形式,再带入最优策略形式,从而得到更新公式
  • 也就是说,AWR、IQL和AWAC这三个方法的目标是为了策略提升量最大化 ,而策略梯度法的目标是为了原始目标最大化(梯度提升法)

附录:为什么 IQL 效果比 AWR 好?

  • IQL和 AWR 的 Q 值是不同策略的优势函数,IQL 的优势函数是在 \(\tau\) 分位点期望动作策略分布上的 Q 和 V,即 \(A^{\pi^*}(s,a) = Q^{\pi^*}(s,a) - V^{\pi^*}(s)\),而AWR的优势函数是真实的轨迹回报和V值 \(A^{\pi_k}(s,a) = R_{s,a}^{\mathcal{D}} - V^{\pi_k}(s)\)
  • IQL 不是迭代训练,是先学好 Q 值(不依赖策略),再利用学好的 Q 值一次性提取策略
  • 标准的 AWR 是 off-policy 的,是一种迭代训练的流程,V 值学习依赖策略与环境交互的轨迹数据,策略学习也依赖上一步的V值,V值,策略,轨迹三者是不断优化的
  • 如果把 AWR 直接用到 Offline R L场景下,则不再与环境交互,AWR 退化到学习一次V值,接着一次性学习策略;
    • Offline RL 下学到的 V 值是行为策略对应的 V 值,不是最优的 V 值,但这本身应该没有问题
    • 基于统计的 \(R_{s,a}^{\mathcal{D}}\) 方差可能很大
  • 使用公式 \(L_\pi(\phi) = \mathbb{E}_{(s,a) \sim D} \left[ \exp(\beta (Q_{\theta’}(s, a) - V_\psi(s))) \log \pi_\phi(a|s) \right]\) 来迭代策略时,Q 值和 V 值应该使用什么样的才是最优的?
    • 这个公式是从最大化策略提升项得到的,在推导策略提升时,这里使用的A值(对应到Q值和V值)是上一步策略对应的值 \(A^\mu(s,a)\),即旧策略 \(\mu\) 对应 Q 值和 V 值,而我们的目标是在 \(\mu\) 的基础上有所提升,得到优秀的新策略 \(\pi\),所以 Q 值和 V 值最好是优秀的策略对应的Q值和V值,否则可能我们的策略 \(\pi\) 在不好的策略上提升,结果也可能不是很优秀
  • 补充问题:可以随便使用一个策略来评估优势函数吗?
    • 回答是不可以,因为不同策略下,A 值选择不同动作以后的值是不同的,显然学到的策略也不同,从推导看,必须使用上一步的才可以

附录:贝尔曼方程收敛性及 \(\tau\) 的分析

  • 关于参数 \(\tau\) 的一些分析,原始论文中关于 \(\tau\) 的分析如下:

  • 当 \(\tau = 0.5\),相当于是SARSA算法;当 \(\tau \rightarrow 1\),相当于是Q-Learning算法

  • 对于任意的 \(\tau\),Q值和V值迭代都会收敛,且Q值和V值会收敛到 \(Q_{\tau}(s,a)\) 和 \(V_{\tau}(s)\),Lamma1中最后两行就是两者的贝尔曼方程,其中 \(\mathbb{E}_{a \sim \mu(\cdot|s)}^\tau\) 表示 \(\mu(\cdot|s)\) 分布下的 \(\tau\) 期望分位值(或 \(\tau\) 阶期望分位数)。注意,我们在说分位数时,还需要说明是那个随机变量或者哪个分布的分位数,否则没有意义

  • 为什么说Q值和V值迭代都会收敛到 \(Q_{\tau}(s,a)\) 和 \(V_{\tau}(s)\) 呢?

    • 理解:这里的 \(\tau\) 期望分位动作可以视作是一个策略,每次选择动作时,不选择最优动作,也不选择随机动作,而是选择 \(\tau\) 期望分位点动作,这样,可以得到跟论文中一样的结论:当 \(\tau = 0.5\),相当于是SARSA算法;当 \(\tau \rightarrow 1\),相当于是Q-Learning算法
    • 证明:定义一个策略如下:
      $$\pi_\tau(s) = \mathop{\text{arg_expectile}^\tau}_a(Q(s,a))$$
      该策略表示在状态 \(s\) 下,该策略会选择使得Q值等于 \(Q(s,a)\) 关于动作 \(a\) 的 \(\tau\) 期望分位点的动作,则期望分位动作策略对应的贝尔曼方程跟普通策略下的贝尔曼方程没有区别
    • 更详细的来说:
      • Q值:假定已经有了 \(V_\tau(s’)\),此时Q值的更新是学习当前状态 \(s\) 下,按照当前状态对应的 \(\tau\) 期望分位动作,以及后续策略也采用 \(\tau\) 期望分位动作得到的价值 \(V_\tau(s’)\) 来进行拟合的目标值(注意,这里跟其他贝尔曼方程一样,一旦动作决定了, \(r(s,a)\) 就确定了,我们所说的期望分位动作就是对动作 \(a\) 的分布而言的, \(Q(s,a)\) 的拟合只考虑 \((s,a)\) 状态动作对即可,不需要考虑期望分位动作);
      • V值:假定已经有了 \(Q_{\tau}(s,a)\),V值可以从 \(Q_{\tau}(s,a)\) 中学到 \(V_\tau(s’)\),这里需要使用 \(Q_{\tau}(s,a)\) 而不是 \(Q_{\pi_\beta(s,a)}\) 的原因是,V的本质是 \(Q(s,a)\) 关于动作 \(a\) 期望,但直接求期望只到了当前状态 \(s\) 这一层,如果使用 \(Q_{\pi_\beta(s,a)}\) 来学习那么学到的不是 \(V_\tau(s’)\) ( \(V_\tau(s’)\) 是指后续的动作也是 \(\tau\) 期望分位动作来定义的,正如Q值和V值的常规贝尔曼方程一样)

Implicit 名字的来源

  • Implicit 含义是“隐式的”,与隐式约束的隐式不等价,在IQL中表示通过期望回归隐式的学到了最优价值函数 \(V^*(s) = \max Q(s,a)\)

IQL 可能存在的问题

  • IQL 没有没有像 CQL 一样对非行为策略的 Q 值进行打压(甚至学习过程中全程未学习未知状态动作对的 Q 值),也没有像 BCQ 一样对动作选择进行限制,理论上可能会因为对 OOD 状态动作 Q 值高估而出现问题
  • IQL 源码实现时的解法:采用 Twin Q 来缓解高估问题(理解:对于数据集中存在的,两个 Q 网络都能估准;对于数据集中不存在的,可能都估不准,但是我们取最小的那个,可以缓解对未知状态动作对 Q 值的高估问题)

Python——Ray-分布式架构简单了解


整体介绍

  • Ray 是一个用于分布式计算的开源框架,专为构建和运行分布式应用程序而设计
  • Ray 提供了简洁的 API,让开发者能够轻松地将单机程序扩展到分布式集群上,同时保持代码的可读性和可维护性
  • Ray 最初由 UC Berkeley 的 RISELab 开发,现在由 Anyscale 公司维护,广泛应用于机器学习、强化学习、并行计算等领域
  • Ray 既可以在本地实现并行计算,又可以非常容易的扩展到集群模式,实现分布式计算
  • Ray 与深度学习框架如 TensorFlow、PyTorch 和 MXNet 等互相兼容

Ray 的核心架构

  • Ray的系统架构采用了混合任务调度的思路,遵循典型的 Master-Slave 设计,但与传统分布式系统有所不同

Ray 中的关键组件总结

  • Ray在集群部署模式下启动了以下关键组件:
    • GlobalScheduler(全局调度器) :运行在Master节点上,负责接收本地调度器提交的任务,并将任务分发给合适的本地任务调度器执行
    • RedisServer :Master节点上启动的Redis服务器,用于保存分布式任务的状态信息(ControlState),包括对象机器的映射、任务描述、任务 debug 信息等
    • LocalScheduler(本地调度器) :每个 Slave 节点上启动的本地调度器,用于提交任务到全局调度器,以及分配任务给当前机器的 Worker 进程
    • Worker进程 :每个 Slave 节点上可以启动多个 Worker 进程执行分布式任务,并将计算结果存储到 ObjectStore
    • ObjectStore(对象存储) :每个 Slave 节点上的存储系统,用于存储只读数据对象,Worker 可以通过共享内存的方式访问这些对象数据,有效减少内存拷贝和对象序列化成本。ObjectStore 底层由 Apache Arrow 实现
    • Plasma :每个 Slave 节点上的ObjectStore管理器,当 Worker 访问本地 ObjectStore 上不存在的远程数据对象时,Plasma 会主动拉取其它 Slave 上的对象数据到当前机器

执行模型

  • Ray的执行模型基于动态任务图 ,这与 TensorFlow 中的静态计算图有本质区别:
    • TensorFlow的计算图用于表征神经网络,在单个应用中执行很多次
    • Ray的任务图用于表征整个应用,并仅执行一次
    • 任务图对于前台是未知的,随着应用的运行而动态地构建
    • 一个任务的执行可能创建更多的任务,形成动态依赖关系

代码示例

并行计算示例(无状态)

  • 基于 Ray 的并行计算代码 Demo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import ray
    import time
    import numpy as np

    # 初始化 Ray,默认在本地启动
    ray.init()

    # 使用 @ray.remote 装饰器将函数转换为分布式任务
    @ray.remote
    def compute_square(x):
    # 模拟耗时计算
    time.sleep(1)
    return x * x

    # 生成一些数据
    data = np.arange(10)

    # 并行执行任务
    start_time = time.time()
    # 创建任务对象引用
    square_refs = [compute_square.remote(i) for i in data]
    # 等待所有任务完成并获取结果
    results = ray.get(square_refs)
    end_time = time.time()

    print(f"串行计算结果: {[i*i for i in data]}")
    print(f"Ray 并行计算结果: {results}")
    print(f"Ray 并行计算耗时: {end_time - start_time:.4f} 秒")

    # 关闭 Ray
    ray.shutdown()

    # 串行计算结果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    # Ray 并行计算结果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    # Ray 并行计算耗时: 1.4069 秒

串行计算示例(有状态)

  • 基于 Ray 的串行计算代码 Demo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import ray
    import time

    # 初始化 Ray
    ray.init()

    # 使用 @ray.remote 装饰器定义 Actor 类
    @ray.remote
    class Counter:
    def __init__(self):
    self.count = 0

    def increment(self):
    time.sleep(1) # 模拟耗时操作
    self.count += 1
    return self.count

    def get_count(self):
    return self.count

    # 创建 Actor 实例
    counter = Counter.remote()

    # 并行调用 Actor 方法
    start_time = time.time()
    # 提交多个增量任务
    increment_refs = [counter.increment.remote() for _ in range(10)]
    # 获取所有增量任务的结果
    results = ray.get(increment_refs)
    # 获取最终计数
    final_count = ray.get(counter.get_count.remote())
    end_time = time.time()

    print(f"每次增量结果: {results}")
    print(f"最终计数: {final_count}")
    print(f"执行耗时: {end_time - start_time:.4f} 秒")

    # 关闭 Ray
    ray.shutdown()

    # 每次增量结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    # 最终计数: 10
    # 执行耗时: 10.1293 秒

分布式调度示例(集群模式)

  • 以上代码经过非常简单的修改即可进入集群模式

  • Ray 集群部署包括三个步骤(下面以 6379 端口为例展示流程)

  • 第一步:启动主节点 ,运行 ray start --head 从主节点启动集群

    1
    ray start --head --port=6379 --redis-password='your_secure_password_123'
    • 注:可通过 --redis-password 设置密码(可选),防止未授权节点加入,也可以不使用该参数
  • 第二步:启动工作节点 ,运行 ray start --address=<主节点IP> 加入集群

    1
    ray start --address='<head-node-ip>:6379' --redis-password='your_secure_password_123'
    • 执行上述命令后工作节点就会:
      • 自动连接到主节点
      • 等待接收任务
      • 执行主节点分配的计算任务
      • 将结果返回给主节点
  • 第三步:在主节点上运行的代码中连接集群

    1
    ray.init(address='auto', _redis_password='your_secure_password_123')
    • 注:以上代码仅在主节点上运行,工作节点不需要显示运行任何代码,仅需要启动并加入集群即可
  • 关闭 Ray 服务:

    1
    ray stop
  • 特别说明:集群模式与普通单机并行模式的区别很小,仅需要增加修改以上代码即可(其他代码都不需要修改)

  • Ray 在分布式下默认有许多默认功能:

    • 自动负载均衡:Ray 会自动将任务分配到空闲节点
    • 容错能力:如果某个工作节点失败,Ray 会重新调度任务
  • 集群模式工作流程总结:

    • 主节点通过 Redis 将任务(remote 函数或者类对象)放入队列
    • 空闲的工作节点从队列中获取任务
    • 工作节点执行任务
    • 将运算结果通过 共享内存/Object Store 返回给主节点

附录:工作节点启动高级配置

  • 可以通过参数调整工作节点行为:
    1
    2
    3
    4
    5
    ray start --address='<head-node-ip>:6379' \
    --redis-password='your_secure_password_123' \
    --num-cpus=8 \ # 限制使用8个CPU核心
    --num-gpus=1 \ # 声明有1个GPU可用
    --object-store-memory=100000000 \ # 设置对象存储大小

附录:Ray 集群状态监控

  • Ray 提供了 Web UI 用于监控集群状态
  • 在主节点启动时已经启用了 Dashboard(默认端口8265)
  • 在浏览器访问:http://<主节点IP>:8265
  • 在 Dashboard 中可以看到:
    • 集群节点列表和资源使用情况
    • 当前运行的任务
    • 历史任务统计
    • 每个节点的CPU/内存使用情况

RL——PPO

  • 参考链接:
    • 原始论文:Proximal Policy Optimization Algorithms, 2017, OpenAI
    • 相关博客:影响PPO算法性能的10个关键技巧(附PPO算法简洁Pytorch实现)

PPO 方法介绍

PPO 的目标

  • PPO目标定义
    $$
    \begin{aligned}
    \max_{\theta_\text{new}} \quad &\mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\left[\frac{\pi_{\theta_\text{new}}(a|s)}{\pi_{\theta_\text{old}}(a|s)} A_{\pi_{\theta_\text{old}}}(s,a)\right] \\
    &\text{s.t. } \quad \quad \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}} \left[D_{\text{KL}}(\pi_{\theta_\text{old}}, \pi_{\theta_\text{new}})\right] \le \delta
    \end{aligned}
    $$
  • PPO目标详细推导见RL——TRPO-PPO-目标函数基础推导

PPO-Penalty

  • 又名PPO-惩罚
    $$
    \begin{aligned}
    \max_{\theta}&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\left[\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a) - \beta D_{KL}(\pi_{\theta_{\text{old}}}(\cdot|s), \pi_\theta(\cdot|s))\right]
    \end{aligned}
    $$

PPO-Clip

  • 又名PPO截断
    $$
    \begin{aligned}
    \max_\theta&\ \ \mathbb{E}_{s\sim \rho_{\theta_{\text{old}}},a\sim q(a|s)}\min\left(\color{blue}{\frac{\pi_\theta(a|s)}{q(a|s)}A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(\frac{\pi_\theta(a|s)}{q(a|s)}, 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)
    \end{aligned}
    $$
  • 理论上,以上采样分布可以是任意分布 ,实际上使用Old策略效果更好,样本利用率也更高,所以常用的PPO目标一般会如下定义:
    $$
    \begin{aligned}
    \max_\theta&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\min\left(\color{blue}{\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}, 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)
    \end{aligned}
    $$
    • 以上目标是 \(\max_\theta\),实际实现时会在令损失函数等于负的目标函数
  • 令 \(r(\theta) = \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} \),则有:
    $$
    \begin{aligned}
    \max_\theta&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\min\left(\color{blue}{r(\theta)A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(r(\theta), 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)
    \end{aligned}
    $$

PPO-Clip 进阶讨论

  • 副标题:PPO-Clip的损失函数究竟在做什么?为什么需要使用 \(\min\) 操作?
  • 参考链接:如何理解 PPO-CLIP 目标函数中的 clip 和 min 操作?过犹不及论 - Finch的文章 - 知乎
  • PPO的目标定义如下:
    $$
    \begin{aligned}
    \max_\theta&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\min\left(\color{blue}{\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}, 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)
    \end{aligned}
    $$
    • 注意: 发生截断时, \((1+\epsilon)A_{\theta_{\text{old}}}(a,s)\) 或 \((1-\epsilon)A_{\theta_{\text{old}}}(a,s)\) 对策略参数 \(\theta\) 的梯度为 0 :
      • 常数 \((1+\epsilon)\) 和 \((1+\epsilon)\) 对策略参数的梯度为 0
      • \(A_{\theta_{\text{old}}}(a,s)\) 本身与旧策略有关(但本质上也只是按照旧策略与环境交互得到 Reward 而已,也不会回传梯度到旧策略),与当前策略参数 \(\theta\) 无关,对策略参数 \(\theta\) 的梯度为 0;
      • \(A_{\theta_{\text{old}}}(a,s)\) 计算优势函数时用到了价值网络,这也与当前策略参数 \(\theta\) 无关,对策略参数 \(\theta\) 的梯度为 0(特别地,\(A_{\theta_{\text{old}}}(a,s)\) 是经过 stop_gradient 得到的,也不会影响价值网络的参数)
  • \(clip + \min\) 操作讨论 ,关于 \(\min\left(\color{blue}{r(\theta)A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(r(\theta), 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)\),由于 \(A_{\theta_{\text{old}}}(a,s) > 0\) 是有正有负的(\(A=Q-V\) 的加权平均),对于某个样本来说:
    • 当 \(A_{\theta_{\text{old}}}(a,s) > 0\) 时,要提升目标动作概率 \(\pi_\theta(a|s)\) :
      • 若 \(r(\theta) > 1+\epsilon\),则说明相对原始策略 \(\pi_{\theta_\text{old}(a|s)}\),\(\pi_\theta(a|s)\) 已经提升够多了 ,不希望再继续提升(偏离原始策略太多容易不稳定),\(clip + \min\) 操作可以将这个样本的目标值截断为 \((1+\epsilon)A_{\theta_{\text{old}}}(a,s)\),这与策略参数 \(\theta\) 无关 ,不会有梯度回传,即该样本相当于被废弃了(不考虑求均值会用到样本数量)
      • 若 \(r(\theta) \leq 1+\epsilon\),则目标动作概率 \(\pi_\theta(a|s)\) 还小 ,可以正常更新以提升该动作的概率(问题:这里其实是无法控制目标概率更新的幅度的,更新后的真实值可能超过 \(1+\epsilon\) 这个阈值)
    • 当 \(A_{\theta_{\text{old}}}(a,s) < 0\) 时,要降低目标动作概率 \(\pi_\theta(a|s)\) :
      • 若 \(r(\theta) < 1-\epsilon\),则说明相对原始策略 \(\pi_{\theta_\text{old}(a|s)}\),\(\pi_\theta(a|s)\) 已经降低的够多了 ,不希望再继续降低(偏离原始策略太多容易不稳定),\(clip + \min\) 操作可以将这个样本的目标值截断为 \((1-\epsilon)A_{\theta_{\text{old}}}(a,s)\),这与策略参数 \(\theta\) 无关,不会有梯度回传,即该样本相当于被废弃了(不考虑求均值会用到样本数量)
      • 若 \(r(\theta) \geq 1+\epsilon\),则目标动作概率 \(\pi_\theta(a|s)\) 较大 ,可以正常更新以降低该动作的概率(问题:这里其实是无法控制目标概率更新的幅度的,更新后的真实值可能小于 \(1-\epsilon\) 这个阈值)
    • 思考:由于 \(r(\theta) > 1+\epsilon\) 的样本动作概率不许继续提升(但可以被降低),\(r(\theta) < 1-\epsilon\) 的样本动作概率不许继续降低(但可以提升),所以整体来说,所有动作的概率都倾向于维持 \(r(\theta) \in [1-\epsilon, 1+\epsilon]\) 之间(只是倾向于,不能完全保证)
    • 严格One-Step更新下,损失函数可做如下简化:此时旧策略采样的样本仅更新一次模型即丢弃(即epoch=1,且一次更新完所有参数,batch_size足够大),且立刻会将新策略的更新同步到旧策略上,保证每次更新模型前新旧策略完全一致 ,则无需使用Clip操作和min操作(因为 \(r(\theta)=1\)),PPO目标函数将可以简化为如下形式(注意,虽然此时 \(r(\theta) = \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}=1\),但是必须保留 \(r(\theta)\),因为梯度传导需要分子 \(\pi_\theta(a|s)\)):
      $$
      \begin{aligned}
      \max_\theta&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\left(\color{blue}{\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a)}\right)
      \end{aligned}
      $$
      • 建议epoch大于1的理由1 :One-Step更新样本效率低,如果设置的学习率过大又会引发不稳定,所以建议还是多次epoch更新,增加样本利用率
      • 建议epoch大于1的理由2 :epoch大于1的多次更新还有补救回调的作用,当策略已经更新偏离旧策略太多时,PPO损失函数保证可以有机会被拉回来
      • 补充讨论:实际上,严格One-Step更新下,PPO降级为普通PG ,更详细的讨论见附录
    • 补充:Simplified PPO-Clip Objective中有PPO简化的推导,能更加清晰的显示PPO的 \(clip + \min\) 操作
  • 一些博客(比如:Visualize the Clipped Surrogate Objective Function)有关于Clip仅限制了单边的讨论(有效性有待商榷):
    • 当 \(A_{\theta_{\text{old}}}(a,s) > 0\) 时, \(r(\theta)\) 在Clip后的生效范围在 \(r(\theta) \in [0,1+\epsilon]\),也就是 \(1-\epsilon\) 边界会失效(问题 :此时失效是正常的吧,因为此时 \(A_{\theta_{\text{old}}}(a,s) > 0\),我们要提升目标动作概率,当前的策略目标动作概率越小,我们越应该提升,这反而是PPO的设计:为了使得整体策略不偏离旧策略太远?)
    • 当 \(A_{\theta_{\text{old}}}(a,s) < 0\) 时, \(r(\theta)\) 在Clip后的生效范围在 \(r(\theta) \in [1-\epsilon,+\infty]\),也就是 \(1+\epsilon\) 边界会失效(同上问题 :此时失效是正常的吧,因为此时 \(A_{\theta_{\text{old}}}(a,s) < 0\),我们要降低目标动作概率,当前的策略目标动作概率越大,我们越应该降低,这反而是PPO的设计:为了使得整体策略不偏离旧策略太远?)
    • 改进方案(有效性有待商榷) :在on-policy的设定下,我们认为策略新旧策略的比值 \(r(\theta)\) 不会太大,一般不会出现问题,但是off-policy设定下,可能会出现问题,所以需要再加一层Clip(参考自:Visualize the Clipped Surrogate Objective Function)
      $$
      \begin{aligned}
      \max_\theta&\ \ \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\max\left( \color{red}{\eta A_{\theta_{\text{old}}}(a,s)}, \min\left(\color{blue}{r(\theta)A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(r(\theta), 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)\right)
      \end{aligned}
      $$
      • 其中 \(\color{red}{\eta}\) 是超参数(理解:一般会比较大,比如 5 或 10 等?)
      • 注:综合上面的问题,这里的结论不一定合理 ,这种做法的有效性还有待商榷,因为 Clip 不是在控制每一个epoch更新后的 \(r(\theta)\),而是根据更新前的 \(r(\theta)\) 判断是否要继续更新对应的状态动作对
      • 如果从比值过大可能是对应异常值导致来看(比如模型推理错误导致异常值),这里确实可以做一下截断
        • 否则使用保留梯度的截断更合适(至少保留部分梯度),详情见附录
      • 特别说明:理论上来说,上述的 \(\max\left( \color{red}{\eta A_{\theta_{\text{old}}}(a,s)}, \cdot\right)\) 只会在 \(A_{\theta_{\text{old}}}(a,s) < 0\) 时生效,因为 \(A_{\theta_{\text{old}}}(a,s) > 0\) 时会被更小的上界 \(1+\epsilon\) 提前 Clip 掉(相当于 \(\color{red}{\eta}\) 没有生效)

PPO 网络更新

  • Critic 网络更新(原始论文中未明确给出 Critic 网络更新的公式,实际上 Critic 网络的更新有直接使用真实折扣奖励作为目标值、TD-Error 作为损失函数和使用GAE 作为目标值等版本,这里给出TD-Error 作为损失函数的形式,更多详情见后面的章节单独讨论)
    $$
    Loss_{\text{critic}} = \sum (r_t + \gamma V^{\bar{w}}(s_{t+1}) - V^{w}(s_{t})) ^ 2
    $$
    • 这里虽然使用 Target V 网络表达,但实际上PPO一般不需要使用 Target V 网络
    • 这里V值拟合的目标是策略 \(\pi_\theta\) 对应的V值 \(V^{\pi_\theta}\)
    • \(r_t = r(s_t, a_t)\vert_{a_t \sim \pi_\theta(\cdot|s_t)}\),训练用的整个轨迹链路都是从策略 \(\pi_\theta\) 采样得到的
  • Actor网络更新
    $$
    Loss_{\text{actor}} = - \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\min\left(\color{blue}{r(\theta)A_{\theta_{\text{old}}}(s,a)}, \color{red}{clip\left(r(\theta), 1-\epsilon, 1+\epsilon\right)A_{\theta_{\text{old}}}(a,s)}\right)
    $$
    • 其中: \(r(\theta) = \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} \)

PPO Critic 网络更新的其他方式

  • 损失函数形式的选择 :在PPO算法中,Critic网络(即价值函数网络)的更新通常采用均方误差(MSE)损失或Huber Loss(也称为 Smooth_l1_loss),通过梯度下降来最小化价值函数的预测误差
  • PPO中Critic的核心更新公式为 MSE损失等,但目标值 \( V_{\text{target} } \) 的计算方式可能因具体实现而异(单步TD、GAE等)。实际代码中通常结合以下步骤:
    • 从经验缓冲区采样数据 \((s_t, r_t, s_{t+1})\)
    • 计算目标值 \( V_{\text{target} } \)(如单步TD或GAE)
    • 最小化 \( L_{\text{critic} } \) 更新Critic参数 \( \theta \)
  • Critic 损失函数(MSE) :
    $$
    L_{\text{critic} } = \frac{1}{2} \mathbb{E}_{(s_t) \sim \text{batch} } \left[ \left( V_{\theta}(s_t) - V_{\text{target} }(s_t) \right)^2 \right]
    $$
    • \( V_\theta(s_t) \) 是当前Critic网络的输出,\( \theta \) 为网络参数
    • \( V_{\text{target} }(s_t) \) 是目标值,可以有多种实现,具体实现见下文
可选目标1:基于 TD 误差的更新(Temporal Difference Learning)
  • Critic的目标是拟合状态值函数 \( V^\pi(s) \),通过TD误差计算当前值函数的预测与目标值的差异

  • TD目标值(单步) :
    $$
    V_{\text{target} }(s_t) = r_t + \gamma V_{\text{old} }(s_{t+1})
    $$

    • \( \gamma \) 是折扣因子
    • \( V_{\text{old} } \) 是旧Critic网络的输出(稳定训练),需要注意这里不是Target网络,而是PPO每个episode可能会更新多个epoch ,需要保证更新过程中Critic网络的学习目标是不变的,即每个epoch中 ,目标值\( V_{\text{target} }(s_t) \) 始终是不变的 ,实现时,确保目标值是提前计算得到的即可,具体实现可以如下(参考自动手学强化学习实现):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # ...
      td_target = rewards + self.gamma * self.critic(next_states) * (1 - dones)
      # ...
      for _ in range(self.epochs):
      critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
      self.critic_optimizer.zero_grad()
      critic_loss.backward()
      self.critic_optimizer.step()
      # ...
  • 此时的Critic 损失函数相当于:
    $$
    L_{\text{critic} } = \frac{1}{2} \mathbb{E}_{(s_t) \sim \text{batch} } \left[ \left( \color{red}{r_t + \gamma V_{\text{old} }(s_{t+1})} - V_{\theta}(s_t)\right)^2 \right]
    $$

可选目标2:广义优势估计(GAE)结合多步TD
  • 当使用GAE(Generalized Advantage Estimation)时,Critic的目标值会引入多步TD误差的加权平均,进一步减少方差
  • GAE 优势函数 :
    $$
    A_t^{\text{GAE}(\gamma, \lambda)} = \sum_{k=0}^{T-t} (\gamma \lambda)^k \delta_{t+k}
    $$
    • 其中 \( \delta_t = r_t + \gamma V_{\text{old} }(s_{t+1}) - V_{\text{old} }(s_t) \) 是单步TD误差,\( \lambda \) 是GAE超参数
  • Critic 目标值(GAE) :
    $$
    V_{\text{target} }(s_t) = A_t^{\text{GAE} } + V_{\text{old} }(s_t)
    $$
  • 此时的Critic 损失函数相当于:
    $$
    L_{\text{critic} } = \frac{1}{2} \mathbb{E}_{(s_t) \sim \text{batch} } \left[ \left( \color{red}{A_t^{\text{GAE} } + V_{\text{old} }(s_t)} - V_{\theta}(s_t)\right)^2 \right]
    $$
可选目标3:折扣真实奖励
  • 对于状态 \( s_t \),其目标值 \( V_{\text{target}}(s_t) \) 是从 \( t \) 时刻开始到回合结束的累计折扣奖励:
    $$
    V_{\text{target}}(s_t) = \sum_{k=0}^{T-t} \gamma^k r_{t+k}
    $$
    • \( T \) 是回合终止时间步,
    • \( \gamma \) 是折扣因子(如0.99),
    • \( r_{t+k} \) 是 \( t+k \) 时刻的即时奖励
  • 此时的Critic损失函数相当于:
    $$
    L_{\text{critic} } = \frac{1}{2} \mathbb{E}_{(s_t) \sim \text{batch} } \left[ \left( \color{red}{\sum_{k=0}^{T-t} \gamma^k r_{t+k}} - V_{\theta}(s_t)\right)^2 \right]
    $$
其他优化:目标网络(Target Network,一般PPO不需要)
  • 为稳定训练,Critic的目标值 \( V_{\text{target} } \) 可能通过慢更新的目标网络计算(类似DQN):
    $$
    V_{\text{target} }(s_t) = r_t + \gamma V_{\bar{\theta}}(s_{t+1})
    $$
    • 其中 \( \bar{\theta} \) 是目标网络参数,通过Polyak平均更新(一种加权平均方法,即RL中常说的软更新):
      $$
      \theta^- \leftarrow \tau \theta + (1 - \tau) \bar{\theta} \quad (\tau \ll 1)
      $$

PPO 的一些实践说明

  • 常用的形式是 PPO-Clip 形式,实践中效果更好
  • 一般来说 PPO 需要使用 Target V 网络,使用 Target V 网络会导致收敛较慢
  • Critic 网络的损失函数可以归回方法中常用的 smooth_l1_loss(即huber_loss),以减少异常值带来的影响
  • PPO-Clip 中一般设置 \(\epsilon=0.2\)
  • PPO原始论文中,每次采样到的数据会作 K 次 epochs,且不同游戏使用的次数不同,在 Mujoco 中使用 \(epochs=10\),Roboschool 中使用 \(epochs=15\),Atari 中使用 \(epochs=3\)

PPO 连续动作实现离散动作的实现主要区别

模型建模
  • 策略网络 :连续动作需要使用 \(\mu_\theta,\sigma_\theta\) 表示均值和方差,连续分布下,每个动作的概率理论上都是0,但借助概率密度函数的含义,可以通过计算

  • 采样方式 :采样时需要创建分布来采样,由于不需要梯度回传,所以不需要使用重参数法

  • Critic网络 :由于离散连续场景都用V网络,仅仅评估状态下的价值即可,与动作无关,连续动作处理不需要特殊修改

    新旧策略比值计算方式不同
  • 离线动作按照推导中的实现即可

    1
    2
    3
    4
    def compute_surrogate_obj(self, states, actions, advantage, old_log_probs, actor):  # 计算策略目标
    log_probs = torch.log(actor(states).gather(1, actions))
    ratio = torch.exp(log_probs - old_log_probs)
    return torch.mean(ratio * advantage)
  • 连续动作需要使用概率密度函数来实现

    1
    2
    3
    4
    5
    6
    def compute_surrogate_obj(self, states, actions, advantage, old_log_probs, actor):
    mu, std = actor(states)
    action_dists = torch.distributions.Normal(mu, std)
    log_probs = action_dists.log_prob(actions) # 返回\log(f(actions)),f为概率密度函数
    ratio = torch.exp(log_probs - old_log_probs) # 这里可以直接用于算概率之间的比值理论是概率密度函数的含义
    return torch.mean(ratio * advantage) # 注意torch内部实现这里的梯度可以回传到actor网络上(基于参数mu,std可以运算得到log_prob,所以梯度可以回传)

PPO 的训练技巧

  • 参考:影响PPO算法性能的10个关键技巧(附PPO算法简洁Pytorch实现)
  • 亲自测试实践总结(主要以 ‘CartPole-v0’ 环境测试):
    • Advantage Normalization 能让 Critic Loss 和 Policy Loss 都更加平滑(比较稳定),但是对整体回报不一定有收益,某些情况下还出现了波动(待确定原因)
    • State Normalization 会严重拖慢训练速度,实际测试时发现一个有趣的现象:使用 State Normalization 技巧后,会导致策略先逐步收敛到最优策略再突然下降,降幅很大且不再恢复,状态和策略都陷入了崩溃状态(结论是 State Normalization 技巧要慎用)
    • Orthogonal Initialization 一般都会有正向的效果,虽然某些场景下不一定提升很大
    • Reward Scaling 会导致策略不稳定(甚至无法收敛)
    • 增加 Policy Entropy 会导致收敛不稳定,且超参数很敏感
  • 附上述实验的代码:
    >>>点击展开折叠内容...
    1
     

Advantage Normalization

  • 最早出自The Mirage of Action-Dependent Baselines in Reinforcement Learning,对 Advantage Function 进行归一化,用于提升 PG 方法的性能

  • 具体方法:减去均值除以方差

  • 理解:

    • 归一化 将 Advantage 的均值强制设为 0:
      • 这意味着在当前的 Batch 中,大约有一半的动作会被认为是“好动作”(\(A>0\),增加概率),另一半是“坏动作”(\(A<0\),减少概率)
      • 这能有效防止 Policy 总是往一个方向跑(例如 Reward 全是正数时),显著加快收敛
    • 归一化 将方差设为 1
      • 这使得 Loss 的量级不会因为 Reward 的绝对数值大小而剧烈波动,使得超参数(如 Learning Rate)更容易调节
  • 实现方案:

    • 方案一:Batch Advantage Normalization(BAN),对当前 Batch 的所有 Advantage 求均值和方差
    • 方案二:Mini-Batch Advantage Normalization(MBAN),仅对当前 Mini-Batch Advantage Normalization
  • 实践中,BAN 效果最好,MBAN 效果次之,不使用任何 AN 效果最差;

    • 理解,方案一和方案二并未限定具体采样的轨迹是多少个,但是主要思路是尽量在更多的样本上统计均值和方差,减少波动,这样效果更好些
  • 关于 MBAN,ppo-implementation-details博客中有详细实现

  • 问题:GAE 中还需要做 Advantage Normalization 吗?是否是在计算 GAE 之前做归一化?

    • 回答,可以做,是在 GAE 之后做
  • 在一个带 GAE 和 Advantage Normalization 的 PPO 的一个训练迭代中,流程通常如下:

    • 第一步:采集数据(Rollout) :Agent 与环境交互,采集一定步数的数据。假设采集了 \(N\) 个步骤(例如 2048 步)
    • 计算 GAE :利用这 \(N\) 个数据,计算出每一个时间步的 Advantage值,得到一个向量 \(\mathbf{A} = [A_1, A_2, …, A_N]\)
    • 计算统计量(非滑动) :直接计算这 \(N\) 个数据的均值和标准差:
      $$ \mu_{batch} = \frac{1}{N} \sum_{i=1}^{N} A_i $$
      $$ \sigma_{batch} = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (A_i - \mu_{batch})^2} $$
    • 执行归一化 :
      $$ A_{norm}^{(i)} = \frac{A_i - \mu_{batch}}{\sigma_{batch} + \epsilon} $$
    • Mini-batch 训练 :将归一化后的数据打乱(Shuffle),切分成多个小批次(Mini-batch)进行 SGD 更新
为什么不用滑动平均?
  • Advantage 的定义是相对的 :Advantage \(A(s,a) = Q(s,a) - V(s)\) 衡量的是动作 \(a\) 比“平均表现”好多少
  • 分布漂移(Non-stationarity) :随着 Policy 的更新,Agent 的能力在变,Value Function 也在变
    • 上一次迭代算出的 Advantage 分布与当前迭代的分布可能完全不同
    • 如果使用历史数据的滑动平均,会引入过时的统计信息,导致对当前策略评估的偏差
  • Advantage Normalization 的核心目的是为了降低方差(Variance Reduction) 并确保 Policy Gradient 的更新幅度在不同 Batch 间保持稳定,而不是为了将数据缩放到某个固定的物理尺度
代码示例
  • 类似 开源框架 Stable Baseline3 的伪代码逻辑如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 采集数据并计算 GAE (在 rollout_buffer 中完成)
    rollout_buffer.compute_returns_and_advantage(...)

    # 在准备训练数据时进行归一化
    # 注意:这里是对整个 buffer 的 advantage 进行操作
    advantages = rollout_buffer.advantages
    if self.normalize_advantage:
    # 直接计算当前 buffer 的均值和标准差
    advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)

    # 之后再进行 mini-batch 循环更新
    for epoch in range(n_epochs):
    for rollout_data in rollout_buffer.get(batch_size):
    # 使用已经归一化好的 advantages 计算 loss
    pass

State Normalization

  • 对状态做归一化

  • State Normalization 的核心在于,与环境交互的过程中,维护一个动态的关于所有经历过的所有 State 的 Mean 和 Std, 然后对当前的获得的 State 做normalization

  • 经过 Normalization 后的 State 符合 Mean=0,Std=1 的正态分布,用这样的状态作为神经网络的输入,更有利于神经网络的训练

  • 采用滑动增量更新的方式(详细证明见附录):

    • 均值:\(\mu_{\text{new}} = \mu_{\text{old}} + \frac{1}{n}(x-\mu_{\text{old}})\)
    • 方差中间变量:\(S_{\text{new}} = S_{\text{old}} + (x-\mu_{\text{old}})\cdot(x-\mu_{\text{new}})\)
      • 注意这个值除以 \(n\) 才是方差
  • 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class RunningMeanStd:
    # Dynamically calculate mean and std
    def __init__(self, shape): # shape:the dimension of input data
    self.n = 0
    self.mean = np.zeros(shape)
    self.S = np.zeros(shape)
    self.std = np.sqrt(self.S)

    def update(self, x):
    x = np.array(x)
    self.n += 1
    if self.n == 1:
    self.mean = x
    self.std = x
    else:
    old_mean = self.mean.copy()
    self.mean = old_mean + (x - old_mean) / self.n
    self.S = self.S + (x - old_mean) * (x - self.mean)
    self.std = np.sqrt(self.S / self.n )

    class Normalization:
    def __init__(self, shape):
    self.running_ms = RunningMeanStd(shape=shape)

    def __call__(self, x, update=True):
    # Whether to update the mean and std,during the evaluating,update=Flase
    if update:
    self.running_ms.update(x)
    x = (x - self.running_ms.mean) / (self.running_ms.std + 1e-8)

    return x
  • 在模型实现时,状态归一化这个函数是添加到策略网络和状态网络层的输入端的,实现 Demo 如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def forward(self, x):
    if config.use_state_norm:
    norm_x = []
    for i in range(x.size(0)): # 逐个动作归一化
    norm_x.append(torch.tensor(self.state_norm(x[i]), dtype=torch.float32))
    x = torch.stack(norm_x, dim=0)
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    return self.fc3(x)

Reward Normalization

  • 与 State Normalization 的方案,动态维护所有获得过的 Reward 的 Mean 和 Std,然后再对当前的 Reward 做 Normalization
  • 问题:是对单次 Reward 做了 Normalization 吗?如果 Reward 已经做了 Normalization,GAE 中实际上就已经使用了标准化后的 Reward 了,Advantage 是否还需要做呢?
  • 常常被替换为 Reward Scaling
  • 虽然 Advantage 不用滑动平均,但 Reward Normalization 通常是使用滑动平均的
    • Reward Normalization :
      • 目的是让环境反馈的 Reward 尺度统一(比如不管是股票涨跌的金额,还是游戏得分,都缩放到 1 左右)
      • 因为环境的物理属性是不变的,所以用滑动平均(Running Mean/Std)来估计环境 Reward 的全局统计特性是合理的
    • Advantage Normalization :是对“优势”的归一化,是策略更新内部的一个数值稳定技巧,只关注当前批次

Reward Scaling

  • 相关论文:PPO-Implementation matters in deep policy gradients A case study on PPO and TRPO
  • Reward Scaling 与 Reward Normalization 的区别在于,Reward Scaling 是动态计算一个 standard deviation of a rolling discounted sum of the rewards,然后只对当前的 reward 除以这个 std(不减去均值?)
  • Reward Normalization 和 Reward Scaling 二选一即可,建议使用 Reward Scaling 而不是 Reward Normalization 即可
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class RewardScaling:
    def __init__(self, shape, gamma):
    self.shape = shape # reward shape=1
    self.gamma = gamma # discount factor
    self.running_ms = RunningMeanStd(shape=self.shape)
    self.R = np.zeros(self.shape)

    def __call__(self, x):
    self.R = self.gamma * self.R + x
    self.running_ms.update(self.R)
    x = x / (self.running_ms.std + 1e-8) # Only divided std
    return x

    def reset(self): # When an episode is done,we should reset 'self.R'
    self.R = np.zeros(self.shape)

Policy Entropy(Entropy Bonus)

  • 在 Actor 的 Loss 中增加一项策略熵,在最大化收益的同时,最大化策略熵,增加探索性(理解:同时有正则的作用)
  • 但是增加以后会新增加一个新的超参数,且模型对该参数很敏感

Learning Rate Decay

  • 学习率逐步衰减
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    def lr_decay(self, total_steps):
    lr_a_now = self.lr_a * (1 - total_steps / self.max_train_steps)
    lr_c_now = self.lr_c * (1 - total_steps / self.max_train_steps)
    for p in self.optimizer_actor.param_groups:
    p['lr'] = lr_a_now
    for p in self.optimizer_critic.param_groups:
    p['lr'] = lr_c_now

Gradient Clip

  • 梯度裁剪
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # Update actor
    self.optimizer_actor.zero_grad()
    actor_loss.mean().backward()
    if self.use_grad_clip: # Trick 7: Gradient clip
    torch.nn.utils.clip_grad_norm_(self.actor.parameters(), 0.5)
    self.optimizer_actor.step()

    # Update critic
    self.optimizer_critic.zero_grad()
    critic_loss.backward()
    if self.use_grad_clip: # Trick 7: Gradient clip
    torch.nn.utils.clip_grad_norm_(self.critic.parameters(), 0.5)
    self.optimizer_critic.step()

Orthogonal Initialization

  • 正交初始化(Orthogonal Initialization)是为了防止在训练开始时出现梯度消失、梯度爆炸等问题所提出的一种神经网络初始化方式。具体的方法分为两步:
    • 用均值为 0,标准差为1的高斯分布初始化权重矩阵
    • 对这个权重矩阵进行奇异值分解,得到两个正交矩阵,取其中之一作为该层神经网络的权重矩阵
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    # orthogonal init
    def orthogonal_init(layer, gain=1.0):
    nn.init.orthogonal_(layer.weight, gain=gain)
    nn.init.constant_(layer.bias, 0)

    class Actor_Gaussian(nn.Module):
    def __init__(self, args):
    super(Actor_Gaussian, self).__init__()
    self.max_action = args.max_action
    self.fc1 = nn.Linear(args.state_dim, args.hidden_width)
    self.fc2 = nn.Linear(args.hidden_width, args.hidden_width)
    self.mean_layer = nn.Linear(args.hidden_width, args.action_dim)
    self.log_std = nn.Parameter(torch.zeros(1, args.action_dim)) # We use 'nn.Paremeter' to train log_std automatically
    if args.use_orthogonal_init:
    print("------use_orthogonal_init------")
    orthogonal_init(self.fc1)
    orthogonal_init(self.fc2)
    orthogonal_init(self.mean_layer, gain=0.01)

    def forward(self, s):
    s = torch.tanh(self.fc1(s))
    s = torch.tanh(self.fc2(s))
    mean = self.max_action * torch.tanh(self.mean_layer(s)) # [-1,1]->[-max_action,max_action]
    return mean

    def get_dist(self, s):
    mean = self.forward(s)
    log_std = self.log_std.expand_as(mean) # To make 'log_std' have the same dimension as 'mean'
    std = torch.exp(log_std) # The reason we train the 'log_std' is to ensure std=exp(log_std)>0
    dist = Normal(mean, std) # Get the Gaussian distribution
    return dist

    class Critic(nn.Module):
    def __init__(self, args):
    super(Critic, self).__init__()
    self.fc1 = nn.Linear(args.state_dim, args.hidden_width)
    self.fc2 = nn.Linear(args.hidden_width, args.hidden_width)
    self.fc3 = nn.Linear(args.hidden_width, 1)
    if args.use_orthogonal_init:
    print("------use_orthogonal_init------")
    orthogonal_init(self.fc1)
    orthogonal_init(self.fc2)
    orthogonal_init(self.fc3)

    def forward(self, s):
    s = torch.tanh(self.fc1(s))
    s = torch.tanh(self.fc2(s))
    v_s = self.fc3(s)
    return v_s

Adam Optimizer Epsilon Parameter

  • 实践中,从官方默认值 1e-8 改成 1e-5
  • 原因?

Tanh Activation Function

  • 将 ReLU 换成 tanh 激活函数
  • 建议 PPO 算法默认使用激活函数
  • 原因?

Value Clipping

  • 核心目标:为了训练的稳定性,对价值进行裁剪

  • 实现方式1:Stable Baselines3 的 PPO 实现中有如下代码 /ppo/ppo.py#L234-L244

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if self.clip_range_vf is None:
    # No clipping
    values_pred = values
    else:
    # Clip the difference between old and new value
    # NOTE: this depends on the reward scaling
    values_pred = rollout_data.old_values + th.clamp(
    values - rollout_data.old_values, -clip_range_vf, clip_range_vf
    )
    # Value loss using the TD(gae_lambda) target
    value_loss = F.mse_loss(rollout_data.returns, values_pred)
    • 从代码可以看出,Stable Baselines3 认为只要是当前价值网络的预估值超过旧网络预估值的情况,都是异常情况,则当前样本不置信,将当前样本的损失设置为固定值(通过Clip实现,此时该样本对价值网络参数的梯度为0,相当于丢弃样本了)
  • 实现方式2:一些地方也看到过类似表达(最早是在RLHF中看到的)

    1
    2
    3
    4
    5
    6
    def critic_loss_fn(self, values, old_values, returns, mask):
    values_clipped = torch.clamp(values, old_values - clip_range_vf, old_values + clip_range_vf)
    vf_loss1 = (values - returns) ** 2
    vf_loss2 = (values_clipped - returns) ** 2
    vf_loss = torch.mean(torch.max(vf_loss1, vf_loss2) * mask) / mask.sum()
    return vf_loss
    • 忽略上面的 mask 部分,是语言模型训来特有的
    • 这种实现的基本思路是:如果 Clip 后的价值与真实值的误差(MSE)大于当前策略,则使用 Clip 后的,此时该样本的 Loss 对价值网络参数的梯度为0,相当于丢弃样本了
    • 理解 max 操作:当策略误差已经比 Clip 后的值对应的误差还要小了,就不要使用该误差了(也就是说当前样本的误差相对上一轮已经小了一定大小了,不需要继续更新了,这种做法称为保守更新),只有 MSE 误差相对原始价值网络对应的误差变小时,才丢弃样本

附录-均值方差滑动更新公式证明

均值更新推导

  • 均值更新推导详情:
    $$
    \begin{align}
    \mu_n &= \frac{1}{n}\sum_{i=1}^n x_i \\
    &= \frac{1}{n}(\sum_{i=1}^{n-1} x_i + x_n) \\
    &= \frac{1}{n}(\sum_{i=1}^{n-1} x_i + x_n) \\
    &= \frac{1}{n}((n-1)\cdot\frac{1}{n-1}\sum_{i=1}^{n-1} x_i + x_n) \\
    &= \frac{1}{n}((n-1)\mu_{n-1} + x_n) \\
    &= \frac{1}{n}(n\mu_{n-1} + x_n - \mu_{n-1}) \\
    &= \mu_{n-1} + \frac{1}{n}(x_n - \mu_{n-1})
    \end{align}
    $$

方差更新推导

  • 将方差中间变量 \(S_n\) 展开(这里 \(S_n\) 除以n才是方差)有:
    $$
    \begin{align}
    S_n &= \sum_{i=1}^{n} (x_i - \mu_n)^2 \\
    &= \sum_{i=1}^{n-1} (x_i - \mu_n)^2 + (x_n - \mu_n)^2 \\
    &= \sum_{i=1}^{n-1} (x_i - \mu_{n-1} + \mu_{n-1} - \mu_n)^2 + (x_n - \mu_n)^2 \\
    &= \sum_{i=1}^{n-1} (x_i - \mu_{n-1})^2 + 2(\mu_{n-1} - \mu_{n})\sum_{i=1}^{n-1}(x_i - \mu_{n-1}) + (n-1)(\mu_{n-1} -\mu_n)^2 + (x_n - \mu_n)^2 \\
    &= S_{n-1} + 2(\mu_{n-1} - \mu_{n})(\sum_{i=1}^{n-1} x_i - (n-1)\mu_{n-1}) + (n-1)(\mu_{n-1} -\mu_n)^2 + (x_n - \mu_n)^2 \\
    &= S_{n-1} + 2(\mu_{n-1} - \mu_{n})((n-1)\mu_{n-1} - (n-1)\mu_{n-1}) + (n-1)(\mu_{n-1} -\mu_n)^2 + (x_n - \mu_n)^2 \\
    &= S_{n-1} + 2(\mu_{n-1} - \mu_{n})\cdot 0 + (n-1)(\mu_{n-1} -\mu_n)^2 + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (n-1)(\mu_{n-1} -\mu_n)^2 + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (n-1)(\mu_{n-1} -\mu_n)(\mu_{n-1} -\mu_n) + (x_n - \mu_n)^2 \\
    &= S_{n-1} + ((n-1)\mu_{n-1} -(n-1)\mu_n)(\mu_{n-1} -\mu_n) + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (n\mu_{n} - x_n -(n-1)\mu_n)(\mu_{n-1} -\mu_n) + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (\mu_{n} - x_n)(\mu_{n-1} -\mu_n) + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (x_n - \mu_{n})(\mu_n - \mu_{n-1}) + (x_n - \mu_n)^2 \\
    &= S_{n-1} + (x_n - \mu_{n})(\mu_n - \mu_{n-1} + x_n - \mu_n)\\
    &= S_{n-1} + (x_n - \mu_{n})(x_n - \mu_{n-1})\\
    \end{align}
    $$
  • 最终有: \(S_n = S_{n+1} + (x_n - \mu_{n})(x_n - \mu_{n-1})\)

DPPO(Distributed PPO)

  • DPPO是PPO的分布式版本,引入了分布式计算的概念,允许多个计算节点(或智能体)并行地与环境交互,收集数据,并将这些数据用于更新全局模型
  • 分布式架构不仅加快了数据收集的速度,还提高了算法处理大规模并行任务的能力,使得学习过程更加高效

附录:一次采样仅更新一次的 PPO 讨论

  • 问题 :一次采样仅更新一次的 PPO 下,此时是 on-policy 的场景,为什么损失函数看起来和普通的 PG 不相等?(注:可以回顾一下,普通 PG 的更新公式是严格按照 on-policy 更新推导的,二者理应相等)
  • 回答 :其实此时两者的更新公式是一致的
  • 补充说明 :一次采样仅更新一次的 PPO 场景下,此时旧策略采样的样本仅更新一次模型即丢弃(即 epoch=1,且一次更新完所有参数,batch_size 足够大),且立刻会将新策略的更新同步到旧策略上,保证每次更新模型前新旧策略完全一致

普通策略梯度更新公式

  • 普通策略梯度的更新为:
    $$\theta \leftarrow \theta + \alpha \nabla_\theta \log \pi_\theta(a_t|s_t) G_t^n$$
  • 以上梯度更新对应的损失函数为:
    $$ Loss(\theta) = - \log \pi_\theta(a_t|s_t) G_t^n $$
  • 进一步求导有:
    $$\nabla_\theta Loss(\theta) = - \frac{\nabla_\theta \pi_\theta(a_t|s_t)}{\pi_\theta(a_t|s_t)} G_t^n$$
  • 注意,以上更新使用的 \(s_t,a_t,G_t^n\) 等均来源于当前策略,由于此时有 \(\pi_\theta = \pi_{\theta_\text{old}}\),所以上面的更新公式也可以写成
    $$\nabla_\theta Loss(\theta) = - \color{blue}{\frac{\nabla_\theta \pi_\theta(a_t|s_t)}{\pi_{\theta_\text{old}}(a_t|s_t)}} \color{red}{G_t^n}$$

PPO 简化后的更新公式

  • 一次采样仅更新一次的PPO,其简化后的损失函数为:
    $$ Loss(\theta) = - \mathbb{E}_{s \sim \rho_{\pi_{\theta_\text{old}}}, a \sim \pi_{\theta_\text{old}}}\left(\frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a)\right)$$
  • 将期望形式转换为采样后有:
    $$ Loss(\theta) = - \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}A_{\theta_{\text{old}}}(s,a)$$
  • 进一步求导有:
    $$ \nabla_\theta Loss(\theta) = - \color{blue}{\frac{\nabla_\theta \pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)}}\color{red}{A_{\theta_{\text{old}}}(s,a)}$$
    • 注:此时有 \(\pi_\theta = \pi_{\theta_\text{old}}\)

总体对比

  • 总结来看,针对一次采样一次更新的 PPO,简化后的更新公式与普通 PG 完全相同,更新使用的样本 \((s,a)\) 也都是从当前策略采样的(即 on-policy 场景),两者的唯一区别是梯度权重选择:
    • 简化 PPO:基于当前策略采样得到的 GAE 估计 \(A_{\theta_{\text{old}}}(s,a)\)
    • 普通 PG:基于当前策略采样的蒙特卡罗收益 \(G_t^n\),除了 REINFORCE 外,其他的方法实际也常用Q函数或者优势函数 A 等估计值来替代,故简化后的 PPO 实际上可以看做是梯度权重是 GAE 的普通 PG 算法

附录:Dual-Clip PPO

Dual-Clip PPO 核心方法总结

  • 原始论文链接:(Dual-Clip PPO)Mastering Complex Control in MOBA Games with Deep Reinforcement Learning, 2020 AAAI, Tencent
  • Dual-Clip PPO 是腾讯 AI Lab 为解决 MOBA 1v1 游戏(如《王者荣耀》)中深度强化学习训练难题而提出的 PPO(Proximal Policy Optimization)改进算法 ,核心目标是在大规模离线训练场景下,解决传统 PPO 因策略偏差过大导致的收敛不稳定问题,适配 MOBA 游戏庞大的状态空间(约 \(10^{600}\))与动作空间(约 \(10^{18000}\))
  • Dual-Clip PPO 跟前面章节中提到的 Visualize the Clipped Surrogate Objective Function 本质是一个事情,这里借助腾讯的文章,进行一些更详细的说明和探讨
    • 注:该博客引用了另一篇文章 Towards Delivering a Coherent Self-Contained Explanation of Proximal Policy Optimization, 20210815 的最早时间晚于 腾讯的论文之后

设计背景:传统 PPO 的局限性

  • 在 MOBA 1v1 游戏的大规模分布式训练中,传统 PPO 面临两大核心问题:
    • 策略偏差过大 :训练数据来自多源历史策略(如不同训练阶段的 AI 自对弈轨迹),这些轨迹与当前训练的目标策略 \(\pi_{\theta}\) 差异显著,导致概率比 \(r_t(\theta) = \frac{\pi_{\theta}(a_t|s_t)}{\pi_{\theta_{old} }(a_t|s_t)}\) 可能异常巨大
    • 方差失控 :当优势函数估计值 \(\hat{A}_t < 0\)(即动作 \(a_t\) 为“劣势动作”)时,巨大的 \(r_t(\theta)\) 会使 \(r_t(\theta) \cdot \hat{A}_t\) 产生无界负向偏差,导致策略更新方向混乱,训练难以收敛

核心改进逻辑:双重裁剪机制

  • 传统 PPO :仅通过单一裁剪(Clip)限制概率比 \(r_t(\theta)\) 的范围(\(1-\epsilon \leq r_t(\theta) \leq 1+\epsilon\)),以避免策略更新幅度过大;
  • Dual-Clip PPO :在传统 PPO 的基础上,针对 \(\hat{A}_t < 0\) 的场景增加第二重裁剪 ,通过下界限制 \(r_t(\theta) \cdot \hat{A}_t\) 的负向偏差,具体逻辑如下:
    • 第一重裁剪(继承传统 PPO) :对概率比 \(r_t(\theta)\) 进行范围约束,确保策略更新“贴近”历史策略,避免突变:
      \(clip(r_t(\theta), 1-\epsilon, 1+\epsilon)\),其中 \(\epsilon\) 为超参数(实验中设为 0.2)
    • 第二重裁剪(新增) :当 \(\hat{A}_t < 0\) 时,对 \(r_t(\theta) \cdot \hat{A}_t\) 增加下界 \(c \cdot \hat{A}_t\)(\(c > 1\) 为超参数,实验中设为 3),限制其负向偏差的最大值
  • 这一改进的核心逻辑是:即使 \(r_t(\theta)\) 异常大,“劣势动作”的损失也不会无限制减小,避免策略被极端样本误导
    • 但这也限制了超过 reference 组过高的动作概率朝下更新,使用软性的梯度缩放可能会更好(即降低这个比例,同时保持梯度继续更新)

数学表达:目标函数定义

  • Dual-Clip PPO 的核心是优化以下目标函数,通过“先 min 后 max”的双重裁剪实现稳定更新:
  • 当 \(\hat{A}_t < 0\) 时,目标函数为:
    $$
    \hat{\mathbb{E} }_t\left[ \max\left( \min\left( r_t(\theta) \cdot \hat{A}_t,\ clip(r_t(\theta), 1-\epsilon, 1+\epsilon) \cdot \hat{A}_t \right),\ c \cdot \hat{A}_t \right) \right]
    $$
    • \(\hat{\mathbb{E} }_t\):对批量样本的经验期望;
    • \(r_t(\theta)\):当前策略与历史策略的概率比;
    • \(\hat{A}_t\):优势函数估计值(衡量动作 \(a_t\) 相对平均水平的优劣);
    • \(\epsilon = 0.2\)、\(c = 3\):实验验证的最优超参数,平衡探索与收敛

关键优势:适配 MOBA 训练需求

  • 注:Dual-Clip PPO 针对 MOBA 1v1 游戏的训练特性,解决了传统算法的核心痛点,论文中提到的以下具体优势主要针对这个场景
  • 保证收敛稳定性 :通过第二重裁剪限制负向偏差,即使在多源离线数据(如百万级 CPU 生成的自对弈轨迹)场景下,也能避免策略更新“失控”,实验中 AI 训练 80 小时后 Elo 评分趋于稳定(达到职业选手水平)
  • 适配大规模分布式训练 :支持“离线数据生成-在线模型训练”解耦的系统架构(如 60 万 CPU 生成样本、1064 块 GPU 训练),无需依赖在线实时采样,大幅提升训练效率(单 GPU 每秒处理 8 万样本)
  • 兼容多标签动作 decoupling :MOBA 游戏的动作需拆解为“按钮类型(如技能/移动)+ 目标单位(如敌方英雄/小兵)”等独立标签,Dual-Clip PPO 可与“控制依赖解耦”策略结合,对每个动作标签独立优化,同时保证整体策略收敛

实际效果展示

  • 在《王者荣耀》1v1 模式的实验中,Dual-Clip PPO 是 AI 击败职业选手的关键组件之一:
  • 与传统 PPO 相比,Dual-Clip PPO 使训练收敛时间缩短约 20% ,且 AI 在与职业选手的 BO5 对战中胜率达 100%(如法师英雄貂蝉 3:0 击败联赛顶尖法师选手);
  • 结合“动作掩码(Action Mask)”“目标注意力(Target Attention)”等策略后,AI 在 2100 场公开对战中胜率达 99.81%,验证了其在复杂控制场景下的有效性

思考

  • 适用于 off-policy 场景,且需要较为严重的 off-policy 才需要
  • 改成保留梯度的截断方式理论会更合适,既可以避免异常点,又可以保证已经过高的动作概率得以被惩罚
    • 注:截止到 2025 年,快手发布的 OneRec 也提出了一种方法 Early Clipped GRPO, 其中用到的方法就是本文提到的保留梯度的截断方式

RL——PPO论文精读

本文是 PPO 的论文精读,经典值的多次回味

  • 参考链接:
    • 原始论文:Proximal Policy Optimization Algorithms, OpenAI, 2017
    • 博主的其他解读:RL——PPO

Paper Summary

  • 近端策略优化(PPO)是一类新的 Policy Gradient 方法(Actor 与环境中交互采样数据,并使用随机梯度上升优化 surrogate 目标函数,交替进行)
  • PPO 兼具 TRPO 的优势,但相对 TRPO,实现更简单、更通用,且样本复杂度更低
  • 标准的 Policy Gradient 方法对每个数据样本执行一次梯度更新,而论文提出了一种新的目标函数 ,支持对 minibatch 数据进行多轮更新
    • 注:论文的核心创新点在于这个新的目标函数可以对数据进行多轮更新(使用重要性采样实现类似 off-policy 的更新)
  • 在多个基准任务上测试了 PPO,包括模拟机器人运动和 Atari 游戏,结果表明 PPO 优于其他 online Policy Gradient 方法;
    • 在样本复杂度、实现简易性和训练时间之间取得了良好的平衡
  • 评价:
    • 在大部分场景下,PPO 都是值得最初尝试的 Online RL 方法,简洁但非常有效的方法,OpenAI 确实有实力
    • 新增补充:LLM 时代来临以后,PPO 已经成为了 RLHF 的默认方法,OpenAI 的含金量还在上升

Policy Optimization

  • 目前针对神经网络函数逼近的 RL 主要的方法包括:
    • Deep Q-Learning
    • “vanilla” Policy Gradient 方法
    • 信任域/自然策略梯度(trust region / natural policy gradient)方法
      • TRPO与自然策略梯度的关系简单讨论见:RL——自然策略梯度法
  • 现有方法在可扩展性(适用于大型模型和并行实现)、数据效率和鲁棒性(即无需调参即可适用于多种问题)方面仍有改进空间
    • Q-Learning(带函数逼近)在许多简单问题上表现不佳且理论理解不足
    • vanilla Policy Gradient 方法的数据效率和鲁棒性较差,而 TRPO 实现复杂,且不兼容包含噪声(如dropout)或参数共享(如策略与值函数共享参数或辅助任务)的架构
  • 论文旨在通过提出一种新算法来改进现状,该算法在保持 TRPO 数据效率和可靠性能的同时,仅需一阶优化(注意:TRPO 是包含二阶求导的,所以性能好但很慢)
  • 论文提出了一种基于裁剪概率比(clipped probability ratios) 的新目标函数,该函数对策略性能形成悲观估计(即下界)
  • 策略优化:交替执行 1)从策略中采样数据;2)在同一批采样数据上进行多轮优化
  • 实验内容:
    • 比较不同代理目标函数的性能 :发现基于 Clip 版本表现最佳
    • 比较 PPO 与文献中的其他算法:在连续控制任务中,PPO 优于其他对比算法;在 Atari 游戏中,其样本复杂度显著优于 A2C,与 ACER 相当,但实现更简单
      • 问题:如何理解这里的样本复杂度?
      • 回答:需要采样的样本数,从文章附录图 6 可以看到,收敛速度远远快与 A2C

Policy Optimization

Policy Gradient Methods

  • Policy Gradient 方法通过计算 Policy Gradient 的估计值,并将其代入随机梯度上升算法中实现优化。最常用的梯度估计器形式为:
    $$
    \hat{g} = \hat{\mathbb{E} }_{t}\left[\nabla_{\theta}\log\pi_{\theta}(a_{t} \mid s_{t})\hat{A}_{t}\right]
    $$
    • \(\pi_{\theta}\) 是随机策略
    • \(\hat{A}_{t}\) 是时间步 \(t\) 的优势函数估计值
    • 这里的期望 \(\hat{\mathbb{E} }_{t}[\dots]\) 表示在有限样本批次上的经验平均,算法交替进行采样和优化
    • 使用自动微分软件的实现通过构造目标函数(其梯度为 Policy Gradient 估计值)实现优化;
    • 梯度估计值 \(\hat{g}\) 通过对以下目标函数求导得到:
      $$
      L^{PG}(\theta) = \hat{\mathbb{E} }_{t}\left[\log\pi_{\theta}(a_{t} \mid s_{t})\hat{A}_{t}\right].
      $$
  • 虽然可以基于同一轨迹对损失 \(L^{PG}\) 进行多步优化,但这样做缺乏理论依据,且经验上常导致策略更新过大(见原始论文第6.1节;结果未展示,但与“无裁剪或惩罚”设置相似或更差)
    • 理解:这里第一次更新是 on-policy,第二次更新则是 off-policy,若要继续更新应该是需要重要性采样实现

信任域方法(Trust Region Methods)

  • 在 TRPO 中,目标函数(surrogate 目标)在策略更新大小的约束下最大化,具体形式为:
    $$
    \text{maximize} \quad \hat{\mathbb{E} }_{t}\left[\frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\theta_{\text{old} } }(a_{t} \mid s_{t})}\hat{A}_{t}\right] \\
    \hat{\mathbb{E} }_{t}\left[\text{KL}\left[\pi_{\theta_{\text{old} } }(\cdot \mid s_{t}), \pi_{\theta}(\cdot \mid s_{t})\right]\right] \leq \delta. \tag{3 & 4}
    $$
    • 其中, \(\theta_{\text{old} }\) 是更新前的策略参数向量
    • 通过对目标函数进行线性近似、对约束进行二次近似,可以高效地使用共轭梯度算法近似求解该问题
  • TRPO的理论支持使用惩罚项而非约束,即求解以下无约束优化问题:
    $$
    \underset{\theta}{\text{maximize} } \quad \hat{\mathbb{E} }_{t}\left[\frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\theta_{\text{old} } }(a_{t} \mid s_{t})}\hat{A}_{t} - \beta \text{KL}\left[\pi_{\theta_{\text{old} } }(\cdot \mid s_{t}), \pi_{\theta}(\cdot \mid s_{t})\right]\right] \tag{5}
    $$
    • 其中, \(\beta\) 为系数
    • 这是因为某些代理目标(计算状态上的最大KL而非均值)形成了策略 \(\pi\) 性能的下界(即悲观界)
    • TRPO 使用硬约束而非惩罚项,因为很难选择一个适用于不同问题(甚至同一问题中学习过程中特性变化)的 \(\beta\) 值
    • 因此,为了实现一阶算法模拟 TRPO 的单调改进目标,实验表明仅选择固定惩罚系数 \(\beta\) 并用 SGD 优化惩罚目标(公式5)是不够的,还需额外修改

裁剪代理目标(Clipped Surrogate Objective)

  • 设 \(r_{t}(\theta)\) 表示概率比:
    $$r_{t}(\theta) = \frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\theta_{\text{old} } }(a_{t} \mid s_{t})}$$
    • 特别地:
      $$r(\theta_{\text{old} }) = 1$$
  • TRPO 最大化以下 surrogate 目标:
    $$
    L^{CPI}(\theta) = \hat{\mathbb{E} }_{t}\left[\frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\theta_{\text{old} } }(a_{t} \mid s_{t})}\hat{A}_{t}\right] = \hat{\mathbb{E} }_{t}\left[r_{t}(\theta)\hat{A}_{t}\right].
    $$
    • 上标 \(CPI\) 指代保守策略迭代,该目标函数首次在此提出
      • 理解:其实就是包含重要性采样的 Policy Gradient 损失函数版本
    • 若无约束,最大化 \(L^{CPI}\) 会导致策略更新过大,因此,作者考虑如何修改目标函数,以惩罚使 \(r_{t}(\theta)\) 偏离 1 的策略变化
  • 作者提出的主要目标函数如下:
    $$
    L^{CLIP}(\theta) = \hat{\mathbb{E} }_{t}\left[\min\left(r_{t}(\theta)\hat{A}_{t}, \color{red}{\text{clip}(r_{t}(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_{t}}\right)\right]
    $$
    • \(\epsilon\) 为超参数(例如 \(\epsilon = 0.2\) )
    • 该目标函数的动机如下:
      • min 内的第一项为 \(L^{CPI}\) ;第二项通过裁剪概率比修改代理目标,消除了 \(r_{t}\) 超出区间 \([1-\epsilon, 1+\epsilon]\) 的动机
      • 最后,论文对裁剪和未裁剪目标取最小值,因此最终目标是未裁剪目标的下界(即悲观界)
      • 通过此方案,论文仅在概率比变化使目标改善时忽略该变化,而在其使目标恶化时保留它
      • 注意,在 \(\theta_{\text{old} }\) 附近(即 \(r=1\) 时), \(L^{CLIP}(\theta)\) 与 \(L^{CPI}(\theta)\) 一阶等价,但随着 \(\theta\) 远离 \(\theta_{\text{old} }\) ,二者差异增大
  • 图1 绘制了 \(L^{CLIP}\) 中的单一项(即单个时间步 \(t\) )随概率比 \(r\) 的变化曲线
  • 图2 展示了代理目标 \(L^{CLIP}\) 的另一种直观解释,显示了在连续控制问题上沿策略更新方向插值时多个目标的变化情况。可以看出, \(L^{CLIP}\) 是 \(L^{CPI}\) 的下界,并对过大的策略更新施加惩罚
  • 吐槽:原始论文对该目标函数的解释不够清楚

Adaptive KL Penalty Coefficient

  • 另一种方法(可作为裁剪代理目标的替代或补充)是使用KL散度惩罚,并通过调整惩罚系数使得每次策略更新达到目标KL散度值 \(d_{\text{target} }\)
    • 注:实验发现 KL 惩罚的性能不如裁剪代理目标,但因其为重要基线,仍在此介绍
  • 该算法最简单的实现步骤如下:
    • 第一步:使用多轮 minibatch SGD 优化以下 KL 惩罚目标:
      $$
      L^{KLPEN}(\theta) = \hat{\mathbb{E} }_{t}\left[\frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\theta_{\text{old} } }(a_{t} \mid s_{t})}\hat{A}_{t} - \beta \text{KL}\left[\pi_{\theta_{\text{old} } }(\cdot \mid s_{t}), \pi_{\theta}(\cdot \mid s_{t})\right]\right]
      $$
    • 第二步:计算 \(d = \hat{\mathbb{E} }_{t}\left[\text{KL}\left[\pi_{\theta_{\text{old} } }(\cdot \mid s_{t}), \pi_{\theta}(\cdot \mid s_{t})\right]\right]\) :
      • 若 \(d < d_{\text{target} }/1.5\) ,则 \(\beta \leftarrow \beta/2\)
      • 若 \(d > d_{\text{target} } \times 1.5\) ,则 \(\beta \leftarrow \beta \times 2\)
      • 更新后的 \(\beta\) 用于下一次策略更新
  • 实验效果:此方案偶尔会出现KL散度与 \(d_{\text{target} }\) 显著偏离的情况,但这种情况较少,且 \(\beta\) 会快速调整
  • 超参数设定和实验结论:
    • 参数 1.5 和 2 为启发式选择,但算法对其不敏感
    • 初始 \(\beta\) 值为另一超参数,但因算法快速调整,实际影响不大

Algorithm

  • 前几节介绍的代理损失(surrogate losses)可以通过对典型的 Policy Gradient 实现进行微小改动来计算和微分。对于使用自动微分的实现,只需构建损失函数 \( L^{CLIP} \) 或 \( L^{KLPEN} \) 来代替 \( L^{PG} \) ,并对此目标函数执行多步随机梯度上升
  • 大多数计算方差缩减的优势函数估计方法会利用学习到的状态价值函数 \( V(s) \) ;例如,广义优势估计(GAE),或有限时域估计器(finite-horizon estimators)
  • 如果使用在策略和价值函数之间共享参数的神经网络架构,则必须使用一个结合了策略替代项和价值函数误差项的损失函数。根据以往工作的建议,可以通过添加熵奖励来进一步增强此目标 ,以确保充分的探索。结合这些项,论文得到以下目标函数,每次迭代时(近似)最大化:
    $$
    L_{t}^{CLIP+VF+S}(\theta) = \hat{\mathbb{E} }_{t}\big[L_{t}^{CLIP}(\theta) - c_{1}L_{t}^{VF}(\theta) + c_{2}S[\pi_{\theta}] (s_t)\big],
    $$
    • \( c_{1}, c_{2} \) 是系数
    • \( S \) 表示熵奖励
    • \( L_{t}^{VF} \) 是平方误差损失 \( (V_{\theta}(s_{t}) - V_{t}^{\text{targ} })^{2} \)
  • 一种流行的 Policy Gradient 实现风格(特别适合与循环神经网络一起使用)让策略运行 \( T \) 个时间步(其中 \( T \) 远小于回合长度),并使用收集到的样本进行更新。这种风格需要一个不超出时间步 \( T \) 的优势估计器。Asynchronous methods for deep reinforcement learning中使用的估计器为:
    $$
    \hat{A}_{t} = -V(s_{t}) + r_{t} + \gamma r_{t+1} + \cdots + \gamma^{T-t+1}r_{T-1} + \gamma^{T-t}V(s_{T}),
    $$
    • 其中 \( t \) 指定了给定长度为 \( T \) 的轨迹段中的时间索引
    • 推广这一选择,我们可以使用截断的广义优势估计,当 \( \lambda=1 \) 时退化为式(10):
      $$
      \hat{A}_{t} = \delta_{t} + (\gamma\lambda)\delta_{t+1} + \cdots + (\gamma\lambda)^{T-t+1}\delta_{T-1},\\
      \text{where} \quad \delta_{t} = r_{t} + \gamma V(s_{t+1}) - V(s_{t}).
      $$
  • 使用固定长度轨迹段的近端策略优化(PPO)算法如下所示
    • 每次迭代中,\( N \) 个(并行)执行者各收集 \( T \) 个时间步的数据;然后基于这些 \( NT \) 个时间步的数据构建代理损失,并使用 minibatch SGD (或通常为了更好的性能使用Adam)优化它,进行 \( K \) 轮

Experiments

代理目标的比较

  • 论文在不同超参数下比较了几种代理目标

  • 论文将代理目标 \( L^{CLIP} \) 与几种自然变体和消融版本进行比较:

    • 无裁剪或惩罚(No clipping or penalty) :\( L_{t}(\theta) = r_{t}(\theta)\hat{A}_{t} \)
    • 裁剪(Clipping) :\( L_{t}(\theta) = \min(r_{t}(\theta)\hat{A}_{t}, \text{clip}(r_{t}(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_{t}) \)
    • KL penalty(fixed or adaptive) :\( L_{t}(\theta) = r_{t}(\theta)\hat{A}_{t} - \beta \text{KL}[\pi_{\theta_{\text{old} } }, \pi_{\theta}] \)
  • 对于KL惩罚,可以使用固定惩罚系数 \( \beta \) 或自适应系数(如第4节所述,目标KL值为 \( d_{\text{target} } \) ),论文还尝试了对数空间裁剪,但发现性能没有提升

    • 问题:这里是指将指数换成对数?
  • 由于论文需要为每个算法变体搜索超参数,因此选择了一个计算成本较低的基准测试:

    • 使用了 OpenAI Gym 中实现的 7 个模拟机器人任务,这些任务基于 MuJoCo 物理引擎;
    • 每个任务训练一百万个时间步
  • 除了裁剪参数 \( \epsilon \) 和KL惩罚参数 \( \beta, d_{\text{target} } \) 需要搜索外,其他超参数见表3(原文在附录中)

  • 论文使用了一个具有两个隐藏层(每层64个单元)的全连接 MLP 来表示策略,输出高斯分布的均值,具有可变标准差

  • 论文没有在策略和价值函数之间共享参数(因此系数 \( c_{1} \) 无关),也没有使用熵奖励

  • 每个算法在 7 个环境中各运行 3 次随机种子,每次运行的得分通过计算最后 100 回合的平均总奖励来确定

  • 论文对每个环境的得分进行了平移和缩放,使得随机策略得分为 0,最佳结果为 1,并在 21 次(\(7 \times 3\))运行中取平均,为每个算法设置生成一个标量得分

  • 结果如表1所示(注意,在没有裁剪或惩罚的设置中,得分为负,因为在某个环境(HalfCheetah)中得分非常低,甚至低于初始随机策略)

连续控制领域与其他算法的比较

  • 论文将PPO(使用 第3节 的“clipped”代理目标)与文献中几种其他方法进行比较(这些方法在连续控制问题上表现良好)
  • 论文比较了以下算法的调优实现:
    • 信任域策略优化(TRPO)
    • 交叉熵方法(CEM)
    • 自适应步长的普通 Policy Gradient
    • A2C:A2C代表 Advantage Actor Critic,是 A3C 的同步版本,论文发现其性能与异步版本相同或更好
    • 带信任域的 A2C
  • 对于PPO,论文使用上一节的超参数, \( \epsilon=0.2 \),结果如图3显示,PPO在几乎所有连续控制环境中都优于之前的方法

连续控制领域的展示:人形机器人跑步与转向(Humanoid Running and Steering)

  • 为了展示 PPO 在高维连续控制问题上的性能,论文在涉及 3D 人形机器人的任务上进行训练,机器人需要跑步、转向,甚至在被方块击中时从地面爬起
  • 论文测试的三个任务是:
    • (1) RoboschoolHumanoid:仅向前运动;
    • (2) RoboschoolHumanoidFlagrun:目标位置每200个时间步或达到目标时随机变化;
    • (3) RoboschoolHumanoidFlagrunHarder:机器人被方块击中并需要从地面爬起
  • 图5展示了学习策略的静态帧
  • 图4展示了三个任务的学习曲线
  • 超参数见表4,在并行工作中,Heess等人[]使用PPO的自适应KL变体(第4节)学习了3D机器人的运动策略

Atari领域与其他算法的比较

  • 论文还在 Arcade Learning Environment 基准测试上运行了 PPO,并与调优良好的 A2C 和 ACER 实现进行了比较
  • 对于所有三种算法,论文使用了与 Asynchronous methods for deep reinforcement learning 相同的策略网络架构
  • PPO的超参数见表5,对于其他两种算法,论文使用了针对此基准测试优化的超参数
  • 附录B提供了所有 49 款游戏的结果表和学习曲线
  • 论文考虑以下两个评分指标:
    • (1) 整个训练期间每回合的平均奖励(偏向快速学习);
    • (2) 训练最后100回合的平均奖励(偏向最终性能)
  • 表2显示了每种算法“获胜”的游戏数量,其中评分指标是三次试验的平均值

附录B:更多Atari游戏上的性能表现

  • 此处论文展示了 PPO 与 A2C 在 49 款 Atari 游戏上的对比结果
  • 图6:PPO与A2C在OpenAI Gym中所有49款Atari游戏上的对比(截至发表时),图6显示了三种随机种子的学习曲线
  • 表6:PPO与A2C在Atari游戏上的平均最终得分(最后100回合,40M游戏帧后),表6列出了平均性能
1…141516…61
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

608 posts
49 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4