元学习的目标是让模型“学会学习”,在少量样本的新任务上快速适应。然而在实践里,一个常见的尴尬是:模型在训练任务集上表现惊艳,一旦换成分布略有差异的新任务或新域,准确率立刻塌方。这种现象被称为元过拟合(meta-overfitting),它和普通的过拟合不太一样——问题不出在样本层面,而出在任务分布层面。本文围绕这一问题展开,重点讨论域泛化与域适应两条技术路线,并给出可落地的实现思路。

元过拟合的根源在哪里
要解决问题,先得把病因看清楚。元学习的训练通常分为内循环和外循环:内循环在单个任务的支撑集上做快速适应,外循环根据查询集的表现更新元参数。过拟合往往发生在两个层面。
第一个层面是任务分布覆盖不足。MAML及其变体在训练时假设任务采样自同一个分布 p(T),如果训练任务的多样性不够,比如全部来自同一批基础类别、同一类图像风格,模型学到的初始参数就只会对这类任务“快”,遇到源域之外的查询分布,适应能力急剧衰减。研究表明,MiniImageNet上训练的MAML,直接迁移到CUB鸟类数据集时性能远低于重新训练,就是任务域偏窄的典型表现。
第二个层面是内外循环的优化冲突。内循环的梯度步长如果设置过大,模型容易在支撑集上走向记忆而非泛化,尤其是任务内类别数少、样本量小时,内循环更新可能直接把类别特征“刻”进参数里。有论文指出,去掉MAML的内循环梯度后模型性能几乎不变(ANIL实验),说明很多时候学到的其实是表征而非适应能力,这也解释了为何换域后性能下降——表征本身没有做到域不变。
域泛化:不接触目标域也能抗分布偏移
域泛化的核心思想是:训练时只用多个源域,目标是让模型在未见过的目标域上依然有效。应用在元学习里,最自然的做法是把“域”本身当作元学习的任务维度来构造。
元域泛化(Meta-Domain Generalization)是这样操作的:把不同源域的数据打包成元学习任务,训练时模型被要求在一个域上快速适应,同时在另一个域上验证。这种跨域的内外循环设计,强迫初始参数落在对各域都友好的区域。下面给出一个简化的PyTorch实现框架,展示如何用MAML风格的双层更新实现跨域训练:
import torch
import copy
def meta_train_across_domains(model, domains, inner_steps=5, inner_lr=0.01, outer_lr=0.001):
"""
model: 共享初始参数的元模型
domains: 字典,key为域名,value为(data, labels)
思路:内循环在域A上适应,外循环在域B的查询集上计算元损失
"""
meta_opt = torch.optim.Adam(model.parameters(), lr=outer_lr)
domain_names = list(domains.keys())
for it in range(1000):
# 随机抽取两个不同的域:一个做支撑,一个做查询
src, tgt = torch.randperm(len(domain_names))[:2]
src_name, tgt_name = domain_names[src], domain_names[tgt]
fast_weights = copy.deepcopy(model.state_dict())
# 内循环:仅在源域支撑集上快速适应
for step in range(inner_steps):
loss = compute_loss(model, fast_weights, domains[src_name])
grads = torch.autograd.grad(loss, list(fast_weights.values()))
fast_weights = {k: w - inner_lr * g for (k, w), g in zip(fast_weights.items(), grads)}
# 外循环:在目标域查询集上评估并更新元参数
meta_loss = compute_loss(model, fast_weights, domains[tgt_name])
meta_opt.zero_grad()
meta_loss.backward()
meta_opt.step()
除了双层优化结构,特征层面的域不变性约束同样重要。常见手段包括:一是特征对齐,用MMD(最大均值差异)或CORAL损失拉近不同域特征分布的二阶统计量;二是域对抗训练,在特征提取器后面接一个域判别器,用梯度反转层(GRL)让特征“骗过”判别器;三是数据层面的域增广,例如用RandConv风格随机卷积核重绘样本、用频域滤波扰动纹理,人为扩充域的多样性。这些方法成本低、效果好,是工程上值得优先尝试的方案。
还需要提醒一点:域泛化路线下,一致性正则往往被低估。对同一样本施加两种不同的域风格扰动,要求模型输出保持一致,能显著抑制模型对特定域纹理的依赖。它的实现只多一项KL散度损失,几乎不增加训练开销。
域适应:利用无标注目标域数据的主动弥合
与域泛化不同,域适应(Domain Adaptation)的前提是训练阶段可以接触到目标域数据——通常是无标注的。这在实际业务中很常见:目标场景能采到海量图片,但打标成本高昂。此时可以把域适应模块嫁接到元学习框架上。
第一种思路是无监督域适应加对抗对齐。特征提取器同时服务元学习分类头和域判别器,域判别器负责区分样本来自源域还是目标域,梯度反转让特征提取器朝“域不可分”方向优化。配合元学习的任务采样机制,模型在学会快速适应任务的同时,表征也完成了跨域对齐。
import torch.nn as nn
from torch.autograd import Function
class GradReverse(Function):
"""梯度反转层:前向恒等,反向取负"""
@staticmethod
def forward(ctx, x, lamb):
ctx.lamb = lamb
return x.view_as(x)
@staticmethod
def backward(ctx, grad_output):
return grad_output.neg() * ctx.lamb, None
class DAFeatureExtractor(nn.Module):
def __init__(self, backbone, feat_dim, num_domains=2):
super().__init__()
self.backbone = backbone
self.domain_classifier = nn.Sequential(
nn.Linear(feat_dim, 256), nn.ReLU(),
nn.Linear(256, num_domains)
)
def forward(self, x, lamb=1.0):
feat = self.backbone(x)
# 判别域来源,同时反转梯度让特征趋于域不变
domain_pred = self.domain_classifier(GradReverse.apply(feat, lamb))
return feat, domain_pred
第二种思路是自训练伪标签。先用源域训练的元模型对目标域无标注数据打伪标签,筛选置信度高的样本加入训练集,迭代多轮。要注意两点:伪标签阈值建议从高往低逐步放松(比如从0.95降到0.7),避免早期噪声累积;最好加入类别平衡的采样策略,否则目标域中占比大的类别会通过伪标签自我强化,造成偏置放大。
第三种思路针对元学习特有结构:梯度对抗更新。有工作(如Meta-MTL、跨域MAML变体)在内循环更新方向上做文章,要求内循环的更新不仅能降低当前任务损失,还要能降低其他域任务的损失,即寻找对域偏移鲁棒的更新方向。这类方法计算开销偏大,但在域间差异剧烈的场景(如从自然图像迁移到素描、医学影像)收益明显。
工程实践中的选型与调参建议
两条路线并非互斥,实际项目里经常组合使用。如果目标域完全无法访问,例如做通用小样本工具,只能走域泛化路线,重点投入数据增广与特征对齐;如果目标域数据可获取,优先上域适应,通常收益更快。一个经验性的组合是:先用多源域加风格增广做元训练打底,再用目标域无标注数据做对抗对齐微调,最后视情况叠加一轮伪标签自训练。
调参方面有几个易踩的坑。其一,内循环步数不宜多,跨域场景下inner_steps超过5往往加剧元过拟合,1到3步更稳妥;其二,域对抗的λ系数要用warm-up策略,训练初期设小(如0.01),逐步升到1.0,否则特征提取器会被域判别器带偏;其三,评估一定要严格划分“未见域”,只用随机划分的训练测试集会严重高估跨域性能,正确的做法是按域整体留出,比如用五个域训练、留一个域做测试,轮换验证。
最后,模型规模也要克制。元学习本身样本效率高,参数过多时更容易记忆任务而非学习通用表征,配合小一点的骨干网络加更强的数据增广,往往比大模型裸训的跨域表现更好。把域泛化的正则手段和域适应的对齐手段合理编排,元过拟合问题就能得到实质性的缓解,让“学会学习”的模型真正经得起域偏移的考验。