FAIR 的 higher:给模块打补丁,现成模型也能反传过优化器

Generalized Inner Loop Meta-Learning

Edward Grefenstette, Brandon Amos, Denis Yarats, Phu Mon Htut, Artem Molchanov, Franziska Meier, Douwe Kiela, Kyunghyun Cho, Soumith Chintala

cs.LG, stat.ML

2019-10-04

FAIR 把 MAML 和学学习率收成同一套可微内环 GIMLI,并开源 higher:给模块打补丁即可 unroll;CIFAR 上可学 299 组学习率,MAML++ 换骨干后 miniImageNet 5-shot 从 68.32% 提到 76.73%。

这篇在解决什么

2019 年前后,MAML、可微超参搜索、学出来的优化器、学出来的损失函数,看起来像不同方向。它们共享同一套形状:内环用梯度更新模型参数 θ,外环再用内环跑完后的验证损失去更新另一组元参数 φ。φ 可以是学习率,可以是初始化,可以是损失里的权重。

工程卡点很具体。PyTorch 的 nn.Module 把权重藏在对象里,前向不能按步塞进另一组权重;optimizer.step() 原地改参数,计算图在这一步被掐断。想把内环展开、把二阶梯度送回 φ,通常得把模型重写成无状态函数,优化器也得手写可微版。换骨干或把 SGD 换成 Adam,两套都得再写。

GIMLI 给这套嵌套优化一个形式化,并写清梯度何时存在。配套库 higher 在运行时绕开上面两个框架限制,现成模块和第三方实现不用重写就能 unroll。

方法

元参数拆成两块:φ^opt 管优化器(学习率一类),φ^loss 管每一步训练损失(任务混合、正则、初始化)。内环走 T 步:先算训练损失对 θ 的梯度 Gt,再让优化器 optt(θt, φ^opt, Gt) 产出 θ{t+1}。外环拿终点参数的验证损失 L^val(θ),对 φ 反传,再用一个元优化器更新 φ。

这条链要通,三件事必须成立:

算法是截断的 BPTT。先复制模型和优化器状态,unroll J 步并留住中间的权重、梯度和激活;再从验证损失往回走,用动态规划把对 φ^opt 和 φ^loss 的局部贡献累加。每一步用 stop-gradient 把优化器对 θ、G、φ 的几条路径拆开。

higher 做两件脏活。monkeypatch() 递归克隆 nn.Module 的父类,改掉 forward:调用时先把状态权重换成传入的那一组,再跑原来的前向。预加载权重、第三方模块都能用,前提是没在运行时糟蹋父类。getdiffoptim() 把优化器的原地更新换成跟踪梯度的运算,并复制优化器状态,多次 unroll 不会污染外环那一份。

跟当时的 learn2learn、Torchmeta 比,这条路更贴金属:不附赠现成算法训练环,也不强迫用他们的无状态积木重写模型。

结果

实验是在演示库能干什么,不是在刷 few-shot 榜。

CIFAR-10 上训 DenseNet-BC(k=12)。一条基线把学习率钉在 0.1,跑满 300 epoch;另一条按 Huang et al. 2016 的多步退火,在第 150 和 225 epoch 把学习率除以 10。元学习版把训练集按 99:1 切开,给内环 299 个参数组各挂一个学习率,初值都是 0.1。每个 epoch 做一次元更新:内环 unroll 15 步,验证集上取 10 个 batch 算元损失,元优化器是默认超参的 Adam,batch 从 64 降到 16,3 个种子。正文没有测试准确率,只给了 Figure 1 的曲线,说法是更快摸到接近当时的 SOTA,样本效率好过手写退火。

第二组在官方 MAML++ 代码上换骨干和内环优化器,Omniglot 与 miniImageNet,内环一律 5 步。一个独立改动:BatchNorm 全程走训练模式,不再用 MAML++ 逐步存的统计量。光这一条就把他们自己的 VGG+SGD 抬上去了。Adam 作内环时,动量统计从外环 Adam 拷过来,并学每组学习率和 β。

设置原版 MAML++本库 VGG+SGD扫描最优
Omniglot 5-way 1-shot99.53±0.26%99.62±0.08%99.91±0.05%(resnet-4+SGD)
Omniglot 20-way 1-shot97.65±0.05%97.21±0.11%99.00±0.33%(resnet-12+SGD)
miniImageNet 5-way 1-shot52.15±0.26%56.33±0.27%56.33±0.27%(仍是 vgg+SGD)
miniImageNet 5-way 5-shot68.32±0.44%75.13±0.67%76.73±0.52%(resnet-8+SGD)

miniImageNet 1-shot 的增益几乎全来自 BN 改法,换 ResNet / DenseNet 没有再涨。Omniglot 5-way 5-shot 已经顶在 99.9%,扫描最优 99.87% 还略低于原论文的 99.93%。完整表(Table 2)只收录三天内跑完 3 个种子的组合,缺行;有的 Adam 跑崩了,同一设置标准差到 11.64 和 15.19,resnet-12+Adam 在 5-shot 掉到 37.40%。

为什么重要

2019 年谁要试「MAML 换 ResNet」或「内环改 Adam」,先得把 fast weights 和可微优化器重写一遍。higher 把这层工程税砍掉了。299 组学习率替代手写退火,也说明连续超参可以嵌进同一条二阶反传,不必另开贝叶斯优化。

对现在做训练的人,这是一篇工具论文。它没有新的 few-shot 算法,表上的数字是「换骨干就能扫」的副产品。如果你已经在用函数式变换或隐式微分做 bilevel,GIMLI 的三条可微条件仍然值钱:先问优化器步骤在不在图上,再问 Hessian 通不通。

局限与存疑

论文没有单独的局限节,实验自己把缺口露出来了。CIFAR-10 那组只有图、没有测试准确率,「快了多少、最终差多少」无法核对。MAML++ 扫描不完整,没跑完的组合直接从表里拿掉。Adam 内环方差很大,稳定性没有被当成问题来分析。

内存上,unroll 要留住中间参数和激活,J 不能随便拉长;所有设定锁在 5 步,也没去试他们自己点过的方向:BN 改成训练模式之后,测试时能否多 unroll。验证指标不可微时,文里只说换代理或 REINFORCE,没有实验。monkey-patch 假定模块老实用 nn.Module,写得很野的第三方代码不在承诺范围。跟 Franceschi 等人的 bilevel 分析比,这篇不谈近似解怎么收敛;Reptile、隐式 MAML 那种一阶捷径也只是点到为止。

术语

原文与代码

社区讨论

相关论文

全部论文解读