详解rlinf强化学习四步曲

四步流程的设计原理

这是一个典型的 RECAP (Reward-Conditioned Advantage Pretraining) 离线 RL 流程,每个步骤之间存在严格的数据依赖关系,必须按顺序执行。


步骤 1:计算回报 (compute_returns.py)

作用 :为数据集中每个时间步计算 rewardreturn

关键逻辑compute_returns.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_returns.py#L54-L86)):

  • 使用 DynamicReturn 方法:每步 reward=-1,成功时最后一步 reward=0,失败时最后一步 reward=failure_reward
  • 通过反向迭代计算 return:G_t = r_t + gamma * G_{t+1}
  • 输出:meta/returns_{tag}.parquet,包含 return, reward, prompt

为什么需要单独做?

  • 原始数据集通常没有 reward 和 return 标签
  • 这是后续所有步骤的数据基础,必须在训练前完成

步骤 2:训练价值模型 (train_value.py)

作用 :训练一个 ValueCriticModel,用于估计状态价值 V(o_t)

关键逻辑train_value.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/train_value.py#L47-L55)):

  • 使用 SFTRunnerFSDPValueSftWorker 进行分布式训练
  • 输入:图像观测 + 任务描述
  • 输出:状态价值估计

为什么需要单独做?

  • 优势计算需要价值函数 V(o_t) 的估计
  • 价值模型是中间产物,需要先训练好才能计算优势

步骤 3:计算优势值 (compute_advantages.py)

作用:使用训练好的价值模型,为每个时间步计算优势标签

关键公式compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L804)):

复制代码
A = normalize(r_{t:t+N}) + gamma^N * V(o_{t+N}) - V(o_t)

为什么需要单独做?

  • 优势值是衡量数据质量的关键指标
  • 正优势表示"好"的状态-动作对,负优势表示"坏"的
  • 需要先训练价值模型才能计算,所以必须在步骤 2 之后

步骤 4:CFG RL 训练 (train_cfg.py)

作用:使用计算好的优势值作为标签,训练策略模型

关键逻辑train_cfg.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/policy_optimization/cfg_rl/train_cfg.py#L45-L56)):

  • 使用 FSDPCfgWorker 进行 CFG (Classifier-Free Guidance) 训练
  • 输入:图像观测 + 任务描述 + 优势标签
  • 输出:优化后的策略模型

为什么需要单独做?

  • 这是最终的策略优化阶段
  • 需要步骤 3 计算好的优势标签作为监督信号

依赖关系图

复制代码
原始数据集
    ↓
Step 1: compute_returns → meta/returns.parquet
    ↓
Step 2: train_value_sft → value_checkpoint
    ↓
Step 3: compute_advantages → meta/advantages.parquet
    ↓
Step 4: train_cfg → 最终策略模型

为什么不能合并?

  1. 计算成本差异大

    • Step 1 是纯 CPU 数据处理,速度快
    • Step 2 和 Step 4 是 GPU 训练,耗时较长
    • Step 3 是 GPU 推理,中等耗时
  2. 数据依赖:每个步骤的输出是下一步的输入

  3. 可调试性:分开执行便于检查每一步的输出是否正确

  4. 资源调度:不同步骤可能需要不同的硬件资源(CPU vs GPU)

  5. 增量更新:如果只修改了某个步骤的参数,可以只重新运行该步骤


总结

这个四步流程是 RECAP 算法的标准实现模式:

  1. 标注回报 → 为离线数据添加 reward/return 标签
  2. 训练价值模型 → 学会评估状态好坏
  3. 计算优势 → 区分好/坏的数据点
  4. 优化策略 → 基于优势信号提升策略

让我深入分析训练价值模型的逻辑。首先查看核心代码文件。

训练价值模型的核心逻辑

一、整体架构

价值模型训练是一个监督学习 过程,目标是学习一个状态价值函数 V(o_t),它能根据当前观测(图像 + 任务描述)预测该状态的长期回报。

核心组件关系

复制代码
SFTRunner (训练调度)
    ↓
FSDPValueSftWorker (训练 Worker)
    ↓
ValueCriticModel (模型本体)
    ├── ValueExpert (SigLIP + Gemma3)
    └── ValueHead (分类价值预测头)
    ↓
ValueDataset + ValueDataCollator (数据加载)

二、数据准备阶段

1. 数据源

价值模型需要 Step 1 计算好的 return 数据,来自 meta/returns.parquet(由 compute_returns.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_returns.py) 生成)。

2. 数据归一化

fsdp_value_sft_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_value_sft_worker.py#L242-L288) 中,计算全局 return 范围:

python 复制代码
# 从所有数据集的 stats.json 中读取
global_return_min = min(all_mins)  # 通常约 -700
global_return_max = max(all_maxs)  # 通常为 0

然后将 return 归一化到 [-1, 0] 区间:

python 复制代码
def normalize(x):
    return_range = global_return_max - global_return_min
    return (x - global_return_min) / return_range - 1.0
3. 数据加载器构建

fsdp_value_sft_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_value_sft_worker.py#L141-L467) 中的 build_dataloader() 方法:

  • 创建 ValueProcessor:处理图像(224x224,归一化到 -1,1)和文本(模板:Task: {prompt}.
  • 创建 ValueDataCollator:负责批处理,将样本转换为模型输入格式
  • 创建 ValueDataset:从 LeRobot 数据集中读取观测和目标价值

三、模型架构

1. ValueCriticModel

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L129-L174) 定义了核心模型:

python 复制代码
class ValueCriticModel(nn.Module):
    def __init__(self, config):
        # 1. ValueExpert: SigLIP + Gemma3 视觉语言模型
        self.value_expert = ValueExpert(
            expert_configs={"value": expert_config},
            siglip_path=siglip_path,
            gemma3_path=gemma3_path,
        )
        
        # 2. ValueHead: 分类价值预测头
        self.value_head = ValueHead(
            hidden_size=expert_config.width,
            num_bins=config.num_bins,      # 通常 201
            v_min=config.v_min,            # -1.0
            v_max=config.v_max,            # 0.0
        )
2. ValueHead

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L85-L127) 负责将隐藏状态映射到价值:

python 复制代码
class ValueHead(nn.Module):
    def __init__(self, hidden_size, num_bins, v_min, v_max):
        self.cls_embedding = nn.Embedding(1, hidden_size)  # 可学习的 CLS token
        self.value_proj = nn.Linear(hidden_size, num_bins) # 投影到 bins
        self.atoms = torch.linspace(v_min, v_max, num_bins)  # 离散化的价值点

关键设计 :使用分类分布来表示价值,而不是直接回归。这有两个好处:

  • 处理价值的多模态分布
  • 提供不确定性估计
3. 前向传播流程

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L311-L358) 中的 forward() 方法:

复制代码
输入: observation (图像 + tokenized_prompt)
    ↓
1. embed_prefix(): 嵌入图像和文本,得到 prefix_embs
    ↓
2. embed_suffix(): 创建 [CLS] token embedding
    ↓
3. _forward_expert(): 两阶段前向传播
   Phase 1: 预填充 frozen VLM,获取 KV cache
   Phase 2: 使用 KV cache 运行 value expert,得到 [CLS] 的隐藏状态
    ↓
4. _compute_value_from_hidden(): 从隐藏状态计算价值
    ↓
输出: predicted_values, logits, probs, loss

四、价值估计的计算方式

1. 从隐藏状态到价值

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L466-L471):

python 复制代码
def _compute_value_from_hidden(self, cls_hidden):
    logits = self.value_head(cls_hidden)           # [B, num_bins]
    probs = F.softmax(logits, dim=-1)              # [B, num_bins]
    values = (probs * self.value_head.atoms).sum(dim=-1)  # [B]
    return values, logits, probs

公式V(o) = Σ(p_i * z_i),其中 p_i 是第 i 个 bin 的概率,z_i 是该 bin 的价值值。

2. 损失函数

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L473-L519) 使用分类交叉熵损失

python 复制代码
def _compute_categorical_loss(self, logits, target_values):
    # 将连续目标值投影到离散 bins(Dirac delta projection)
    b = (target_values - self.v_min) / self.delta_z
    l = b.floor().long().clamp(0, self.num_bins - 1)  # 左 bin
    u = b.ceil().long().clamp(0, self.num_bins - 1)   # 右 bin
    
    # 计算到两个 bin 的距离,分配概率
    d_to_l, d_to_u = b - l.float(), u.float() - b
    target_probs[batch_idx, l] += d_to_u  # 距离远的权重高
    target_probs[batch_idx, u] += d_to_l
    
    # 交叉熵损失
    loss = -(target_probs * F.log_softmax(logits, dim=-1)).sum(dim=-1)

原理 :将连续的目标价值 y 分解为两个相邻 bin 的加权组合,然后计算预测分布与目标分布的交叉熵。


五、训练循环

1. 核心训练步骤

fsdp_value_sft_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_value_sft_worker.py#L469-L562) 中的 run_training()

python 复制代码
def run_training(self):
    self.model.train()
    
    for idx in range(self.gradient_accumulation):  # 梯度累积
        batch = next(self.data_iter)
        obs, target_values, extra = self._prepare_input(batch)
        
        with self.amp_context:  # 混合精度
            result = self.model(observation=obs, target_values=target_values)
            loss = result.loss
        
        scaled_loss = loss / self.gradient_accumulation
        self.grad_scaler.scale(scaled_loss).backward()  # 反向传播
    
    grad_norm, lr_list = self.optimizer_step()  # 参数更新
    self.optimizer.zero_grad()
    self.lr_scheduler.step()
    
    return train_metrics
2. 评估指标

训练过程中监控的指标(fsdp_value_sft_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_value_sft_worker.py#L496-L538)):

指标 含义
loss 分类交叉熵损失
predicted_value_mean/std 预测价值的统计量
target_value_mean/std 目标价值的统计量
cat_acc_best 最佳 bin 预测准确率
cat_acc_neighbor 相邻 bin 预测准确率
mae 平均绝对误差
value_spearman Spearman 秩相关系数(衡量排序能力)

六、关键设计总结

设计点 实现方式 优势
价值表示 分类分布(201 bins) 支持多模态价值分布,提供不确定性
损失函数 Dirac delta 投影 + 交叉熵 平滑处理连续价值的离散化
视觉编码 SigLIP 预训练模型 强大的视觉特征提取
语言编码 Gemma3 预训练模型 理解任务描述
两阶段前向 KV cache 预填充 加速推理,支持长序列
梯度累积 多 micro-batch 支持大全局 batch size
分布式训练 FSDP 多 GPU 高效训练

七、训练输出

训练完成后,保存的检查点包含:

  • 模型权重(actor/ 目录)
  • 配置文件
  • 归一化统计量(norm_stats.pt

这些检查点随后被 Step 3 的 compute_advantages.py 加载,用于计算优势值。

为什么说是监督学习?

监督学习的核心特征是:有明确的输入-输出配对(X, Y),模型学习从输入 X 预测输出 Y

在价值模型训练中:

监督学习要素 价值模型实现
输入 X 观测(图像 + 任务描述)
标签 Y 该状态的真实 return(由 Step 1 预先计算)
损失函数 预测值与真实 return 的分类交叉熵
训练目标 最小化预测误差,学会从观测估计价值

对比强化学习 :RL 中没有预先标注的标签,模型通过与环境交互获得奖励,通过策略梯度或价值迭代来学习。而这里的价值模型是离线的、有标签的,完全符合监督学习范式。


模型架构:输入处理与每一步的目的

整体数据流

复制代码
原始输入 (图像 + 任务文本)
    ↓  Step 1: 预处理与编码
图像 → SigLIP → 视觉特征 (1152维)
文本 → Gemma3 → 文本特征 (640维)
    ↓  Step 2: 特征对齐
视觉特征 → 投影层 → 640维 (与文本特征对齐)
    ↓  Step 3: 前缀嵌入
图像嵌入 + 文本嵌入 → prefix_embs (双向注意力)
    ↓  Step 4: 两阶段前向传播
Phase A: Gemma3 处理 prefix → KV cache
Phase B: Expert 处理 [CLS] → 利用 KV cache 交叉注意力
    ↓  Step 5: 价值预测
[CLS] 隐藏状态 → ValueHead → 分类分布 → 期望值

Step 1:预处理与编码

图像编码

value_expert.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/value_expert.py#L142-L154):

python 复制代码
def embed_image(self, image):
    # [B, 3, H, W] → [B, 256, 1152]
    feats = self.vision_tower(image).last_hidden_state
    return self.multi_modal_proj(feats)  # → [B, 256, 640]

目的

  • SigLIP:将原始图像转换为语义丰富的视觉特征(1152维)
  • 投影层:将视觉特征从 1152 维降到 640 维,与 Gemma3 的隐藏维度对齐
文本编码

value_expert.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/value_expert.py#L156-L168):

python 复制代码
def embed_language_tokens(self, tokens):
    # [B, L] → [B, L, 640]
    return self.gemma3.model.embed_tokens(tokens)

目的:将任务描述文本转换为与视觉特征同维度的文本嵌入


Step 2:特征对齐

value_expert.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/value_expert.py#L100-L106):

python 复制代码
# 全新初始化的投影层
self.multi_modal_proj = nn.Linear(1152, 640, bias=True)
nn.init.normal_(self.multi_modal_proj.weight, std=0.02)
nn.init.zeros_(self.multi_modal_proj.bias)

目的

  • SigLIP 输出 1152 维,Gemma3 使用 640 维
  • 需要一个可训练的投影层,学习如何将视觉特征映射到语言模型的语义空间
  • 这是跨模态对齐的关键:让图像和文本在同一语义空间中表示

Step 3:前缀嵌入

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L281-L299):

python 复制代码
def embed_prefix(self, images, img_masks, lang_tokens, lang_masks):
    # 嵌入每个摄像头的图像
    for img, img_mask in zip(images, img_masks):
        img_emb = self.value_expert.embed_image(img)  # [B, 256, 640]
        embs.append(img_emb)
    
    # 嵌入语言 tokens
    lang_emb = self.value_expert.embed_language_tokens(lang_tokens)  # [B, L, 640]
    embs.append(lang_emb)
    
    # 拼接成前缀
    return torch.cat(embs, dim=1), torch.cat(pad_masks, dim=1)

目的

  • 将多视角图像和文本拼接成一个统一的序列
  • 这个序列作为"前缀",后续的 CLS token 需要关注它来预测价值

Step 4:两阶段前向传播

这是模型最核心的设计,分为 Phase A 和 Phase B。

Phase A:Gemma3 处理前缀

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L405-L464):

python 复制代码
# Mode A: prefix-only --- Gemma3 处理前缀并缓存 KV
(prefix_out, _), past_kv = self.value_expert.forward(
    attention_mask=prefix_attn_4d,
    inputs_embeds=[prefix_embs, None],  # suffix_embs=None → Mode A
    use_cache=True,
)

目的

  • 预填充 KV cache:Gemma3 处理前缀序列(图像+文本),将中间结果(键值对)缓存起来
  • 双向注意力:前缀内部是完全双向的,图像和文本可以相互关注
  • 输出隐藏状态:得到前缀的最后隐藏状态
Phase B:Expert 处理 CLS token
python 复制代码
# Mode B: suffix-only --- Expert 利用 KV cache 进行交叉注意力
(_, suffix_out), _ = self.value_expert.forward(
    attention_mask=full_attn_4d,
    inputs_embeds=[None, suffix_embs],  # prefix_embs=None → Mode B
    past_key_values=past_kv,            # 使用 Phase A 的 KV cache
    expert_name="value",
)

目的

  • 交叉注意力CLS token 通过 KV cache 关注整个前缀序列(图像+文本)
  • 提取全局信息CLS token 的隐藏状态聚合了所有输入信息
  • 专家网络:使用独立的 GemmaForCausalLM 专家,专门学习价值预测任务

为什么要用两阶段?

原因 说明
内存效率 KV cache 避免重复计算前缀的注意力
架构约束 Gemma3 的滑动窗口注意力不支持层间交错
模块化 VLM(Phase A)和专家(Phase B)可以独立训练/冻结
推理加速 在线推理时,前缀只需计算一次,后续 token 可复用 KV cache

Step 5:价值预测

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L466-L471):

python 复制代码
def _compute_value_from_hidden(self, cls_hidden):
    logits = self.value_head(cls_hidden)           # [B, 201]
    probs = F.softmax(logits, dim=-1)              # [B, 201]
    values = (probs * self.value_head.atoms).sum(dim=-1)  # [B]
    return values, logits, probs

目的

  • 分类分布:将 CLS 隐藏状态投影到 201 个 bin 的 logits
  • 概率归一化:softmax 得到每个 bin 的概率
  • 期望计算V(o) = Σ(p_i * z_i),将分布转换为标量价值

为什么用分类而不是回归?

方案 优点 缺点
分类(当前方案) 支持多模态价值分布,提供不确定性估计,训练更稳定 需要更多参数,计算稍复杂
直接回归 简单,参数少 难以处理价值的多峰分布,训练不稳定

损失计算:监督学习的核心

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L473-L519):

python 复制代码
def _compute_categorical_loss(self, logits, target_values):
    # 将连续目标值投影到离散 bins
    b = (target_values - self.v_min) / self.delta_z
    l = b.floor().long().clamp(0, self.num_bins - 1)
    u = b.ceil().long().clamp(0, self.num_bins - 1)
    
    # Dirac delta 投影:分配概率到相邻 bins
    d_to_l, d_to_u = b - l.float(), u.float() - b
    target_probs[batch_idx, l] += d_to_u
    target_probs[batch_idx, u] += d_to_l
    
    # 交叉熵损失
    loss = -(target_probs * F.log_softmax(logits, dim=-1)).sum(dim=-1)

目的

  • Dirac delta 投影:将连续的真实 return(标签)转换为离散的概率分布
  • 交叉熵:衡量预测分布与目标分布的差异
  • 监督信号:通过最小化损失,让模型学会从观测预测正确的价值

监督学习视角下的完整流程

复制代码
训练数据 (X, Y)
    ├─ X: observation = {images, prompt}
    ├─ Y: target_values = 归一化后的 return (来自 Step 1)
    │
    ↓ 前向传播
    ├─ 编码: images → SigLIP → proj → 640维
    │        prompt → Gemma3 → 640维
    │
    ├─ 拼接: image_emb + text_emb → prefix_embs
    │
    ├─ Phase A: Gemma3 处理 prefix → KV cache
    │
    ├─ Phase B: [CLS] → Expert → 利用 KV cache → cls_hidden
    │
    └─ 预测: cls_hidden → ValueHead → predicted_values
    │
    ↓ 损失计算
    └─ loss = cross_entropy(predicted_distribution, target_distribution)
    │
    ↓ 反向传播
    └─ 更新模型参数,减小预测误差

总结:每一步的设计目的

步骤 组件 目的
1 SigLIP 提取图像的高层语义特征
2 投影层 将视觉特征对齐到语言模型空间
3 Gemma3 嵌入 将文本转换为语义嵌入
4 前缀拼接 组合多模态信息为统一序列
5 Phase A 预填充 KV cache,双向理解输入
6 Phase B CLS 关注前缀,聚合全局信息
7 ValueHead 将隐藏状态转换为价值分布
8 期望计算 从分布得到标量价值
9 交叉熵损失 监督学习的核心:最小化预测误差

这个架构的本质是:利用预训练的视觉语言模型提取特征,通过可训练的专家网络和价值头,学习从观测到价值的映射函数 ------这是一个典型的多模态监督学习任务。

价值模型本质:数据到分布的映射函数

一、核心定义

价值模型 ValueCriticModel 本质上是一个概率映射函数

复制代码
V: Observation → Distribution(return)
组成部分 数学表示 代码实现
输入空间 o ∈ Observation 图像 + 任务描述
输出空间 `p(· o) ∈ Δ^K`
映射函数 V(o) = Σ(p_i(o) * z_i) _compute_value_from_hidden(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L466-L471)

二、为什么是"到分布"的映射?

1. 代码中的分布输出

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L466-L471):

python 复制代码
def _compute_value_from_hidden(self, cls_hidden):
    logits = self.value_head(cls_hidden)           # [B, 201] --- 未归一化的分布参数
    probs = F.softmax(logits, dim=-1)              # [B, 201] --- 归一化的概率分布
    values = (probs * self.value_head.atoms).sum(dim=-1)  # [B] --- 分布的期望值
    return values, logits, probs

关键设计 :模型输出的是一个离散概率分布,而不是单一数值:

复制代码
输入: 观测 o
    ↓
输出: p(z|o) = [p(z₁|o), p(z₂|o), ..., p(z₂₀₁|o)]
    
其中 z_i = -1.0 + i * Δz,  Δz = (0 - (-1)) / 200 = 0.005

最终价值: V(o) = E_z[p(z|o)] = Σ(p_i * z_i)
2. 为什么不用直接回归?
方案 表达能力 不确定性 训练稳定性
分类分布(当前) 强:支持多峰分布 有:方差 = Σ(p_i * (z_i - μ)^2) 高:交叉熵比 MSE 更稳定
直接回归 弱:只能表达单峰分布 无:无法估计不确定性 低:极端值会主导损失

在机器人强化学习中,同一个观测可能对应多种不同的未来回报(比如不同策略会导致不同结果),分布表示能更好地捕捉这种不确定性


三、监督学习如何让分布"正确表达"数据?

1. 标签的分布化

modeling_critic.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/value_model/recap/modeling_critic.py#L473-L519) 中的损失计算:

python 复制代码
def _compute_categorical_loss(self, logits, target_values):
    # 将连续标签 y 转换为离散分布
    b = (y - v_min) / Δz
    l = floor(b),  u = ceil(b)  # 找到相邻的两个 bins
    
    # Dirac delta 投影:概率分配到相邻 bins
    target_probs[l] += distance_to_u
    target_probs[u] += distance_to_l
    
    # 交叉熵:最小化预测分布与目标分布的差异
    loss = -Σ(target_probs * log(predicted_probs))

核心思想 :将每个训练样本的标签 y(真实 return)转换为一个尖峰分布(Dirac delta),然后让模型学习拟合这个分布。

2. 训练过程的本质
复制代码
训练目标:min_D KL(target_distribution || predicted_distribution)

其中:
- target_distribution: 以真实 return 为中心的尖峰分布
- predicted_distribution: 模型输出的 201-bin 分布
- KL 散度通过交叉熵损失实现

效果:随着训练进行,模型学会将概率质量集中在真实 return 附近,分布逐渐变窄、峰值逐渐对齐。


四、从分布到标量价值

1. 期望值计算
python 复制代码
# 分布的期望 = 加权平均
V(o) = Σ(p_i * z_i)

# 分布的方差(可选,衡量不确定性)
Var(o) = Σ(p_i * (z_i - V(o))^2)
2. 为什么需要分布?

在后续的优势计算中(Step 3),compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L804) 使用的是标量价值

python 复制代码
advantage = reward_sum + gamma_k * v_next - v_curr

但分布表示在训练阶段有重要作用:

阶段 使用形式 原因
训练时 完整分布 提供更丰富的监督信号,训练更稳定
推理时 期望值 简化计算,符合优势公式的需求

五、"正确表达"的含义

1. 统计意义上的正确性

训练目标是让模型的预测分布尽可能接近真实的价值分布:

复制代码
当训练收敛时:
p_model(z|o) ≈ p_true(z|o)

其中 p_true(z|o) 是观测 o 下真实 return 的分布
2. 实际验证指标

fsdp_value_sft_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_value_sft_worker.py#L81-L98) 中的评估指标:

python 复制代码
def _compute_spearman(values_a, values_b):
    # Spearman 秩相关系数:衡量排序能力
    # 完美排序时 = 1.0,完全随机时 ≈ 0
    rank_a = argsort(values_a)
    rank_b = argsort(values_b)
    return correlation(rank_a, rank_b)
指标 衡量内容 理想值
cat_acc_best 预测 bin 是否命中最佳 bin 越高越好
cat_acc_neighbor 预测 bin 是否命中相邻 bin 越高越好
mae 平均绝对误差 越低越好
value_spearman 价值排序能力 接近 1.0

价值模型"正确表达"的核心标准 :能够准确预测不同观测之间的价值相对大小(排序),而不仅仅是绝对数值。


六、总结:映射函数的完整表达

复制代码
价值模型 V 是一个"数据 → 分布"的映射函数:

输入: 观测 o = {图像, 任务描述}
    ↓
特征提取: o → h (SigLIP + Gemma3 + Expert)
    ↓
分布参数化: h → logits (ValueHead)
    ↓
概率归一化: logits → p(z|o) (softmax)
    ↓
期望值: p(z|o) → V(o) = Σ(p_i * z_i)

训练目标: 通过交叉熵损失,让 p_model(z|o) 逼近 p_true(z|o)

最终价值: V(o) 是分布的期望值,表示在观测 o 下的期望长期回报

关键理解

  1. 映射函数:是的,V 是从观测到价值的映射函数
  2. 到分布:模型内部输出的是完整的概率分布,这是"正确表达"的关键
  3. 期望值:最终用于优势计算的是分布的期望值,兼顾了表达能力和计算效率

第三步:计算优势值(Compute Advantages)

一、核心目标

第三步的目标是为数据集中的每个时间步 计算一个优势值(Advantage) ,用于衡量该时间步的"好坏"程度。这个优势值将作为第四步 CFG RL 训练的监督信号

二、优势值的定义

核心公式(compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L804)):

复制代码
A_t = normalize(R_{t:t+N}) + γ^N * V(o_{t+N}) - V(o_t)
组成部分 含义 来源
R_{t:t+N} 未来 N 步的折扣奖励和 Step 1 预计算的 reward
V(o_t) 当前状态的价值估计 价值模型推理
V(o_{t+N}) N 步后状态的价值估计 价值模型推理
normalize(·) 将奖励归一化到 -1, 0 全局 return 范围

直观理解

  • 正优势:当前状态比预期更好(好的决策)
  • 负优势:当前状态比预期更差(坏的决策)

三、完整流程

复制代码
Step 1: 加载价值模型和数据
    ↓
Step 2: Phase 1 --- 批量推理 V(o_t)
    ↓
Step 3: Phase 2 --- 计算优势值
    ↓
Step 4: 计算统一阈值
    ↓
Step 5: 保存优势值到数据集

四、Step 1:加载价值模型和数据

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L937-L939):

python 复制代码
value_model = ValueCriticModel.from_checkpoint(
    checkpoint_dir=cfg.advantage.value_checkpoint,
    device=device,
    **model_kwargs
)

关键点

  • 加载 Step 2 训练好的价值模型检查点
  • 模型处于评估模式(model.eval()),使用推理模式
  • 加载输入变换(_input_transform)和处理器(processor

五、Step 2:Phase 1 --- 批量推理 V(o_t)

这是计算优势值的核心计算阶段,需要对数据集中的每个样本进行价值模型推理。

5.1 数据加载器构建

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L618-L638):

python 复制代码
advantage_dataset = ValueInferenceDataset(
    dataset,
    robot_type,
    tasks,
    input_transform=value_model._input_transform,
    prepare_observation_cpu=worker_cpu_prep,
    returns_sidecar=returns_sidecar,
)

设计优化

  • CPU 预处理:图像预处理和 tokenization 在 DataLoader worker 中完成,减少 GPU 等待时间
  • 扩展范围extended_end = shard_end + action_horizon,确保能获取 o_{t+N} 的价值
5.2 批量推理

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L652-L678):

python 复制代码
def process_value_batch(obs_list, meta_list):
    # 批量推理
    batch_results = value_model.infer_batch(
        obs_list,
        batch_size=batch_size,
        pretransformed=cpu_prep_in_workers,
        already_cpu_prepared=cpu_prep_in_workers,
    )
    
    # 存储结果
    for result, meta_info in zip(batch_results, meta_list):
        local_idx = int(meta_info["global_idx"]) - shard_start
        v_values[local_idx] = float(result["value"])  # V(o_t)
        meta_ep_idx[local_idx] = int(meta_info["episode_index"])
        meta_frame_idx[local_idx] = int(meta_info["frame_index"])
        meta_return[local_idx] = float(meta_info["true_return"])
        meta_reward[local_idx] = float(meta_info["reward"])

输出 :得到一个数组 v_values,存储了每个样本的价值估计 V(o_t)

5.3 预取优化

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L702-L737):

python 复制代码
with ThreadPoolExecutor(max_workers=1) as prefetch_pool:
    pending_future = prefetch_pool.submit(_fetch_next)
    
    for batch_idx in range(batch_count):
        batch = pending_future.result()  # 获取预取的 batch
        if batch_idx + 1 < batch_count:
            pending_future = prefetch_pool.submit(_fetch_next)  # 预取下一个 batch
        
        process_value_batch(*batch)  # GPU 推理

目的 :在 GPU 处理当前 batch 的同时,CPU 预取下一个 batch,实现数据加载和 GPU 计算的流水线并行


六、Step 3:Phase 2 --- 计算优势值

这是优势计算的核心算法阶段,使用预计算的价值估计和奖励数据计算每个时间步的优势值。

6.1 遍历每个样本

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L759-L823):

python 复制代码
for i in range(shard_size):
    gidx = shard_start + i
    ep_idx = int(meta_ep_idx[i])
    frame_idx = int(meta_frame_idx[i])
    true_return = float(meta_return[i])
6.2 处理 episode 边界
python 复制代码
ep_end = int(ep_ends.get(ep_idx, gidx + 1))
next_gidx = gidx + action_horizon
is_next_pad = next_gidx >= ep_end  # 是否超出 episode 边界
num_valid = min(action_horizon, ep_end - gidx)  # 有效奖励步数

处理逻辑

  • 如果 t + N 超出 episode 边界(is_next_pad=True),则 V(o_{t+N}) = 0(终止状态价值为 0)
  • num_valid 表示实际可用的未来奖励步数
6.3 获取价值估计
python 复制代码
v_curr = float(v_values[i])  # V(o_t)

if is_next_pad:
    v_next = 0.0  # 终止状态价值为 0
else:
    next_local_idx = next_gidx - shard_start
    v_next = float(v_values[next_local_idx])  # V(o_{t+N})
6.4 计算奖励和
python 复制代码
if abs(gamma - 1.0) < 1e-8:
    # gamma=1.0 时,直接用 return 差
    if is_next_pad:
        reward_sum_raw = true_return
    else:
        reward_sum_raw = true_return - float(meta_return[next_local_idx])
else:
    # gamma≠1.0 时,计算折扣奖励和
    reward_slice = meta_reward[i : i + num_valid]
    reward_sum_raw = float(np.sum(gamma_powers[:num_valid] * reward_slice))

公式R_{t:t+N} = Σ(γ^i * r_{t+i})

6.5 归一化奖励
python 复制代码
def normalize(x):
    ret_range = ret_max - ret_min  # 如 [-700, 0]
    return (x - ret_min) / ret_range - 1.0  # 映射到 [-1, 0]

reward_sum = normalize(reward_sum_raw)

目的:将原始奖励和归一化到与价值模型输出相同的范围(-1, 0),确保优势值计算的数值稳定性。

6.6 计算最终优势值
python 复制代码
gamma_k = gamma**num_valid if discount_next_value else 1.0
advantage = reward_sum + gamma_k * v_next - v_curr

公式回顾

复制代码
A_t = normalize(R_{t:t+N}) + γ^N * V(o_{t+N}) - V(o_t)

保存结果

python 复制代码
results["advantage"].append(advantage)
results["return"].append(true_return)
results["value_current"].append(v_curr)
results["value_next"].append(v_next)
results["reward_sum"].append(reward_sum)
results["reward_sum_raw"].append(reward_sum_raw)
results["num_valid_rewards"].append(num_valid)

七、Step 4:计算统一阈值

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L1075-L1078):

python 复制代码
positive_quantile = cfg.advantage.get("positive_quantile", 0.3)
combined_advantages = np.concatenate(all_advantages)
unified_threshold = quantile_threshold(combined_advantages, positive_quantile)

目的 :将连续的优势值转换为布尔标签(好/坏),用于 CFG RL 训练。

方法

  • 将所有数据集的优势值合并
  • 取第 (1 - positive_quantile) 百分位数作为阈值
  • 默认 positive_quantile=0.3,即选择 top 30% 的样本作为"好"样本

示例

python 复制代码
# 假设有 10000 个优势值
# positive_quantile=0.3 → 取第 7000 个分位数作为阈值
# 大于等于阈值的样本标记为 positive=True

八、Step 5:保存优势值

compute_advantages.py(file:///Users/mac/projects/robot/RLinf/examples/offline_rl/advantage_labeling/recap/process/compute_advantages.py#L864-L915):

python 复制代码
def save_advantages_to_dataset(dataset_path, advantages_df, threshold, ...):
    save_df = advantages_df.copy()
    save_df.rename(columns={"advantage": "advantage_continuous"}, inplace=True)
    
    # 转换为布尔标签
    save_df["advantage"] = save_df["advantage_continuous"] >= threshold
    
    # 保存到 meta/advantages.parquet
    adv_filename = f"advantages_{tag}.parquet" if tag else "advantages.parquet"
    save_df.to_parquet(meta_dir / adv_filename, index=False)

输出文件meta/advantages.parquet,包含:

  • episode_index:所属 episode
  • frame_index:帧索引
  • advantage_continuous:连续优势值
  • advantage:布尔标签(好/坏)
  • value_current:当前价值估计
  • value_next:未来价值估计
  • reward_sum:归一化奖励和

九、关键设计总结

设计点 实现方式 优势
两阶段计算 Phase 1 批量推理,Phase 2 逐个计算 避免重复推理,提升效率
扩展范围 extended_end = shard_end + N 确保能获取 V(o_{t+N})
预取优化 ThreadPoolExecutor 预取数据 GPU/CPU 流水线并行
内存管理 定期刷盘到临时 parquet 避免 OOM
统一阈值 跨数据集百分位数 保证不同数据集的公平性
分布式支持 torchrun 多 GPU 加速大规模数据集处理

十、为什么优势值能衡量"好坏"?

优势值的直觉理解:

复制代码
A_t = 实际回报 - 预期回报

实际回报 = normalize(R_{t:t+N}) + γ^N * V(o_{t+N})
预期回报 = V(o_t)

如果 A_t > 0: 实际回报 > 预期回报 → 好决策
如果 A_t < 0: 实际回报 < 预期回报 → 坏决策

在第四步 CFG RL 训练中,模型会学习:

  • advantage=True(好样本)时,模仿数据中的动作
  • advantage=False(坏样本)时,避免数据中的动作

这样就能从混合的成功/失败数据中,提取高质量的训练信号

第四步:CFG RL 训练

一、核心目标

第四步的目标是使用第三步计算好的优势标签 ,通过 CFG(Classifier-Free Guidance) 技术训练策略模型,使其学会模仿"好"动作,避免"坏"动作

二、什么是 CFG?

CFG 是一种源自扩散模型的技术,核心思想是:

复制代码
推理时:v = (1 - w) * v_uncond + w * v_cond

训练时:分别训练无条件分支(v_uncond)和条件分支(v_cond)
分支 输入 训练目标
无条件分支 原始任务描述 学习基础动作分布
条件分支 带优势标签的任务描述 学习受优势信号引导的动作分布

在推理时,通过调整 w(guidance_scale),可以控制模型更倾向于生成"好"动作还是"坏"动作。


三、完整流程

复制代码
Step 1: 加载数据集(含优势标签)
    ↓
Step 2: 预处理(生成正负引导 prompt)
    ↓
Step 3: 样本路由(决定走条件分支还是无条件分支)
    ↓
Step 4: 前向传播(计算 flow loss)
    ↓
Step 5: 反向传播(更新模型参数)

四、Step 1:加载数据集

4.1 AdvantagePreservingDataset

cfg_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/data/datasets/recap/cfg_model.py#L34-L130):

python 复制代码
class AdvantagePreservingDataset:
    def __init__(self, base_dataset, transformed_dataset, advantages_lookup):
        self._transformed_dataset = transformed_dataset
        self._advantage_by_index = self._build_advantage_index(
            base_dataset, advantages_lookup
        )
    
    def __getitem__(self, idx):
        sample = self._transformed_dataset[idx]
        sample["advantage"] = self._advantage_by_index[idx]  # 布尔值:True=好样本
        return sample

关键设计

  • meta/advantages.parquet 加载优势标签(第三步的输出)
  • 建立 (episode_index, frame_index) → bool 的映射
  • 确保每个样本都带有 advantage 字段
4.2 数据加载器

fsdp_cfg_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_cfg_worker.py#L251):

python 复制代码
data_loader = CFGDataLoaderImpl(data_config, torch_data_loader)

CFGDataLoaderImpl 每次迭代返回 (observation, actions, advantage) 三元组。


五、Step 2:预处理(生成正负引导 Prompt)

5.1 TokenizePromptWithGuidance

cfg_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/data/datasets/recap/cfg_model.py#L149-L188):

python 复制代码
class TokenizePromptWithGuidance:
    def __call__(self, data):
        prompt = data.pop("prompt")
        
        # 生成原始 prompt 的 token
        tokens, token_masks = self.tokenizer.tokenize(prompt, state)
        
        # 生成正负引导 prompt 的 token
        positive_prompt = f"{prompt}\nAdvantage: positive"
        negative_prompt = f"{prompt}\nAdvantage: negative"
        
        positive_tokens, positive_masks = self.tokenizer.tokenize(positive_prompt, state)
        negative_tokens, negative_masks = self.tokenizer.tokenize(negative_prompt, state)
        
        return {
            "tokenized_prompt": tokens,
            "tokenized_positive_guidance_prompt": positive_tokens,
            "tokenized_negative_guidance_prompt": negative_tokens,
            ...
        }

示例

复制代码
原始 prompt: "Pick up the block and place it on the table"
正引导 prompt: "Pick up the block and place it on the table\nAdvantage: positive"
负引导 prompt: "Pick up the block and place it on the table\nAdvantage: negative"

六、Step 3:样本路由

6.1 compute_cfg_routing_masks

openpi_cfg_action_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/openpi_cfg/openpi_cfg_action_model.py#L106-L160):

python 复制代码
def compute_cfg_routing_masks(advantage, positive_only_conditional, unconditional_prob):
    positive_mask = advantage
    negative_mask = ~positive_mask
    
    # 决定哪些样本走条件分支,哪些走无条件分支
    if positive_only_conditional:
        # 只有正样本才可能走条件分支
        positive_conditional_mask = positive_mask & (random_values > unconditional_prob)
        negative_conditional_mask = torch.zeros_like(positive_mask)
    else:
        # 正负样本都可能走条件分支
        guidance_mask = random_values > unconditional_prob
        positive_conditional_mask = positive_mask & guidance_mask
        negative_conditional_mask = negative_mask & guidance_mask
    
    return {
        "positive_conditional_mask": positive_conditional_mask,
        "positive_unconditional_mask": positive_mask & ~positive_conditional_mask,
        "negative_conditional_mask": negative_conditional_mask,
        "negative_unconditional_mask": negative_mask & ~negative_conditional_mask,
    }

路由逻辑

复制代码
unconditional_prob = 0.1(默认)

对于正样本:
  - 90% 概率走条件分支(使用正引导 prompt)
  - 10% 概率走无条件分支(使用原始 prompt)

对于负样本(positive_only_conditional=True 时):
  - 100% 走无条件分支(使用原始 prompt)
6.2 为什么需要无条件分支?
原因 说明
稳定性 无条件分支防止模型过度依赖引导信号
多样性 保持基础动作分布的多样性
CFG 推理 推理时需要无条件输出作为基线

七、Step 4:前向传播

7.1 Prompt 选择

openpi_cfg_action_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/openpi_cfg/openpi_cfg_action_model.py#L512-L543):

python 复制代码
if self.config.positive_only_conditional:
    # 正样本条件分支用正引导 prompt,其他用原始 prompt
    final_lang_tokens = torch.where(
        positive_conditional_mask.unsqueeze(-1),
        positive_guidance_lang_tokens,  # 正引导
        lang_tokens,                    # 原始
    )
else:
    # 条件分支:正样本用正引导,负样本用负引导
    guidance_lang_tokens = torch.where(
        positive_mask.unsqueeze(-1),
        positive_guidance_lang_tokens,
        negative_guidance_lang_tokens,
    )
    final_lang_tokens = torch.where(
        conditional_mask.unsqueeze(-1),
        guidance_lang_tokens,           # 引导 prompt
        lang_tokens,                    # 原始 prompt
    )
7.2 Flow Loss 计算

openpi_cfg_action_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/openpi_cfg/openpi_cfg_action_model.py#L363-L444):

python 复制代码
def _compute_flow_losses(self, images, img_masks, state, actions, lang_tokens, ...):
    # 1. 采样时间步和噪声
    time = self.sample_time(actions.shape[0], device)
    noise = self.sample_noise(actions.shape, device)
    
    # 2. 构造扩散过程中的中间状态
    x_t = time * noise + (1 - time) * actions  # 噪声+动作的插值
    u_t = noise - actions  # 目标速度
    
    # 3. 嵌入前缀(图像+文本)
    prefix_embs, prefix_pad_masks, prefix_att_masks = self.embed_prefix(
        images, img_masks, lang_tokens, lang_masks
    )
    
    # 4. 嵌入后缀(状态+动作+时间)
    suffix_embs, suffix_pad_masks, suffix_att_masks, adarms_cond = (
        self.embed_suffix(state, x_t, time)
    )
    
    # 5. 前向传播
    suffix_out = self.paligemma_with_expert.forward(
        inputs_embeds=[prefix_embs, suffix_embs],
        ...
    )[0][1]
    
    # 6. 预测速度
    v_t = self.action_out_proj(suffix_out)
    
    # 7. 计算 MSE 损失
    per_element_loss = F.mse_loss(u_t, v_t, reduction="none")
    flow_loss = per_element_loss.mean()
    
    return flow_loss, per_element_loss.detach().mean(dim=(-1, -2))

核心公式

复制代码
扩散过程:x_t = t * noise + (1 - t) * actions
目标速度:u_t = noise - actions
预测速度:v_t = model(x_t, t, observation)
损失:L = MSE(u_t, v_t)

这是一个标准的**流匹配(Flow Matching)**损失,用于训练扩散模型。


八、Step 5:反向传播

fsdp_cfg_worker.py(file:///Users/mac/projects/robot/RLinf/rlinf/workers/sft/fsdp_cfg_worker.py#L339-L457):

python 复制代码
def run_training(self):
    self.model.train()
    
    for idx in range(self.gradient_accumulation):
        # 获取 batch:(observation, actions, advantage)
        observation, actions, advantage = next(self.data_iter)
        
        # 前向传播
        loss, metrics_data = self.model(
            data={
                "observation": observation,
                "actions": actions,
                "advantage": advantage,
            }
        )
        
        # 反向传播(梯度累积)
        loss = loss / self.gradient_accumulation
        self.grad_scaler.scale(loss).backward()
    
    # 参数更新
    self.optimizer_step()
    self.optimizer.zero_grad()
    self.lr_scheduler.step()
    
    return train_metrics

九、推理时的 CFG 引导

9.1 sample_actions

openpi_cfg_action_model.py(file:///Users/mac/projects/robot/RLinf/rlinf/models/embodiment/openpi_cfg/openpi_cfg_action_model.py#L692-L816):

python 复制代码
def sample_actions(self, observation, noise=None):
    guidance_type = self.config.guidance_type  # "positive"(默认)
    
    # 1. 计算无条件输出
    v_t_uncond = self.denoise_step(
        state, prefix_pad_masks_uncond, past_key_values_uncond, x_t, time
    )
    
    # 2. 计算条件输出(使用正引导 prompt)
    if guidance_type == "positive":
        v_t_cond = self.denoise_step(
            state, prefix_pad_masks_cond, past_key_values_cond, x_t, time
        )
    
    # 3. CFG 融合
    v_t = (1 - guidance_scale) * v_t_uncond + guidance_scale * v_t_cond
    
    # 4. 扩散采样
    x_t = x_t + dt * v_t
    
    return {"actions": x_t}

CFG 公式

复制代码
v_t = (1 - w) * v_uncond + w * v_cond

其中 w = cfgrl_guidance_scale(默认 1.0)

直观理解

  • w=0:只使用无条件输出,不考虑优势信号
  • w=1:完全使用条件输出,最大化优势引导
  • w>1:放大条件信号(类似扩散模型的分类器引导)

十、关键设计总结

设计点 实现方式 优势
正负引导 Prompt {prompt}\nAdvantage: positive/negative 让模型理解优势信号
样本路由 positive_only_conditional=True 只对正样本进行条件引导,避免学习坏动作
Flow Matching 损失 MSE(u_t, v_t) 稳定训练扩散模型
梯度累积 多 micro-batch 支持大全局 batch size
FSDP 分布式 自动混合精度 + 梯度检查点 高效训练大模型

十一、四步流程的完整闭环

复制代码
Step 1: compute_returns
    原始数据 → reward/return 标签

Step 2: train_value_sft
    数据 + return → ValueCriticModel(学习状态价值估计)

Step 3: compute_advantages
    数据 + ValueCriticModel → advantage 标签(衡量动作好坏)

Step 4: train_cfg_rl
    数据 + advantage → PolicyModel(学会模仿好动作,避免坏动作)
    
    训练时:
      正样本 → 条件分支(正引导 prompt)→ 学习好动作
      负样本 → 无条件分支(原始 prompt)→ 保持基础分布
    
    推理时:
      CFG 融合:v = (1-w)*v_uncond + w*v_cond
      只使用正引导,让模型偏向生成好动作

十二、为什么 CFG RL 有效?

机制 效果
优势标签过滤 从混合数据中提取高质量训练信号
条件分支 让模型在正样本上学习更好的动作
无条件分支 保持模型的泛化能力和多样性
CFG 融合 推理时灵活控制生成方向

这种方法的核心思想是:利用监督学习训练价值模型,然后用价值模型为离线数据打标签,最后用 CFG 技术让策略模型学会区分好/坏动作

相关推荐
c_lb72881 小时前
2026年下半年AI量化工具,阶段不同重点也不同
人工智能·python
变量未定义~1 小时前
ST表-龙骑士军团【算法赛】
数据结构·算法
IT_陈寒1 小时前
React中useEffect的依赖项把我坑惨了
前端·人工智能·后端
a1117761 小时前
SLAM 学习笔记(三)视觉里程计2(VO) 特征点法
人工智能·笔记·学习
湘美书院--湘美谈教育1 小时前
湘美谈教育湘美书院绥宁文学系列:AI时代的小说,雪峰号
大数据·人工智能·深度学习·机器学习·生活
汤姆yu1 小时前
面向具身智能的物理视频模拟器:蚂蚁灵波LingBot-Video开源模型全解析
java·大数据·人工智能·gpt·开源·大模型·音视频
三品吉他手会点灯1 小时前
嵌入式机器学习 - 学习笔记1.1.2 - 机器学习的局限性与伦理
人工智能·笔记·嵌入式硬件·学习·机器学习
Xpower 171 小时前
详细解释 Codex 最近一次功能更新,以及 GPT-5.6 Sol、Terra、Luna 的能力、价格、使用方式和适用场景。
人工智能·python·gpt·学习
技术小黑1 小时前
RNN算法实战系列02 | 医疗成本预测(LSTM 回归)
人工智能·pytorch·rnn·深度学习·lstm