激活函数的角色

激活函数是神经网络中引入非线性的关键组件。没有激活函数,多层线性变换的堆叠等价于单层线性变换——网络的表达能力将被严重限制。激活函数的选择直接影响模型的训练动态、收敛速度和最终性能。

在LLM时代,激活函数的演进从ReLU到GELU再到SwiGLU,每一步都带来了可测量的性能提升。

ReLU时代

ReLU的革命性

ReLU(Rectified Linear Unit)的定义极其简单:

ReLU(x) = max(0, x)

在ReLU之前,sigmoid和tanh是主流选择,但它们存在梯度消失问题——深层网络中梯度指数衰减。ReLU的梯度在正区间恒为1,有效解决了梯度消失问题,使得深层网络的训练成为可能。

ReLU的缺陷

Dead ReLU问题:当输入持续为负时,ReLU的梯度为零,神经元将永久"死亡"无法恢复。这在学习率设置不当时尤为严重。

非零中心化:ReLU的输出始终非负,导致后续层的输入分布偏向正方向,影响梯度下降效率。

Leaky ReLU与变体

为解决Dead ReLU问题,多种变体被提出:

def leaky_relu(x, negative_slope=0.01):
    """Leaky ReLU: 负区间保留小梯度"""
    return torch.where(x > 0, x, negative_slope * x)

def prelu(x, alpha):
    """Parametric ReLU: 负区间斜率可学习"""
    return torch.where(x > 0, x, alpha * x)

def elu(x, alpha=1.0):
    """ELU: 负区间平滑过渡到指数"""
    return torch.where(x > 0, x, alpha * (torch.exp(x) - 1))

这些变体在CV领域有一定应用,但在LLM中几乎未被采用——LLM的激活函数走上了另一条路。

GELU:Transformer的原始选择

定义

GELU(Gaussian Error Linear Unit)将输入的高斯分布概率与输入本身相乘:

GELU(x) = x · Φ(x)

其中 Φ(x) 是标准正态分布的累积分布函数。实践中常使用近似:

def gelu(x):
    """精确GELU"""
    return 0.5 * x * (1 + torch.erf(x / math.sqrt(2)))

def gelu_tanh_approx(x):
    """tanh近似(更快)"""
    return 0.5 * x * (1 + torch.tanh(math.sqrt(2/math.pi) * (x + 0.044715 * x**3)))

GELU vs ReLU

GELU相比ReLU有两个关键优势:

  1. 平滑过渡:在零点附近,GELU是平滑的而非硬截断。这使得梯度更连续,训练更稳定
  2. 随机正则化:GELU隐含了一种随机dropout机制——输入越大,被保留的概率越高。这在一定程度上起到了自正则化的作用

原始Transformer(Attention is All You Need)选择了GELU,此后BERT、GPT系列也沿用至今。

SwiGLU:现代LLM的标配

GLU家族

GLU(Gated Linear Units)引入了门控机制:

GLU(x) = (xW + b) ⊗ σ(xV + c)

其中 σ 是sigmoid函数,⊗ 是逐元素乘法。GLU用一个"门"来控制信息的通过量。

SwiGLU的定义

SwiGLU将GLU中的sigmoid替换为Swish激活函数:

SwiGLU(x, W, V) = Swish(xW) ⊗ (xV)
Swish(x) = x · σ(x)
class SwiGLU(nn.Module):
    def __init__(self, d_model, d_ff):
        super().__init__()
        self.w_gate = nn.Linear(d_model, d_ff, bias=False)
        self.w_up = nn.Linear(d_model, d_ff, bias=False)
        self.w_down = nn.Linear(d_ff, d_model, bias=False)
    
    def forward(self, x):
        gate = self.w_gate(x)
        up = self.w_up(x)
        # Swish门控
        activated = F.silu(gate) * up  # silu = swish
        return self.w_down(activated)

SwiGLU的优势

  1. 门控机制:信息流由门控动态调节,增强了模型的表达能力
  2. Swish平滑性:Swish继承了GELU的平滑特性,训练稳定
  3. 稀疏激活:门控机制自然产生稀疏激活,类似于隐式的MoE

为什么SwiGLU取代了GELU?

PaLM、LLaMA、Qwen等2026年的主流LLM几乎都采用了SwiGLU。研究表明,在相同参数预算下,SwiGLU比GELU的困惑度低约0.1-0.3。虽然提升看似不大,但在大模型规模下,这0.1的改进相当于显著的计算资源节省。

需要注意的是,SwiGLU比标准GELU FFN多一个线性投影(从d_model到d_ff),因此参数量略多。实践中通常将d_ff缩小为原来的2/3来保持参数量一致。

其他值得关注的激活函数

Mish

Mish(x) = x · tanh(softplus(x))

Mish在零点附近更平滑,且在负区间保留了部分梯度。在CV任务中表现优异,但在LLM中尚未广泛采用。

ReGLU

ReGLU(x, W, V) = ReLU(xW) ⊗ (xV)

用ReLU替代Swish作为门控函数。比SwiGLU计算更简单,但性能略差。

GeGLU

GeGLU(x, W, V) = GELU(xW) ⊗ (xV)

用GELU作为门控。性能介于ReGLU和SwiGLU之间。

激活函数的选择指南

按任务类型

模型类型推荐激活函数理由
CNN(分类)ReLU/Mish简单高效,CV任务验证充分
RNN/LSTMtanh输出范围[-1,1],适合递归
Transformer (FFN)SwiGLU2026年最佳实践
Transformer (旧)GELU兼容性需求
MoE ExpertSwiGLU与密集模型一致

按性能需求

  • 追求最高性能:SwiGLU
  • 追求推理速度:ReLU(计算最简单)
  • 追求训练稳定:GELU或SwiGLU
  • 兼容旧框架:GELU

2026年前沿方向

学习型激活函数

让网络自动学习最优激活函数:

class LearnableActivation(nn.Module):
    def __init__(self, n_parameters=16):
        super().__init__()
        self.control_points = nn.Parameter(torch.randn(n_parameters))
    
    def forward(self, x):
        # 使用样条插值实现连续激活函数
        return spline_interpolation(x, self.control_points)

稀疏激活函数

专门为稀疏计算设计的激活函数,在MoE场景下特别有意义:

def sparse_swish(x, threshold=0.1):
    """带阈值门控的稀疏Swish"""
    swish = x * torch.sigmoid(x)
    return torch.where(swish.abs() > threshold, swish, torch.zeros_like(swish))

上下文感知激活

激活函数的参数随上下文变化——不同层、不同位置使用不同的激活策略:

class ContextAwareActivation(nn.Module):
    def __init__(self, d_model, n_activations=4):
        super().__init__()
        self.router = nn.Linear(d_model, n_activations)
        self.activations = [
            F.silu, F.gelu, F.relu, lambda x: x * torch.sigmoid(x)
        ]
    
    def forward(self, x):
        weights = F.softmax(self.router(x.mean(dim=-1, keepdim=True)), dim=-1)
        outputs = [act(x) for act in self.activations]
        return sum(w * o for w, o in zip(weights.unbind(-1), outputs))

工程实现注意事项

  1. 融合Kernel:将激活函数与前一线性层融合到同一个CUDA kernel中,减少显存读写
  2. 精度选择:在FP16/BF16训练中,激活函数的中间计算建议在FP32下进行以避免数值问题
  3. 内存占用:GLU系列激活函数需要存储两个分支的中间结果,梯度检查点策略需相应调整
  4. 推理优化:推理时Swish的sigmoid可以用查表近似,减少计算开销

结语

激活函数看似是深度学习中最"微小"的组件,但其影响深远。从ReLU到SwiGLU的演进,每一步都凝聚了对训练动态和信息流的深入理解。在AGI系统的构建中,选择正确的激活函数是确保模型性能和训练稳定性的基础。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。