【机器人 / 强化学习】SERL:让真机强化学习从“难用”走向“可复现”的强化学习框架 ----(4)算法篇(DrQ vs VICE)
【机器人 / 强化学习】SERL:让真机强化学习从“难用”走向“可复现”的强化学习框架 ----(4)算法篇(DrQ vs VICE)
在前三篇文章中,我们详细介绍了 SERL 框架的架构、环境搭建和数据采集流程。作为 SERL 系列的核心篇章,本文深入剖析 SERL 背后的两大关键算法——DrQ(Data-regularized Q-learning) 和 VICE(Variational Inverse Control with Expert)。这两个算法在真机强化学习中分别解决“样本效率”和“从演示中学习”两大痛点。我们将通过大量代码实战演示,揭示它们的工作原理与在 SERL 中的具体应用。## 1. DrQ:数据增强驱动的 Q-learningDrQ 的核心思想是利用图像增强技术提升样本效率。在真机场景中,每个样本都极其昂贵,DrQ 通过对观测图像进行随机裁剪、色彩抖动等操作,让同一个样本产生多个视角,从而让 Q 网络学到更鲁棒的表征。### 1.1 DrQ 的损失函数与实现DrQ 的 Q 学习损失函数为: L DrQ = E ( s , a , r , s ′ ) ∼ D [ ( Q ( s , a ) − ( r + γ E a ′ ∼ π [ Q ˉ ( s ′ , a ′ ) ] ) ) 2 ] + λ ⋅ AugLoss \mathcal{L}_{\text{DrQ}} = \mathbb{E}_{(s,a,r,s') \sim \mathcal{D}} \left[ \left( Q(s,a) - (r + \gamma \mathbb{E}_{a'\sim\pi}[ \bar{Q}(s',a') ]) \right)^2 \right] + \lambda \cdot \text{AugLoss} LDrQ=E(s,a,r,s′)∼D[(Q(s,a)−(r+γEa′∼π[Qˉ(s′,a′)]))2]+λ⋅AugLoss其中 AugLoss 确保不同增强版本下的 Q 值一致性。下面我们实现一个简化版 DrQ 的 Q 网络更新代码:pythonimport torchimport torch.nn as nnimport torch.nn.functional as Fimport numpy as npfrom collections import dequefrom torchvision import transforms# 定义随机图像增强变换class DrQAugmentation: def __init__(self, img_size=84, shift=4): self.transform = transforms.Compose([ transforms.RandomCrop(size=img_size, padding=shift), transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1) ]) def __call__(self, img): # img: (C, H, W) numpy array or torch tensor if isinstance(img, np.ndarray): img = torch.from_numpy(img).float() # 应用增强 augmented = self.transform(img.unsqueeze(0)) # 添加batch维度 return augmented.squeeze(0) # 移除batch维度class DrQAgent: def __init__(self, obs_dim, action_dim, hidden_dim=256, lr=3e-4): self.q_net = nn.Sequential( nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1), nn.ReLU(), nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), nn.ReLU(), nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.Flatten(), nn.Linear(64 * 21 * 21, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, action_dim) ) self.target_q_net = nn.Sequential(*[layer for layer in self.q_net]) self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=lr) self.augmentation = DrQAugmentation() self.replay_buffer = deque(maxlen=100000) self.gamma = 0.99 self.tau = 0.005 # 软更新系数 def update(self, batch_size=64): if len(self.replay_buffer) < batch_size: return # 从回放缓冲区采样 batch = random.sample(self.replay_buffer, batch_size) states = torch.stack([torch.tensor(s[0]) for s in batch]) actions = torch.tensor([s[1] for s in batch]) rewards = torch.tensor([s[2] for s in batch]) next_states = torch.stack([torch.tensor(s[3]) for s in batch]) dones = torch.tensor([s[4] for s in batch]) # 对当前状态和下一状态进行数据增强 states_aug = torch.stack([self.augmentation(s) for s in states]) next_states_aug = torch.stack([self.augmentation(s) for s in next_states]) # 计算当前Q值 q_values = self.q_net(states_aug).gather(1, actions.unsqueeze(1)).squeeze(1) # 计算目标Q值(使用目标网络) with torch.no_grad(): next_q_values = self.target_q_net(next_states_aug).max(1)[0] target_q = rewards + (1 - dones.float()) * self.gamma * next_q_values # 损失函数:MSE + 增强一致性正则化(简化版) q_loss = F.mse_loss(q_values, target_q) # 可选:添加增强一致性损失(让不同增强版本的Q值接近) # 此处简单实现:对同一状态进行两次增强,计算Q值差异 states_aug2 = torch.stack([self.augmentation(s) for s in states]) q_values2 = self.q_net(states_aug2).gather(1, actions.unsqueeze(1)).squeeze(1) consistency_loss = F.mse_loss(q_values, q_values2) total_loss = q_loss + 0.1 * consistency_loss # 反向传播 self.optimizer.zero_grad() total_loss.backward() self.optimizer.step() # 软更新目标网络 for param, target_param in zip(self.q_net.parameters(), self.target_q_net.parameters()): target_param.data.copy_(self.tau * param.data + (1 - self.tau) * target_param.data) def select_action(self, state, epsilon=0.1): # epsilon-greedy策略 if np.random.random() < epsilon: return np.random.randint(0, self.q_net[-1].out_features) else: state_tensor = torch.tensor(state, dtype=torch.float32).unsqueeze(0) with torch.no_grad(): q_values = self.q_net(state_tensor) return q_values.argmax().item()代码解析:- DrQAugmentation 类实现了随机裁剪和色彩抖动,这正是 DrQ 提升样本效率的关键。- 在更新时,我们对状态进行两次增强,并计算 Q 值的一致性损失,这相当于实现了 DrQ 论文中的“Q-value regularization”。- 实际 SERL 中使用了更复杂的架构(如 ResNet 编码器),但核心思想一致。## 2. VICE:从演示中学习逆动力学VICE 解决的是如何在稀疏奖励场景下利用专家演示。它通过训练一个逆动力学模型(Inverse Dynamics Model, IDM),将当前状态映射到成功可能性,从而将演示数据转化为奖励信号。### 2.1 VICE 的算法流程VICE 的核心是训练一个判别器(Discriminator)区分专家轨迹和随机轨迹,然后用这个判别器的输出作为奖励函数。其损失函数为: L VICE = − E ( s , a ) ∼ D expert [ log D ( s , a ) ] − E ( s , a ) ∼ D random [ log ( 1 − D ( s , a ) ) ] \mathcal{L}_{\text{VICE}} = -\mathbb{E}_{(s,a) \sim \mathcal{D}_{\text{expert}}}[\log D(s,a)] - \mathbb{E}_{(s,a) \sim \mathcal{D}_{\text{random}}}[\log(1 - D(s,a))] LVICE=−E(s,a)∼Dexpert[logD(s,a)]−E(s,a)∼Drandom[log(1−D(s,a))]其中 D ( s , a ) D(s,a) D(s,a) 输出的是状态-动作对属于专家分布的概率。### 2.2 实现 VICE 判别器训练下面我们用 PyTorch 实现一个 VICE 判别器,并在仿真环境(简化版)中演示训练过程:pythonimport torchimport torch.nn as nnimport torch.optim as optimimport numpy as npfrom torch.utils.data import DataLoader, TensorDatasetclass VICEDiscriminator(nn.Module): """VICE 判别器:输入 (state, action),输出属于专家分布的概率""" def __init__(self, state_dim, action_dim, hidden_dim=256): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim + action_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 1), nn.Sigmoid() # 输出概率值 ) def forward(self, state, action): x = torch.cat([state, action], dim=1) return self.net(x)def generate_expert_demonstrations(num_episodes=50, env_dim=10): """模拟生成专家演示数据(在真实场景中,这来自人类远程操控)""" # 假设专家轨迹是到达目标位置 expert_data = [] for _ in range(num_episodes): # 随机初始化状态(位置和速度) state = np.random.randn(env_dim) trajectory = [] for step in range(100): # 专家动作:简单的比例控制(模拟人类操控) target = np.ones(env_dim) * 2.0 # 目标位置 action = 0.5 * (target - state) # 比例控制 trajectory.append((state.copy(), action.copy())) # 环境转移(简化版) state = state + action * 0.1 + np.random.randn(env_dim) * 0.01 expert_data.extend(trajectory) return expert_datadef train_vice(expert_data, state_dim, action_dim, epochs=100): # 准备数据 states_expert = torch.tensor([s for s, a in expert_data], dtype=torch.float32) actions_expert = torch.tensor([a for s, a in expert_data], dtype=torch.float32) # 生成随机数据(作为负样本) states_random = torch.randn_like(states_expert) actions_random = torch.randn_like(actions_expert) # 创建数据集 dataset_expert = TensorDataset(states_expert, actions_expert) dataset_random = TensorDataset(states_random, actions_random) # 创建判别器 discriminator = VICEDiscriminator(state_dim, action_dim) optimizer = optim.Adam(discriminator.parameters(), lr=1e-3) loss_fn = nn.BCELoss() # 训练循环 for epoch in range(epochs): # 交替采样正负样本 batch_size = 64 expert_loader = DataLoader(dataset_expert, batch_size=batch_size, shuffle=True) random_loader = DataLoader(dataset_random, batch_size=batch_size, shuffle=True) batch_loss = 0.0 for (s_exp, a_exp), (s_rand, a_rand) in zip(expert_loader, random_loader): # 正样本:标签为1 pred_exp = discriminator(s_exp, a_exp) loss_exp = loss_fn(pred_exp, torch.ones_like(pred_exp)) # 负样本:标签为0 pred_rand = discriminator(s_rand, a_rand) loss_rand = loss_fn(pred_rand, torch.zeros_like(pred_rand)) loss = loss_exp + loss_rand optimizer.zero_grad() loss.backward() optimizer.step() batch_loss += loss.item() if epoch % 10 == 0: print(f"Epoch {epoch}, Loss: {batch_loss / len(expert_loader):.4f}") return discriminator# 训练VICE判别器state_dim = 10action_dim = 10expert_data = generate_expert_demonstrations()trained_discriminator = train_vice(expert_data, state_dim, action_dim)# 使用判别器作为奖励函数def compute_reward(state, action): """将VICE判别器的输出转换为奖励信号""" state_t = torch.tensor(state, dtype=torch.float32).unsqueeze(0) action_t = torch.tensor(action, dtype=torch.float32).unsqueeze(0) with torch.no_grad(): prob = trained_discriminator(state_t, action_t).item() # 奖励 = log(prob) - log(1 - prob) 或直接使用 prob return np.log(prob + 1e-8) # 注意:实际应用中需要剪裁代码解析:- 我们构造了一个简单的模拟环境,其中专家演示通过比例控制生成(模拟人类操作)。- VICEDiscriminator 是一个小型 MLP,输入状态和动作,输出一个 0-1 之间的概率。- 训练过程交替采样专家数据和随机数据,优化二元交叉熵损失。- 训练完成后,我们可以用 compute_reward 函数将判别器输出转化为奖励信号,在强化学习训练时替代稀疏奖励。## 3. DrQ vs VICE:在 SERL 中的协同与分工在 SERL 框架中,DrQ 和 VICE 并非对立关系,而是互补的:| 特性 | DrQ | VICE ||------|-----|------|| 解决的问题 | 样本效率低(图像观测) | 奖励稀疏(需要演示) || 核心机制 | 数据增强 + 一致性正则 | 逆动力学 + 对抗训练 || 适用场景 | 高维图像输入的任务 | 有专家演示但奖励定义困难的任务 || 在 SERL 中的角色 | 视觉编码器 + Q 学习 | 奖励函数生成器 |实际使用中,SERL 会先使用 VICE 从演示中学习奖励函数,然后 DrQ 基于这个稠密奖励进行高效的强化学习训练。最终的真机部署流程如下:演示数据 → VICE训练判别器 → 生成奖励函数 → DrQ训练策略 → 真机运行## 4. 总结本文深入解析了 SERL 框架的两大核心算法 DrQ 和 VICE,并通过可运行的代码示例展示了它们的实现细节。DrQ 通过数据增强解决了真机场景下样本稀缺的问题,VICE 则利用专家演示将稀疏奖励转化为稠密奖励,两者组合构成了 SERL 在真机强化学习中实现“数据高效 + 奖励可定义”的基石。在实际部署中,我们建议:- 若任务有清晰的成功判断标准(如到达目标位置),优先使用 DrQ 配合简单奖励函数。- 若任务需要复杂行为模仿(如抓取、装配),则必须引入 VICE 从演示中学习奖励。SERL 的价值在于将这些算法工程化为统一框架,让研究人员可以专注于任务本身,而不是重复实现这些复杂的算法细节。作为全栈工程师,理解这些算法的底层原理,将帮助你在真机强化学习的道路上走得更远。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)