本文是对神经结构搜索(NAS)的简单综述,在写作的过程中参考了文献[1]列出的部分文献。深度学习技术发展日新月异,市面的书很难跟上时代的步伐,本人希望写出一本内容经典、新颖的机器学习教材,此文是对《机器学习与应用》,清华大学出版社,雷明著一书的补充。该书目前已经重印了3次,收到了不少读者的反馈,对于之前已经发现的笔误和印刷错误,在刚印刷出的这一版中已经做了校正,我会持续核对与优化,力争写成经典教材,由于水平和精力有限,难免会有不少错误,欢迎指正。年初时第二版已经修改完,将于上半年出版,补充了不少内容(包括梯度提升,xgboost,t-SNE等降维算法,条件随机场等),删掉了源代码分析,例子程序换成了python,以sklearn为基础。
简介
深度学习可以自动学习出有用的特征,脱离了对特征工程的依赖,在图像、语音等任务上取得了超越其他算法的结果。这种成功很大程度上得益于新神经网络结构的出现,如ResNet、Inception、DenseNet等。但设计出高性能的神经网络需要大量的专业知识与反复试验,成本极高,限制了神经网络在很多问题上的应用。神经结构搜索(Neural Architecture Search,简称NAS)是一种自动设计神经网络的技术,可以通过算法根据样本集自动设计出高性能的网络结构,在某些任务上甚至可以媲美人类专家的水准,甚至发现某些人类之前未曾提出的网络结构,这可以有效的降低神经网络的使用和实现成本。
NAS的原理是给定一个称为搜索空间的候选神经网络结构集合,用某种策略从中搜索出最优网络结构。神经网络结构的优劣即性能用某些指标如精度、速度来度量,称为性能评估。这一过程如下图所示。
在搜索过程的每次迭代中,从搜索空间产生“样本”即得到一个神经网络结构,称为“子网络”。在训练样本集上训练子网络,然后在验证集上评估其性能。逐步优化网络结构,直至找到最优的子网络。
搜索空间,搜索策略,性能评估策略是NAS算法的核心要素。搜索空间定义了可以搜索的神经网络结构的集合,即解的空间。搜索策略定义了如何在搜索空间中寻找最优网络结构。性能评估策略定义了如何评估搜索出的网络结构的性能。对这些要素的不同实现得到了各种不同的NAS算法,本节将选择有代表性的进行介绍。
神经结构搜索并不是一个新的领域,早在1990年代就有研究人员对此进行了尝试[7-10],文献[1]对截止2018年的NAS算法进行了综述,这一领域当前还处于高速发展阶段,各种新的方法不断出现。目前已有商业化的NAS系统,如Google公司的Cloud AutoML服务,百度公司的AutoDL。
搜索空间
搜索空间定义了NAS算法可以搜索的神经网络的类型,同时也定义了如何描述神经网络结构。神经网络所实现的计算可以抽象成一个无孤立节点的有向无环图(DAG),图的节点代表神经网络的层,边代表数据的流动。每个节点从其前驱节点(有边射入)接收数据,经过计算之后将数据输出到后续节点(有边射出)。理论上说,只要是无孤立节点的DAG,都是合法的神经网络结构。按照不同的尺度,神经网络的结构定义包含如下层次的信息:
(1)网络的拓扑结构。网络有多少个层,这些层的连接关系。从简单的图结构到任意的DAG也反映了整个神经网络结构的发展历程。最简单的神经网络是线性链式结构,其对应的图的每个节点最多只有一个前驱,一个后续,类似于数据结构中的链表。早期的全连接神经网络,卷积神经网络都是这种拓扑结构。Inception、ResNet、DenseNet中的节点允许有多个前驱,多个后续,从而形成了多分支、跨层连接结构,它们是更复杂的图。这些典型的拓扑结构如下图所示。
在描述网络的拓扑结构时,一般采用前驱节点来定义,即定义每个节点的前驱节点,一旦该信息确定,则网络拓扑结构确定。
(2)每个层的类型。除了第一个层必须为输入层,最后一个层必须为输出之外,中间的层的类型是可选的,它们代表了各种不同的运算即层的类型。典型有全连接,卷积,反卷积,空洞卷积,池化,激活函数等。但这些层的组合使用一般要符合某些规则。
(3)每个层内部的超参数。卷积层的超参数有卷积核的数量,卷积核的通道数,高度,宽度,水平方向的步长,垂直方向的步长等。全连接层的超参数有神经元的数量。激活函数层的超参数有激活函数的类型,函数的参数(如果有)等。各种典型层的超参数如下表所示。
如果一个节点的前驱节点只有一个,则直接以前驱节点的输出值作为本节点的输入。如果前驱节点有多个,需要将前驱节点的值汇总后输入本节点,这里有两种策略:相加和拼接,前者的典型代表是ResNet,后者的典型代表是DenseNet。由于神经网络的层数不固定,每层的超参数数量也不固定,因此描述网络结构的参数是变长的。
为了提高搜索效率,有时候会搜索空间进行限定或简化。在某些NAS实现中会把网络切分成基本单元(cell,或block),通过这些单元的堆叠形成更复杂的网络。基本单元由多个节点(神经网络的层)组成,它们在整个网络中重复出现多次,但具有不同的权重参数。另外一种做法是限定神经网络的整体拓扑结构,借鉴于人类设计神经网络的经验。这些做法虽然减少了NAS算法的计算量,但也限定了算法能够寻找的神经网络的类型。
由于描述神经网络结构的参数含有离散数据(如拓扑结构的定义,层的类型,层内的离散型超参数),因此网络结构搜索是一个离散优化问题。定义结构的参数数量一般比较大,因此属于高维优化问题。另外,对于该问题,算法不知道优化目标函数的具体形式(每种网络结构与该网络的性能的函数关系),因此属于黑盒优化问题。这些特点为NAS带来了巨大的挑战。
搜索策略
搜索策略定义了如何找到最优的网络结构,通常是一个迭代优化过程,本质上是超参数优化问题。目前已知的搜索方法有随机搜索,贝叶斯优化,遗传算法,强化学习,基于梯度的算法。其中强化学习,遗传学习,基于梯度的优化是目前的主流算法,也是本章介绍的重点。
强化学习
基于强化学习的NAS算法[4-6]将神经网络结构设计看作一个强化学习问题,学习得到一个产生网络结构的最优策略。这里的智能体是设计神经网络结构的算法,用于输出神经网络结构描述,强化学习算法使得生成的神经网络的性能最优化。为了用强化学习求解,可以将神经网络的设计看做一个动作序列,每次执行动作确定网络的一部分结构如层。神经网络在验证集上的性能值是强化学习中的奖励值。
由于神经网络的结构参数长度不固定,因此需要用一个可变长度的串描述网络结构,算法需要输出这种不定长的串。循环神经网络可以输出不固定长度是数据,因此可以用它来生成网络结构的描述,文献[2]提出的NAS采用了这种方案。
算法用一个称为控制器的循环神经网络生成描述子网络结构的串,从而确定子网络的结构。然后在训练集上训练子网络,在验证集上计算其精度值。以精度值作为反馈信号,采用策略梯度算法更新控制器网络的参数。在迭代时,控制器会以给予那些有更高精度值的神经网络以更高的概率值,从而确保策略函数能够输出最优网络结构。这一过程如下图所示。
算法的输出限定为分层的网络结构,第n个网络层以第n-1个网络层为基础。网络结构生成可抽象为序列生成问题,按层逐次预测网络结构。在RNN中,每5个输出值定义一个神经网络层。上一时刻的输出是本时刻的输入,确保RNN基于前面n-1层所有的结构信息来预测第n层的结构。RNN的输出层是softmax回归,根据它确定结构参数。对于卷积核高度,可以限定输出值为[ 1,3,5,7]四个数,RNN的softmax输出是取这4个数的概率值。
控制器每一时刻的输出包括:卷积核的数量,卷积核的高度,卷积核的宽度,卷积操作在水平方向的步长,卷积操作在垂直方向的步长。这一过程如下图所示
实现时考虑典型的网络结构。对于卷积核的数量,取值范围为[ 24,36,48,64],卷积核的高度取值范围为[ 1,3,5,7],卷积核宽度的取值范围与高度相同。卷积步长可以固定为1,也可以按照 [ 1,2,3]取值。
这里需要考虑的一个问题是何时终止预测,实现时限定了神经网络的层数,达到一定的层之后,停止输出。在训练过程中这个值会逐步增加。
控制器生成该描述串之后,接下来在训练集上训练该子网络,这里采用了REINFORCE算法。目标函数为子网络在验证集上的精度的数学期望
L(\theta )=E_{\Delta \sim p_{\theta (\cdot )}}\left [ R(\Delta ) \right ]L(θ)=EΔ∼pθ(⋅)[R(Δ)]
其中\thetaθ是控制器的参数,\DeltaΔ是子网络,P_{\theta }(\Delta )Pθ(Δ)是控制器输出的子网络所服从的概率分布,R(\Delta )R(Δ)是子网络在验证集上的精度值。直观的目标是某种结构的子网络准确率越高,则控制器生成该网络结构的概率越大。因此可以按照下式计算控制器的参数
\bigtriangledown _{\theta }L(\theta )= \sum_{1}^{T}E_{p(a_{1:T};\theta )}\left [ \bigtriangledown _{\theta }\ln p(a_{t}\mid a_{t-1:1};\theta ) R\right ]▽θL(θ)=∑1TEp(a1:T;θ)[▽θlnp(at∣at−1:1;θ)R]
其中R为子网络的准确率,p为生成该子网络结构的概率。实现时使用采样来近似数学期望值
\frac{1}{m}\sum_{k=1}^{m}\sum_{t=1}^{T}\bigtriangledown _{\theta }\ln p(a_{t}\mid a_{t-1:1};\theta )R_{k}m1∑k=1m∑t=1T▽θlnp(at∣at−1:1;θ)Rk
其中m为mini-batch的样本数,T为神经网络的层数。为了解决REINFORCE算法计算出的梯度值偏差问题,在计算梯度时减掉了均值b
\frac{1}{m}\sum_{k=1}^{m}\sum_{t=1}^{T}\bigtriangledown _{\theta }\ln p(a_{t}\mid a_{t-1:1};\theta )(R_{k}-b)m1∑k=1m∑t=1T▽θlnp(at∣at−1:1;θ)(Rk−b)
其中b为所有奖励的均值。前面介绍的方法只能生成标准的线性结构网络,通过改进可以生成跨层连接的卷积神经网络以及循环神经网络,通过在控制器softmax输出中增加相关信息而实现。
文献[2]奠定了用强化学习解决NAS问题的基础,但面临计算量大的问题。一种解决方案是对搜索空间进行简化,限定网络结构为某些类型。回顾卷积网络的发展历史,各种典型卷积神经网络一般都具有某些重复、规整的结构,如ResNet中的跨层连接块,GoogLeNet中的Inception块等。如果能预测出这种基本块结构,然后将其堆叠形成网络,既可以降低搜索成本,又能使得网络随着输入数据的尺寸动态扩展,对于大尺寸的输入图像,只需要增加堆叠的块数即可。
文献[3]采用了这种思想,提出了一种称为NASNet的方法。NASNet预测出基本块(building block),在小规模的CIFAR-10数据集上训练,然后将学习得到的网络结构迁移到更大规模的ImageNet数据集上。控制器预测的是基本两种网络单元,分别称为普通单元(Normal Cell)和约简单元(Reduction Cell)。前者不改变输入图像的尺寸,后者将图像的高度和宽度减半。根据这种设计,搜索整个神经网络结构的任务被简化为搜索最优基本块结构。除了降低搜索空间的大小,这种做法还使得在一个数据集上搜索得到的网络结构更容易泛化到其他数据集上。
完整的神经网络通过这些相同结构的基本单元堆叠形成,但各个基本单元有不同的权重参数。对于不同尺寸和规模的数据集,使用了不同数量的基本块。下图为用于CIFAR-10和ImageNet数据集的网络结构。
上图中的乘以N表示这种基本块堆叠N次。算法的核心是如何生成基本块。控制器网络用RNN实现,其输出层为softmax,用于生成描述神经网络结构的决策。每个基本单元由B个块构成,每个块有两个输入,执行某一运算后产生输出。下图为生成每个块的方法,包含5个步骤:
(1)选择一个隐含状态作为第1个输入。
(2)选择一个隐含状态作为第2个输入。
(3)为第1个隐含状态选择一个运算。
(4)为第2个隐含状态选择一个运算。
(5)为两个运算的结果选择一个合并方式,执行合并。
隐含状态即神经网络前面的层的输出结果,如CNN中的卷积特征图像,或RNN中的隐含状态。然后对两个输入各选择一个运算,再将两个运算的结果合并。这一过程如下图所示。
作用于隐含状态上的运算包括各种卷积,池化等操作。运算结果的合并方式有相加,拼接两种选择。
下图为生成一个基本单元的过程。图中上方为候选隐含状态集合,第1次选择H1和H2作为输入,分别执行池化和卷积运算,然后相加,得到H3,并将其加入候选隐含状态集合。接下来生成第2个块,选择H2和H3作为输入,分布执行卷积和恒等运算,将结果进行拼接,产生H4。其他的以此类推。在这里B的值由人工设定。
下图是典型的普通块,这里B=4。
下图是典型的约简块,同样的B=4,这里将4个临时结果拼接,形成h_{t}ht作为本单元的输出值。
在生成网络结构描述之后,训练子网络和控制器网络的方法与文献[2]相同,不同的是策略梯度算法采用了PPO算法(Proximal Policy Optimization)。
NASNet虽然在速度上有提升,但计算量还是太大。作为这一系列方法的改进,文献[4]提出了一种称为ENAS(Efficient Neural Architecture Search)的算法,通过在各个网络之间共享权重来减少计算量。由于各个子网络共享权重,因此每个子网络不需要从头开始训练,这极大的提高了搜索速度。
ENAS将NAS看做是寻找最优子图的问题,问题的解是一张大的图的子图。图23-1展示了这一概念。在这种图表示中,图的顶点为某种计算(如卷积,池化,相加),边表示数据的流动。下图的图有6个顶点,任意两个节点之间都可能有边连接,但边的方向只能是从编号较小的节点指向编号较大的节点,以防止环的出现。各个顶点可以对应于神经网络中的层,数据只能从编号小的层流向编号大的层。这个图的最优子图包含全部6个顶点,边为图中红色的边。
使用这种表示,可以将NAS限定为在一个固定顶点数的图中寻找最优子图。神经网络的结构描述同样由RNN实现的控制器生成。对于卷积神经网络和循环神经网络采用了不同的描述,控制器生成这两种神经网络单元的方法也不同,下面分别介绍。
循环神经网络中可以选择的操作为激活函数,包括ReLU和tanh两种类型。下图为一个子图以及对应的循环神经网络。
该图有4个顶点,红色的边表示信息的流动,黑色的边无效即没有使用。右图为对应的循环神经网络单元,运算节点的编号与左图中图的顶点编号对应。节点1接收x_{t}xt和h_{t-1}ht−1作为输入,执行下面的运算
h_{1}=tanh(x_{t}\cdot W^{(x)}+h_{t-1}\cdot W_{1}^{(h)})h1=tanh(xt⋅W(x)+ht−1⋅W1(h))
这里的激活函数选用tanh,权重矩阵为本节点的参数。节点2以节点1的输出值为输入,选择ReLU作为激活函数,执行下面的运算
h_{2}=ReLU(h_{1}\cdot W_{2,1}^{(h)})h2=ReLU(h1⋅W2,1(h))
该节点同样有权重矩阵。节点3以节点2的输出值为输入,选择ReLU作为激活函数,执行下面的运算
h_{3}=ReLU(h_{2}\cdot W_{3,2}^{(h)})h3=ReLU(h2⋅W3,2(h))
节点4以节点1的输出值作为输入,选择tanh作为激活函数,执行下面运算
h_{4}=ReLU(h_{1}\cdot W_{4,1}^{(h)})h4=ReLU(h1⋅W4,1(h))
节点3和4没有后续节点,因此根据它们计算输出值。输出值为它们的均值
h_{t}=(h_{3}+h_{4})/2ht=(h3+h4)/2
对于每个j< lj<l的节点对,都有一个独立的权重矩阵W_{l,j}^{(h)}Wl,j(h),为每个节点l一旦确定其前驱节点j,则使用该矩阵。在ENAS中,所有循环单元共用一组相同的权重参数。
下面介绍控制器如何生成该网络结构。控制器在每次预测时需要做两个决策:确定以哪个节点的输出值作为输入即作为当前节点的前驱,为当前节点选用哪种激活函数。对于第1个节点,输入值是确定的,为x_{t}xt和h_{t-1}ht−1,控制器只用为其选择激活函数。接下来生成节点2,首先选择一个节点作为输入即作为节点2的前驱节点,然后为节点2选择激活函数。其他节点依次类推,这一过程如下图所示。
假设循环神经网络的单元有N个节点,在生成第i个节点时,可以选择的前驱节点为[0,i-1]之间的整数,有i种情况,因此网络的拓扑结构有N!种。对于每种网络拓扑,每个节点的激活函数有4种选择,分别为tanh,sigmoid,identity,ReLU。因此可以搜索的网络结构总共有4^{N}\times N!4N×N!种情况。
下面介绍卷积神经网络的生成方式。下图给4个节点的卷积神经网络,同样的,红色的边表示有效边,黑色的未激活。这个图对应的网络结构如右图所示。
与循环神经网络不同的是,这里每个节点可以允许有多个前驱节点。例如节点3有两个前驱,分别为1和2。
生成卷积神经网络结构的方法与循环神经网络类似:为当前节点选择前驱节点,为当前节点选择要使用的运算。这两个决策结果形成卷积神经网络的一个层。这一过程如上图所示。
对于第k层,小于等于k-1的不同层都可以用来作为它的输入,因此有2^{k-1}2k−1种连接关系。对于上图中的卷积神经网络,在k=4时选择{1,3}作为它的前驱,导致第1、3个层都与第4个层连接。这种做法可以形成任意的跨层连接。
在每个节点处允许的运算有6种情况,分别是:3\times 33×3卷积,5\times 55×5卷积,3\times 33×3深度可分离卷积,5\times 55×5深度可分离卷积,3\times 33×3均值池化,3\times 33×3最大值池化。与循环神经网络相同,每个节点处都有所有运算的参数,并被所有网络结构共享。生成卷积神经网络结构的过程如下图所示。
如果一个卷积神经网络有L个层,则在第k层处有2^{k-1}2k−1种连接关系,由于各个层之间的连接关系是单独确定即相互独立的,因此网络的连接关系及拓扑结构有
2^{1+2+...+L-1}=2^{L(L-1)/2}21+2+...+L−1=2L(L−1)/2
种情况,对于每种网络结构,在每个层有6种可供选择的运算,因此所有可能的网络结构有
6^{L}\times 2^{L(L-1)/2}6L×2L(L−1)/2
种情况。如果L=12,则所有可能的网络结构数为1.6\times 10^{29}1.6×1029。
除了生成整个卷积网络,还可以生成卷积网络额单元然后将其堆叠形成完整的网络,具体做法与NASNet类似,不再重复介绍。
生成网络结构之后,接下来的核心任务是训练子网络和控制器网络。假设控制器网络的参数为\thetaθ,子网络的参数为W,后者被所有子网络共享。这两组参数交替训练,在每次迭代时分两个阶段,首先训练W,然后训练\thetaθ。
第一阶段先固定住控制器的参数,控制器的输出策略为\pi (m;\theta )π(m;θ),从中采样出网络结构。以交叉熵作为损失函数,计算损失函数对w的梯度并更新。第二阶段固定住w,用 REINFORCE算法更新控制器的参数\thetaθ,最大化奖励值的数学期望
E_{m-\pi (m;\theta )}\left [ R(m,w) \right ]Em−π(m;θ)[R(m,w)]
奖励值R(m,w)R(m,w)为子网络m在验证集上的精度值。训练完成之后,根据控制器的策略\pi (m;\theta )π(m;θ)采样出多个子网络并训练,计算它们在验证集上的表现,选择表现最好的网络作为最终生成的网络。
除上述方法之外,用强化学习实现NAS还有其他方案,具体可以阅读参考文献[11-13]。
遗传算法
使用遗传算法求解NAS的思路是将子网络结构编码成二进制串,运行遗传算法得到适应度函数值(神经网络在验证集上的精度值)最大的网络结构,即为最优解。首先随机初始化若干个子网络作为初始解。遗传算法在每次迭代时首先训练所有子网络,然后计算适应度值。接下来随机选择一些子网络进行交叉,变异生成下一代子网络,然后训练这些子网络,重复这一过程,最后找到最优子网络。
首先要解决的问题是如何将神经网络的结构编码成固定长度的二进制串。下面介绍文献[5]的方法,它将网络划分为多个级(stage),对每一级的拓扑结构进行编码。级以池化层为界进行划分,是多个卷积层构成的单元,每一组卷积核称为一个节点(node)。数据经过每一级时,高度,宽度和深度不变。每一级内的卷积核有相同的通道数。每一次卷积之后,执行批量归一化和ReLU激活函数。对全连接层不进行编码。对于每一组卷积核,首先对输入数据进行逐元素相加,然后执行卷积。
假设整个神经网络共有S级,级的编号为s=1,…,S 。第S级有k_{S}kS个节点,这些节点的编号为k_{s}=1,…,K_{s}ks=1,…,Ks。第s级的第k_{s}ks个节点为 v_{s,k_{s}}vs,ks。这些节点是有序的,只允许数据从低编号的节点流向高编号的节点以保证生成的是有向无环图。每一级编码的比特数为
1+2+...+(K_{s}-1)=\frac{1}{2}K_{s}(K_{s}-1)1+2+...+(Ks−1)=21Ks(Ks−1)
节点v_{s,k_{s}}vs,ks与v_{s,1},…v_{s,k_{s-1}}vs,1,…vs,ks−1之间都可能有边连接,因此对于它需要k_{s-1}ks−1个比特位。第1个节点不需要编码,因为没有编号更小的节点连接它;第2个节点v_{s,2}vs,2可能有连接(v_{s,1},v_{s,2})(vs,1,vs,2);第3个节点可能有连接(v_{s,1},v_{s,3})(vs,1,vs,3),;其他以此类推。如果节点之间有边连接,则编码为1;否则为0。对于一个S级的网络,总编码位数即长度为
L= \sum_{s=1}^{S}K_{s}(K_{s}-1)L=∑s=1SKs(Ks−1)
下图是一个2级网络的编码结果。第1级有4个节点,第2级有5个节点。需要注意的是为了保证数据的流入和流出,有特定的节点(红色和绿色)充当输入与输出节点。
为确保每个二进制串都是合法的,为每一级定义了两个默认节点。默认输入节点从上一级接收输入,然后将数据送入所有每一前置节点的节点。默认输出节点从所有每一后续节点的节点接收数据,求和,然后执行卷积,将数据送入池化层。默认节点与其他节点之间的连接关系没有进行编码。需要注意的是,如果一个节点时孤立节点,即既没有前驱,也没有后续,则忽略。也就是说,孤立节点不会和默认输入节点、默认输出节点建立连接。这样做是为了保证有更多节点的级可以模拟节点数更少的级所能表示的所有结构。
上图的上半部分为第1级,A0为默认输入节点,A4为默认输出节点。A1-A4为内部节点,需要进行编码。A2的前驱为A1,因此编码为1;A3没有前驱,因此编码为00;A4的前驱为A1,A2,A3,因此编码为111。这一级的编码为1-00-111。上图的下半部分为第2级,B0为默认输入节点,B6为默认输出节点。B1-B5为内部节点,需要编码。B2没有前驱,因此编码为0;B3的前驱为B1,因此编码为10;B4没有前驱,因此编码为000;B5的前驱为B3和B4,因此编码为0011。这一级的完整编码为0-11-000-0011。
对于编码长度为L的网络,所有可能的网络结构数为2^{L}2L。如果一个网络有3级即S=3,各级节点数为(K_{1},K_{2},K_{3})(K1,K2,K3)。则L=19,2^{L}=524288L=19,2L=524288,这个数据非常大,对于更深的网络,搜索空间就更大。
这种编码方式可以表示各种典型的网络拓扑结构,包括VGGNet,ResNet,DenseNet。这些网络的编码如下图所示。对于有些特殊的网络,如GoogLeNet的Inception模块,通过将基本块进行扩充,也可以支持。
在对网络进行二进制编码之后,接下来是标准的遗传算法流程。首先初始化 个随机的个体(子网络),然后执行T次循环。每次循环中有选择,变异,交叉这3种操作。遗传算法生成神经网络的结构之后,从头训练该网络,将其在验证集上的精度作为适应度函数。下面分别进行介绍这些步骤的细节。
(1)初始化。首先随机初始化N个长度为L的二进制串,表示N个网络结构。每个二进制位用伯努利分布的随机数生成,取值为0和1的概率各为0.5。接下来训练这N个网络,得到它们的适应度函数值。
(2)选择。在每次迭代的开始是选择,上一轮迭代生成的N个个体都计算出了适应度函数值。这里使用俄罗斯轮盘赌来确定哪些个体可以生存,选择每个个体的概率与它的适应度函数成正比。因此之前表现好的神经网络有更大的概率被选中,表现最差的网络被剔除。由于在迭代过程中N的值不变,因此有些个体可能会被选中多次。
(3)变异与交叉。变异的做法是每个二进制位分别独立的以某一概率将其值取反 ,即将0变成1,或将1变成0。这个概率值被设置为0.05,因此个体变异不会变异太多。变异不是对二进制位而是对级进行的,即以一定的概率交换两个个体的某一级的二进制位编码。对于下面两个个体
1-00-100 1-00-100
0-10-111 0-10-111
如果选择第2级进行变异,则变异之后的个体为
1-00-100 0-10-111
0-10-111 1-00-100
(4)评估。在每次循环执行完上面的3个步骤之后,接下来要对生成的神经网络进行评估即计算它们的适应度函数值。如果某一网络结构之前没有被评估过,则对其进行训练,在验证集上得到精度值,作为适应度函数值。如果某一网络之前被评估过,此次也从头开始训练,然后计算它各次评估值的均值。完整的算法如下。
输入:参考数据集D,迭代次数T,每次迭代时的个体数N,变异概率P_{M}PM,交叉概率P_{C}PC,变异参数q_{M}qM,交叉参数q_{C}qC
初始化:随机生成个体
\left \{ {M_{0,n}} \right \}_{n=1}^{N}{M0,n}n=1N
计算它们的识别精度作为适应度函数值
for t=1,…,T do
选择:根据上一代的个体
\left \{ M_{t-1,n} \right \}_{n=1}^{N}{Mt−1,n}n=1N
,用俄罗斯轮盘赌生成新个体
\left \{ M_{t-1,n} \right \}_{n=1}^{N}{Mt−1,n}n=1N
交叉:对每一对个体
\left \{ M_{t,2n-1},M_{t,2n} \right \}_{n=1}^{N/2}{Mt,2n−1,Mt,2n}n=1N/2
用概率P_{C}PC和参数q_{C}qC执行交叉
变异:对所有非交叉个体
\left \{ M_{t,n} \right \}_{n=1}^{N}{Mt,n}n=1N
用概率P_{M}PM和参数q_{M}qM进行变异
评估:计算所有个体
\left \{ M_{t,n} \right \}_{n=1}^{N}{Mt,n}n=1N
的识别精度
end for
输出:最后一代的所有个体
\left \{ M_{T,n} \right \}_{n=1}^{N}{MT,n}n=1N
以及它们的识别精度
对于NAS问题,在遗传算法的初始化,样本选择,交叉,变异等环节均有改进方案。更多的基于遗传算法的NAS搜索可以阅读参考文献[14-19]。
基于梯度的优化算法
前面介绍的NAS算法都存在计算量大的问题,虽然存在改进方案。强化学习、遗传算法等方案低效的一个原因是结构搜索被当作离散空间(网络结构的表示是离散的,如遗传算法中的二进制串编码)中的黑箱优化问题,无法利用梯度信息来求解。
其中一种解决思路是将离散优化问题连续化。文献[6]提出了一种称为可微结构搜索(Differentiable Architecture Search,简称DARTS)的算法,将网络结构搜索转化为连续空间的优化问题,采用梯度下降法求解,可高效地搜索神经网络架构,同时得到网络的权重参数。
DARTS将网络结构、网络单元表示成有向无环图,对结构搜索问题进行松弛,转化为连续变量优化问题。目标函数是可导的,能够用梯度下降法求解,同时得到网络结构和权重等参数。算法寻找计算单元,作为最终网络结构的基本构建块。这些单元可以堆积形成卷积神经网络,递归连接形成循环神经网络。
首先为网络单元定义有向无环图。假设有向无环图有N个顶点,顶点x_{i}xi为数据的潜在表示,如卷积神经网络的特征图像。图的边(i,j)为某种运算o_{i,j}oi,j,对x_{i}xi进行变换,然后将变换结构送入顶点x_{j}xj。典型的运算有卷积、池化、以及零运算,零运算在后面介绍。每个单元有2个输入节点,1个输出节点。对于卷积单元,输入节点为上两层的输出值;对于循环神经网络,输入节点为当前时刻的输入值以及上一时刻的状态值。因此卷积神经网络和循环神经网络可以用这种图统一表示。输出节点的值根据对所有中间节点进行约简运算(reduction operation,如concatenation)而得到。每个中间节点的值根据其所有前驱节点(编号更小的节点)计算得到,计算公式为
x_{i}= \sum_{j< i}o_{(j,i)}(x_{j})xi=∑j<io(j,i)(xj)
零运算是一种特殊的运算,它表示两个节点之间没有关系,即节点x_{i}xi的值不会用来计算x_{j}xj的值。下图是一个有向无环图表示。
接下来对问题进行松弛,转化为连续优化问题。对于某一网络结构,顶点x_{i}xi到顶点x_{j}xj的运算是确定的。在这里将其概率化,即表示为各种运算的概率叠加。假设O为所有候选运算的集合,o(\cdot )o(⋅)为作用于x_{i}xi的某种运算。则节点x_{j}xj的值是各种候选运算的结果的概率叠加,而使用每种运算的概率用softmax回归表示。计算公式为
\bar{o}_{(i,j)}(x)=\sum_{o\in O}\frac{exp(\alpha _{o,(i,j)})}{\sum_{o\in O}exp(\alpha _{o,(i,j)})}o(x)oˉ(i,j)(x)=∑o∈O∑o∈Oexp(αo,(i,j))exp(αo,(i,j))o(x)
其中\alphaα为系数值,用于计算使用每种运算的概率值。如果定义向量
\alpha =[\alpha _{1(i,j)}...\alpha _{\left | O \right |,(i,j)}]α=[α1(i,j)...α∣O∣,(i,j)]
则网络结构搜索问题可以转换为求解这些向量。得到这些向量的值之后即可确定网络解结构,在每个顶点处选择概率最大的运算作为该节点的运算
O_{i,j}=arg max_{o\in O}\alpha _{o,(i,j)}Oi,j=argmaxo∈Oαo,(i,j)
这些向量可以看做是网络结构的编码表示。在将问题进行松弛之后,接下来的目标是同时求解网络结构\alphaα以及所有混合运算所对应的权重W。算法的目标是最优化验证集上的损失,采用梯度下降法。
假设L_{train}Ltrain与L_{val}Lval分别为训练集与验证集上的损失,它们不仅与网络结构有关,还与网络权重参数有关。结构搜索的目标是寻找最优的结构\alpha ^{*}α∗以最小化验证集损失L_{val}(w^{*},\alpha ^{*})Lval(w∗,α∗)。而由此结构所决定的最优权重w^{*}w∗通过最小化训练集上的损失而确定
w^{*}=arg min_{w}L_{train}(w,\alpha ^{*})w∗=argminwLtrain(w,α∗)
因此该最优化问题是一个双层优化问题,\alphaα为上层优化变量,而w为下层优化变量,可以形式化的表述为
min_{\alpha }L_{val}(w^{*}(\alpha ),\alpha )\\s.t \quad w^{*}(\alpha )=argmin_{w}L_{train}(w,\alpha )minαLval(w∗(α),α)s.tw∗(α)=argminwLtrain(w,α)
精确求解上面的优化问题成本太高,只要\alphaα有任何改变,都需要求解内层优化问题得到w^{*}(\alpha )w∗(α)。DARTS进行了近似求解,梯度下降时交替优化权重和结构。在第k次迭代时,根据当前结构\alpha _{k-1}αk−1,通过将w_{k-1}wk−1向着使得训练误差L_{train}(w_{k-1},\alpha _{k-1})Ltrain(wk−1,αk−1)最小化的方向移动而得到w_{k}wk。接下来固定住w_{k}wk,最小化在验证集上的损失而得到新的网络结构。这一步优化的目标为
L_{val}\left ( w_{k}-\xi \bigtriangledown _{w}L_{train}(w_{k},\alpha _{k-1}),\alpha _{k-1} \right )Lval(wk−ξ▽wLtrain(wk,αk−1),αk−1)
这里先对w进行了一次梯度下降迭代,\xiξ为学习率。根据复合函数的求导法则,上面的目标函数对\alphaα的梯度为
\bigtriangledown _{\alpha }L_{val}(w,\alpha )-\xi \bigtriangledown _{\alpha ,w}^{2}L_{train}(w,\alpha )\bigtriangledown _{w}L_{val}(w,\alpha )▽αLval(w,α)−ξ▽α,w2Ltrain(w,α)▽wLval(w,α)
其中
w^{'}=w-\xi \bigtriangledown _{w}L_{train}(w,\alpha )w′=w−ξ▽wLtrain(w,α)
上面梯度计算公式的第二项为矩阵与向量的乘法,为了加快计算速度,用有限差分公式进行了近似。完整的算法流程如下所示。
为每条边(i,j)根据其参数化的向量\alpha _{i,j}αi,j计算混合操作\bar{o}_{i,j}oˉi,j
while没有收敛,do
根据\bigtriangledown _{w}L_{train}(w,\alpha )▽wLtrain(w,α)更新权重w
根据\bigtriangledown _{\alpha }L_{val}(w-\xi \bigtriangledown _{w}L_{train}(w,\alpha ),\alpha )▽αLval(w−ξ▽wLtrain(w,α),α)更新结构\alphaα
对每条边(i,j)用o_{ij}=arg max_{o\in O}\alpha _{o,(i,j)}oij=argmaxo∈Oαo,(i