ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【Pytorch】Paper Reading: GELUs

【Pytorch】Paper Reading: GELUs

Paper Reading: GELUs

1. 简介

文章全名: GAUSSIAN ERROR LINEAR UNITS (GELUs)

这个激活函数是LZ在看BERT的时候,提及到的,因为对这个激活函数还不是很熟悉,于是就补充下对应的知识。主要想知道几个问题:

  • 激活函数是怎么实现的?
  • 与之前的激活函数相比,它的优势在什么地方?
  • 使用它对训练结果有什么影响?

什么是GELUs?是指高斯误差线性单元(Gaussian Error Linear Unint, GELU),这是一种高性能的神经网络激活函数,表达式为x Φ ( x ) x\Phi(x)xΦ(x),其中Φ ( x ) \Phi(x)Φ(x)表示的标准高斯累积分布函数,GELU的非线性是根据输入的值对输入进行加权,在ReLU中,是按照( x 1 x > 0 ) (x\textbf{1}_{x>0})(x1x>0​),也就是通过符号对输入进行门控,在文中,作者针对ReLU,ELU和GELU进行实验评估,发现所有考虑的计算机视觉,自然语言处理和语音性能都会有所改进,这么牛?不知道这种激活函数与板端的兼容性怎么样…

2. Why GELU?

学习深度学习,我们最先接触的激活函数都是Sigmoid activations,常见的还有ReLU,ELU等但是这些激活函数或多或少都会有一定的缺陷,具体可以看这篇文章。

我们为了防止模型过拟合,会对网络进行随机的正则化,例如给隐藏层增加噪声,或者进行dropout操作。这些正则化操作是独立于激活函数的,同时也是独立于网络的输入的。

于是,作者想了一个新的激活函数,就是本文的主角了,GELU,它本身就具有了随机正则化的效果,这使得神经元的输出更加具有概率性。

3. GELU具体是怎么做的呢?

作者想要创造出一个激活函数,可以同时拥有以下的性质:

  • dropout:随机让神经元失活,具体的操作可以看成是输入随机乘以0

  • zoneout:随机将这个时刻的激活替换为上一时刻的激活,也就是将输入随机乘以1

  • ReLU: 激活函数,随机的将输入乘以0或者1

综合上面的描述,作者将输入随机乘以0或者1,具体是0还是1是随机确定的,但是也由输入来进行确定。

具体的操作如下:

可以将神经元的输入x xx乘以一个分布m ~ B e r n o u l i ( Φ ( x ) ) m~Bernouli(\Phi(x))m~Bernouli(Φ(x)), 其中Φ ( x ) = P ( X < = x ) , X ~ N ( 0 , 1 ) \Phi(x)=P(X<=x), X~N(0, 1)Φ(x)=P(X<=x),X~N(0,1)是标准正态分布的累积分布函数,作者选择这个分布是因为神经元的输入通常是服从正态分布的,尤其是在batchnorm以后。在这种情况下,随着x的输入越小,那么输入被“丢弃”的概率会更高,因此这个函数应用在输入的x是随机的,并且在一定程度上依赖于输入。

最终定义的GELU为:

G E L U ( x ) = x P ( X < = x ) = x Φ ( x ) = x ⋅ 1 2 [ 1 + e r f ( x 2 ) ] GELU(x) = xP(X<=x)=x\Phi(x)=x\cdot{\frac 12}[1+erf(\frac x{\sqrt2})]GELU(x)=xP(X<=x)=xΦ(x)=x⋅21​[1+erf(2​x​)]

还有一个近似:

0.5 x ( 1 + t a n h [ 2 π ( x + 0.044715 x 3 ) ] ) 0.5x(1+tanh[\sqrt{\frac 2 \pi}(x+0.044715x^3)])0.5x(1+tanh[π2​​(x+0.044715x3)])

在pytorch中可以按照如下方式进行验证

defxyplot(x_vals,y_vals,name):plt.figure()plt.rcParams['figure.figsize']=(5,3.5)plt.plot(x_vals.detach().numpy(),y_vals.detach().numpy(),label=name,linewidth=1.5,color='#FF0000')plt.grid(True,linestyle=':')plt.legend(loc='upper left')# dark_background, seaborn, ggplotplt.style.use("seaborn")ax=plt.gca()ax.spines['right'].set_color("none")ax.spines['top'].set_color("none")ax.spines['bottom'].set_position(("data",0))ax.spines['left'].set_position(("data",0))ax.spines['bottom'].set_linewidth(0.5)ax.spines['left'].set_linewidth(0.5)ax.xaxis.set_ticks_position('bottom')ax.yaxis.set_ticks_position('left')plt.savefig(func_pic_res+"{}.jpg".format(name))deftest_GELU():x=torch.arange(-10.0,10.0,0.1,requires_grad=True)y=F.gelu(x)xyplot(x,y,'GELU')# 导数y.sum().backward()xyplot(x,x.grad,'grad of GELU')defgelu(x):returnx*0.5*(1.0+torch.erf(x/math.sqrt(2.0)))deftest_GLUE2():x=torch.arange(-10.0,10.0,0.1,requires_grad=True)y=gelu(x)xyplot(x,y,'GELU2')# 导数y.sum().backward()xyplot(x,x.grad,'grad of GELU2')

最后测试得到的图如下所示:

这是使用pytoch内置GELU得到的图



这是按照论文公式得到的图

4. 结论

接下来论文都是讲述一些实验了,要是感兴趣的小伙伴可以自己去研究下哈,总的结论就是作者对这个激活函数进行大量的实验,GELU的准确性超过了ELU和ReLU,所以想要可以成为非线性激活函数的一个替代方案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进