GELU,全称为Gaussian Error Linear Unit,也算是RELU的变种,是一个非初等函数形式的激活函数。它由论文《Gaussian Error Linear Units (GELUs)》提出,后来被用到了GPT中,再后来被用在了BERT中,再再后来的不少预训练语言模型也跟着用到了它。随着BERT等预训练语言模型的兴起,GELU也跟着水涨船高,莫名其妙地就成了热门的激活函数了。

gelu函数图像

gelu函数图像

在GELU的原始论文中,作者不仅提出了GELU的精确形式,还给出了两个初等函数的近似形式,本文来讨论它们是怎么得到的。

GELU函数 #

GELU函数的形式为
GELU(x)=xΦ(x)


其中Φ(x)是标准正态分布的累积分布函数,即
Φ(x)=xet2/22πdt=12[1+erf(x2)]

这里erf(x)=2πx0et2dt。然后原论文还提了两个近似:
xΦ(x)xσ(1.702x)

以及
xΦ(x)12x[1+tanh(2π(x+0.044715x3))]

现在仍然有不少Transformer架构模型的实现都是用近似(4)作为GELU函数的实现。不过很多框架已经有精确的erf计算函数了,所以初等函数近似形式的价值可能不会很大,因此大家就当是一道数学分析练习题吧~

用啥近似 #

显然,要找GELU的近似形式,就相当于找Φ(x)近似,这也等价于找erf(x2)的近似。

erf函数图像

erf函数图像

首先,我们要解决第一个问题:用什么函数来近似。从erf(x)图像我们可以看出它的特点:

1、它是一个奇函数,即erf(x)=erf(x)

2、它单调递增,并且limxerf(x)=1,limx+erf(x)=1

奇函数我们有很多,比如x2n+1,sinx,tanx,tanhx等,并且奇函数的叠加、复合函数依然是奇函数,比如sin(x+x3);又是奇函数,又单调递增且有界的,我们最容易想到的可能是tanhx,事实上,tanhx确实跟erf(x)很相似。

因此,我们可以从tanhx出发,构造一些可能的拟合形式,比如
{tanh(ax+bx3+cx5)atanhx+btanh3x+ctanh5xatanhbx+ctanhdx+etanhfx

怎样近似 #

有了待拟合的形式之外,下面要考虑的就是怎么拟合、以什么标准拟合的问题了,说白了,就是想个办法求出各项系数来。一般来说,有两种思路:局部拟合和全局拟合。

局部拟合 #

局部拟合基于泰勒展开,比如考虑近似形式tanh(ax+bx3),我们在x=0处展开,得到
erf(x2)tanh(ax+bx3)=(2πa)x+(a33b132π)x3+


让前两项为0,刚好得到两个方程,求解得到
a=2π,b=4π32π3/2

代入xΦ(x),并换成数值形式,那么就是
xΦ(x)12x[1+tanh(2π(x+0.0455399x3))]

全局拟合 #

(8)已经跟式(4)很接近了,但是第二个系数还是差了点。这是因为(8)纯粹是局部近似的结果,顾名思义,局部近似在局部会很精确,比如上面的推导是基于x=0处的泰勒展开,因此在x=0附近会比较精确,但是离0远一点时误差就会更大。因此,我们还需要考虑全局误差。

比较容易想到的全局误差是积分形式的,比如用g(x,θ)去逼近f(x)时,我们去算
minθ[f(x)g(x,θ)]2dxminθ|f(x)g(x,θ)|dx


但是,每个x处的误差重要性可能不一样,因此为了不失一般性,还要乘以一个权重λ(x),即
minθλ(x)[f(x)g(x,θ)]2dxminθλ(x)|f(x)g(x,θ)|dx

不同的λ(x)会导致不同的解,哪个λ(x)最适合,也不容易选择。

因此,我们不去优化这种积分形式的误差,我们优化一个更直观的minmax形式的误差:
minθmaxx|f(x)g(x,θ)|


这个式子很好理解,就是“找一个适当的θ,使得最大的|f(x)g(x,θ)|都尽可能小”,这样的目标符合我们的直观理解,并且不涉及到权重的选取。

混合拟合 #

基于这个思想,我们固定a=2π,然后去重新求解tanh(ax+bx3)。固定这个a是因为它是一阶局部近似,我们希望保留一定的局部近似,同时希望b能尽可能帮我们减少全局误差,从而实现局部近似与全局近似的混合。所以,现在我们要求解
minbmaxx|erf(x2)tanh(ax+bx3)|


用scipy可以轻松完成求解:

import numpy as np
from scipy.special import erf
from scipy.optimize import minimize

def f(x, b):
    a = np.sqrt(2 / np.pi)
    return np.abs(erf(x / np.sqrt(2)) - np.tanh(a * x + b * x**3))

def g(b):
    return np.max([f(x, b) for x in np.arange(0, 4, 0.001)])

options = {'xtol': 1e-10, 'ftol': 1e-10, 'maxiter': 100000}
result = minimize(g, 0, method='Powell', options=options)
print(result.x)

最后得到b=0.035677337314877385,对应的形式就是:
xΦ(x)12x[1+tanh(2π(x+0.04471491123850965x3))]


最后几位有效数字可能有误差,但前面部分已经跟式(4)完美契合了~补充说明下,式(4)提出自论文《Approximations to the Cumulative Normal Function and its Inverse for Use on a Pocket Calculator》,已经是40多年前的结果了。

至于第一个近似,则来自论文《A logistic approximation to the cumulative normal distribution》,它是直接用σ(λx)全局逼近Φ(x)的结果,即
minλmaxx|Φ(x)σ(λx)|


解得λ=1.7017449256323682,即
Φ(x)σ(1.7017449256323682x)

这跟式(3)同样很吻合。

文章小结 #

本文带大家一起做了道数学分析题——介绍了GELU激活函数,并试图探索了它的两个近似形式的来源,成功了水出了这篇博文~

转载到请包括本文地址:https://spaces.ac.cn/archives/7309

更详细的转载事宜请参考:《科学空间FAQ》

如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。

如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!

如果您需要引用本文,请参考:

苏剑林. (Mar. 26, 2020). 《GELU的两个初等函数近似是怎么来的 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/7309

@online{kexuefm-7309,
        title={GELU的两个初等函数近似是怎么来的},
        author={苏剑林},
        year={2020},
        month={Mar},
        url={\url{https://spaces.ac.cn/archives/7309}},
}