激活函数与门控前馈网络
“ReLU 已过时”太宽泛;“只需掌握 ReLU、Sigmoid、Tanh”又明显落后。激活选择已经按架构、训练规模、数值精度和硬件分化。现代 Transformer 还常把普通两层 FFN 改成门控结构,这不只是更换一个逐元素函数。
这里讨论的是单个位置内部的非线性与通道混合;跨位置的信息交换见注意力机制。FFN 的门控对通道数值做乘法,而 MoE 路由为 token 选择专家网络,两者可以同时存在。参数怎样通过损失更新,见训练过程。
逐元素激活
是标准正态 CDF。GELU 常用精确 erf 表达式或 tanh 近似,不同 kernel 在混合精度下可能有小差异。Swish 论文讨论可学习 ,而框架中的 SiLU 通常固定 ;文献常把二者近似同义,比较时必须写清实现。
这里 , 为标准正态分布的累积分布函数。精确 GELU 使用 ;常见的 tanh 表达式才是近似。表中的 假设 ;若 PReLU 的参数不受此约束,应按分段形式定义:非负输入取 ,负输入取 。
SiLU 的导数为 。在零点,ReLU、GELU、SiLU 的输出均为零,但 GELU 与 SiLU 的导数为 ;ReLU 在此没有唯一导数,实现时须选择约定。 时,SiLU 约为 ,导数约为 。因此 SwiGLU 的门值不是限制在 内的概率,可以为负,也可以大于一。
查看清晰大图重点看负输入:ReLU 直接输出零,GELU 先落到零以下,在输入很负时又趋近零;α = 1 的 ELU 则趋近 −1。蓝线是标准 GELU。曲线能解释它们怎样响应输入,不能据此给任务效果排高低。
为什么“更平滑”不等于“更先进”
平滑函数可改善某些优化轨迹和大规模训练结果,但收益与初始化、归一化、宽度、数据、优化器和 fused kernel 纠缠。ReLU 仍有三个实际反例:
- 卷积、轻量或量化部署中,简单 kernel 和精确零可能更重要;
- 小数据或小模型的误差常由数据与正则化主导;
- 一个稳定 ReLU 基线可以揭示复杂门控的收益是否只是参数增加。
因此“论文中的更高平均分”不能直接变成所有任务的默认。
GLU 与门控 FFN
普通 Transformer FFN 常写成
门控线性单元(GLU)把两条投影相乘。简化写法为
在 Transformer 中,常见变体可写为
ReGLU 则把门分支换成 ReLU。门控层有 、、 三个主要投影,普通 FFN 只有输入/输出两个;若仍使用相同中间宽度,参数和计算都会增加。等参数比较通常要缩小门控层的中间维度,具体比例取决于是否计 bias、embedding 和对齐约束,不能把某个固定比例当定律。
不含偏置、输入和输出宽度均为 、中间宽度为 的普通 FFN 有 个权重。门控 FFN 若中间宽度为 ,则有 个权重;令二者相等得 。例如 时取 ,两者均有 144 个权重。门和值分支各把 6 维映射到 8 维,逐元素相乘后仍为 8 维,再投影回 6 维。这是 GLU Variants Improve Transformer 中的局部参数量比较,不保证实测延迟相同。
现代架构中的位置
- BERT 原始架构使用 GELU;
- PaLM 报告采用 SwiGLU;
- Llama 3 架构报告也采用 SwiGLU,并把它与 RMSNorm、RoPE、GQA 等一起作为完整 block 的一部分;
- ConvNeXt 展示了现代 ConvNet 也可采用 GELU,而非只有 Transformer 使用平滑激活。
这些是论文所述实例,不证明 GELU/SwiGLU 对所有规模、模态或硬件占优。架构包中的多项改动共同变化时,也不能把收益全部归因于激活。
输出激活不要混入隐藏层争论
- 二分类常训练一个 logit,并在解释概率时应用 Sigmoid;
- 互斥多分类使用 logits 与 Softmax;
- 多标签使用独立 Sigmoid;
- 回归可用线性、正值或有界变换,取决于目标支持集。
为数值稳定,训练损失通常直接接收 logits,而不是先手动计算 Sigmoid/Softmax 再取对数。
失败模式
公平选择协议
- 先固定数据切分、初始化族、优化器、步数和停止规则;
- 比较普通 ReLU/GELU/SiLU FFN 时保持宽度和参数可解释;
- 比较 GEGLU/SwiGLU 时同时给出中间维、总参数、FLOPs 和峰值内存;
- 至少重复多个 seed,报告均值、离散度和失败;
- 在目标 dtype、编译器和硬件上测端到端吞吐;
- 预先确定质量、延迟、内存与稳定性阈值,而不是结果出来后挑指标。
更新规则与偏差
本文偏重稠密网络、Transformer 和 GPU 训练。脉冲神经网络、周期激活、隐式神经表示、可学习 spline、符号/逻辑门以及特定加速器可能需要完全不同的选择。每次出现新函数时,应先问它改变的是表达、优化、参数预算还是 kernel,而不是继续扩充“激活函数动物园”。