激活函数-GELU(平滑的 ReLU 变体) 作者:马育民 • 2026-01-24 10:26 • 阅读:10000 # 介绍 GELU(Gaussian Error Linear Unit,高斯误差线性单元), 是一种非线性激活函数。由Dan Hendrycks和Kevin Gimpel于2016年提出。 它是深度学习模型中广泛使用的激活机制之一,尤其在Transformer架构(如BERT、GPT系列)中扮演关键角色。GELU的设计灵感来源于高斯分布的累积分布函数(CDF),旨在 **解决传统激活函数(如ReLU)的一些局限性**,例如“死亡ReLU”问题(负输入被完全抑制,导致梯度为零)和非平滑性,从而提升模型的训练稳定性和性能。 一句话解释:是一种**平滑的 ReLU 变体**,现在在 **Transformer / BERT 等模型里几乎是默认激活函数**。 # 核心思想 ### ReLU ReLU 的工作方式可以被看作一个“硬门控 (hard gate)”:当输入 `x>0` 时,门是开的(输出 `x`);当 `x≤0` 时,门是关的(输出 `0`)。 ### GELU GELU 的核心思想则更为精妙,它采用了一个**“随机门控 (stochastic gate)”**。一个神经元的输出值 `x` 将乘以一个在 `[0, 1]` 之间的值,**这个值是 随机生成的**,但其概率分布取决于输入 `x` 本身。 具体来说,GELU 使用标准正态分布的累积分布函数 (CDF) ,用 $$\Phi(x)$$ 表示,来作为这个门控的概率值。 - 如果输入 `x` 越大,$$\Phi(x)$$ 就越接近 `1`,意味着这个输入有很大概率被“保留”。 - 如果输入 `x` 越小(越负),$$\Phi(x)$$ 就越接近 `0`,意味着这个输入有很大概率被“置零”。 这种方式将神经元的激活与它的数值分布联系起来,实现了一种数据驱动的、非线性的门控机制。 # 公式 GELU(Gaussian Error Linear Units)可以理解为: “**带一点随机性、平滑版的 ReLU**”。 常用 **近似公式**(有上面可知,有随机数,所以用近似公式,代码里一般用这个): $$\text{GELU}(x) = x \cdot \Phi(x)$$ 其中: - $$x$$ 是输入(标量或张量元素) - $$\Phi(x)$$ 是 标准正态分布的 CDF,常用近似: $$\Phi(x) \approx 0.5 \left(1 + \tanh\left[\sqrt{\frac{2}{\pi}} \left(x + 0.044715 \* x^3\right)\right]\right)$$ **提示:**这个近似公式在 PyTorch 和 TensorFlow 等主流框架中被广泛采用。 ### 完整公式 $$\text{GELU}(x) \approx 0.5 \* x \* \left(1 + \tanh\left[\sqrt{\frac{2}{\pi}} \left(x + 0.044715 * x^3\right)\right]\right)$$ ### 直观理解 - 对每个输入 \(x\),以某种概率把它“关掉”或“减弱”,这个概率跟 \(x\) 的大小有关 - 比 ReLU 更平滑,**不是硬切 0,而是连续变化** # GELU vs. ReLU (视觉对比)  绘制图像代码: ``` import torch import torch.nn as nn import matplotlib.pyplot as plt gelu = nn.GELU() relu = nn.ReLU() x = torch.linspace(-3, 3, 100) print("x.shape:", x.shape) print("x:\n", x) y_gelu = gelu(x) y_relu = relu(x) plt.figure(figsize=(8, 3)) for i, (y, label) in enumerate(zip([y_gelu, y_relu], ["GELU", "ReLU"]), 1): plt.subplot(1, 2,i) plt.plot(x, y) plt.title(f"{label} activation function") plt.xlabel("x") plt.ylabel(f"{label}(x)") plt.grid(True) plt.tight_layout() plt.show() ``` ### 绘制图像 为了直观地比较GELU函数与ReLU函数,将它们并排绘制出来 ``` gelu = GELU() relu = nn.ReLU() x = torch.linspace(-3, 3, 100) print("x.shape:", x.shape) print("x:\n", x) y_gelu = gelu(x) y_relu = relu(x) plt.figure(figsize=(8, 3)) for i, (y, label) in enumerate(zip([y_gelu, y_relu], ["GELU", "ReLU"]), 1): plt.subplot(1, 2,i) plt.plot(x, y) plt.title(f"{label} activation function") plt.xlabel("x") plt.ylabel(f"{label}(x)") plt.grid(True) plt.tight_layout() plt.show() ``` ### ReLU 在 **原点有一个尖锐的拐点**: - 在 $$ x \leq 0 $$ 时为一条直线 $$y=0$$ - 在 $$x>0$$ 时为一条直线 $$y=x$$ ### GELU - 当 $$x$$ 很大时,函数曲线非常接近 $$y=x$$ - 当 $$x$$ 是很大的负数时,函数曲线非常接近 $$y=0$$ - 在原点附近平滑过渡,并且在负值区域会略低于 $$x$$ 轴。 # 和 ReLU 比较优缺点 ### 优点 - 输出更平滑,**梯度更连续**,训练更稳定 - 在 Transformer、BERT、GPT 等模型中,效果普遍好于 ReLU - 对不同尺度的输入更“友好”,不容易一下子饱和 ### 缺点 - 计算比 ReLU 稍复杂一点(但在现代硬件上影响不大) - 形式稍复杂,不像 ReLU 那样一眼就懂 # 什么时候用 GELU? - 做 **Transformer / 大语言模型 / 注意力模型**:优先用 GELU - 做 **CV 里的 ViT、Swin Transformer** 等:也常用 GELU - 普通 CNN、MLP: - 用 ReLU 完全没问题 - 如果想试试提升一点效果,可以把 ReLU 换成 GELU 做对比 --- # 实现 ### pytorch实现 ```python import torch import torch.nn as nn # 使用官方实现的 GELU activation = nn.GELU() x = torch.randn(10) # 示例输入 y = activation(x) print(y) ``` 参考: https://juejin.cn/post/7562095943442235444 https://blog.csdn.net/u013172930/article/details/153544283 原文出处:http://malaoshi.top/show_1GW2eLqXgzw9.html