第15章:行为克隆与模仿学习

章节大纲

  1. 开篇段落
  2. 行为克隆基础:监督学习方法 - 从演示到策略的映射 - 损失函数设计 - 网络架构选择
  3. 数据集收集:遥操作vs演示学习 - 遥操作系统设计 - 人类演示数据采集 - 数据增强技术
  4. 分布偏移问题与DAgger算法 - 协变量偏移的本质 - DAgger算法原理 - 实践中的改进
  5. 逆强化学习(IRL)原理 - 奖励函数学习 - 最大熵IRL - 深度IRL方法
  6. GAIL与对抗模仿学习 - 生成对抗框架 - 判别器与生成器设计 - 训练稳定性技巧
  7. 案例研究:特斯拉FSD的模仿学习架构
  8. 高级话题:离线强化学习与保守Q学习(CQL)
  9. 本章小结
  10. 练习题
  11. 常见陷阱与错误
  12. 最佳实践检查清单

开篇段落

模仿学习是机器人获得复杂技能的重要途径,它通过学习专家演示来绕过困难的奖励函数设计问题。本章深入探讨从简单的行为克隆到复杂的逆强化学习的各种方法,重点关注实际部署中的分布偏移问题及其解决方案。我们将学习如何从人类演示中提取有效策略,理解不同数据收集方法的权衡,以及如何在真实机器人系统中实现稳定的模仿学习。通过特斯拉FSD的案例分析,我们将看到这些技术如何在工业级系统中落地。

学习目标:

  • 掌握行为克隆的基本原理和实现细节
  • 理解分布偏移问题的本质及DAgger等解决方案
  • 学会设计高效的数据收集系统
  • 了解IRL和GAIL等高级方法的原理与应用
  • 能够在实际机器人项目中选择和实现合适的模仿学习方法

1. 行为克隆基础:监督学习方法

1.1 从演示到策略的映射

行为克隆将模仿学习问题转化为监督学习问题。给定专家演示数据集 $\mathcal{D} = \{(s_t, a_t)\}_{t=1}^N$,其中 $s_t$ 是状态,$a_t$ 是专家动作,我们的目标是学习策略 $\pi_\theta(a|s)$ 来最小化:

$$\mathcal{L}(\theta) = \mathbb{E}_{(s,a) \sim \mathcal{D}}[\ell(\pi_\theta(s), a)]$$ 其中 $\ell$ 是损失函数。这种方法的核心假设是:如果策略能够准确预测专家在每个状态下的动作,那么执行这个策略就能复现专家的行为。

然而,这个假设在实践中存在重要限制:

  1. 因果混淆:网络可能学习到虚假的相关性
  2. 时序依赖:单步预测忽略了动作序列的时序结构
  3. 多模态性:专家可能在相同状态下采取不同的有效动作

1.2 损失函数设计

损失函数的选择直接影响学习效果:

连续动作空间

  • MSE损失:$\ell_{MSE} = ||a - \hat{a}||^2$
  • 优点:梯度稳定,优化简单
  • 缺点:对异常值敏感,假设高斯噪声

  • Huber损失: $$\ell_{Huber} = \begin{cases} \frac{1}{2}(a - \hat{a})^2 & \text{if } |a - \hat{a}| \leq \delta \\ \delta(|a - \hat{a}| - \frac{1}{2}\delta) & \text{otherwise} \end{cases}$$

  • 结合了MSE和MAE的优点,对异常值更鲁棒

  • 混合密度网络(MDN): $$p(a|s) = \sum_{i=1}^K \alpha_i(s) \mathcal{N}(a; \mu_i(s), \Sigma_i(s))$$

  • 能够建模多模态分布,特别适合存在多个合理动作的场景

离散动作空间

  • 交叉熵损失:$\ell_{CE} = -\sum_i a_i \log \hat{a}_i$
  • 焦点损失(Focal Loss):处理类别不平衡问题

1.3 网络架构选择

架构设计需要考虑输入模态和任务特性:

视觉输入处理

图像输入 → CNN特征提取器 → 
         ↓
    空间注意力机制
         ↓
    特征融合层 → 策略头

多模态融合架构

视觉流: ResNet/ViT  视觉特征
                    
                     融合模块  共享表示  动作预测
                    
本体感知流: MLP  本体特征

时序建模

  • 因果Transformer:处理长期依赖,支持变长历史
  • LSTM/GRU:计算效率高,适合实时系统
  • 时序卷积网络(TCN):并行化好,感受野可控

2. 数据集收集:遥操作vs演示学习

2.1 遥操作系统设计

遥操作是收集机器人演示数据的主要方法之一。设计良好的遥操作系统需要在易用性、精度和数据质量之间取得平衡。

遥操作接口类型

  1. 直接控制接口: - 操纵杆/手柄:适合移动平台,但精度有限 - 6D鼠标(SpaceMouse):提供6自由度控制,适合精细操作 - 触觉设备(Phantom Omni/Touch):提供力反馈,增强操作感知

  2. 主从式遥操作

主臂(人操作) → 运动捕捉 → 运动重定向 → 从臂(机器人)
                           ↓
                      数据记录系统
  • 优点:直观自然,可以利用人的本体感知
  • 挑战:工作空间映射、动力学差异补偿
  1. 虚拟现实(VR)遥操作: - 使用VR头显和手柄进行沉浸式控制 - 视角选择:第一人称vs第三人称vs混合视角 - 延迟补偿:预测渲染减少控制延迟感

数据同步与时间戳

# 伪代码:多模态数据同步
class DataRecorder:
    def __init__(self):
        self.time_offset = calibrate_clocks()
        self.buffer = CircularBuffer(size=1000)

    def record_step(self):
        t = get_synchronized_time()
        data = {
            'timestamp': t,
            'rgb': camera.get_frame(t),
            'depth': depth_camera.get_frame(t),
            'joint_pos': robot.get_joints(t),
            'joint_vel': robot.get_velocities(t),
            'ee_pose': robot.get_ee_pose(t),
            'action': teleop.get_action(t),
            'force_torque': ft_sensor.get_reading(t)
        }
        self.buffer.add(data)

2.2 人类演示数据采集

直接从人类演示中学习(无需机器人硬件)有其独特优势:

动作捕捉系统

  • 光学系统(Vicon/OptiTrack):高精度,但需要标记点
  • 惯性系统(Xsens/Perception Neuron):便携,但存在漂移
  • 计算机视觉方法(OpenPose/MediaPipe):低成本,但精度受限

从人类演示到机器人动作的映射

  1. 运动重定向(Retargeting): $$a_{robot} = f(s_{human}, \phi_{human}, \phi_{robot})$$ 其中 $\phi$ 表示形态学参数

  2. 任务空间映射: - 关注末端执行器轨迹而非关节角度 - 使用逆运动学求解机器人关节指令

  3. 学习式映射: - 训练神经网络学习人到机器人的映射 - 可以处理形态学差异和动力学约束

2.3 数据增强技术

数据增强对提高策略泛化性至关重要:

几何增强

  • 随机裁剪和缩放:增强对物体大小变化的鲁棒性
  • 视角变换:模拟不同相机位置
  • 镜像翻转:适用于对称任务

时序增强

  • 速度扰动:改变执行速度±20%
  • 动作噪声注入:$a' = a + \epsilon, \epsilon \sim \mathcal{N}(0, \sigma^2)$
  • 轨迹插值:在关键帧之间生成中间状态

域随机化

视觉域随机化参数:

- 光照:强度[0.5, 2.0],色温[4000K, 7000K]
- 纹理:随机替换物体和背景纹理
- 相机:内参扰动±5%,位置噪声±5cm
- 物理:摩擦系数[0.5, 1.5],质量±20%

混合增强策略: $$\mathcal{D}_{aug} = \mathcal{D}_{orig} \cup \mathcal{T}_1(\mathcal{D}) \cup \mathcal{T}_2(\mathcal{D}) \cup ...$$ 其中 $\mathcal{T}_i$ 是不同的增强变换


3. 分布偏移问题与DAgger算法

3.1 协变量偏移的本质

行为克隆的核心问题是训练时和测试时的状态分布不匹配。这种分布偏移(distribution shift)会导致误差累积和性能退化。

问题的数学描述

  • 训练分布:$s \sim d_{\pi^*}(s)$ (专家策略诱导的状态分布)
  • 测试分布:$s \sim d_{\pi_\theta}(s)$ (学习策略诱导的状态分布)
  • 当 $\pi_\theta \neq \pi^*$ 时,$d_{\pi_\theta} \neq d_{\pi^*}$

误差累积分析: 假设单步预测误差为 $\epsilon$,在长度为 $T$ 的轨迹中:

  • 理想情况:总误差 $O(\epsilon T)$
  • 实际情况:总误差 $O(\epsilon T^2)$ (由于复合误差)

这种二次增长说明了为什么即使很小的预测误差也会导致长期任务失败。

可视化分布偏移

专家轨迹:  s₀ → s₁ → s₂ → s₃ → ... → sₜ
            ↓    ↓    ↓    ↓         ↓
专家动作:  a₀*  a₁*  a₂*  a₃*  ...  aₜ*

学习轨迹:  s₀ → s₁' → s₂' → s₃' → ... → sₜ'
            ↓    ↓     ↓     ↓          ↓
预测动作:  â₀   â₁    â₂    â₃   ...   âₜ

偏差累积:  0 → δ₁ → δ₁+δ₂ → δ₁+δ₂+δ₃ → ...

3.2 DAgger算法原理

Dataset Aggregation (DAgger) 通过迭代收集数据来解决分布偏移问题:

算法流程

算法: DAgger
输入: 初始数据集 D₀ = {(s,a*)}从专家收集
      专家策略 π*
      迭代次数 N

1.  D₀ 上训练初始策略 π₁
2. for i = 1 to N:
   a. 用当前策略 πᵢ 收集轨迹
   b. 对轨迹中的每个状态 s,查询专家动作 a* = π*(s)
   c. 聚合数据:Dᵢ = Dᵢ₋₁  {(s, a*)}
   d.  Dᵢ 上重新训练策略 πᵢ₊₁

3. 返回最终策略 πₙ₊₁

关键改进

  1. 在线数据收集:使用学习策略访问的状态,而非专家状态
  2. 专家标注:为这些状态获取专家标签
  3. 数据聚合:混合历史数据防止遗忘

理论保证: DAgger 提供了误差界: $$J(\pi_{DAgger}) - J(\pi^*) \leq O(\epsilon T)$$ 相比行为克隆的 $O(\epsilon T^2)$,这是显著改进。

3.3 实践中的改进

SafeDAgger: 为了安全性,在危险状态下切换到专家控制:

def safe_dagger_step(state, learned_policy, expert_policy, safety_checker):
    if safety_checker.is_safe(state):
        action = learned_policy(state)
        label = expert_policy(state)  # 仍然记录专家标签
    else:
        action = expert_policy(state)  # 专家接管
        label = action
    return action, (state, label)

HG-DAgger (Human-Gated DAgger): 让人类专家决定何时介入:

  • 专家观察机器人执行
  • 当偏离期望行为时,专家接管控制
  • 减少标注成本,只在关键时刻查询

ThriftyDAgger: 选择性查询最有价值的状态:

  • 使用不确定性估计(如集成方差)
  • 优先查询高不确定性状态
  • 平衡探索和标注成本

EnsembleDAgger: 使用策略集成提高鲁棒性:

class EnsembleDAgger:
    def __init__(self, n_models=5):
        self.models = [create_model() for _ in range(n_models)]

    def predict(self, state):
        predictions = [m(state) for m in self.models]
        action = np.mean(predictions, axis=0)
        uncertainty = np.std(predictions, axis=0)
        return action, uncertainty

    def should_query_expert(self, uncertainty, threshold=0.1):
        return np.max(uncertainty) > threshold

4. 逆强化学习(IRL)原理

4.1 奖励函数学习

逆强化学习的核心思想是:专家的行为隐含了某个未知的奖励函数,通过观察专家演示来推断这个奖励函数。

问题形式化: 给定:

  • 专家演示轨迹 $\{\tau_1^*, \tau_2^*, ..., \tau_n^*\}$
  • MDP中除奖励外的所有要素 $(\mathcal{S}, \mathcal{A}, P, \gamma)$

目标:学习奖励函数 $R(s,a)$ 使得专家策略 $\pi^*$ 是最优的

基础IRL算法

1. 初始化奖励函数 R
2. 重复
   a. 用当前R求解最优策略 π
   b. 计算特征期望
      μ_π = E[Σ γ^t φ(s_t, a_t) | π]
      μ_E = E[Σ γ^t φ(s_t, a_t) | 专家演示]
   c. 更新奖励函数使 μ_π 接近 μ_E

3. 返回学习到的奖励函数 R

特征匹配原理: IRL假设奖励函数可以表示为特征的线性组合: $$R(s,a) = w^T \phi(s,a)$$ 专家策略应该最大化期望奖励: $$\pi^* = \arg\max_\pi \mathbb{E}_{\tau \sim \pi}[w^T \mu_\pi]$$

4.2 最大熵IRL

最大熵IRL (MaxEnt IRL) 解决了奖励函数的歧义性问题:

原理:在所有解释专家行为的奖励函数中,选择导致最大策略熵的那个。

概率模型: 轨迹的概率正比于其累积奖励: $$P(\tau) \propto \exp(R(\tau)) = \exp(\sum_t R(s_t, a_t))$$ 目标函数: 最大化对数似然: $$\mathcal{L} = \sum_i \log P(\tau_i^*) - \log Z$$ 其中 $Z = \sum_\tau \exp(R(\tau))$ 是配分函数。

软值迭代

def soft_value_iteration(R, gamma=0.99, threshold=1e-4):
    V = np.zeros(n_states)
    while True:
        V_new = soft_bellman_backup(V, R, gamma)
        if np.max(np.abs(V - V_new)) < threshold:
            break
        V = V_new

    # 计算软Q函数
    Q = R + gamma * expected_V(V)

    # 导出策略
    π = np.exp(Q - logsumexp(Q, axis=1, keepdims=True))
    return π, V, Q

def soft_bellman_backup(V, R, gamma):
    Q = R + gamma * V_next  # V_next是下一状态的值
    return logsumexp(Q, axis=1)  # 软最大值

4.3 深度IRL方法

神经网络奖励函数

class NeuralRewardFunction(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dims=[256, 256]):
        super().__init__()
        input_dim = state_dim + action_dim
        layers = []
        for h_dim in hidden_dims:
            layers.extend([
                nn.Linear(input_dim, h_dim),
                nn.ReLU(),
                nn.Dropout(0.1)
            ])
            input_dim = h_dim
        layers.append(nn.Linear(input_dim, 1))
        self.net = nn.Sequential(*layers)

    def forward(self, states, actions):
        x = torch.cat([states, actions], dim=-1)
        return self.net(x)

Guided Cost Learning (GCL): 结合采样和重要性权重的高效IRL算法:

  1. 采样阶段:从当前策略采样轨迹
  2. 重要性采样:计算轨迹权重 $$w_i = \frac{p(\tau_i | \text{optimal})}{p(\tau_i | \text{sample policy})}$$

  3. 奖励更新:最大化加权对数似然

对抗IRL (AIRL): 使用判别器区分专家和策略轨迹: $$D(s,a) = \frac{\exp(f_\theta(s,a))}{\exp(f_\theta(s,a)) + \pi(a|s)}$$ 其中 $f_\theta$ 是学习的奖励函数。


5. GAIL与对抗模仿学习

5.1 生成对抗框架

Generative Adversarial Imitation Learning (GAIL) 将模仿学习转化为生成对抗问题,避免了显式的奖励函数建模。

核心思想

  • 生成器:策略网络 $\pi_\theta$ 生成轨迹
  • 判别器:区分专家轨迹和策略轨迹
  • 通过对抗训练使策略生成的轨迹与专家轨迹无法区分

目标函数: $$\min_\theta \max_\omega \mathbb{E}_{\pi^*}[\log D_\omega(s,a)] + \mathbb{E}_{\pi_\theta}[\log(1-D_\omega(s,a))] - \lambda H(\pi_\theta)$$ 其中 $H(\pi_\theta)$ 是策略熵,用于鼓励探索。

与IRL的联系: GAIL可以看作是IRL和RL的组合,但跳过了显式的奖励函数学习:

传统IRL+RL: 专家演示 → 奖励函数 → 最优策略
GAIL:       专家演示 → 最优策略 (通过对抗学习)

5.2 判别器与生成器设计

判别器架构

class Discriminator(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dims=[256, 256]):
        super().__init__()
        input_dim = state_dim + action_dim
        layers = []
        for h_dim in hidden_dims:
            layers.extend([
                nn.Linear(input_dim, h_dim),
                nn.Tanh(),  # Tanh通常比ReLU更稳定
            ])
            input_dim = h_dim
        layers.append(nn.Linear(input_dim, 1))
        self.net = nn.Sequential(*layers)

    def forward(self, states, actions):
        return torch.sigmoid(self.net(torch.cat([states, actions], dim=-1)))

    def compute_reward(self, states, actions):
        # 用判别器输出作为奖励信号
        D = self.forward(states, actions)
        return torch.log(D + 1e-8) - torch.log(1 - D + 1e-8)

策略生成器: 使用任何策略梯度算法(PPO/TRPO/SAC),将判别器输出作为奖励:

class GAILTrainer:
    def __init__(self, policy, discriminator, expert_dataset):
        self.policy = policy
        self.discriminator = discriminator
        self.expert_dataset = expert_dataset
        self.policy_optimizer = PPO(policy)

    def train_step(self):
        # 1. 收集策略轨迹
        policy_batch = self.collect_trajectories(self.policy)

        # 2. 采样专家数据
        expert_batch = self.expert_dataset.sample(len(policy_batch))

        # 3. 更新判别器
        self.update_discriminator(expert_batch, policy_batch)

        # 4. 计算策略奖励
        rewards = self.discriminator.compute_reward(
            policy_batch.states, 
            policy_batch.actions
        )

        # 5. 更新策略
        self.policy_optimizer.update(policy_batch, rewards)

5.3 训练稳定性技巧

GAIL训练容易不稳定,需要特殊技巧:

1. 梯度惩罚 (WGAN-GP): $$\mathcal{L}_D = -\mathbb{E}_{expert}[D] + \mathbb{E}_{policy}[D] + \lambda \mathbb{E}_{\hat{x}}[(||\nabla_{\hat{x}}D||_2 - 1)^2]$$ 其中 $\hat{x}$ 是专家和策略数据的插值。

2. 谱归一化: 限制判别器的Lipschitz常数:

from torch.nn.utils import spectral_norm

class SpectralNormDiscriminator(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            spectral_norm(nn.Linear(state_dim + action_dim, 256)),
            nn.Tanh(),
            spectral_norm(nn.Linear(256, 256)),
            nn.Tanh(),
            spectral_norm(nn.Linear(256, 1))
        )

3. 缓冲区重放: 维护历史策略数据缓冲区,防止判别器过拟合当前策略:

class ReplayBuffer:
    def __init__(self, capacity=10000):
        self.buffer = deque(maxlen=capacity)

    def add(self, trajectories):
        self.buffer.extend(trajectories)

    def sample(self, batch_size):
        return random.sample(self.buffer, batch_size)

4. 学习率调度

# 判别器学习率应该较小
d_lr = 3e-4
g_lr = 3e-4

# 使用不同的更新频率
d_updates_per_g_update = 5  # 判别器更新更频繁

变体算法

InfoGAIL: 加入互信息最大化,学习可解释的潜在码: $$\mathcal{L} = \mathcal{L}_{GAIL} + \lambda I(c; \tau)$$ SQIL (Soft Q Imitation Learning): 简化的离线模仿学习方法:

  • 专家数据奖励设为 r=1
  • 策略数据奖励设为 r=0
  • 直接用SAC/TD3等离线RL算法训练

案例研究:特斯拉FSD的模仿学习架构

系统架构概述

特斯拉FSD (Full Self-Driving) 系统是工业界最大规模的模仿学习部署之一。其核心是从百万级人类驾驶数据中学习驾驶策略。

数据规模

  • 数据来源:全球超过100万辆特斯拉车辆
  • 数据量:每天处理10PB+的驾驶数据
  • 场景覆盖:各种天气、路况、交通状况

系统架构

传感器输入 → 感知网络 → 特征融合 → 策略网络 → 控制输出
     ↑                      ↑
  8个相机              BEV特征空间

关键技术特点

1. 大规模数据收集系统: - 影子模式(Shadow Mode):在人类驾驶时运行神经网络,记录预测与实际的差异 - 触发式收集:当检测到有趣或困难场景时自动上传数据 - 自动标注:利用未来帧信息自动生成训练标签

2. 多任务学习架构

# 伪代码:多任务策略网络
class FSDPolicyNetwork(nn.Module):
    def __init__(self):
        self.backbone = VisionTransformer()
        self.bev_decoder = BEVDecoder()

        # 多个输出头
        self.trajectory_head = TrajectoryHead()
        self.speed_head = SpeedHead()  
        self.lane_change_head = LaneChangeHead()
        self.traffic_light_head = TrafficLightHead()

    def forward(self, camera_inputs):
        features = self.backbone(camera_inputs)
        bev_features = self.bev_decoder(features)

        outputs = {
            'trajectory': self.trajectory_head(bev_features),
            'speed': self.speed_head(bev_features),
            'lane_change': self.lane_change_head(bev_features),
            'traffic_light': self.traffic_light_head(bev_features)
        }
        return outputs

3. 端到端学习vs模块化设计: - 早期版本:模块化(感知→规划→控制) - 当前趋势:端到端(传感器→动作) - 混合方案:端到端主干+安全约束模块

处理分布偏移的策略

1. 主动学习循环

部署 → 收集困难案例 → 人工标注 → 重训练 → 验证 → 部署
         ↑                                    ↓
         ←────────────────────────────────────

2. 对抗样本挖掘: - 识别模型失败的场景 - 生成相似的合成场景 - 增强训练数据多样性

3. 在线适应: - 车载模型微调(受限于算力) - 个性化驾驶风格学习 - 持续学习防止灾难性遗忘

工程挑战与解决方案

延迟优化

  • 模型量化:FP32→INT8
  • 模型剪枝:去除冗余连接
  • 知识蒸馏:大模型→小模型

安全保障

  • 多重冗余:多个独立模型投票
  • 规则约束:硬编码安全规则
  • 降级策略:检测到异常时平滑降级

高级话题:离线强化学习与保守Q学习(CQL)

离线强化学习的动机

离线强化学习从固定数据集学习,不需要与环境交互,这对机器人应用特别重要:

  • 安全性:避免危险的探索
  • 数据利用:充分利用历史数据
  • 计算效率:可以离线批量训练

核心挑战

  • 外推误差:Q值在未见过的状态-动作对上的过估计
  • 分布偏移:数据分布与策略分布不匹配
  • 有限覆盖:数据可能不覆盖所有重要区域

保守Q学习(CQL)原理

CQL通过学习Q函数的下界来避免过估计问题:

CQL损失函数: $$\mathcal{L}_{CQL}(\theta) = \alpha \mathbb{E}_{s \sim \mathcal{D}}[\log \sum_a \exp(Q_\theta(s,a)) - \mathbb{E}_{a \sim \pi_\beta(a|s)}[Q_\theta(s,a)]] + \mathcal{L}_{SAC}(\theta)$$

其中:

  • 第一项:推高所有动作的Q值
  • 第二项:降低数据集中动作的Q值
  • 结果:对OOD(out-of-distribution)动作保守估计

实现细节

class CQL:
    def __init__(self, q_network, policy, alpha=1.0):
        self.q_network = q_network
        self.policy = policy
        self.alpha = alpha

    def compute_cql_loss(self, states, actions, rewards, next_states, dones):
        # 标准TD损失
        q_values = self.q_network(states, actions)
        with torch.no_grad():
            next_q = self.compute_target_q(next_states)
            target_q = rewards + (1 - dones) * self.gamma * next_q
        td_loss = F.mse_loss(q_values, target_q)

        # CQL正则化项
        # 计算log-sum-exp over actions
        num_samples = 10
        random_actions = torch.rand(states.shape[0], num_samples, self.action_dim)
        random_q = self.q_network(states.unsqueeze(1), random_actions)
        logsumexp_q = torch.logsumexp(random_q, dim=1)

        # 数据集动作的Q值
        dataset_q = self.q_network(states, actions)

        # CQL损失
        cql_loss = (logsumexp_q - dataset_q).mean()

        return td_loss + self.alpha * cql_loss

离线模仿学习与离线RL的结合

IQL (Implicit Q-Learning): 避免显式策略改进,直接从数据学习:

def iql_loss(q, v, states, actions, rewards, next_states):
    # 学习V函数(期望Q值的分位数)
    with torch.no_grad():
        target_q = q(states, actions)
    v_loss = expectile_loss(v(states), target_q, tau=0.7)

    # 学习Q函数
    with torch.no_grad():
        next_v = v(next_states)
    target = rewards + gamma * next_v
    q_loss = F.mse_loss(q(states, actions), target)

    # 通过advantage加权行为克隆学习策略
    with torch.no_grad():
        advantage = q(states, actions) - v(states)
        weight = torch.exp(advantage / beta)
    policy_loss = -weight * log_prob(actions).mean()

    return q_loss + v_loss + policy_loss

实践考虑

数据质量评估

def assess_dataset_quality(dataset):
    metrics = {
        'coverage': compute_state_coverage(dataset),
        'return_distribution': compute_return_stats(dataset),
        'trajectory_length': compute_trajectory_lengths(dataset),
        'action_diversity': compute_action_entropy(dataset)
    }
    return metrics

混合在线/离线学习

  1. 先用离线数据预训练
  2. 在线微调with安全约束
  3. 定期更新离线数据集

本章小结

本章系统介绍了模仿学习的核心方法和实践技术:

关键概念回顾

  1. 行为克隆:将模仿学习转化为监督学习,简单有效但存在分布偏移问题
  2. DAgger算法:通过迭代数据收集解决分布偏移,提供线性误差界
  3. 逆强化学习:从演示中学习奖励函数,理解专家的潜在目标
  4. GAIL:通过对抗学习直接学习策略,避免显式奖励建模
  5. 离线强化学习:从固定数据集学习,CQL通过保守估计避免外推误差

核心公式汇总

  • 行为克隆损失:$\mathcal{L}(\theta) = \mathbb{E}_{(s,a) \sim \mathcal{D}}[\ell(\pi_\theta(s), a)]$
  • DAgger误差界:$J(\pi_{DAgger}) - J(\pi^*) \leq O(\epsilon T)$
  • 最大熵IRL:$P(\tau) \propto \exp(\sum_t R(s_t, a_t))$
  • GAIL目标:$\min_\theta \max_\omega \mathbb{E}_{\pi^*}[\log D_\omega] + \mathbb{E}_{\pi_\theta}[\log(1-D_\omega)]$
  • CQL正则化:$\alpha[\log \sum_a \exp Q(s,a) - \mathbb{E}_{a \sim \pi_\beta}[Q(s,a)]]$

方法选择指南

  • 数据充足+任务简单 → 行为克隆
  • 可以查询专家 → DAgger
  • 需要理解意图 → IRL
  • 大规模复杂任务 → GAIL
  • 安全性要求高 → 离线RL(CQL/IQL)

练习题

基础题

练习15.1:分布偏移的影响 给定一个简单的1D导航任务,专家策略是 $a^* = -\text{sign}(s)$(向原点移动)。如果行为克隆的策略有误差 $\hat{a} = a^* + \epsilon$,其中 $\epsilon \sim \mathcal{N}(0, 0.01)$,计算T=100步后的期望位置偏差。

提示

考虑误差的累积效应,每步的误差会影响下一步的状态。

答案

误差累积导致期望偏差为 $O(\epsilon \sqrt{T}) \approx 0.1 \times \sqrt{100} = 1.0$。这展示了即使小误差也会随时间累积。

练习15.2:DAgger数据聚合 假设初始数据集有1000个专家样本,每轮DAgger收集500个新样本。如果使用0.5的混合比例(50%新数据,50%历史数据),第3轮训练时的有效数据集大小是多少?

提示

考虑数据聚合策略和采样比例。

答案

总数据量:1000 + 500×3 = 2500个样本。使用50-50混合,有效训练集包含1250个历史样本和1250个最新样本的混合。

练习15.3:GAIL判别器输出解释 如果GAIL的判别器对专家数据输出0.9,对策略数据输出0.3,这意味着什么?应该如何调整训练?

提示

判别器输出表示样本来自专家的概率。

答案

判别器能够轻易区分专家(0.9)和策略(0.3)数据,说明策略还需要改进。应该:1)增加策略更新步数,2)检查是否存在模式崩塌,3)可能需要调整学习率。

挑战题

练习15.4:多模态动作处理 设计一个混合密度网络(MDN)来处理十字路口左转/直行的多模态决策。网络应该输出什么?如何从输出中采样动作?

提示

MDN输出多个高斯分量的参数:权重、均值和方差。

答案

网络输出:K个分量的 {πₖ(权重), μₖ(均值), σₖ(标准差)}。采样过程:1)根据权重πₖ采样分量索引k,2)从N(μₖ, σₖ²)采样动作。对于左转/直行,K=2通常足够。

练习15.5:IRL奖励函数设计 给定抓取任务的专家演示,设计一个包含以下特征的奖励函数:距离物体、夹爪开合度、末端速度。如何确定特征权重?

提示

使用最大熵IRL框架,通过特征匹配学习权重。

答案

奖励函数:R(s,a) = w₁·(-dist) + w₂·grip_match + w₃·(-velocity)。通过最大熵IRL学习权重:1)计算专家特征期望,2)迭代优化权重使策略特征期望匹配专家,3)使用软值迭代求解策略。

练习15.6:离线RL数据集诊断 给定一个机器人操作数据集,设计一个诊断流程来评估其是否适合离线RL训练。应该检查哪些指标?

提示

考虑覆盖度、质量、多样性等多个维度。

答案

诊断流程:1)状态覆盖度(使用KDE估计),2)回报分布(检查是否有高质量演示),3)动作多样性(计算熵),4)轨迹完整性(检查截断),5)OOD检测(训练VAE检测异常)。根据这些指标决定是否需要数据增强或收集更多数据。

练习15.7:安全DAgger实现 设计一个安全的DAgger变体用于真实机器人,要求:1)防止危险动作,2)最小化人工干预,3)保证学习效率。

提示

结合安全约束、不确定性估计和主动学习。

答案

实现方案:1)安全层:使用CBF(控制屏障函数)过滤危险动作,2)不确定性触发:只在模型不确定时查询专家(使用集成方差),3)优先级队列:根据(不确定性×任务重要性)排序查询,4)回滚机制:检测到异常立即切换专家控制。

练习15.8:GAIL训练调试 你的GAIL训练出现模式崩塌(所有轨迹相似)。列出可能的原因和对应的解决方案。

提示

考虑判别器过强、探索不足、奖励信号等因素。

答案

原因及解决方案:1)判别器过强→降低判别器学习率/容量,2)探索不足→增加策略熵正则化,3)奖励信号退化→使用梯度惩罚或谱归一化,4)数据不平衡→使用重放缓冲区,5)初始化不当→使用行为克隆预训练策略。


常见陷阱与错误

1. 数据收集陷阱

错误:只收集成功轨迹

# 错误:偏向性数据收集
if task_successful:
    dataset.add(trajectory)  # 只记录成功案例

正确:包含失败和恢复

# 正确:全面数据收集
dataset.add(trajectory)
if not task_successful:
    dataset.add_recovery_demo()  # 添加恢复演示

2. 时序忽视

错误:独立处理每个时间步

# 错误:忽略历史
action = policy(current_state)

正确:考虑时序依赖

# 正确:包含历史
action = policy(current_state, history[-k:])

3. 分布偏移处理不当

错误:训练后直接部署

# 错误:忽视分布偏移
model.eval()
deploy_to_robot(model)  # 危险!

正确:渐进式部署

# 正确:安全部署
for confidence_threshold in [0.9, 0.7, 0.5]:
    deploy_with_safety_net(model, confidence_threshold)
    collect_failure_cases()
    retrain_model()

4. GAIL训练不稳定

错误:固定超参数

# 错误:不adaptive
d_optimizer = Adam(lr=1e-3)
g_optimizer = Adam(lr=1e-3)

正确:自适应调整

# 正确:动态调整
if discriminator_accuracy > 0.8:
    d_lr *= 0.5  # 降低判别器学习率
if generator_loss > threshold:
    g_lr *= 1.1  # 提高生成器学习率

5. 离线RL外推问题

错误:直接最大化Q值

# 错误:Q值过估计
action = argmax(Q(state, a) for a in action_space)

正确:保守估计

# 正确:CQL保守策略
action = sample_from_policy(state)
if Q(state, action) < conservative_threshold:
    action = closest_dataset_action(state)

最佳实践检查清单

数据收集阶段

  • [ ] 数据集包含多样化的场景和条件
  • [ ] 包含失败案例和恢复演示
  • [ ] 多模态数据正确同步(时间戳对齐)
  • [ ] 数据质量指标计算完成
  • [ ] 隐私和安全考虑已处理

模型设计阶段

  • [ ] 选择适合任务复杂度的架构
  • [ ] 考虑了多模态动作的可能性
  • [ ] 包含适当的正则化(dropout, weight decay)
  • [ ] 设计了合理的损失函数
  • [ ] 考虑了时序依赖性

训练阶段

  • [ ] 实现了数据增强策略
  • [ ] 监控训练/验证损失曲线
  • [ ] 检查分布偏移指标
  • [ ] 定期保存检查点
  • [ ] 实现了早停机制

评估阶段

  • [ ] 在多个测试场景评估
  • [ ] 计算任务成功率和效率指标
  • [ ] 分析失败模式
  • [ ] 与基线方法对比
  • [ ] 进行消融实验

部署阶段

  • [ ] 实现安全约束和失效保护
  • [ ] 设置置信度阈值
  • [ ] 准备回滚方案
  • [ ] 建立监控和日志系统
  • [ ] 制定增量部署计划

持续改进

  • [ ] 建立数据飞轮(部署→收集→改进)
  • [ ] 实现在线适应机制
  • [ ] 定期重新评估和更新
  • [ ] 记录和分析边缘案例
  • [ ] 保持与最新研究同步