0x04 VICE

vice.py 是 SERL 中一个非常高级的组件,它实现了 VICE(Variational Inverse Control with Events)算法。它的核心任务是:当机器人没有明确的奖励信号时,通过观察"成功图片"来给自己打分。这在真实的机器人部署中极其重要,因为你无法在真实世界里写出一个完美的数学公式来判断"杯子是否抓稳了"。

VICE 通过将策略访问过的所有状态以负标签的形式加入分类器的训练集,并在每次迭代后更新分类器,从而解决该问题。这样,强化学习过程类似于生成对抗网络(GAN),其中策略充当生成器,奖励分类器则作为判别器。

4.1 背景 & 原理

VICE 是 RLPD 的"心跳"。在 train_rlpd.py 这种算法中,Demo 数据只是素材,而 VICE 的奖励才是灵魂。没了奖励,机器人就失去了进化的动力。

4.1.1 动机:为什么要搞 VICE?

在仿真环境(如 MuJoCo)中,奖励函数很好写:奖励 = - 距离。

但在真实世界中:

  • 你怎么知道机械臂末端离杯子还有几厘米?(除非你装了极其昂贵的外部捕捉系统)。
  • 你怎么判断线缆是否进了槽位?

VICE 的思路:人类只需要给机器人看几张"任务成功"的照片(Goal Images),剩下的让机器自己去悟。

4.1.2 变分推断(Variational Inference)

变分推断是一种"用简单模型逼近复杂真相"的方法。

它的核心逻辑是:

  • 真相太复杂,无法直接计算:就像你(没有经过美术训练)无法直接画出某个美女的真容
  • 构建一个简化模型:用简单的几何形状(椭圆、柳叶眉、杏仁眼)构建一个"标准模型"
  • 调整模型参数:根据观察到的线索,不断调整模型的参数(脸型的长宽比、眉毛的弧度等)
  • 优化目标:让简化模型"最好地解释观察到的线索"
  • 结果:简化模型虽然不是真容,但已经非常接近真容

一句话总结:变分推断就是"用一个可调的简化模型,去逼近一个复杂但无法直接计算的真相"。

贝叶斯推断的困境

在贝叶斯统计中,我们想计算后验分布:p(z|x) = p(x|z) * p(z) / p(x)

其中:

  • z 是隐变量(如女子的真容)
  • x 是观测数据(如你看到的线索)
  • p(x|z) 是似然(给定真容,看到线索的概率)
  • p(z) 是先验(对真容的初始假设)
  • p(x) 是证据(看到线索的总概率)

问题:p(x) 需要对所有可能的 z 积分,这在复杂模型中几乎不可能计算。

变分推断的解决方案

既然无法直接计算 p(z|x),那就用一个简单的 q(z) 来近似它。

  • 目标:找到最好的 q(z),使其尽可能接近 p(z|x)。
  • ELBO = E_q[log p(x,z)] - E_q[log q(z)]
  • 最大化 ELBO 等价于最小化 q(z) 和 p(z|x) 之间的 KL 散度。
4.1.3 VICE 原理

VICE 是一种"从专家示范中学习评判标准"的方法。VICE 不是教你"怎么走路",而是教你"什么算走到了目的地"。一旦你知道目的地在哪,你自然就知道该往哪走了。

它的核心逻辑是:

  • 不直接学"怎么做",而是学"什么算做好",即,你不需要告诉机器人"怎么做",也不需要给它一条完整的示范路径。
  • 通过观察大量专家示范(成功案例)和非专家示范(失败案例),即,你只需要给它看一堆"成功长什么样子"的图片(goal state examples)。
  • "事件"的定义来自你提供的成功状态样本,不需要完整示范轨迹。
  • 用变分推断(variational inference)训练一个分类器,输出"当前状态是成功状态的概率",作为reward。
  • 然后用这个分类器来学"这个状态像不像成功",把这个"像成功的概率"当作奖励信号,来训练强化学习。
  • 目标不是"最大化累积分数",而是"让某个事件(event)发生的概率最大",发生关键时刻越多,reward 越高。
4.1.4 对抗性进化:猫鼠游戏

VICE 把强化学习变成了一个"找游戏"。

  1. 准备正样本:你手动把机器人摆到成功位置,拍 20 张照片。这些照片标签为 1。
  2. 准备负样本:机器人自己在环境里瞎跑拍到的照片。这些照片标签为 0。
  3. 训练判别器:训练一个二分类神经网络(Binary Classifier),输入一张图,输出"它有多像成功照片"。
  4. 生成奖励:Reward = Classifier(当前图片),机器人发现:"只要我做出的动作让画面越来越像那 20 张照片,我的得分就越高。"

VICE 实际上是一个类似 GAN(生成对抗网络)的结构:

  • 分类器(猫):努力区分"真正的成功"和"机器人自以为是成功的动作"。
  • 机器人(鼠):努力做出让分类器认为是"成功"的动作。

随着训练的进行,分类器变得越来越挑剔,机器人也随之变得越来越精准。

如果 VICE 坏了:

  • 在线试错 被 VICE 判为 0。
  • 人类演示 也被 VICE 判为 0。
  • 结果:Critic 只能接受着这个事实 —— "这个世界没有奖赏,做什么都是徒劳"。Q.网络随之萎缩到 0。
  • 此时,Actor 唯一的指路明灯就只剩下那个额外的 BC Loss 了。所以,在 VICE 坏掉的情况下:
    • 如果没有 BC Loss,机器人会彻底变疯。
    • 如果有 BC Loss,机器人会退化成一个笨拙的模仿者,不再有进化的动力。

4.2 网络架构

VICE Agent 在标准 Actor-Critic 基础上增加了基于视觉的奖励分类器,实现了从人类演示到稀疏奖励的迁移学习。

组件编码器主干网络输出特殊设计
ActorSmallEncoder/ResNetMLP [256,256]动作分布tanh_squash
Critic共享Actor编码器Ensemble MLP [256,256]×2Q值双Critic
TemperatureLagrange标量温度自动调节
VICE Classifier独立编码器MLP [256]二分类logit预训练+微调

核心组件架构如下:

Actor 网络

policy_def = Policy( encoder=encoders["actor"], # 视觉编码器 network=MLP(**policy_network_kwargs), # [256, 256] action_dim=actions.shape[-1], tanh_squash_distribution=True, std_parameterization="uniform", )

Critic 网络

critic_backbone = partial(MLP, **critic_network_kwargs) # [256, 256] critic_backbone = ensemblize(critic_backbone, critic_ensemble_size)( name="critic_ensemble" # 默认2个Critic ) critic_def = partial(Critic, encoder=encoders["critic"], network=critic_backbone)

Temperature 网络

temperature_def = GeqLagrangeMultiplier( init_value=1.0, constraint_shape=(), constraint_type="geq", # 大于等于约束 )

VICE Reward Classifier

vice_def = BinaryClassifier( pretrained_encoder=pretrained_encoder, # 预训练ResNet encoder=vice_encoder_def, # 独立的编码器 network=MLP(**vice_network_kwargs), # [256] enable_stacking=True, )

另外,VICE使用独立的编码器。

# VICE使用独立的编码器 vice_encoders = { image_key: SmallEncoder( features=(32, 64, 128, 256), kernel_sizes=(3, 3, 3, 3), strides=(2, 2, 2, 2), padding="VALID", pool_method="avg", bottleneck_dim=256, spatial_block_size=8, name=f"vice_encoder_{image_key}", ) for image_key in image_keys }

4.3 奖励(reward)的计算

在 VICEAgent 中,奖励(reward)的计算依赖于一个学习到的二分类器(即 vice 网络),该网络用于区分专家/目标状态与智能体的观测。具体计算过程如下:

  • 前向传播与 Sigmoid 激活: 将 next_observations 输入 vice 网络(一个 BinaryClassifier),并通过 sigmoid 函数将输出映射为 0 到 1 之间的概率分数。此过程由 vice_reward 方法实现:

    rews = nn.sigmoid( self.state.apply_fn( {"params": self.state.params}, observation, name="vice", train=False, ) )
  • 二值化(阈值截断): 在 Critic 更新阶段(如 update_critics 和 update_high_utd 方法中),连续的概率分数会被转换为离散的二值奖励。若概率分数大于或等于 0.5,则奖励设为 1.0,否则为 0.0:

    rewards = (self.vice_reward(next_obs) >= 0.5) * 1.0
  • 数据增强: 在计算奖励前,会先通过 data_augmentation_fn 对 next_observations 进行数据增强,以提升奖励信号的鲁棒性。

  • 总结:奖励本质上是一个二值指标(1.0 或 0.0),其取值取决于 vice 分类器是否将 next_observation 判定为属于目标/专家状态分布(即 sigmoid 输出的概率 ≥0.5)。

4.4 训练逻辑

在 VICEAgent 中,BinaryClassifier(在代码中实例化为 vice 网络)的训练逻辑主要集中在 update_vice 方法中。其训练过程使用了二元交叉熵(BCE)损失,并引入了 Mixup、标签平滑(Label Smoothing)和梯度惩罚(Gradient Penalty)等正则化技术来防止 GAN 模式崩溃。

具体训练步骤如下:

数据准备与标签平滑(Label Smoothing):

  • 假设 batch 的后半部分为目标图像(正样本,标签为 1),前半部分为普通观测(负样本,标签为 0)。