Courses
激活函数决定神经网络中哪些信号能够通过、哪些被阻断。若选择不当,模型要么学习过慢,要么无法泛化。多年来,ReLU 因其速度快且对大多数任务“足够好”而成为合理的默认选择。
GELU(Gaussian Error Linear Unit,高斯误差线性单元)改变了这一点。它如今是一些最强大模型背后的激活函数,包括 BERT 和 GPT。
本文将介绍 GELU 的直觉、其公式、与其他激活函数的比较,以及在实践中应如何使用。
如果您对机器学习中的激活函数还很陌生,请先阅读我们的ReLU(修正线性单元)新手指南博文。
什么是 GELU 激活函数?
GELU(高斯误差线性单元)是一种激活函数,它以平滑且具有概率性的方式,根据输入幅度对其加权。
大多数激活函数会做出“通过或阻断”的二元决定。例如,ReLU 会将所有负值置零,而对其他值不作改变。GELU 则不同。它没有硬阈值,而是根据输入的大小平滑缩放,这意味着即使是很小的负值也能对输出有所贡献。
与 ReLU 的不同在于,GELU 在各处都平滑且连续。在零点没有尖角,也不存在突变。这种平滑性在训练中会起作用,因为它为优化器提供了更干净的梯度信息。
GELU 的直觉
可以把 GELU 想象成一种不会一视同仁对待输入的滤波器。
ReLU 很“生硬”——任何负值都会被置零,次次如此。相反,GELU 会问“这个输入值有多可能是有用的?”明显较大且为正的值几乎原样通过;较小或为负的值被缩小,而不是完全切断。
结果就是一条平滑曲线,它抑制不那么相关的信号,但不将其彻底丢弃。
想象您在审阅一摞求职申请。严格的筛选会毫无例外地剔除没有学位的人。更聪明的筛选会考虑那些“差一点”的候选人——也许他们有能弥补的相关经验。GELU 就像更聪明的筛选。它不做死板的切割,而是根据输入的幅度来衡量,并决定放行多少。
这种渐进、带概率的缩放使 GELU 与众不同。没有突变、没有“死亡神经元”——而是对每个输入值做出平滑的“通过或抑制”决策。
GELU 公式
精确的 GELU 公式建立在高斯累积分布函数(CDF)之上,写作:

Gaussian cumulative distribution function
其中,x 是输入值,Φ(x) 表示从标准正态分布中抽取的随机变量小于等于 x 的概率。通俗地说,Φ(x) 告诉您输入值有多“正常”或多符合预期——而 GELU 就用这个概率来缩放输入。
输入越大,Φ(x) 越接近 1,意味着输入几乎原样通过;输入越小,Φ(x) 越接近 0,意味着输入会被抑制。
实践中的近似
精确公式的问题在于计算 Φ(x) 成本较高。它涉及误差函数,而误差函数没有简单的闭式形式,在大规模计算时较慢。
深度学习框架改用如下近似:

GELU approximation formula
该近似使用 tanh,其计算快速,且在现代硬件上支持良好。对于实践中关心的输入范围,该近似与精确公式的结果几乎一致,这也是 PyTorch 和 TensorFlow 等框架默认采用它的原因。
当然,您不必背下任何一个公式。但了解这个近似的存在及其原因,有助于理解在代码中调用 GELU 时实际发生了什么。
GELU 与其他激活函数的比较
每种激活函数处理输入的方式不同,而这些差异会体现在模型训练效果上。
在文字阐述之前,先看一眼它们在视觉上的差异:

GELU 与其他激活函数的对比图
Sigmoid
Sigmoid 将所有输入压缩到 0 到 1 之间。它是平滑的,但存在众所周知的问题:梯度消失。对于非常大或非常小的输入,梯度接近于零,导致更深层停止学习。GELU 没有这个问题,因为它在更宽的输入范围内保持有意义的梯度。
Tanh
Tanh 与 Sigmoid 类似,但以零为中心,输出范围为 -1 到 1。它比 Sigmoid 更好地处理负输入,但在极端值处仍会出现梯度消失。GELU 产生更平滑的输出曲线,有助于深层网络中的梯度流动。
ReLU
ReLU 简单且快速:正值原样通过,负值被置零。零点处的硬性切断会导致“死亡神经元”问题——长期接收负输入的神经元将完全停止更新。GELU 通过缩放负输入而非切断来避免这一点。
Leaky ReLU
Leaky ReLU 通过允许一小部分负输入通过来修复“死亡神经元”问题。它比 ReLU 更进一步,但零点处的过渡仍然是突变的。GELU 整体产生更平滑的曲线,在更深的架构中往往效果更好,因为梯度质量尤为重要。
总而言之,以下是这五种激活函数之间的差异:

GELU 与其他激活函数的对比表
为何在 Transformer 中使用 GELU
Transformer 也是深度神经网络。而网络越深,梯度质量就越重要。
像 BERT 和 GPT 这样的模型将数十层堆叠在一起。在这样的深度下,梯度流的小问题会被放大。如果激活函数在某些区域产生不稳定或近零的梯度,网络的早期层在训练中几乎不会更新,也就学不到什么。
GELU 通过在更宽的输入范围内保持梯度平滑且非零来避免这一点。它不像 ReLU 那样有零边界的切断,因此优化器在每一层都能获得更干净的信号,而不仅是靠近输出的层。
另外还有一个原因使 GELU 非常契合 Transformer 架构。
Transformer 通过注意力机制处理输入,会产生范围广泛的激活值——既有正值也有负值。与存在突变的函数相比,平滑的激活函数能更好地处理这种范围。
在最初的 BERT 论文中,作者选择了 GELU 而非 ReLU,并在基准测试中报告了更好的结果。GPT 也沿用了同样的选择。自那以后,GELU 成为大多数基于 Transformer 的架构中的默认激活函数,并非因为它新,而是因为它在这些模型所处的规模下表现更佳。
GELU 的实践
在模型中使用 GELU 与使用其他激活函数一样简单。PyTorch 和 TensorFlow 都内置支持。
PyTorch
在 PyTorch 中,您可以将 GELU 作为独立模块使用,或内联到模型定义中。以下是一个使用 GELU 的简单前馈模块:
import torch
import torch.nn as nn
class FeedForwardBlock(nn.Module):
def __init__(self, input_dim, hidden_dim):
super().__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.act = nn.GELU()
self.fc2 = nn.Linear(hidden_dim, input_dim)
def forward(self, x):
return self.fc2(self.act(self.fc1(x)))
block = FeedForwardBlock(input_dim=512, hidden_dim=2048)
x = torch.randn(8, 512)
output = block(x)
nn.GELU() 位于两个线性层之间,这正是您在 Transformer 的前馈子层中会看到的位置。激活在第一次投影之后、第二次投影之前运行。
TensorFlow
在 TensorFlow 中,可通过 Keras API 使用 GELU:
import tensorflow as tf
from tensorflow import keras
model = keras.Sequential([
keras.layers.Dense(2048, input_shape=(512,)),
keras.layers.Activation("gelu"),
keras.layers.Dense(512)
])
x = tf.random.normal((8, 512))
output = model(x)
您也可以将其作为字符串参数直接传给 Dense 层:
keras.layers.Dense(2048, activation="gelu")
两种方式的结果相同。
GELU 在网络中的位置
GELU 的位置与其他激活函数相同——紧跟在线性变换之后、下一层之前。在 Transformer 架构中,这意味着位于前馈子层内、两次全连接投影之间。在其他深层网络中,将其置于线性或卷积层之后,让它在向前传递前对输出进行缩放。
GELU 的优势
如果您读到这里,已经了解了与其他激活函数相比,GELU 的主要卖点。这里简要回顾:
- 平滑的激活:GELU 产生连续、可微的曲线,没有突变,为优化器在每一步提供更干净的信息。
- 更好的梯度流:GELU 不会将负输入置零,因此梯度仍可通过接收负值的神经元传播,降低训练过程中神经元“死亡”的风险。
- 更适合深度模型的性能:在诸如 Transformer 等深层架构中,更平滑的梯度带来的累积效应,往往能转化为较之简单激活函数更好的训练结果。
GELU 的局限
GELU 并非在任何场景下都是最佳选择。以下是您需要注意的几个局限:
-
相比 ReLU 计算更昂贵:GELU 涉及误差函数或基于
tanh的近似,两者成本都高于 ReLU 的简单阈值操作。在包含大量层的大模型中,这些开销会累积。 -
不够直观:像 ReLU 这样函数的直觉很清晰——正值通过、负值不通过。GELU 的概率性缩放更难解释。
-
并非总是必要:对于浅层网络或更简单的任务,GELU 往往没有实质优势。ReLU 或 Leaky ReLU 通常能以更低的计算成本取得相近表现。
总之,如果您在构建 Transformer 或其它深层架构,GELU 是一个稳妥的默认选择。对于其他情形,建议在采用前先做基准测试。
结语
GELU 不是普适升级,也不是可以替代 ReLU 的“一刀切”方案。它是一种在特定场景下值得采用的深思熟虑的设计选择——如深度网络与 Transformer 模型。
如果您在使用 BERT、GPT 或任何基于 Transformer 的模型,无论是否意识到,您已经在使用 GELU。现在您知道它为何存在了。
对于其他情况,激活函数的选择取决于权衡。没有任何一种函数总能获胜;了解各自的特点,能让您基于信心而非习惯做出选择。
如果您仍然觉得不同激活函数之间的差异令人困惑,欢迎报名我们的机器学习工程师学习路径,为机器学习与 MLOps 的职业发展做好准备。
FAQs
什么是 GELU 激活函数?
GELU(Gaussian Error Linear Unit,高斯误差线性单元)是神经网络中的一种激活函数。与 ReLU 不同,GELU 以概率性方式根据输入的幅度平滑地进行缩放。这使其更适合梯度质量更重要的深层架构。
GELU 与 ReLU 有何不同?
ReLU 以零为硬阈值将所有负输入置零,这可能导致神经元停止学习——即所谓“死亡神经元”问题。GELU 则通过逐步缩小负输入而非切断来避免这一点。这样能带来更平滑的梯度流,并在深层网络中表现更好。
何时应优先使用 GELU 而非其他激活函数?
GELU 最适用于深层架构,尤其是基于 Transformer 的模型,如 BERT 和 GPT。对于浅层网络或更简单的任务,计算开销很少能证明从 ReLU 切换过来的合理性。以 ReLU 作为基准开始,之后再进行测试对比。
精确的 GELU 公式与其近似有何区别?
精确的 GELU 公式使用高斯累积分布函数,需要计算误差函数——这一操作在大规模情况下较慢。近似形式则以 tanh 替代,它是一种更快且在现代硬件上支持良好的表达式。在实践中,两者的结果几乎一致,这也是大多数框架默认使用近似形式的原因。
GELU 能在 PyTorch 和 TensorFlow 中使用吗?
是的,这两个框架都支持 GELU。在 PyTorch 中,您可以在模型定义中使用 nn.GELU() 模块。在 TensorFlow 中,您可以将 "gelu" 作为字符串传入任一层的 activation 参数,或使用 keras.layers.Activation("gelu")。