激活函数发展的新里程——EvoNorms
之所以把这个函数称为激活函数发展的新里程,我的理由就是给人们提供了一种新视角,甚至说打破了之前的固有思维。神经网络为了更加容易训练,提出了Normalization,这种思路取得的成功,使得现在的神经网络越来越离不开归一化。之前写过一篇关于 Normalization的文章,是站在框架的角度去写的,有兴趣可以阅读一下,也便于对本文理解深刻。
随着网络的发展,归一化通常和激活函数都是在一起出现的,都伴随着卷积而出现。所以也就难免很多人把归一化和激活放在一起讲,但是那么多归一化和那么多激活,并不是所有的效果都好,也基本上就产生了,大家所常见的几种范式,比如BN-ReLU,GN-ReLU,BN-Swish等。既然联系已经这么紧密了,能否通过一个操作,一次搞定呢。或者对于财大气粗的谷歌,这种传统的计算范式就真的好么,人家可以利用各种组合,把组合出来的这么多结果搜索找出最好的。
搜索的过程也具有很好的借鉴意义,感兴趣的同学建议阅读原文。本文直接讨论这个组合算法的特点。
回顾以前的归一化方法,BN是基于批统计量来计算样本的均值和方差,而像是LayerNorm,则就独立于批统计量,同时还有GroupNorm,则是弱化了批统计量的概念。而在实现中BN因为有批统计量的需求,所以需要使用 指数滑动均值来计算统计量,并保存。
谷歌在算法中也搜索了两类计算,一种是需要使用批统计量的,这一类算法被称为EvoNorm-B系列。一种是独立于批统计量的,这一类算法称为EvoNorms-S系列。而两个系列中评分最高,总体效果最好的,则分别就是EvoNorm-B0和EvoNorm-S0。这就是本文的主角。
EvoNorm-B0
需要使用批统计量,则基本上就是对标BN的,但是这个算法还有激活函数的功能,一个BN不够打,再加一个ReLU。因为搜索的时候,是基于计算图替换的,下面给出EvoNorm-B0的计算图。这个计算图保证了输入和输出的维度不变,因为归一化层+激活层的特点也是不改变输入维度,只是进行函数映射。
图中节点所表示的意思,从图中轻易就能获取,初始节点包括输入,输出,以及两个沿通道维度的可训练节点,分别被被初始化为0和1,称为 v 0 v_0 v0和 v 1 v_1 v1。未使用的节点表示这个过程中未采用的节点,但是在搜索过程中,使用了遗传算法的变体,所以这些节点可以用于突变。
计算图不够直观,这里看看表达式,这个表达式一个就干了正则化和激活两件事。
这个时候,再来看看BN-ReLU的组合表达式,其实发现功能类似,参与运算的元素(计算图中的节点)也是类似的。
反观EvoNorm-B0,很大的一个特点就是不单单使用一种方差,而是使用两种方差的混合作为分母,前者就是BatchNorm中使用的方差,后者则是InstanceNorm使用的方差,显然这个计算更加复杂了。但是效果好啊,你问为什么要这要搞,搜索出来的啊。但是也有解释,那就是前者方差可以在batch中捕获数据的全局信息,而后者则很好捕获每张图像的局部信息。然后在分母上取了最大,这样提供了非线性,连激活函数都省了。
实验结果表明,在ImageNet上,在目标检测和分割任务中,甚至在生成任务中,EvoNorm-B0都是相对于BN-ReLU更好的。
pytorch实现
class EvoNormB0_2d(nn.Module):
def __init__(self, num_features, nonlinearity=True, affine=True, momentum=0.9, eps=1e-5, training=True):
super(EvoNormB0_2d, self).__init__()
self.nonlinearity = nonlinearity
self.training = training
self.momentum = momentum
self