直觉理解
激活函数是神经网络每一层线性变换之后的那个非线性函数。没有它,多层网络叠在一起仍然只是一个线性变换,见神经网络与反向传播。
选择激活函数主要看两点:一是它提供了什么样的非线性;二是它的导数在反向传播时是否「好用」,即梯度会不会在某些区间消失。深度学习的激活函数大致经历了 sigmoid/tanh → ReLU → GELU → 门控单元(如 SwiGLU)这样的演变。
定义与公式
sigmoid 与 tanh
sigmoid 把输入压到 ,tanh 压到 。它们在输入绝对值较大时进入饱和区,导数趋近于 0,深层网络中容易出现梯度消失。现在它们很少用作隐藏层的激活,但仍常用于输出概率(sigmoid)和门控结构(如 LSTM 的门)。
ReLU
正区间导数恒为 1,因此梯度不会因经过这个激活函数而缩小,但仍会受到权重矩阵和其他层的影响。2012 年的 AlexNet 用它训练卷积网络取得成功,之后它被广泛采用。负区间的导数为 0,若某单元对所有训练输入都输出 0,相关参数可能长期收不到有效梯度,称为「神经元死亡」。Leaky ReLU 等变体用负区间的小斜率缓解这一问题。
GELU
是标准正态分布的累积分布函数:,其中 。它是按 在标准正态分布中的累计概率给输入加权,不是说确定的输入 自身「有多大概率为正」。GELU 平滑可导,负区间也有非零输出。GELU 由 Hendrycks 和 Gimpel 在 2016 年提出,BERT、GPT-2、GPT-3 等模型使用它或其近似实现。
SiLU / Swish
形状与 GELU 接近。常说的 Swish 采用 ;当 时与 SiLU 相同。
门控线性单元与 SwiGLU
GLU 类结构把输入分别做两次线性变换,其中一路经过激活函数后与另一路逐元素相乘。原始 GLU 用 sigmoid 门;SwiGLU 用 SiLU,门值并不限制在 。下面给出包含输出投影的完整 SwiGLU 前馈层,省略偏置:
门控让网络可以按输入动态决定每个维度通过多少信息。Shazeer 在 2020 年的《GLU Variants Improve Transformer》中比较了多种变体,发现用它们替换 Transformer 前馈层能提升效果。LLaMA 系列等主流开源大语言模型都采用 SwiGLU 前馈层。因为多了一个权重矩阵,为了保持参数量不变,隐藏层维度通常从 缩小到约 。
Softmax
Softmax 严格说不是逐元素的激活函数,而是把一个向量变成概率分布:
它用在分类模型和语言模型的输出层,也用在注意力机制里计算注意力权重。
代码示例
import torch
import torch.nn as nn
import torch.nn.functional as F
x = torch.linspace(-3, 3, 7)
print(F.relu(x))
print(F.gelu(x))
print(torch.sigmoid(x))
class SwiGLU(nn.Module):
"""LLaMA 等模型使用的门控前馈层"""
def __init__(self, d_model, d_hidden):
super().__init__()
self.w_gate = nn.Linear(d_model, d_hidden, bias=False)
self.w_up = nn.Linear(d_model, d_hidden, bias=False)
self.w_down = nn.Linear(d_hidden, d_model, bias=False)
def forward(self, x):
return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
print(SwiGLU(8, 16)(torch.randn(2, 8)).shape) # torch.Size([2, 8])在 AI 中的作用
在 Transformer 里,激活函数主要出现在每一层的前馈网络中。前馈层占了模型参数的大头,它的激活函数选择会直接影响效果:原始 Transformer 用 ReLU,BERT 和 GPT 系列换成 GELU,如今的大语言模型普遍使用 SwiGLU。
参考资料
相关
- 神经网络与反向传播:激活函数的导数决定梯度能否回传。
- Transformer:前馈层中的激活函数。
- 归一化:与激活函数一起决定每层输出的数值范围。
- 概率与统计:GELU 定义里用到的正态分布。