小编推荐:
七期飞跃计划还剩7个名额,联系小编,获取你的专属算法工程师学习计划(联系小编SIGAI_NO1)
SIGAI特约作者
尹相楠
里昂中央理工 在读博士
本文介绍两个 Meta Learning 的算法:MAML 和 Reptile。原始论文分别见:
Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks
下载地址
https://arxiv.org/pdf/1703.03400.pdf
和
Reptile: a Scalable Metalearning Algorithm
下载地址
https://d4mucfpksywv.cloudfront.net/research-covers/reptile/reptile_update.pdf
文章内容结合了李宏毅老师的课程、toy example,以及个人的理解,可能文字有点多,但力求通俗易懂
背景
Meta Learning 中文翻译为“元学习”,它研究的不是如何提升模型解决某项具体的任务(分类,回归,检测)的能力,而是研究如何提升模型解决一系列任务的能力。
如果把训练算法类比成学生在学校的学习,那么传统的机器学习任务对应的是不同科目,例如数学、语文、英语,每个科目上训练一个模型。而 Meta Learning 则是要提升一个学生整体的学习能力,让学生学会学习(就是所谓的 learn to learn)。就像所有的学生都上一样的课,做一样的作业,可偏偏有的学生各科成绩都好,有的学生偏科,而有的学生各科成绩都差。
各科成绩都好的学生,说明他大脑 Meta Learning 的能力强,可以迅速适应不同科目的学习任务。
而对于偏科的学生,他们大脑的 Meta Learning 能力就相对弱一些,只能学习某项具体的任务,换个任务就不 work 了。对这种学生,老师的建议一般是:“在弱科上多花一点时间”,可这么做是有风险的,最糟糕的一种情况是:弱势科目没学好,强势科目成绩反而下降了。可以看到,现如今大多数深度神经网络都是“偏科生”,且不说分类、回归这样差别较大的任务对应的网络模型完全不同,即使同样是分类任务,把人脸识别网络架构用在分类 ImageNet 数据上,就未必能达到很高的准确率。
至于各科成绩都差的学生,说明他们不但 Meta Learning 能力弱,在任何科目上的学习能力都弱,需要被老师重点关照……
Meta Learning 的算法有很多,有些高大上的算法可以针对不同的训练任务,输出不同的神经网络结构和超参数,例如 Neural Architecture Search (NAS) 和 AutoML。这些算法大多都相当复杂,我们普通人难以实现。另外一种比较容易实现的 Meta Learning 算法,就是本文要介绍的 MAML 和 Reptile,它们不改变深度神经网络的结构,只改变网络的初始化参数。
从网络参数初始化谈起
训练神经网络的第一步是初始化参数。当前大多数深度学习框架都收录了不同的参数初始化方法,例如均匀分布、正太分布,或者用xavier_uniform,kaiming_uniform,xavier_normal,kaiming_normal等算法。除了用随机数,也可以用预训练的网络参数来初始化神经网络,也就是所谓 transfer learning,或者更准确地说是 fine-tuning 的技术。如果不了解 fine-tuning 技术,可以阅读这篇博客:
Building powerful image classification models using very little data,
地址链接
https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html
它通过微调预训练的 VGG-16 网络,用较少的数据训练了一个高精度的猫\狗分类器(这是我当年跑通的第一个 deep learning 算法,从此走上炼丹的不归路)。fine-tuning 之所以能 work,是因为预训练的神经网络本身就有很强的特征提取能力,能够提取很多有含义的特征,例如毛皮,耳朵,鼻子,眼睛,分辨猫狗,只需要知道这些特征是如何组合的就好了,这比从头开始学习如何提取毛皮、耳朵、鼻子等特征要高效得多。
利用预训练的网络进行参数初始化,相当于赋予了网络很多先验知识。类比我们人类,让一个小学没毕业的人去听高等数学,显然他是无法听懂的;而让一个高考数学满分的高中毕业生去听,他可能要学得轻松得多。如果忽略智商因素,我们人类的大脑从结构上说都是大同小异,为啥表现差别那么大呢?因为它们积累的知识量不同,后者积累的知识更多,也就是常说的“基础扎实”,换成神经网络的术语,就是后者的网络只需要 fine-tune 一下就好了,而前者需要 train from scratch ,要补很多课才行。
通过上面的例子我们发现,预训练的网络比随机初始化的网络有更强的学习能力,因此 fine-tuning 也算是一种 Meta Learning 的算法。它和我们今天要介绍的 MAML 以及 Reptile 都是通过初始化网络参数,使神经网络获得更强的学习能力,从而在少量数据上训练后就能有很好的性能。
训练数据:以 task 为基本单位
对于传统的机器学习问题,每个模型通常只用来解决一个任务,要么是人脸识别,要么是物体分类,要么是物体检测,等等。即使有多输出的网络,例如同时检测人脸的位置和关键点,本质上其训练任务(task)还是一个,换一个任务又要从头开始训练(不考虑 fine-tuning 的技术)。而 MAML 专注于提升模型整体的学习能力,而不是解决某个具体问题的能力,因此,它的训练数据是以 task 为基本单位的,每个 task 都有自己独立的损失函数。训练时,不停地在不同的 task 上切换,从而达到初始化网络参数的目的,最终得到的模型,面对新的 task 时可以学习得更快。
还拿学生的例子类比,Meta Learning 相当于让学生学习多门功课,比如第一节数学,第二节英语,第三节历史,等等……每门功课是一个 task。听说山东高考要考一门“基本能力测试”,这门课会学很多特别杂的知识(类比既检测人脸的位置,又检测人脸关键点坐标的网络),但这归根结底只是一门课而已,并不能因为它学的知识杂就变成了好几门课。
下图例子,Task 1 是猫狗分类任务,而 Task 2 是苹果橘子分类任务。Learning Algorithm F 即 Meta Learning 的算法,它在 Task 1 上经过训练,吐出一个分类器f1用于分类猫和狗,在测试集上算出的损失为l1,在 Task 2 上又吐出一个分类器f2,损失函数是l2。
不同 task 图示。图片来源:李宏毅老师的 PPT
我们始终要牢记,Meta Learning 最终的目的是要让模型获得一个良好的初始化参数。这个初始化参数在训练 task 上表现或许并不出色,但以这个参数为起点,去学习新的 task 时,学得会又快又好。而普通的 Learning,则是着眼于解决当前的 task,不会考虑如何面对新的 task。
损失函数的奥妙:初始化参数掌控全场,分任务参数各自为营
李宏毅老师的 PPT 给出了 MAML 损失函数 (1) 和普通训练 (2) 的损失函数的对比。其中
代表网络的基础参数,
代表基于网络基础参数学习到的第 n 个分任务的参数,
代表以
为参数的分任务 n 的损失函数。
MAML 中,不同分任务对应一套不同的参数,它们都是基于网络参数
通过梯度下降得到的。这些参数和初始化参数
很像,但又不完全一样,因为
是最终我们需要的初始化参数,它不能受制于某个分任务,而是要掌控全场。
而普通的学习任务,如公式 (2) 不同的分任务对应了同一套参数
,这样得到的
将是照顾到所有分任务的全局最优解。但在当前所有训练 task 上的全局最优解,一定是好的初始化值么?下面两图就是反例。
左图为普通训练,右图代表 MAML 初始化。图片来源:李宏毅老师的 PPT
上图中横坐标代表网络参数,纵坐标代表损失函数。浅绿和墨绿两条曲线代表两个 task 的损失函数随参数变化曲线。我们发现如果用公式 (2),我们将得到左图的效果:在靠近墨绿色曲线的附近,找到可以使两个任务损失函数之和最小的参数
。但是如果用它作为初始化参数,在 task 2 上训练,我们最终将收敛到 task 2 左侧的 local minima,而不是其右侧更小的 global minima。而使用公式 (1) 时,
会掌控全场,它不太在意训练集上的损失,而是着眼于最大化网络的学习能力。如右图所示,这个
就是很好的初始化参数,往左往右,皆可到达不同任务的 global minima。即:左侧图片得到的全局最优解,并不是模型学习能力最强的地方,也就是说,用这个全局最优解初始化网络参数,再在新任务上做 fine-tuning,最终得到的模型可能不会收敛到新任务的最优解。这就是 MAML 优于 fine-tuning 的地方。
再举个例子,最近教育界常常讲通识教育,素质教育,不能唯成绩论,防止学生变成做题考试的机器。这里面就暗合了 Meta Learning 的道理。我们在学校中学了很多门课,傻子都知道以后工作了并不会门门都用得上,但我们还是得学它们。因为我们要通过学习这些看似无用的课,获取一种宝贵的能力:那就是学习新知识的能力。我们传统的教育,认为门门功课考高分就是优秀,导致产生一些死读书,读死书的学生,最后人生发展并不是很好。现在教育改革的目标,就是教会学生自主学习,着眼于学生未来的发展,而不纠结于把一个类型的题练八百遍,在规定时间按照规定步骤做出来。(至于是不是可行,以及如何通过高考公平地选拔人才,那是另一个话题了)。
评价标准
既然 Meta Learning 是 learn to learn,那么如何证明 Meta Learning 算法的有效性呢?显而易见,只需要证明用这种算法得到的网络模型学习能力很强就行了。具体到我们的 MAML 和 Reptile,只需要证明,用它们这些算法初始化之后的神经网络,在新的任务上训练,其收敛速率与准确率比从随机初始化的神经网络要高。
这里所谓“新任务”,一般是指难度比较大的任务,毕竟难度大的任务才有区分度嘛,要是都像 MNIST 数据那么简单,随便一训练就 99% 的准确率,也看不出网络初始化参数所起的作用了。因此一般用 few-shot learning 的任务来评估 Meta Learning 算法的有效性。所谓 few-shot learning,就是指每类只有少量训练数据的学习任务(MNIST 每个数字都有上万张训练图片,因此不是 few-shot learning)。数据集 Omniglot:brendenlake/omniglot,是一个类似 MNIST 的手写数据集,如下图所示。该数据集包含 1623 类,每类只有 20 个训练数据,因此它属于 few-shot learning 的范畴,经常作为 benchmark 用来衡量 Meta Learning 算法的性能。
Omniglot
Omniglot 的用法是这样的,从其中采样 N 个类,每个类有 K 个训练样本,组成一个训练任务(task),称为 N-ways K-shot classification。然后再从剩下的类中,继续重复上一步的采样,构建第二个 task,最终构建了 m 个 task。把这 m 个 task 分成训练 task 和测试 task,在训练 task 上训练 Meta Learning 的算法,然后再用测试 task 评估 Meta Learning 得到的算法的学习能力。
MAML
上文中讲了那么多铺垫,终于可以正式开讲 MAML 算法了。抛弃原始论文中复杂的符号表示,采用李宏毅老师 PPT 中直观的图片,左侧为 MAML 算法,右侧为传统的预训练算法。
MAML 算法图示。图片来源:李宏毅老师的 PPT
代表网络初始化的参数,我们采样一个任务(或者好几个任务作为一个 batch,图中显示的是一个任务的情况),编号为 m。如左图第一个绿色箭头所示:基于
计算网络在任务 m 上的损失函数,然后用梯度下降法优化
,以 learning rate
得到任务 m 独有的网络参数
;接下来,如左图第二个绿色箭头所示,基于
计算任务 m 新的损失函数,并求出损失函数在
上的梯度。我们不是用这个梯度优化
,而是优化最初的那个
,用 learning rate
乘以梯度,加到
上,得到
,如第左图一个蓝色箭头所示,该箭头和第二个绿色箭头是平行的,代表
的更新方向为
处的梯度。用完了
之后,就把它扔掉了,下次再采样到任务 m 时,再基于当时的
重新算一遍。(如果这里的 batchsize 不是1,那么在计算各个 task 独有的参数时,还是单独计算,但是计算
的更新时,需要把各个 task 在其对应的
上的梯度加在一起,再更新主任务的参数
。)
接下来和上一步一样,再以
为新的网络初始化参数,采样一个新的任务 n,第一步梯度下降用于更新得到 n 独有的参数
,第二步梯度下降,用任务 n 的损失函数在
上的梯度,更新主网络参数
,从而得到
。以此类推,最终得到网络的初始化参数。
那么,为什么要先在采样出来的任务上更新一步参数,然后再计算梯度呢?这里只说一下我的直观理解:由于 learning rate 很小,其实在任务 m 上更新完了参数后,得到的
和
的差别也不大,因此这个梯度一定程度可以让总任务的损失函数降低。但同时,毕竟
和
还是不一样的,这避免了最终求到的
变成取得所有训练任务损失函数之和的 global minima,从而变成了 model pre-training 那一套。
这个 jupyter notebook 用 pytorch 实现了 MAML 论文中的 toy example
https://github.com/AdrienLE/ANIML/blob/master/ANIML.ipynb
该 toy example 的目标是拟合正弦曲线:
,其中 a、b 都是随机数,每一组 a、b 对应一条正弦曲线,从该正弦曲线采样 K 个点,用它们的横纵坐标作为一组 task,横坐标为神经网络的输入,纵坐标为神经网络的输出。我们希望通过在很多 task 上的学习,学到一组神经网络的初始化参数,再输入测试 task 的 K 个点时,经过快速学习,神经网络能够拟合测试 task 对应的正弦曲线。
对比fine-tune和MAML。图片来源:上文 jupyter notebook 链接
左侧是用常规的 fine-tune 算法初始化神经网络参数。我们观察发现,当把所有训练 task 的损失函数之和作为总损失函数,来直接更新网络参数
,会导致无论测试 task 输入什么坐标,预测的曲线始终是 0 附近的曲线,因为 a 和 b 可以任意设置,所以所有可能的正弦函数加起来,它们的期望值为 0,因此为了获得所有训练 task 损失函数之和的 global minima,不论什么输入坐标,神经网络都将输出 0。
右侧是通过 MAML 训练的网络,把它作为起点,在新任务上训练时,可以很好地拟合目标函数的曲线。
Reptile
Reptile 和 MAML 很像,它的算法如下图所示:
Reptile 算法图示。图片来源:李宏毅老师的 PPT
Reptile 中,每更新一次
,需要 sample 一个 batch 的 task(图中 batchsize=1),并在各个 task 上施加多次梯度下降,得到各个 task 对应的
。然后计算
和主任务的参数
的差向量,作为更新
的方向。这样反复迭代,最终得到全局的初始化参数。
总结
我们跳出复杂的公式,可以看到 MAML 和 Reptile 都做了一件事:在避免得到训练 task global minima 的同时,降低训练 task 的总损失函数。对总的损失函数,有点羞哒哒欲拒还迎的感觉。直观可以这样理解:如果直接用所有训练 task 的损失函数之和对
的梯度更新它,让损失函数取得 global minima(并不是真正的全局最优,而是“相对来说”的全局最优),会造成一种 task 层面的 overfitting,使得网络只能在训练 task 上取得好成绩,一旦换一个没见过的 task,它就表现得不太好了。反之,用单一 task 的参数的梯度,作为模糊的梯度下降的大方向,大差不差、马马虎虎地降低训练 task 总的损失函数,往往能获得一个较好的初始化参数。以这个初始化参数为起点,在新的任务上稍加训练,它反而能学得又快又好。
至于和 fine-tuning 预训练网络的技术的区别,个人认为最明显的是:预训练神经网络时,往往有它自己的任务,并没有考虑到将来别人拿这个网络做别的用途。所以它一心一意把自己的 task 搞好就万事大吉了,至于它能用在别的任务中,纯粹是无心插柳。而 Meta Learning 从一开始,就不以降低训练 task 损失函数为唯一目标,它的目标是各个 task 都学一点,然后学一组有潜力的初始化参数,以备将来新的训练任务。