
做元学习实验做久了你会发现在同一个benchmark里调参跑通的模型换到另一套任务分布上常常失灵。我自己第一次在少样本分类中复现MAML时就遇到过这种事在Omniglot上跑得稳稳的实现原封不动搬到miniImageNet上验证集准确率掉了一大截。起初我怀疑是backbone、学习率或者内循环步数不对排查了两天之后才慢慢意识到问题可能根本不在这些超参数上而是元学习器在训练过程中对任务的分布产生了偏差。这正是TAMLTask 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-agnosticTAML想补的缺口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的标准设置从正弦函数族里采样任务每任务给若干个数据点让模型拟合出目标正弦函数。分类用两个公开benchmarkOmniglot和miniImageNet常见的设定是5-way 1-shot和5-way 5-shotbackbone一般用四层卷积网络。这两块实验的用意不同。回归任务适合观察元学习器对连续任务分布的适应能力分类任务能更直观地体现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约等于MAMLtask 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(dim0)) total_loss outer_loss lambda_reg * reg elif reg_type hessian: grads torch.autograd.grad(outer_loss, model.parameters(), create_graphTrue) 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给出的答案也许不是最终解但它确确实实地把人往正确方向推了一把。