qinghuan
← 监督微调与偏好对齐 / 全部笔记

单元 34 / 监督微调与偏好对齐

P1 · 重点提升L3 · 深入

PPO、GAE 与 RLHF:约束策略更新

先估计一个动作相对预期表现好多少(优势),再分别推导 GAE、PPO 概率比裁剪和 RLHF 参考策略约束,并检查终止与截断。

本篇目录 · 按需要跳转

读完这篇,你能做什么

用 Java 实现区分真正终止与时间截断的 GAE;在 CPU 实验中检查裁剪梯度,并提高合成批次中优选动作的概率。

学习理由、准备要求与知识点

为什么学它

策略变了,采到的动作也会变。优势估计或旧策略概率用错,程序仍可能运行,却已经在优化错误的目标。

先读这些内容

知识点清单

  • 时间差分残差
  • GAE反向递推
  • 终止与时间截断
  • 旧策略概率固定
  • PPO裁剪代理目标
  • 优势正负号
  • RLHF参考KL
  • 价值函数与策略分工

运行前先确认论文机制按文中版本说明;Java 17 完整算法,Python 3.14 与 PyTorch 2.9.1+cpu 的小型 CPU 实验。未执行 GPU、多机或大规模模型训练。

01先弄懂它是什么

希望策略更常选到效果好的动作,先要知道它比平时好多少。策略 pi(a|s) 表示在状态 s 选择动作 a 的概率,价值 V(s) 估计从这个状态开始的折扣回报;优势 A(s,a) 再比较该动作与通常水平。优势为正,希望提高它的概率;为负,希望降低。PPO 利用旧策略收集的交互记录,通过一个局部代理目标更新策略,并控制对这批记录的重复使用,而不是直接规定每个好动作加多少概率。

整理训练数据时,一条长度为 T 的交互记录,其奖励、价值和优势都可以排成 [T] 向量;同时运行 B 个环境,常用形状 [T,B]。r_t 是执行动作后得到的奖励,gamma 控制每往后一步怎样折扣,lambda 控制优势估计怎样结合短期和长期信息。这两个系数按步骤起作用,不以秒计;如果一步动作耗时变了,同样 gamma 对真实时间的折扣含义也会变。

RLHF 中几个“模型”各有不同工作。当前策略正在被更新;旧策略是采集这一批动作时用的策略,我们要保存它当时给这些动作的概率,同一批反复训练期间不能改写。参考策略则提供一个比较基准,用于惩罚当前策略偏离太远,因此不能因为它也比较旧,就和采样用的旧策略混在一起。

价值模型估计从当前状态继续下去能获得多少累计回报;奖励模型负责评分,例如判断某个回答更符合偏好,它与预测后续累计结果的价值模型也不是一回事。

文中这几个词,先认一认

TD残差
先把“这一步拿到的奖励”与“下一状态的估计价值乘以折扣系数”相加,再减去原先对当前状态的价值估计,得到一次新旧估计的差额。任务真正结束还是仅因时间限制中断,会决定能否保留下一状态的估计。
GAE
从当前步开始,把后续各步的 TD 残差按权重加起来:越往后一步,就再乘一次 gamma×lambda,用这个总和估计当前动作比通常水平好多少。gamma 控制未来奖励的折扣,lambda 调整近处和远处信息的份量;递推不能跨到重置后的新一局。
概率比
对于已经采样到的动作,当前策略给它的概率,除以采样时旧策略给它的概率。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

可以把边界问题拆成两问:这一步还能借用末状态对未来回报的估计吗?还能沿数组接着读下一条轨迹吗?公式分别用 bootstrap 和 continue 控制:delta_t=r_t+gamma×bootstrap_t×V(next_t)-V(s_t),A_t=delta_t+gamma×lambda×continue_t×A_(t+1),后一个从后往前算。

任务真正结束时,两项开关都为零。如果只是计时上限到了,任务本身未自然结束,可以保留末状态的价值估计,这叫自举;但记录后面若已是重置后的新一局,就不能继续累加。只用一个 done 标记前,必须核对环境包装器怎样处理,否则学到的目标可能被改掉。

PPO 先看当前策略给已采动作的概率,比旧策略大了还是小了:ratio=exp(logpi_new-logpi_old)。它优化 min(ratio×A,clip(ratio,1-eps,1+eps)×A) 的平均值,先乘优势,再比较两项。

优势为正时,超过上界继续提高概率不再增加这项收益;优势为负时,低于下界继续降低概率也不再获益。但如果改动方向让目标变差,仍应有梯度纠正,所以不能见到 ratio 超界就把梯度全清零。

这里的裁剪改变的是训练所用的代理目标,并不是把模型所有动作的概率比强行锁在区间内,也不等于严格限制整体 KL。因为多个动作共享参数,更新还会影响未采到的动作。实际训练仍需观察近似 KL、熵、裁剪比例和价值损失,并限制更新轮数。本篇 CPU 实验只对固定合成批次中的两个动作 logits 做更新,检查优势正负与梯度方向,没有包含真实语言生成、奖励模型训练或完整在线采样。

先跑通一个最小例子

用PyTorch 2.9.1+cpu运行experiment.py。固定合成轨迹和动作批次,验证GAE、裁剪分支与真实梯度更新;不宣称复现完整PPO或RLHF。

展开收起代码 · experiment.pypython · 42 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

experiment.py · python
import torch
torch.manual_seed(11)
torch.set_num_threads(1)
torch.set_default_dtype(torch.float64)

def gae(rewards, values, next_values, terminal, continuation, gamma=1., lam=1.):
    advantage = torch.zeros_like(rewards)
    future = torch.tensor(0.)
    for t in reversed(range(len(rewards))):
        delta = rewards[t] + gamma * next_values[t] * (1-terminal[t]) - values[t]
        future = delta + gamma * lam * continuation[t] * future  # 递推不跨越重置边界。
        advantage[t] = future
    return advantage

assert torch.allclose(gae(torch.tensor([1.,2.]),torch.zeros(2),torch.zeros(2),torch.tensor([0.,1.]),torch.tensor([1.,0.])),torch.tensor([3.,2.]))
assert torch.allclose(gae(torch.tensor([1.]),torch.tensor([2.]),torch.tensor([4.]),torch.tensor([0.]),torch.tensor([0.]),.9),torch.tensor([2.6]))
assert torch.allclose(gae(torch.tensor([1.]),torch.tensor([2.]),torch.tensor([4.]),torch.tensor([1.]),torch.tensor([0.]),.9),torch.tensor([-1.]))
def clipped_loss(log_ratio, advantage):
    ratio = log_ratio.exp()
    clipped = ratio.clamp(.8, 1.2)
    return -torch.minimum(ratio * advantage, clipped * advantage).mean()

z = torch.tensor([2.]).log().requires_grad_()
clipped_loss(z, torch.tensor([1.])).backward()
assert z.grad.item() == 0.  # 正优势且超过上界,直接奖励继续增大的梯度被截断。
assert abs(clipped_loss(torch.tensor([2.]).log(),torch.tensor([-1.])).item()-2.)<1e-12
logits = torch.nn.Parameter(torch.zeros(2))
actions = torch.tensor([1,1,1,0])
advantages = torch.tensor([1.,1.,1.,-1.])
old_logp = torch.log_softmax(logits,dim=0)[actions].detach()  # 同一批的旧概率固定。
optimizer = torch.optim.SGD([logits], lr=.1)
for _ in range(4):
    optimizer.zero_grad()
    new_logp = torch.log_softmax(logits,dim=0)[actions]
    loss = clipped_loss(new_logp-old_logp, advantages)
    loss.backward()
    optimizer.step()
probability = torch.softmax(logits,dim=0)[1].item()
assert .5 < probability < 1
assert torch.isfinite(logits).all()
print(f"GAE=[3.0,2.0] preferred_action_probability={probability:.6f}")
print("PASS 7 checks; fixed synthetic batch, not full RLHF")

核对运行结果输出GAE=[3.0,2.0],优选动作概率大于0.5,以及PASS 7 checks。

正在加载流程图…

旧概率固定于采样时刻;终止、自举和轨迹继续三个判断先于GAE与PPO更新。
查看流程图源码(Mermaid)
flowchart TD
  N0["保存采样动作的旧对数概率"]
  N1["区分终止和轨迹截断"]
  N2["反向计算GAE与价值目标"]
  N3["按正负优势计算裁剪目标"]
  N4["检查KL与失败样本后再采样"]
  N0 --> N1
  N1 --> N2
  N2 --> N3
  N3 --> N4

按顺序看,每一步做了什么

  1. 保存采样动作的旧对数概率
  2. 区分终止和轨迹截断
  3. 反向计算GAE与价值目标
  4. 按正负优势计算裁剪目标
  5. 检查KL与失败样本后再采样

03对照实现代码

RolloutBuffer的GAE反向递推 (新标签页)↗

Stable-Baselines3 v2.7.0;PPO论文v2与GAE论文v6 · RolloutBuffer.compute_returns_and_advantage

原创单步GAE公式模型;用显式双掩码解释边界,与SB3实际接口不同。CPU实验使用PyTorch真实自动微分,但未运行SB3环境或完整RLHF。

核心机制代码 · python
def one_gae_step(reward, value, next_value, future, gamma, lam, bootstrap, continuation):
    delta = reward + gamma * bootstrap * next_value - value  # 先形成一步TD残差。
    advantage = delta + gamma * lam * continuation * future  # 沿同一轨迹向后累计。
    target = advantage + value  # 供价值网络拟合的lambda回报目标。
    return advantage, target

固定版本RolloutBuffer保存rewards、values、episode_starts和log_probs,计算时从最后时间步倒序遍历。末步使用外部last_values与dones,内部步骤参考下一条episode_starts。这样做是因为buffer中存储的是每个时间步开头的标记,索引错一位会把前一局的奖励传播到后一局。

源码先算 delta,再更新 last_gae_lam,最后把 advantages+values 存成 returns。这里的 returns 是用来训练价值函数的 lambda 目标,不一定是把完整一局的实际奖励直接相加。lambda 为零时只参考一步 TD;lambda 为一、轨迹完整且终点处理正确时,优势才可写成相应折扣回报减去价值的形式。读变量名时,要一起看它具体怎样算出来。

时间到了但任务尚未自然结束时,不能只看 buffer 里一个结束标记,还要沿 on_policy_algorithm.collect_rollouts 检查处理过程。某些终止观测会先用于给奖励补上折扣价值,再由 buffer 的结束标记阻断跨局传播。本篇教学函数把 bootstrap 和 continuation 分开,是为了看清这两件事;真实 buffer 函数并不是就有两个同名输入参数。

PPO.train随后重新评价同一批动作,以保存的old_log_prob形成概率比,并使用两个乘优势目标的逐项最小值。优势标准化和价值裁剪是额外配置,不应偷偷加入推导再宣称完全相同的损失。看源码要同时看采样、buffer后处理与训练,孤立复制三行目标无法保证训练语义一致。

代码先调用谁,再调用谁

  1. collect_rollouts采集与保存旧概率
  2. 处理时间上限末状态价值
  3. compute_returns_and_advantage倒序遍历
  4. 训练时重新评价同一批动作
  5. 形成裁剪目标与价值损失

04动手写一个例子

带双边界掩码的GAE

输入每步奖励、当前与下一状态价值、真终止和轨迹继续标记,返回优势数组;有限数值和gamma、lambda范围需验证。

输入 1
奖励[1,2]、价值0、gamma=lambda=1、末步终止
输出 1
优势[3,2]
输入 2
r=1、V=2、nextV=4、截断、gamma=.9
输出 2
优势2.6

算当前这一步的优势,需要把后面若干步的 TD 残差按衰减权重加起来。若每换一个起点就重新从头加,这些后半段会重复计算,总工作量随轨迹长度的平方增长。可以改为从最后一步向前走,把后面已经算好的结果存进 future;当前步只需把它乘上衰减系数,再加自己的 delta。这样复用同一段后续计算,就能在线性时间得到相同的有限序列结果。

倒序扫描前future代表下一时间步的优势;遇到不继续的边界先屏蔽它,再加入当前delta。即使上一局与下一局在数组中相邻,也不会通过这个变量串起来。真终止却声明继续属于自相矛盾输入,程序拒绝而非猜测读者想表达什么。

空数组返回空结果,说明没有待处理轨迹;NaN、超范围折扣或运算溢出则明确失败。该算法检验的是给定价值预测和边界标记下的算术正确性,不判断价值网络是否准确。输出被后续训练使用时通常应视为固定目标,避免无意让梯度沿采样缓冲区继续传播。

为什么这样写是对的每次写入a[i]时,它等于从i开始且受continuation边界限制的折扣TD残差和。

时间 O(T)额外空间 O(T)输出,O(1)额外状态

每个时间步仅访问一次,future复用已求后缀。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

展开收起代码 · Main.java · 完整可运行程序Java · 42 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    static void near(double expected,double actual) {
        if (!Double.isFinite(actual) || Math.abs(expected-actual)>1e-9*Math.max(1,Math.abs(expected)))
            throw new AssertionError(expected+" != "+actual);
    }
    static void check(boolean ok) { if(!ok) throw new AssertionError(); }
    static void bad(Runnable r) {
        try { r.run(); } catch(IllegalArgumentException e) { return; }
        throw new AssertionError("invalid input accepted");
    }
static double[] gae(double[] r,double[] v,double[] next,boolean[] terminal,
                        boolean[] continues,double gamma,double lambda) {
        if(r==null||v==null||next==null||terminal==null||continues==null
            ||r.length!=v.length||r.length!=next.length||r.length!=terminal.length
            ||r.length!=continues.length||!Double.isFinite(gamma)||!Double.isFinite(lambda)
            ||gamma<0||gamma>1||lambda<0||lambda>1) throw new IllegalArgumentException("input");
        double[] a=new double[r.length]; double future=0;
        for(int i=r.length-1;i>=0;i--) {
            if(!Double.isFinite(r[i])||!Double.isFinite(v[i])||!Double.isFinite(next[i])
                ||(terminal[i]&&continues[i])) throw new IllegalArgumentException("transition");
            double delta=r[i]+gamma*(terminal[i]?0:next[i])-v[i]; // 真终止不自举。
            future=delta+gamma*lambda*(continues[i]?future:0); // 截断也停止跨段传播。
            if(!Double.isFinite(future)) throw new IllegalArgumentException("overflow");
            a[i]=future; // 从后向前保存当前时间步的优势。
        }
        return a;
    }
    public static void main(String[] args) {
double[] a=gae(new double[]{1,2},new double[]{0,0},new double[]{0,0},
            new boolean[]{false,true},new boolean[]{true,false},1,1);
        near(3,a[0]); near(2,a[1]);
        near(2,gae(new double[]{1,2},new double[]{0,0},new double[]{0,0},new boolean[]{false,true},new boolean[]{true,false},.5,1)[0]);
        near(2.6,gae(new double[]{1},new double[]{2},new double[]{4},new boolean[]{false},new boolean[]{false},.9,1)[0]);
        near(-1,gae(new double[]{1},new double[]{2},new double[]{4},new boolean[]{true},new boolean[]{false},.9,1)[0]);
        near(1,gae(new double[]{1,2},new double[]{0,0},new double[]{0,0},new boolean[]{false,true},new boolean[]{true,false},1,0)[0]);
        check(gae(new double[]{},new double[]{},new double[]{},new boolean[]{},new boolean[]{},1,1).length==0);
        bad(()->gae(new double[]{1},new double[]{0},new double[]{0},new boolean[]{true},new boolean[]{true},1,1));
        bad(()->gae(new double[]{1},new double[]{0},new double[]{0},new boolean[]{true},new boolean[]{false},1,Double.NaN));
        System.out.println("PASS 8 cases");
    }
}

这些特殊情况也要试一试

  • lambda为零退化成单步残差
  • 截断可自举但不跨局传播
  • 真正终止不使用末状态价值
  • 空轨迹与非法数值分别处理
进阶选读 · 异步采样与陈旧策略诊断

场景多工作者采集速度不同,训练批次混有多个策略版本。

做法为样本记录策略版本和旧对数概率,限制陈旧度;检查重要性比与KL分布,不能只增加训练轮数掩盖分布不匹配。

复杂度T×B条样本的GAE为O(TB),神经前向与通信另外计量。

好处与代价更高采样吞吐可能增加策略陈旧,需要在效率和可解释目标之间取舍。

05项目里怎么用

开始训练前先让奖励、优势和价值目标的单位一致。若把终局奖励缩放一百倍,却保持价值损失权重和KL系数不变,整个优化平衡也变了。记录原始奖励、惩罚后奖励与标准化优势,能把奖励尺度问题与策略学习问题区分开。

用于语言模型时,动作通常就是模型生成的 token,因此要明确长短回答怎样求和或取平均。策略目标只应计算回答 token,并说清结束标记 EOS、填充 padding 和截断怎样处理;提示里的 token 不是模型主动选出的动作。保存的旧概率还要与当前计算使用相同的分词、掩码和采样策略,否则新旧概率比就不再是在比较同一件事。

生产或研究训练都应保存策略、价值网络、优化器、调度器、随机数及采样位置等状态。恢复后先对固定小批次重算损失作一致性检查。本文实验不测GPU显存、分布式同步或长序列吞吐,真正扩大规模前需要单独验证这些环节,不能由几次CPU梯度更新推导训练稳定。

容易踩的坑

  • 把截断当真终止丢掉自举
  • 更新时重算并覆盖旧策略概率
  • 忽略负优势改变裁剪分支
  • 认为裁剪严格约束全部KL
  • 奖励尺度变化后不重审其他系数

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
较小lambda低方差、偏向短期估计更依赖价值预测准确早期不稳定任务
较大lambda利用更长回报信息方差更高轨迹可靠且回报可观测
多轮使用同一批提高样本利用率策略逐步偏离采样分布配合KL监测与轮数上限

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01旧策略与参考策略有何区别?

展开参考答案与推导

旧策略定义本批采样概率比,参考策略定义长期偏离约束;生命周期和用途不同。

02为什么要两个边界掩码?

展开参考答案与推导

是否自举与是否继续下一条轨迹是两件事,时间截断可以自举但必须断开递推。

03裁剪是否保证新概率在固定区间?

展开参考答案与推导

不保证,它改变代理目标,共享参数和未采动作仍可能发生较大变化。

换个情况,还会处理吗

01超时回合价值总偏低如何查?

展开参考答案与推导

检查是否把时间截断当真正终止,以及末状态价值是否丢失或重复加入。

02平均奖励上升但KL骤增怎么办?

展开参考答案与推导

检查旧概率版本、采样掩码和更新轮数,减少步长或提前停止,再用独立任务评估。

03价值损失远大于策略损失是否直接除100?

展开参考答案与推导

先核对奖励尺度、目标和归约口径,再依据实验调整系数,不能只让日志数值接近。

写代码试一试

01实现双掩码GAE。

输入 / 条件
奖励[1,2],全零价值,末步终止
输出 / 预期
[3,2]

提示 倒序存后缀

展开参考答案与推导

使用正文delta与future递推。

02验证负优势裁剪。

输入 / 条件
ratio=2,A=-1,eps=.2
输出 / 预期
代理值-2

提示 先相乘再取min

展开参考答案与推导

min(-2,-1.2)=-2,仍保留纠正方向。

03构造截断对照。

输入 / 条件
r=1,V=2,nextV=4,gamma=.9
输出 / 预期
截断2.6,终止-1

提示 只改变bootstrap标记

展开参考答案与推导

固定所有数值,分别断开下一局传播并比较结果。

读完之后,独立完成

课后作业:自己做一遍

本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

用 Java 实现区分真正终止与时间截断的 GAE;在 CPU 实验中检查裁剪梯度,并提高合成批次中优选动作的概率。

本篇重点加练

回合边界和裁剪目标决定学习信号,加练跨边界处理及代理目标的局限。

  • 终止与时间截断
  • PPO裁剪代理目标

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01代码推演

    长度为 4 的 rollout 同时含自然终止和时间截断,自选奖励与价值数值,按明确的边界约定逐步计算 TD 残差与 GAE。

    提交内容
    轨迹表与逆序计算
    怎样算完成
    • 两个边界信号分开
    • 自举值来源明确

    练到的知识点时间差分残差 · GAE反向递推 · 终止与时间截断

  2. 作业 02原理问答

    固定旧策略概率为 0.2,分别选择正负优势和三个新概率,比较 PPO 代理目标;说明裁剪限制的是哪一项。

    提交内容
    分段目标图与解释
    怎样算完成
    • 符号和概率比一致
    • 目标约束不扩大为参数保证

    练到的知识点旧策略概率固定 · PPO裁剪代理目标 · 优势正负号

  3. 作业 03代码推演

    同一观测序列可能来自两个相邻 episode 或一个连续 episode,自拟奖励与价值,比较 GAE 所需的边界信息,说明哪些字段不能省略。

    提交内容
    两种事件账本与目标对照
    怎样算完成
    • 环境重置边界可追踪
    • 仅靠观测值不假定同一轨迹

    练到的知识点终止与时间截断

综合应用

  1. 作业 04编程实践

    编写 CPU rollout 后处理器,处理多个 episode 拼接、最后状态被截断和空轨迹;用手算案例核对 GAE 与回报目标。

    提交内容
    实现与边界测试
    怎样算完成
    • 轨迹不跨 episode 串联
    • 状态长度契约明确

    练到的知识点时间差分残差 · GAE反向递推 · 终止与时间截断

  2. 作业 05排错实验

    在小离散策略实验中分别误更新旧策略缓存、使参考策略参与梯度、让优势连回价值网络;记录梯度与目标的改变。

    提交内容
    三类故障复现
    怎样算完成
    • 旧策略和参考策略身份分开
    • 梯度流证据完整

    练到的知识点旧策略概率固定 · RLHF参考KL · 价值函数与策略分工

  3. 作业 06面试场景

    训练日志显示奖励上升、KL 上升、价值误差也上升。设计一次有限预算诊断,列出要保存的逐样本量及停止条件。

    提交内容
    诊断协议和日志 schema
    怎样算完成
    • 指标定义可复核
    • 单一指标不决定因果结论

    练到的知识点PPO裁剪代理目标 · RLHF参考KL · 价值函数与策略分工

  4. 作业 07编程实践

    CPU 枚举两动作策略的新概率与正负优势,在不同裁剪阈值下记录代理目标及其梯度;检查边界点与平坦区间,声明自动求导约定。

    提交内容
    网格实验及分段图
    怎样算完成
    • 旧概率固定且合法
    • 数值梯度与边界约定分开

    练到的知识点PPO裁剪代理目标

  5. 作业 08排错实验

    构造含截断的短 rollout,分别只修改边界 mask 和只修改裁剪阈值,观察优势与策略目标在哪一层变化,防止把所有差异归为 PPO 超参数。

    提交内容
    分层故障实验
    怎样算完成
    • 一次只修改一个因素
    • 目标变化有中间量证据

    练到的知识点终止与时间截断 · PPO裁剪代理目标

进阶挑战 · 掌握前置后选做

  1. 作业 09设计评审

    在 CPU 小 MDP 中比较两种 GAE 参数和两种裁剪范围,预注册样本预算与种子;报告更新稳定性和回报。

    提交内容
    四条件实验与报告
    怎样算完成
    • 环境交互预算公平
    • 失败配置完整保留

    练到的知识点GAE反向递推 · PPO裁剪代理目标 · 优势正负号

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 10思维拓展 · 反例构造

    同一 PPO 代理目标变好,真实环境回报却变差是否可能?在小型可枚举 MDP 中研究数据分布、边界处理或更新幅度造成的反例。

    提交内容
    反例环境与更新记录
    怎样算完成
    • 代理目标和真实回报独立计算
    • 失效原因有受控对照

    练到的知识点终止与时间截断 · PPO裁剪代理目标

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗