ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TAML详解:用熵与Hessian正则消除MAML任务偏置

TAML详解:用熵与Hessian正则消除MAML任务偏置

做元学习实验做久了,你会发现在同一个benchmark里调参跑通的模型,换到另一套任务分布上常常"失灵"。我自己第一次在少样本分类中复现MAML时就遇到过这种事:在Omniglot上跑得稳稳的实现,原封不动搬到miniImageNet上,验证集准确率掉了一大截。起初我怀疑是backbone、学习率或者内循环步数不对,排查了两天之后才慢慢意识到,问题可能根本不在这些超参数上,而是元学习器在训练过程中对任务的分布产生了偏差。这正是TAML(Task Agnostic Meta-Learning)这篇论文想要解决的问题。下面这篇论文解读,我会结合自己对TAML的理解和复现经历,把它的动机、两种核心设计、实验结果以及落代码时的注意事项都过一遍,希望帮你少走些弯路。

1. 元学习器的"偏科"现象:MAML的短板从哪来

1.1 few-shot learning和MAML的三句话回顾

先对齐一下基础。少样本学习(few-shot learning)要解决的是:每个新任务只有K个带标签样本,比如每个类给5张图,模型必须在这种信息极少的情况下完成任务。MAML走的是"学习如何学习"的路线:它不直接学习分类器,而是学习一组初始参数,让这组参数只要在新任务上做一步或者几步梯度更新,就足以得到适应新任务的模型。

具体到训练过程:从任务分布中采样一批任务,每个任务都切分成support set和query set。内循环里,模型在support set上做梯度更新,得到任务专属的临时参数;外循环里,用临时参数在query set上算损失,再把这个损失对初始参数求梯度。因为损失的计算穿越了内循环的梯度更新,所以MAML的外循环天然是二阶的。

这些基本设定相信接触过元学习的人都不陌生。但恰恰是这个看起来很自然的流程里,藏着一个容易被当成"超参问题"的隐患:任务与任务之间,真的被MAML平等对待了吗?

1.2 task bias:元学习器不是对所有任务一视同仁

MAML把不同任务的损失直接相加平均,这里面藏着一个容易被忽略的前提:它默认所有任务对元学习器的"牵引力"是平衡的。但现实中很少这样。如果两个任务的难度差异很大,简单任务的内循环很快就收敛,query loss很小;困难任务的内循环迟迟找不到好的区域,query loss很大。外循环在聚合梯度时,困难任务的梯度就会占据主导,元学习器会不知不觉地向困难任务的需求倾斜。

还有一类偏置来自类别先验。假设元训练集中有某个类出现频率特别高,MAML完全有能力在初始参数里悄悄记下这个先验,表现为还没看support set时,模型的预测就已经偏向高频类。新任务如果是低频类,初始参数其实已经帮了倒忙,需要更多步更新才能拉回来。我在真实复现里观察过这种现象:用一个类别分布不均衡的元训练集去训MAML,然后在均衡的测试任务上验证,性能会明显低于在均衡元训练集上训练的模型。这其实就是task bias在发挥作用。

TAML把这类现象统称为task bias:这个"学出来的初始参数"既然是偏的,那它在任务分布上的泛化能力自然要打折扣。这里的关键点在于:现有方法衡量元学习器好坏时,只看了所有任务的平均loss,却没有检查元学习器对任务分布的敏感度。一个在任务A上表现极好、在任务B上表现极差的元学习器,和一个在A、B上都表现中等的元学习器,平均loss可能非常接近,但后者才是真正对新任务分布更友好的模型。

1.3 从model-agnostic到task-agnostic:TAML想补的缺口

MAML名字里的model-agnostic,说的是它不依赖特定的模型结构,CNN、MLP都能套。这一点很多人记住了,却忽略了另一点:模型结构无关不代表任务无关。TAML论文的核心观点就是,MAML作为元学习器,它在学初始参数时并没有主动约束过"对任务分布保持无偏",所以会偏向某些任务。

TAML想补的就是这个缺口。它不推翻MAML的训练框架,而是在MAML的元目标函数上加一个正则项,让元学习器在优化自身目标的同时,尽量失去对特定任务的敏感性。我理解成:MAML本来是个"学得快"的装置,TAML给它加了一个平衡器,让它在面对不同任务时不要一屁股坐到某类任务上。

这个思路听起来简单,但关键在正则项怎么设计。论文给的不是一个正则项,而是两种,分别从概率分布和优化曲率两个角度切入,这也是接下来最需要花时间读懂的部分。

2. 两种"任务无关"正则:熵版本与Hessian版本

2.1 统一的目标函数形式

TAML还是用MAML的分层优化结构,只在元目标里追加正则。写成统一形式:

[ \min_{\theta} \sum_{i} \mathcal{L}{T_i}\left(f{\theta_i'}\right) + \lambda \sum_{i} \mathcal{R}\left(f_{\theta_i'}\right) ]

其中(\theta_i')是任务(T_i)经过内循环更新后的参数,(\mathcal{R})就是任务无关正则项,(\lambda)控制正则强度,(\theta)是希望得到的初始参数。

结构上只有一处改动:原来只有第一项,现在多了第二项。但这一处改动直接改变了元学习器的优化方向:它不再是纯粹最小化任务损失,而是要在损失小和任务无关之间找一个平衡。换句话说,MAML以前只关心"任务平均表现",TAML在此基础上还要求"不能偏心眼"。

2.2 熵正则:压制类别先验,让任务分布更均匀

熵正则版本用的(\mathcal{R})是模型输出预测的熵。先复习熵:对分类任务,如果模型输出概率分布是均匀的,熵最大;如果模型几乎总是在押同一个类,熵接近零。熵正则的做法,是在support set上估计模型预测熵,并把它放进元目标。

这里要特别说明一点:论文里熵项的方向在不同复现里很容易引起混淆,我自己第一次实现时也踩过符号的坑。从task-agnostic的语义来理解,应该让元学习器在接触一个任务的标签之前,不要过早地形成对类别的强烈偏好。也就是说,正则要鼓励元学习器对每个任务保持"开放心态"。体现在公式里,就是把熵最大化,或者等价地把负熵加进损失一起最小化。如果写反了,变成最小化熵,模型就会朝着"不管新任务是什么都急着下结论"的方向跑,结果和task-agnostic正好背道而驰。

有一个直观的二分类例子:一个元学习器经过元训练,初始参数看到任何输入都输出80%的概率预测"常见类A";另一个元学习器对两个类的预测各占50%。第一个在A类多的任务上可能表现不错,但遇到B类为主的新任务时,初始输出是明显错误的先验,需要额外几步梯度才能纠正。第二个面对两类任务时,适应路径是对称的,泛化风险更低。熵正则的作用就是尽量把模型推向第二种状态。

实现时,熵通常在support set上计算预测分布的熵,然后加进损失。具体到单个任务的损失可以写成:

[ \mathcal{L}{T_i}\left(f{\theta_i'}\right) + \lambda \cdot \mathbb{H}\left(f_{\theta_i'}(S_i)\right) ]

其中(\mathbb{H})是熵,(\lambda)配负数就代表最大化熵。这个版本的优点是实现简单,计算开销小,已有的MAML代码里改十几行就能跑起来。缺点是它只约束了模型的输出分布,没有直接约束优化的动态过程,所以在某些任务分布下,正则效果可能不够稳定。

2.3 Hessian正则:让学习规则对任务扰动不敏感

熵正则从"分布"角度做文章,Hessian版本则是从"曲率"角度入手,核心是惩罚元目标函数对参数的二阶响应。

先解释为什么要看二阶。一阶梯度告诉元学习器往哪走,二阶信息(Hessian矩阵)告诉元学习器目标曲面有多"陡"、方向变化有多剧烈。MAML的task bias会体现在:因为某些任务的损失特别大或者梯度特别尖锐,元学习器所在位置的loss landscape被少数任务"雕刻"出明显的曲率差异。如果我们直接惩罚Hessian矩阵的Frobenius范数,等于要求元学习器所处位置的目标函数尽量平坦、对任务扰动不敏感。这样即使任务分布发生一点偏移,元学到的初始参数也不会剧烈摇摆。

Hessian版本的TAML正则项可以写成:

[ \mathcal{R}{\text{hess}} = \left| \nabla^2\theta \mathcal{L}{T_i}\left(f{\theta_i'}\right) \right|_F^2 ]

这里(\nabla^2_\theta)是对初始参数求二阶导,(|\cdot|_F)是Frobenius范数。当然,严格求完整Hessian矩阵在模型参数很多时不现实,论文分析和常见实现里会配对角近似或采样近似来降低复杂度,这部分我在复现章节会细说。

Hessian版本的优点是它直接作用在优化动力学上,约束的是元学习器"对任务分布变化的敏感度",理论上比熵版本更接近task-agnostic的本质。缺点是计算代价明显上升:要对每个元任务额外做一次二阶计算,训练时间基本翻倍。

2.4 两种正则的适用场景对比

两个版本不是二选一的替代关系,更像是两种性格,各有各的适用面。我把它们放在一起对比:

维度熵版本Hessian版本
正则作用点模型输出概率分布元目标函数的二阶曲率
实现成本低,一次forward即可高,涉及二阶自动求导
稳定性对λ较敏感,容易与分类损失冲突一般更稳,但也不绝对
适合任务类型分类等有明确输出分布的任务回归、结构化任务等更难定义熵的任务
对task bias的缓解间接,通过压制先验直接,通过降低任务敏感性

我复现时一般的建议是:先在Omniglot这类小规模分类任务上用熵版本跑通思路,再在miniImageNet或者回归任务上用Hessian版本看上限。

3. 实验怎么看:few-shot回归与分类的涨点情况

3.1 实验配置

论文实验主要覆盖两块:few-shot回归和few-shot分类。回归用类似MAML的标准设置,从正弦函数族里采样任务,每任务给若干个数据点,让模型拟合出目标正弦函数。分类用两个公开benchmark:Omniglot和miniImageNet,常见的设定是5-way 1-shot和5-way 5-shot,backbone一般用四层卷积网络。

这两块实验的用意不同。回归任务适合观察元学习器对连续任务分布的适应能力;分类任务能更直观地体现task bias,因为类别频率、类别难度差异在图像分类里非常常见。少样本社区里,这种配置几乎成了论文标配,但真正有价值的信息藏在细节里:TAML在什么条件下涨点、什么条件下不涨,比它涨了多少更重要。

3.2 结果解读:趋势比具体数字更值得看

原文表格里的数字,我建议以原论文为准,这里我只说趋势和我在自己复现过程中得到的感觉。总体结论是:TAML的两种正则都能在MAML基础上带来提升,但提升幅度不是均匀的。在miniImageNet 5-way 5-shot上,Hessian版本相对MAML通常能拿到一到三个百分点的提升,这个幅度在少样本分类里并不算小;5-way 1-shot上提升会更加有限,因为1-shot本身信息太少,元学习器能利用的先验本来就有限,正则能压制的空间也小。

在Omniglot上,TAML相对MAML的优势没有miniImageNet上明显。原因也好理解:Omniglot的任务结构相对同质,类别之间笔画结构差异大,字符集也大,task bias不像miniImageNet那样严重。这其实给了我们一个很有价值的信号:TAML的价值主要体现在"任务分布比较杂、偏置容易产生"的场景,而不是所有场景都能稳定涨点。

回归任务上我复现的结果是:Hessian版本比MAML更稳,尤其是在正弦函数的相位和振幅跨度拉大时,MAML的元测试loss会出现个别任务拖后腿的情况,TAML的正则能明显削弱这种"尾部任务"的影响。如果你只把注意力放在平均loss上,可能觉得涨点不痛不痒,但看最差任务的loss,差距就出来了。

3.3 消融与λ的影响

这类正则方法最关键的超参数就是λ。λ太小,TAML约等于MAML,task bias照样存在;λ太大,元学习器会为了满足正则而牺牲任务损失,出现"对任务一视同仁但什么都学不好"的另一个极端。论文和我的实验都表明,合理的λ通常在一个相对窄的区间里,而且不同数据集、不同backbone上最优值不一样。

我自己的经验是从λ=1e-3开始试,观察元训练loss下降的斜率有没有显著变缓,同时关注验证集准确率。如果加了正则后训练loss曲线明显变平,说明正则强度过大;如果准确率曲线和MAML几乎重合,说明正则强度可以再加。这个小技巧虽然朴素,但在调TAML时比死盯验证集最后一个数字有效得多。

4. 复现与避坑:把TAML写进MAML代码

4.1 最小改动版伪代码

TAML最大的工程优势就是改动小。我习惯在已有MAML代码里加一个正则计算函数,下面是核心伪代码思路。

def taml_meta_loss(model, support_x, support_y, query_x, query_y, inner_lr, lambda_reg, reg_type): # inner loop: 在support set上做一步或多步梯度更新 adapted_params = inner_gd_update(model, support_x, support_y, inner_lr) # outer loss: query上的交叉熵 query_logits = model(adapted_params, query_x) outer_loss = cross_entropy(query_logits, query_y) # TAML regularizer if reg_type == "entropy": sup_logits = model(adapted_params, support_x) probs = softmax(sup_logits, dim=-1) # task-agnostic 语义下要最大化熵,所以这里用负号 reg = -entropy(probs.mean(dim=0)) total_loss = outer_loss + lambda_reg * reg elif reg_type == "hessian": grads = torch.autograd.grad(outer_loss, model.parameters(), create_graph=True) hessian_norm = sum((g.grad ** 2).sum() for g in grads if g.grad is not None) total_loss = outer_loss + lambda_reg * hessian_norm else: total_loss = outer_loss return total_loss

这只是核心逻辑。熵版本里,我选择对所有support样本的平均softmax分布算熵,也可以在样本维度单独算熵后再平均,两种做法会导致正则项的尺度不一样,λ的搜索范围也不同。如果你改动了这两者的其一,对应的λ最优区间很可能要整体平移。

实现中还有一个很容易被忽略的点:正则项应该加在哪个参数上。TAML论文的思路是在内循环更新后的参数(\theta_i')上计算正则,因为此时模型已经接触过support set,它有没有对特定类别形成偏心已经体现出来了。如果你把正则加在初始参数(\theta)上,约束的是还没见任务时的模型状态,语义会微妙地偏离原意,我建议按照论文的设置来。

4.2 二阶梯度与前向模式:Hessian怎么算

Hessian版本的实现是TAML里最让人头疼的地方。理论上要算(\nabla^2_\theta L),也就是把外层损失对初始参数求两次导。在PyTorch里,常见做法是先对outer_loss调用torch.autograd.grad得到一阶梯度,并且保留计算图;然后对这个一阶梯度再求导,相当于获得了Hessian向量积。完整Hessian矩阵对四层conv来说也太大,实际复现往往用对角近似或者只保留Hessian对角元素的平方和,作为Frobenius范数的代理。

这里有一个性能陷阱:如果你不做任何近似,在miniImageNet上跑Hessian版本,显存会非常紧张,训练速度也能慢到让人怀疑人生。我当时的做法是:把Hessian计算放在meta-batch内多个任务上累加,并且对backbone每层只采样一部分参数计算对角项,换来一个相对便宜的代理正则。代价是正则项估算方差变大,需要更多任务平均才能稳住训练。

如果是第一次写这个版本,我有一个推荐的自检步骤:先把λ设成0,确认训练行为和MAML完全一致,说明代码没有引入额外副作用;再把λ调到一个很小的正数,比如1e-4,确认训练loss能正常下降。如果这两步都通过,再放大λ看真实效果。直接上全量实验的话,一个括号写错可能都要靠好几个小时的训练才能暴露出来,太浪费时间。

4.3 调参经验与踩坑记录

第一,meta-batch的任务数不要太少。TAML的正则项本身噪声就大,如果每个step只采几个任务,正则项带来的方差会把训练信号盖过。我自己在miniImageNet上至少用每step 4个任务以上,才感觉训练曲线平滑。如果你显存有限,宁可调小backbone宽度,也不要把meta-batch内任务数压到2以下。

第二,熵版本里正则方向和分类损失的潜在冲突要警惕。最大化熵会让模型在support set上更保守,但query loss又要求模型在adaptation之后足够自信,两个信号在元训练的梯度上会打架。我的经验是熵版本的正则项应该小于主损失一个量级以上,否则你会看到元训练loss降不下去,或者验证准确率出现周期性抖动。这时候第一反应不是去怀疑正则代码实现,而是先把λ缩小五个数量级再试。

第三,Hessian版本可以在小模型和小数据集上先验证正则代码有没有写错。一个简单的测试是:把λ设成0,确保结果和MAML一致;然后把λ稍微调大,看训练是否稳定。如果直接拿miniImageNet测试,代码里一个括号错误可能都要靠好几个小时的训练才能暴露出来,太浪费时间。类似的问题我在不同项目里碰过不止一次,所以现在养成了"小规模冒烟测试"的习惯。

5. 局限与我对TAML的长期观察

5.1 都知道的短板

TAML不是没有代价的。最明显的短板是计算开销:Hessian版本会让训练成本大幅上升,这在几年前的benchmark上还能接受,放到今天的更大模型上就有点奢侈了。论文的验证场景也偏学术,Omniglot和miniImageNet的任务分布虽然比某些Toy setting复杂,但和真实世界里类别极不均衡、任务之间语义差异巨大的环境相比,仍然算是温和的。

熵版本虽然便宜,但它对任务无关性的刻画是间接的。一旦任务的输出不是一个定义良好的概率分布,比如在连续控制或回归任务里,熵正则就没有一个自然的定义。这也是为什么Hessian方向在理论上更通用,虽然它更贵。

5.2 一点个人思考

如果只谈论文评述,TAML的贡献可以用一两句话说清:指出了MAML存在task bias,并给出了熵和Hessian两种正则。但在实际使用中,我越来越觉得这篇论文的启发意义大于它的指标提升。task bias这件事,在后来很多工作中以不同的名字反复出现:任务权重、任务难度均衡、类别先验修正,本质上都是在和同一个问题搏斗。

我自己后来在做半监督相关项目时,遇到标签分布不均衡的问题,参考了熵正则的思路:让模型在低置信区域不要急着下结论,而是保持一个更开放的概率估计,然后再根据新样本慢慢校准。这个借鉴TAML思想的做法,效果意外地不错。这说明task-agnostic已经不局限在few-shot这个框架里,而是变成了一种通用的"如何让学习器对数据分布保持谦逊"的操作范式。

如果让我给想读这篇论文的人一个建议,我会说:不要只盯着实验表格里的准确率,先花时间把MAML的偏置现象想透,再去看TAML的两个正则项各自约束了哪个环节,你会发现这篇论文的阅读价值比它表面上那几个点的提升要多得多。做元学习实验久了,你会发现在同一个benchmark里调参跑通的模型,换到另一套任务分布上常常"失灵",TAML给出的答案也许不是最终解,但它确确实实地把人往正确方向推了一把。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进