☰
元强化学习核心方法解析:MAML、RL²与PEARL实战指南
2026/10/14 10:51:19 网站建设 项目流程

1. 元学习到底在解决什么问题

1.1 从“一个任务一个模型”说起

如果你跟着CS285的课程一路学下来,到Lec18这里应该已经积累了不少强化学习的工具:策略梯度、Actor-Critic、Q-Learning、模型基方法等等。这些方法在单一任务上都能跑出不错的效果,但有一个共同的隐含前提——训练和测试是在同一个任务分布下进行的。换句话说,你训练一个机械臂抓取特定形状的物体,训练完之后它只会抓那个形状,换一个形状就得重新训练。

这个痛点在实际场景里非常致命。机器人要适应不同的地面摩擦力、不同的负载重量、不同的传感器噪声;推荐系统要适应不同用户的偏好分布;自动驾驶要适应不同城市的路况风格。如果每个新任务都从头训练一遍,样本效率和时间成本根本扛不住。

元学习(Meta-Learning)要解决的就是这个问题:让模型学会“如何快速学习”。不是学某一个具体任务,而是学一个“学习算法”或者“初始化参数”,使得面对新任务时,只需要少量样本和少量梯度步就能达到不错的性能。这个思路在监督学习里叫Few-Shot Learning,在强化学习里就是Meta-RL。

1.2 Meta-RL和普通RL的本质区别

普通RL的目标是最大化单个任务的期望回报:

$$\theta^* = \arg\max_\theta J(\theta)$$

Meta-RL的目标则是在一个任务分布 $p(\mathcal{T})$ 上,学到一个适应算法 $f$,使得:

$$\min_\theta \mathbb{E}{\mathcal{T} \sim p(\mathcal{T})} \left[ \mathcal{L}(\mathcal{T}, f\theta(\mathcal{T})) \right]$$

注意这里的 $f_\theta(\mathcal{T})$ 不是直接输出策略,而是输出一个针对该任务适应后的策略。这个适应过程本身可能只用了任务 $\mathcal{T}$ 的少量轨迹。

我个人的理解是:普通RL学的是“解题”,Meta-RL学的是“解题方法”。前者给你一道题的答案,后者给你一本参考书,让你遇到新题时能快速翻到对应章节。

1.3 为什么RL里的元学习比监督学习更难

监督学习里的元学习(比如MAML)已经够复杂了,但Meta-RL还要额外面对几个挑战:

  • 信用分配跨任务:在监督学习里,损失函数直接告诉你预测对不对。在RL里,你只有稀疏的奖励信号,而且奖励还延迟。要搞清楚“哪个适应步骤导致了后来的好结果”非常困难。
  • 非平稳性:策略更新会改变数据分布,元学习的外循环和内循环互相影响,训练极不稳定。
  • 任务分布的设计:监督学习里任务就是不同的分类问题,定义清晰。RL里“任务”可能意味着不同的奖励函数、不同的动力学、不同的初始状态分布,怎么设计任务分布本身就是一门学问。
  • 样本效率的双重压力:内循环要少样本,外循环也要少样本,两头都要省,难度指数级上升。

Lec18主要讲了两大类方法:基于优化的方法(以MAML为代表)和基于上下文的方法(以RL²为代表)。下面我逐个拆解。

2. 基于优化的Meta-RL:MAML及其变体

2.1 MAML的核心思想:学一个好的初始化

MAML(Model-Agnostic Meta-Learning)的思路非常优雅:不学一个能直接解决所有任务的模型,而是学一个“起点”,从这个起点出发,任何任务只需要几步梯度下降就能收敛到好解。

用数学表达就是:

$$\theta^* = \arg\min_\theta \sum_{\mathcal{T}i \sim p(\mathcal{T})} \mathcal{L}{\mathcal{T}i}(\theta - \alpha \nabla\theta \mathcal{L}_{\mathcal{T}_i}(\theta))$$

这里 $\theta$ 是元参数(初始化),$\alpha$ 是内循环学习率,$\mathcal{L}_{\mathcal{T}_i}$ 是任务 $\mathcal{T}_i$ 上的损失。内循环用任务数据做一步或多步梯度更新得到 $\theta_i'$,外循环优化初始参数 $\theta$ 使得所有任务适应后的损失之和最小。

这个式子里最妙的地方在于:外循环的梯度要穿过内循环的更新步骤。也就是说,你要对“梯度更新后的参数”再求梯度。这需要二阶导数,计算成本很高。

2.2 一阶近似:FOMAML和Reptile

为了解决二阶导的计算问题,有两个经典的近似方案:

FOMAML(First-Order MAML)直接忽略二阶项,把内循环更新后的梯度当作常数。具体来说,外循环梯度近似为:

$$\nabla_\theta \mathcal{L}{\mathcal{T}i}(\theta_i') \approx \nabla{\theta_i'} \mathcal{L}{\mathcal{T}_i}(\theta_i')$$

也就是说,只取适应后参数处的梯度,不回传通过内循环更新。这样做计算量大幅降低,效果在多数任务上损失不大。

Reptile更简单粗暴:它不做任何梯度展开,直接把初始参数往适应后的参数方向拉:

$$\theta \leftarrow \theta + \beta (\theta_i' - \theta)$$

你可以理解为:对每个任务跑几步SGD,然后让初始参数朝着这些适应后参数的平均方向移动。Reptile的收敛性分析表明它实际上在优化一个与MAML类似的目标,但实现起来只需要一阶信息。

我实测下来的感受是:如果任务之间差异不大,Reptile和MAML效果差不多;如果任务差异很大,MAML的二阶信息确实有帮助,但训练时间可能翻倍。所以选哪个取决于你的算力预算和任务分布。

2.3 MAML在RL里的具体实现

把MAML用到RL上,需要把“损失”替换成策略梯度目标。具体流程如下:

  1. 从任务分布中采样一批任务 ${\mathcal{T}_i}$
  2. 对每个任务,用当前策略 $\pi_\theta$ 采集少量轨迹(比如K条)
  3. 计算策略梯度 $\nabla_\theta J_{\mathcal{T}_i}(\theta)$
  4. 用梯度更新得到任务特定参数 $\theta_i' = \theta + \alpha \nabla_\theta J_{\mathcal{T}_i}(\theta)$
  5. 用 $\theta_i'$ 再采集少量轨迹,计算适应后的性能
  6. 外循环更新:$\theta \leftarrow \theta - \beta \nabla_\theta \sum_i J_{\mathcal{T}_i}(\theta_i')$

这里有个关键细节:内循环和外循环用的数据必须分开。内循环用support set(少量轨迹)做适应,外循环用query set(另一批轨迹)评估适应效果。如果混用,元学习会过拟合到support set上,新任务上表现很差。

另一个坑是策略梯度的方差。RL的梯度估计本身方差就大,元学习里还要在任务间求平均,方差会进一步放大。常见的做法是:

  • 用PPO或TRPO这类相对稳定的算法作为内循环更新
  • 增加每个任务的轨迹数量
  • 使用基线(baseline)或优势函数(advantage)降低方差
  • 对外循环梯度做裁剪(gradient clipping)

2.4 实操中的参数选择

以下是我在复现MAML-RL时总结的参数经验:

参数典型值说明
内循环步数1-5步数越多,适应能力越强,但二阶导计算越贵
内循环学习率 α0.01-0.1太大会发散,太小适应不够
外循环学习率 β0.001-0.01通常比α小一个量级
每任务轨迹数(内)5-20取决于任务复杂度
每任务轨迹数(外)10-40要比内循环多,保证评估可靠
任务batch size4-16太小梯度噪声大,太大显存吃紧

注意:内循环学习率α不能设得太大,否则适应后的参数会跑到策略空间的很远的地方,外循环梯度会爆炸。我一般从0.01开始试,观察训练曲线再调整。

3. 基于上下文的Meta-RL:RL²与变分方法

3.1 RL²:把适应过程塞进RNN

MAML的思路是“学一个好的起点”,RL²(RL平方)的思路完全不同:把整个学习过程建模成一个序列决策问题,用RNN来记住任务信息。

具体来说,RL²把智能体的历史(状态、动作、奖励)作为RNN的输入,RNN的隐藏状态编码了“当前任务是什么”的信息。策略输出不仅依赖当前状态,还依赖RNN的隐藏状态:

$$a_t \sim \pi(a_t | s_t, h_t), \quad h_t = \text{RNN}(h_{t-1}, s_{t-1}, a_{t-1}, r_{t-1})$$

训练时,每个episode对应一个任务,RNN在episode内部逐步积累任务信息。经过大量任务的训练后,RNN学会了“从少量交互中推断任务”的能力。

这个方法的优雅之处在于:不需要二阶导数,不需要显式的内循环。整个适应过程隐式地发生在RNN的前向传播中。但代价是:RNN的隐藏状态容量有限,任务信息必须压缩在这个固定维度的向量里。

3.2 RL²的训练细节与坑

RL²的训练有几个容易踩坑的地方:

第一,episode长度和任务切换。每个episode必须对应一个固定的任务,episode结束时重置RNN隐藏状态。如果任务在episode中间切换,RNN会学到错误的关联。

第二,奖励的尺度。RNN对奖励的绝对值很敏感。如果不同任务的奖励尺度差异很大,RNN很难区分“任务本身奖励高”和“策略表现好”。常见的做法是对奖励做归一化,或者用奖励的排名而不是绝对值。

第三,探索不足。RL²的策略在早期训练时随机性很强,可能采集不到足够的信息来推断任务。可以在训练初期加入额外的探索噪声,或者用熵正则化鼓励探索。

第四,梯度爆炸。RNN在长序列上训练容易梯度爆炸。用LSTM或GRU可以缓解,但episode太长时仍然有问题。实践中通常把episode长度限制在100-200步。

3.3 变分Meta-RL:PEARL

PEARL(Probabilistic Embeddings for Actor-critic RL)是近年来比较有代表性的变分方法。它的核心思想是:把任务编码成一个隐变量 $z$,用变分推断来学习 $z$ 的后验分布。

具体来说,PEARL有一个编码器 $q_\phi(z | \mathcal{D})$,输入是任务的一些轨迹 $\mathcal{D}$,输出是隐变量 $z$ 的分布。策略 $\pi_\theta(a | s, z)$ 以 $z$ 为条件。训练时优化ELBO(证据下界):

$$\mathcal{L} = \mathbb{E}{z \sim q\phi} \left[ \sum_t \log \pi_\theta(a_t | s_t, z) + \beta \log p(z) - \beta \log q_\phi(z | \mathcal{D}) \right]$$

PEARL的优势在于:离线适应。你可以在不与环境交互的情况下,用少量轨迹推断出 $z$,然后直接部署策略。这在真实机器人场景里非常实用,因为交互成本很高。

3.4 方法对比:MAML vs RL² vs PEARL

维度MAMLRL²PEARL
适应方式梯度更新RNN前向隐变量推断
是否需要二阶导是(或近似)否否
适应样本量少(1-5条轨迹)中(需要足够交互)少(可离线)
任务表示参数空间隐藏状态隐变量分布
训练稳定性中低中高
实现难度中低高
适合场景任务差异中等任务差异大、序列决策交互成本高

我个人的选择逻辑是:如果环境交互便宜、任务差异大,优先试RL²;如果交互贵、需要离线适应,选PEARL;如果任务差异中等、算力充足,MAML是稳妥选择。

4. 任务分布设计与评估方法

4.1 任务分布怎么设计才合理

Meta-RL的效果高度依赖任务分布的设计。如果任务之间差异太小,元学习学不到有用的适应能力;差异太大,又很难找到一个通用的初始化或编码。

常见的任务分布设计维度包括:

  • 奖励函数变化:比如目标位置不同、奖励权重不同
  • 动力学变化:比如摩擦系数、质量、延迟不同
  • 观测变化:比如传感器噪声、观测维度不同
  • 初始状态分布变化:比如起始位置、初始速度不同

我的经验是:从单一维度开始,逐步增加变化维度。比如先只改变目标位置,确认元学习有效后再加入动力学变化。一次性加入太多变化维度,训练会非常困难。

另外,任务分布要有平滑性。如果任务之间的差异是突变的(比如从“走到A点”突然变成“走到B点且地面摩擦翻倍”),元学习很难泛化。理想情况下,任务分布应该是一个连续变化的流形。

4.2 评估Meta-RL的正确姿势

评估Meta-RL模型时,有几个常见的错误:

错误一:在训练任务上评估。这相当于在训练集上测准确率,没有意义。必须留出一批未见过的任务做测试。

错误二:适应样本量不一致。训练时用5条轨迹适应,测试时也用5条,不能多也不能少。否则比较不公平。

错误三:忽略随机性。Meta-RL的评估方差很大,至少跑3-5个随机种子,报告均值和标准差。

错误四:只看最终性能。还要看适应曲线——随着适应样本增加,性能如何变化。好的元学习模型应该在前几条轨迹就快速提升。

我通常用以下指标:

  • 适应后性能:用K条轨迹适应后的平均回报
  • 适应效率:达到某个性能阈值所需的轨迹数
  • 零样本性能:不适应直接部署的性能(衡量初始化的质量)
  • 任务间方差:不同任务上性能的标准差(衡量泛化能力)

4.3 训练不稳定的排查思路

Meta-RL训练不稳定是常态,不是例外。我总结了一套排查流程:

  1. 先检查内循环:单独跑内循环,看单任务适应是否正常。如果内循环都跑不好,外循环肯定崩。
  2. 检查梯度范数:外循环梯度如果爆炸或消失,说明内循环学习率或步数有问题。
  3. 检查任务batch:任务太少会导致外循环梯度噪声大,尝试增大batch size。
  4. 检查数据划分:确认support set和query set没有重叠。
  5. 降低内循环步数:从1步开始,逐步增加,观察训练稳定性。
  6. 换一阶近似:如果二阶导导致不稳定,先换FOMAML或Reptile验证思路。

我踩过最大的坑是:内循环用了太多步(10步),外循环梯度穿过这些步之后变得极其不稳定,训练loss来回震荡。后来降到3步,立刻稳定了。所以内循环步数不是越多越好。

5. 代码实现关键片段与调试技巧

5.1 MAML内循环的实现

以下是一个简化的MAML内循环实现(PyTorch风格):

def inner_loop(policy, task, inner_lr, inner_steps): # 复制一份参数,不影响原始参数 adapted_params = {k: v.clone() for k, v in policy.named_parameters()} for step in range(inner_steps): # 用当前参数采集轨迹 trajectories = collect_trajectories(policy, task, adapted_params) # 计算策略梯度损失 loss = compute_policy_loss(trajectories, adapted_params) # 计算梯度 grads = torch.autograd.grad(loss, adapted_params.values(), create_graph=True) # 手动更新参数 adapted_params = { k: v - inner_lr * g for (k, v), g in zip(adapted_params.items(), grads) } return adapted_params

关键点在于create_graph=True,这样外循环才能对更新后的参数求导。如果不需要二阶导,设为False即可。

5.2 外循环的梯度处理

外循环更新时,需要对所有任务的适应后损失求和再求导:

meta_loss = 0 for task in task_batch: adapted_params = inner_loop(policy, task, inner_lr, inner_steps) # 用适应后参数在query set上评估 query_loss = compute_policy_loss(query_data[task], adapted_params) meta_loss += query_loss meta_loss /= len(task_batch) meta_optimizer.zero_grad() meta_loss.backward() torch.nn.utils.clip_grad_norm_(policy.parameters(), max_norm=1.0) meta_optimizer.step()

梯度裁剪是必须的,否则外循环梯度很容易爆炸。

5.3 RL²的RNN实现要点

RL²的RNN部分需要注意:

class RL2Policy(nn.Module): def __init__(self, obs_dim, act_dim, hidden_dim): super().__init__() self.rnn = nn.LSTM(obs_dim + act_dim + 1, hidden_dim, batch_first=True) self.actor = nn.Linear(hidden_dim, act_dim) self.critic = nn.Linear(hidden_dim, 1) def forward(self, obs, act, rew, hidden=None): # 拼接观测、动作、奖励作为RNN输入 x = torch.cat([obs, act, rew], dim=-1) out, hidden = self.rnn(x, hidden) return self.actor(out), self.critic(out), hidden

训练时,每个episode开始时重置hidden状态。如果batch里有多个episode,需要分别处理。

5.4 调试Meta-RL的实用技巧

  • 先用小任务验证:比如2D导航任务,任务就是目标位置不同。确认元学习有效后再上复杂环境。
  • 可视化适应曲线:画出“适应步数 vs 性能”的曲线,直观判断适应效率。
  • 检查任务编码:如果是PEARL或RL²,可视化隐变量或隐藏状态,看是否真的编码了任务信息。
  • 对比随机初始化:元学习后的初始化应该显著优于随机初始化,否则元学习没学到东西。
  • 监控内循环梯度:内循环梯度如果太小,适应没效果;太大,外循环不稳定。

6. 常见问题与排查速查表

问题可能原因解决方法
外循环loss震荡内循环步数太多、学习率太大减少步数、降低α
新任务上性能差过拟合support set增加query set、正则化
训练初期就发散梯度爆炸梯度裁剪、降低β
适应后性能没提升内循环学习率太小增大α、增加内循环步数
RNN记不住任务隐藏状态太小、episode太短增大hidden dim、延长episode
任务间方差大任务分布太宽缩小任务变化范围
计算太慢二阶导开销大换FOMAML或Reptile
显存不够任务batch太大减小batch、用梯度累积

最后分享一个我常用的调试策略:先固定任务分布,只跑一个任务,确认内循环能正常适应。然后逐步增加任务数量,观察外循环是否稳定。最后再扩大任务分布的变化范围。这个渐进式调试方法能帮你快速定位问题出在哪一层。

7. 我个人在实际操作中的体会

Meta-RL这个方向,论文看起来都很漂亮,但真正复现的时候坑非常多。我最大的体会是:不要一上来就追求SOTA,先把最简单的MAML在2D导航上跑通,理解每一步在做什么,再逐步加复杂度。

另外,任务分布的设计比算法选择更重要。我见过太多人花大量时间调算法,结果任务分布设计得不合理,怎么调都上不去。花时间分析你的任务空间,确保任务之间有共享结构,同时又有足够的多样性,这比换算法收益大得多。

还有一个反直觉的经验:内循环步数少一点往往更好。MAML原论文用1-5步,我试过10步以上,外循环梯度变得极其不稳定,而且适应后的策略容易过拟合到support set。1-3步通常就够了,剩下的适应能力交给外循环学到的初始化。

最后,Meta-RL的训练时间通常是普通RL的5-10倍,做好心理准备。如果算力有限,优先考虑Reptile或RL²这类一阶方法,效果差距没有想象中那么大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询