正文
GRPO 是一种用于强化学习(RLHF)的高效算法,它与 PPO 的最大区别在于 不需要独立的 Critic (评论家) 模型,而是通过从同一个 Prompt 采样多组回复(Group),计算它们之间的相对优势(Relative Advantage)来优化策略。
GRPO 算法原理
GRPO和PPO算法流程对比
这是一个非常棒的需求。通过具体的场景和数字流转,最能看清这两个算法本质的差异。
我们设定的场景是:让模型生成一个微型故事。
- 输入 Prompt:
“请写一个关于一只想要飞翔的兔子的故事。” - 评判标准 (Reward):只要故事里包含“勇气”和“尝试”给高分,逻辑不通或消极给低分。
一、 PPO (Proximal Policy Optimization) 的全流程
在 PPO 中,我们有 4 个角色在场:
- Actor (演员):正在训练的模型。
- Ref Model (老师):冻结的参考模型。
- Critic (评论家):正在训练的价值预测模型。
- Reward Model (打分器):冻结的打分模型。
流程演示
阶段 1:采样 (Rollout)
- Actor 输出:针对 Prompt 生成了 1 个 回答。
- 回答 A:“小白兔爬上了山顶,跳了下来,虽然摔疼了,但它觉得自己离天空更近了。”
- Ref Model 输出:计算回答 A 的概率,发现和自己生成的概率差不多(没有讲乱码)。$\rightarrow$KL 惩罚很小。
阶段 2:打分 (Evaluation)
- Reward Model (RM):读了回答 A,觉得很励志。
- 输出:实际得分 (Reward) = 0.8 分。
阶段 3:评论家预测 (Critic Prediction)
- Critic:它只看了 Prompt(或者 Prompt+回答的前半部分),根据它过去的经验进行预测。
- 心理活动:“以前这种题,Actor 一般只能考 0.5 分。”
- 输出:预测分 (Value) = 0.5 分。
阶段 4:计算优势 (Advantage Calculation)
- 系统计算:$优势 = 实际得分 - 预测分 = 0.8 - 0.5 = \mathbf{+0.3}$。
- 含义:这次表现比预期好了 0.3 分,值得表扬!
阶段 5:反向传播 (Backpropagation)
这里有 两个独立 的训练任务同时进行:
- Actor 训练:
- 收到信号:“回答 A 是个好回答 (+0.3)”。
- 动作:修改参数,增加生成“小白兔爬上山顶…”这句话的概率。
- Critic 训练:
- 收到信号:“我预测了 0.5,实际是 0.8,我预测低了,误差 (MSE) 是 0.09。”
- 动作:修改参数,下次遇到这个题,要把预测值往 0.8 调整,争取下次猜得准一点。
二、 GRPO (Group Relative Policy Optimization) 的全流程
在 GRPO 中,我们把 Critic 开除了。只剩下 3 个角色。
流程演示
阶段 1:组采样 (Group Rollout)
- Actor 输出:针对 Prompt,强制生成 一组 (例如 4 个) 不同的回答。
- 回答 A:“小白兔做了一个滑翔伞,飞了起来。”
- 回答 B:“小白兔只是坐在洞口发呆。” (消极)
- 回答 C:“小白兔爬树跳下来,摔伤了腿,但它很开心。” (励志)
- 回答 D:“asdfg ghjkl…” (乱码/逻辑错误)
阶段 2:打分 (Scoring)
- Reward Model (RM) 给这 4 个回答分别打分:
- A: 0.9 分
- B: 0.2 分
- C: 0.8 分
- D: 0.0 分
阶段 3:计算基准 (Baseline Calculation)
- 不靠预测,靠统计。
- 计算这组的平均分:$(0.9 + 0.2 + 0.8 + 0.0) / 4 = \mathbf{0.475}$ (这是基准线)。
阶段 4:计算优势 (Advantage Calculation)
- 用每个人的分减去平均分(归一化):
- A 的优势:$0.9 - 0.475 = \mathbf{+0.425}$ (表现优异)
- B 的优势:$0.2 - 0.475 = \mathbf{-0.275}$ (表现拉垮)
- C 的优势:$0.8 - 0.475 = \mathbf{+0.325}$ (表现良好)
- D 的优势:$0.0 - 0.475 = \mathbf{-0.475}$ (表现极差)
阶段 5:反向传播 (Backpropagation)
- Actor 训练:
- 收到信号:大力提倡 A 和 C,稍微抑制 B,严厉打击 D。
- 最终的参数更新 (Gradient Update): 系统会计算这4个结果的梯度,最后相加(或取平均)。
- 动作:更新参数,让模型更倾向于输出 A 和 C 的逻辑。
- Critic 训练:无。不需要训练 Critic。
三、 Actor 和 Critic 训练方式的区别
在 PPO 中,这两个模型虽然都在“学习”,但它们学习的目标(Loss Function)完全不同,就像球员和球评的区别。
| 维度 | Actor (演员/策略模型) | Critic (评论家/价值模型) |
|---|---|---|
| 身份 | 生成器 (Generator) | 回归器 (Regressor) |
| 输入 | Prompt | Prompt (有时也包含 Response) |
| 输出 | 下一个 Token 的概率分布 (词表大小, e.g., 32000维) | 一个实数标量 (Value, e.g., 0.5) |
| 学习目标 | 最大化奖励 (Maximize Reward) | 最小化预测误差 (Minimize MSE) |
| Loss 函数 | Policy Loss: 如果优势是正的,让这个动作概率变大; 如果优势是负的,让这个动作概率变小。 |
Value Loss (MSE): $(预测分 - 真实奖励)^2$ 努力让预测分无限接近真实奖励。 |
| 直观理解 | 怎么踢球才能进球? | 怎么预测这场球能不能赢? |
| 训练难度 | 难。因为搜索空间巨大,且容易过拟合。 | 中等。这是一个标准的监督学习任务(拟合曲线)。 |
| GRPO 的处理 | 保留 | 删除 (用统计平均值代替预测) |
总结这一场景
- PPO 就像是你写了一篇作文,老师(Critic)先预估你平时水平是 70 分,结果你考了 80 分。你因为“超常发挥 10 分”而感到开心(更新策略)。同时老师也反思“看来我低估他了,下次预估 75 分”(更新 Critic)。
- GRPO 就像是你一口气写了 4 篇作文。虽然没有老师给你预估,但你自己一比:第 1 篇比平均水平好,第 2 篇比平均水平差。你保留好的,抛弃差的。你不需要一个老师告诉你“平均水平”是多少,你自己算出来的就是平均水平。
VERL-GRPO脚本、参数解析
# Tested successfully on the hiyouga/verl:ngc-th2.6.0-cu126-vllm0.8.4-flashinfer0.2.2-cxx11abi0 image.
# It outperforms the Qwen2 7B base model by two percentage points on the test set of GSM8K.
set -x
python3 -m verl.trainer.main_ppo \
algorithm.adv_estimator=grpo \
data.train_files=$HOME/data/gsm8k/train.parquet \
data.val_files=$HOME/data/gsm8k/test.parquet \
data.train_batch_size=1024 \
data.max_prompt_length=512 \
data.max_response_length=1024 \
data.filter_overlong_prompts=True \
data.truncation='error' \
actor_rollout_ref.model.path=Qwen/Qwen3-8B \
actor_rollout_ref.actor.optim.lr=1e-6 \
actor_rollout_ref.model.use_remove_padding=True \
actor_rollout_ref.actor.ppo_mini_batch_size=256 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=32 \
actor_rollout_ref.actor.use_kl_loss=True \
actor_rollout_ref.actor.kl_loss_coef=0.001 \
actor_rollout_ref.actor.kl_loss_type=low_var_kl \
actor_rollout_ref.actor.entropy_coeff=0 \
actor_rollout_ref.model.enable_gradient_checkpointing=True \
actor_rollout_ref.actor.fsdp_config.param_offload=False \
actor_rollout_ref.actor.fsdp_config.optimizer_offload=False \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=32 \
actor_rollout_ref.rollout.tensor_model_parallel_size=2 \
actor_rollout_ref.rollout.name=vllm \
actor_rollout_ref.rollout.gpu_memory_utilization=0.6 \
actor_rollout_ref.rollout.n=5 \
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=32 \
actor_rollout_ref.ref.fsdp_config.param_offload=True \
algorithm.use_kl_in_reward=False \
trainer.critic_warmup=0 \
trainer.logger='["console","wandb"]' \
trainer.project_name='verl_grpo_example_gsm8k' \
trainer.experiment_name='qwen3_8b_function_rm' \
trainer.n_gpus_per_node=8 \
trainer.nnodes=1 \
trainer.save_freq=20 \
trainer.test_freq=5 \
trainer.total_epochs=15 $@
这是一份使用 Verl (Volcano RL) 框架进行 GRPO (Group Relative Policy Optimization) 算法训练的启动脚本。
GRPO 是一种用于强化学习(RLHF)的高效算法,它与 PPO 的最大区别在于 不需要独立的 Critic (评论家) 模型,而是通过从同一个 Prompt 采样多组回复(Group),计算它们之间的相对优势(Relative Advantage)来优化策略。
下面为您详细拆解每一个参数的含义,并根据功能模块进行分类解释。
1. 核心算法配置 (algorithm)
algorithm.adv_estimator=grpo- 含义:指定优势(Advantage)估计器为 GRPO。
- 解释:这是脚本的核心。传统 PPO 需要一个 Critic 模型来预测分值,而 GRPO 通过对同一个问题生成多个回答(见下文
rollout.n),计算组内平均分作为基线(Baseline),从而节省了 Critic 模型的显存开销。
algorithm.use_kl_in_reward=False- 含义:是否将 KL 散度(模型输出与参考模型的差异)直接加到 Reward(奖励)中。
- 解释:
False表示 KL 散度作为 Loss(损失函数)的一部分在优化时计算,而不是在生成数据阶段直接修改 Reward 分数。这是 PPO/GRPO 的标准做法。
2. 数据配置 (data)
data.train_files=$HOME/data/gsm8k/train.parquetdata.val_files=$HOME/data/gsm8k/test.parquet- 含义:训练集和验证集的文件路径(Parquet 格式)。GSM8K 是一个经典的数学推理数据集。
data.train_batch_size=1024- 含义:全局采样批次大小(Global Batch Size)。
- 解释:指在一次参数更新循环(Epoch)开始前,所有 GPU 共同采集的数据样本总量。即:每次训练迭代,环境会提供 1024 个 Prompt。
data.max_prompt_length=512- 含义:输入 Prompt 的最大 Token 长度。
data.max_response_length=1024- 含义:模型生成回复的最大 Token 长度。
- 解释:数学题通常需要较长的思维链(CoT),所以回复长度设得比输入长。
data.filter_overlong_prompts=True- 含义:自动过滤掉超过
max_prompt_length的数据。
- 含义:自动过滤掉超过
data.truncation='error'- 含义:截断策略。设置为
'error'表示如果数据超长不进行截断,而是直接报错或跳过(结合上一条使用),保证数据完整性。
- 含义:截断策略。设置为
3. Actor (策略模型) & Rollout (采样) & Ref (参考模型)
Verl 使用 actor_rollout_ref 统一命名空间,因为在 RL 过程中这三个角色紧密相关。
A. 模型基础 (model)
actor_rollout_ref.model.path=Qwen/Qwen3-8B- 含义:预训练模型路径(HuggingFace 格式)。
actor_rollout_ref.model.use_remove_padding=True- 含义:是否移除 Padding。
- 解释:配合 FlashAttention 使用,移除无效的填充 Token 以加速计算。
actor_rollout_ref.model.enable_gradient_checkpointing=True- 含义:开启梯度检查点(Gradient Checkpointing)。
- 解释:以时间换空间。不保存中间激活值,而在反向传播时重新计算,能显著降低显存占用(约节省 50%-70% 显存),允许更大的 Batch Size。
B. Actor 训练参数 (actor)
actor_rollout_ref.actor.optim.lr=1e-6- 含义:学习率。RLHF 阶段学习率通常很低(如 1e-6),防止破坏预训练模型的知识。
actor_rollout_ref.actor.ppo_mini_batch_size=256- 含义:PPO 更新时的 Mini-Batch 大小。
- 解释:虽然采集了 1024 个数据(
train_batch_size),但在计算梯度更新时,会切分成 256 大小的块进行更新,以减少显存压力。
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=32- 含义:单张 GPU 上的微批次大小。
- 解释:用于梯度累积(Gradient Accumulation)。即单卡每次前向传播处理 32 条数据。
actor_rollout_ref.actor.use_kl_loss=Trueactor_rollout_ref.actor.kl_loss_coef=0.001actor_rollout_ref.actor.kl_loss_type=low_var_kl- 含义:KL 散度惩罚配置。
- 解释:防止模型为了获得高分而通过“作弊”或输出乱码偏离原模型太远。
0.001是惩罚系数。
actor_rollout_ref.actor.fsdp_config.param_offload=Falseactor_rollout_ref.actor.fsdp_config.optimizer_offload=False- 含义:FSDP(Fully Sharded Data Parallel)的卸载配置。
- 解释:
False表示参数和优化器状态都保留在 GPU 上,不卸载到 CPU。这能保证训练速度最快,但对显存要求高。
C. Rollout 采样/推理配置 (rollout)
actor_rollout_ref.rollout.name=vllm- 含义:使用 vLLM 作为推理引擎。
- 解释:vLLM 生成速度极快,适合 RL 训练中大规模的数据采样环节。
actor_rollout_ref.rollout.gpu_memory_utilization=0.6- 含义:vLLM 占用 GPU 显存的比例(60%)。
- 解释:必须预留一部分显存给 Actor 进行训练(反向传播)。如果设得太高,训练时会 OOM(显存溢出)。
actor_rollout_ref.rollout.n=5- 含义:组采样数量(Group Size)。这是 GRPO 的关键参数。
- 解释:对于每一个 Prompt,模型会生成 5 个不同的回复。GRPO 会计算这 5 个回复的平均奖励,优秀的回复获得正优势,差的获得负优势。
actor_rollout_ref.rollout.tensor_model_parallel_size=2- 含义:推理时的张量并行(TP)大小。
- 解释:将模型切分到 2 张卡上进行推理。
D. Reference 参考模型 (ref)
actor_rollout_ref.ref.fsdp_config.param_offload=True- 含义:参考模型的参数卸载到 CPU。
- 解释:非常明智的设置。Ref 模型只在计算 KL 散度时做一次前向传播,且参数是冻结的(不更新)。将其卸载到 CPU 可以为 Actor 的训练腾出宝贵的 GPU 显存。
4. 训练器通用配置 (trainer)
trainer.n_gpus_per_node=8trainer.nnodes=1- 含义:单机 8 卡训练。
trainer.project_name='verl_grpo_example_gsm8k'trainer.experiment_name='qwen3_8b_function_rm'- 含义:WandB(Weights & Biases)的项目名和实验名,用于在网页端查看 Loss 曲线。
trainer.total_epochs=15- 含义:总共训练 15 个 Epoch。
- 注意:在 RL 中,Epoch 通常指对同一批采集的数据(Replay Buffer)重复利用几次进行更新。这里如果指的是外层循环,则表示采集-更新这个过程重复 15 次。
trainer.save_freq=20- 含义:每 20 个 step 保存一次检查点。
trainer.logger='["console","wandb"]'- 含义:日志同时输出到控制台和 WandB。
总结:数据流向举例
假设你有一个数学题 Prompt:“1+1等于几?”
- Rollout (vLLM):
- 根据
rollout.n=5,模型生成 5 个答案:- A: “2”
- B: “等于2”
- C: “3” (错误)
- D: “是二”
- E: “2”
- 根据
- Reward:
- 环境/Reward Model 给分:A(1.0), B(1.0), C(0.0), D(1.0), E(1.0)。
- 组平均分:0.8。
- Advantage (GRPO):
- A 的优势:1.0 - 0.8 = +0.2 (鼓励)
- C 的优势:0.0 - 0.8 = -0.8 (强烈抑制)
- Actor Update:
- 使用
ppo_mini_batch_size=256的块大小,利用这些优势值更新 Qwen3-8B 的参数。 - 同时,
ref模型计算 KL 散度,确保 Qwen3-8B 不要因为过度优化“1+1”而忘记怎么说正常的人话。 - 由于
ref开启了offload=True,计算完 KL 后它的显存占用会被清理或移至 CPU,保证训练流畅。
- 使用
常见RL、GRPO问题汇总
什么是rollout长尾问题?
背景:在强化学习中,Rollout 生成的序列(例如生成文本等)会被用来计算奖励,并通过这些奖励对模型进行训练。
特点:在实际应用中,生成的序列往往不均匀,有些序列很长、有些很短。或者序列中的有些 tokens 可能出现频率很低,导致学习效果难以快速显现,RL难以在这些区域得到足够的梯度更新。

在LLM领域,长尾问题通常 在模型的 输入和输出 过程中都可能受到影响,但具体的影响方式稍有不同:
- 输入过程的长尾问题(主要针对序列内容、任务类型):可称为罕见任务、低频事件。输入的长尾问题指的是模型可能会面临一些非常稀有的、冷门的或专业的输入,模型在训练时未能充分覆盖这些输入,导致其无法生成准确或相关的响应。
- 输出过程的长尾问题(主要针对序列长度):输出的长尾问题则指的是模型生成的响应可能缺乏多样性(可通过调整采样参数优化)。还可能指的是 大多数响应在数千个 token 内即可完成,少数实例需要极长的生成时间。
影响:
- 泛化能力差:如果模型没有足够的能力处理低频(长尾)事件,它会偏向高频事件,这会导致模型在面对不常见的、罕见的或者边缘输入时表现不佳。
- 实际应用中的误差:对于实际应用,模型可能会错误地忽视或处理不当一些低频事件,这可能会影响用户体验或导致决策错误。例如,推荐系统可能无法正确推荐冷门内容,生成模型可能无法准确生成罕见或特定领域的内容。
- 训练效率低:长尾问题会导致模型学习过程中的低效,因为模型会过度专注于频繁出现的事件,难以在稀有事件上学习到有效的信息。
- 长尾延迟:长尾问题通常是LLM在面对低频事件时出现,由于资源配置、模型性能等方面在低频、高频的分配不均,可能导致长尾问题的推理时间很长。
- 资源分配:对于系统中的低频任务或事件,处理这些任务的资源可能相对较少或者分配不充分。系统可能更多地关注高频事件,这会导致低频事件(即长尾事件)处理时延较大。
- 模型训练的偏差:如果模型在训练过程中没有充分学习到低频事件的特点,生成或响应这些事件时,模型可能会需要更多的时间进行搜索和决策,从而导致更高的延迟。
- 拖慢批次进度:在一个训练批次中,绝大多数响应可能很快就生成完毕,但少数耗时极长的生成任务会拖慢整个批次的进度,导致大量 GPU 资源在同步等待中闲置。
举例:
假设你正在训练一个生成新闻文章的模型,输入是某种特定领域的新闻主题。模型在推理过程中需要根据给定的主题生成一篇文章。
- 短尾(高频事件):例如,关于某个新闻事件的常见描述(如“发生了重大事故”)。这类描述会频繁出现在训练数据中,模型很容易通过奖励函数对这些部分进行有效学习。
- 长尾(低频事件):然而,某些特定主题或事件(如较为小众的领域或罕见的新闻事件)可能只在训练数据中出现过极少次。例如,一个涉及冷门科技事件的新闻报道,这类事件非常罕见,模型在训练过程中几乎没有机会生成这类内容,导致对这类事件的学习较弱,难以生成高质量的内容。
因此,长尾问题表现在:训练时模型很难通过稀疏的奖励信号来优化这些低频事件,最终导致在推理时对冷门、少见的输入或事件的响应较差。
如何缓解Rollout长尾问题?或者优化?
算法层面:方案1:APRIL。参考链接:https://zhuanlan.zhihu.com/p/1956376495128807242
动机:在现有的同步 RL 框架中,一个更为棘手的问题是响应长度的 “长尾分布”:批次训练是一个同步任务,需要等待当前批次中全部prompt都Rollout完毕,才能计算梯度。这会导致,在一个训练批次中,绝大多数响应可能很快就生成完毕,但少数耗时极长的生成任务会拖慢整个批次的进度,导致大量** GPU 资源在同步等待中闲置**。随着模型规模和任务复杂度的提升,这一效率瓶颈愈发限制了 RL 训练的可扩展性。
APRIL关键步骤、机制:
| 机制 | 核心操作 |
|---|---|
| 1. 超额供给 | 在每个训练迭代开始时,请求远超于批次所需数量的 rollout 任务(例如,请求 2N 而非 N)。 |
| 2. 提前终止 | 一旦收集到目标数量(N)的完整响应,立即主动终止所有仍在进行的、尚未完成的生成任务。 |
| 3. 缓存与回收 | 将那些被终止的部分生成序列存入缓冲区,在后续迭代中恢复并继续生成,确保没有计算被浪费。 |
流程拆解:

- 超额供给 (Over-provisioned Generation):在每个训练步骤开始时,启动 N’ 个实例的 rollout 生成,其中 N’ 大于标准批次大小 N(例如,N’ = 2N)。
- 提前终止 (Early Termination):系统持续监控已完成的 rollout 数量。一旦达到目标数量 N,便立即向推理引擎发送信号,终止所有仍在运行的生成任务。
- 缓存后续任务 (Buffering Continuations):已完成的 N 个 rollout 被送往训练引擎进行策略优化。而被终止的那些部分生成序列(partial rollouts)及其状态被完整地存入一个后续缓冲区 (continuation buffer)。
- 优先恢复 (Prioritized Resumption):在下一个训练步骤开始时,系统会优先处理缓冲区中的任务,让它们从中断处继续生成,然后再启动新的 rollout 请求以补足N’的总任务量。
整理思路(个人理解):RL训练是多epoch的,同一个prompt会训练多次。因此,可以在首次(或前几次)进行Rollout时,将原本Rollout.n=5,直接设置生成10条响应,但最终只保留生成速度最快的前5条响应,剩余5条未完整输出的序列保存起来,等到下一个epoch时,遇到相同prompt时 进行续写生成。这样不会有GPU浪费。
问题思考:但是,在利用上一个epoch未完整生成的序列进行训练,本质属于off-policy。
工程层面:
什么是on-policy/off-policy
从第一性原理出发,二者的根本区别在于更新模型策略所用数据的来源。
On-Policy,顾名思义,指的是“用正在学习的策略产生的数据来学习”。即,智能体(在LLM中指语言模型本身)严格使用其当前策略(Policy)与环境交互所产生的数据来更新和优化自身。这意味着,一旦策略发生更新,所有旧的交互数据都将被废弃,因为它们是由一个“过时”的策略产生的。
Off-Policy** ,**则更为灵活,它指的是“用并非当前学习的策略所产生的数据来学习”。智能体可以利用由其他策略(甚至是历史策略或人类示范数据)产生的数据来更新当前策略。这解耦了“数据生成”和“策略学习”两个过程,从而提高了数据利用效率。
| 特性 | on-policy | off-policy |
|---|---|---|
| 第一性原理 | 学习用的数据必须由当前策略生成 | 学习用的数据可以来自任何策略 |
| 数据来源 | 实时交互,在线生成 | 离线数据集或历史数据 |
| 数据效率 | 低,用完即扔 | 高,可重复利用 |
| 核心思想 | 用当前策略产生的经验迭代优化自身 | 解耦数据生成、策略学习 |
| 代表算法 | PPO、GRPO | DPO、DQN、Q-learning |
如果使用模型A生成数据,保存起来,再用这批数据进行模型A的训练,属于on-policy还是off-policy?毕竟模型没变。
off-policy。因为每一个step、epoch,在反向传播、梯度更新过程中,模型参数都会发生微小变化,从而导致模型A变为模型A’。此时应该使用模型A’重新进行响应生成。
假设:
- 模型 A (Old):上个月的模型,生成了一批数据 D 存在硬盘里。
- 模型 B (Current):现在的模型。
- 前提:模型 A 和 模型 B 的参数完全一模一样(权重文件的 Hash 值都一样)。
当你启动训练的第一瞬间:
- 你的策略
和生成数据的策略
是完全重合的。
- 数学上:
。
- 此时,梯度计算是准确的,符合 On-Policy 的定义。
问题出在“训练”这个动作本身的含义上。训练就是修改参数。
- Step 1 结束时:
你用那批数据 D 进行了一次梯度下降。现在的模型变成了 模型 B’ (New)。
显然,模型 B’ ≠ 模型 A。
- Step 2 开始时:
如果你继续使用那批“上个月的数据 D”(由模型 A 生成):
- **做题的人**:还是模型 A(因为它已经固化在数据里了)。
- **学题的人**:是模型 B'。
- **偏差出现**:$\pi_{current}$ 已经变了,而数据分布 $\pi_{behavior}$ 还是老的。
- **判定**:**Off-Policy**。
###