2026-04-15 · transformer
【Transformer 与注意力机制】26|前馈网络:那个看似平平无奇的两层 MLP,其实是「记忆」所在
把 Transformer block 里那个看起来最不起眼的两层 MLP 真正讲清楚——4 倍扩张比的来历、逐位置而不是跨位置的设计、Geva 等人 2021 年提出的「键值记忆」视角、SwiGLU/GLU/GeGLU 的现代变体、参数量分布、可解释性研究、量化时的瓶颈,以及它和 MoE 的关系。


























上一篇我们论证了一件事——纯线性的网络再深,也只是一个线性变换。把 \(W_2(W_1\mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2\) 展开就是 \(W'\mathbf{x} + \mathbf{b}'\)。线性的复合还是线性,这是线性代数的铁律。
所以神经网络要想拟合「弯曲的」关系,必须引入非线性。引入的方式很巧妙——不是改 \(W\mathbf{x} + \mathbf{b}\) 的形状(那样就不再是线性变换了),而是在两个线性变换之间插入一个逐元素的非线性函数。这个函数叫激活函数(activation function)。
形式化地,一个典型的「线性 + 非线性」三明治长这样:
\[\mathbf{h} = \sigma(W_1 \mathbf{x} + \mathbf{b}_1)\] \[\mathbf{y} = W_2 \mathbf{h} + \mathbf{b}_2\]
其中 \(\sigma\) 是激活函数,逐元素作用——也就是说 \(\sigma(\mathbf{z})\) 表示对 \(\mathbf{z}\) 的每一个元素分别应用 \(\sigma\)。
这一篇我们就来仔细看看 \(\sigma\) 这个角色。它看起来微不足道——一个一维标量函数嘛,能有多复杂?但偏偏,激活函数的选择直接决定了深度网络能不能训得动、训得好。一个不合适的激活函数,再大的模型再多的数据都救不了;一个合适的激活函数,能让原本训不动的网络突然焕发生机。
我们会按时间顺序讲:Sigmoid(最早)→ Tanh(改进)→ ReLU(革命)→ Leaky/ELU/SELU(修补)→ Swish/GELU(现代标配)→ SwiGLU/GeGLU(大模型时代)。每一个的诞生都对应着一个时代的痛点,每一次迭代都解决了前一代的某个核心问题。我希望你读完之后,不仅记住每个函数长什么样,更能体会到激活函数演化的逻辑——从生物启发到数值优化,从局部小改到大模型时代的全新设计。
这个细节很多教材会一笔带过,但其实值得停下来想一下。
激活函数作用在向量上,但它是「逐元素」作用——也就是说,对向量 \(\mathbf{z} = (z_1, z_2, \cdots, z_h)\),我们有:
\[\sigma(\mathbf{z}) = (\sigma(z_1), \sigma(z_2), \cdots, \sigma(z_h))\]
每一个分量独立处理,互不干扰。这意味着 \(\sigma\) 本质上只是一个一维函数 \(\mathbb{R} \to \mathbb{R}\),被「广播」到向量上。
为什么要这样设计?为什么不用一个「真·向量到向量」的非线性函数(比如对向量做一些复杂的混合)?
有两个原因。
第一,计算简单。逐元素操作可以完美并行——每个元素分配到 GPU 的一个线程,独立计算。如果用复杂的混合非线性,并行度会被破坏。
第二,线性变换已经在做混合了。\(W\) 矩阵的每一行都是对输入向量的线性组合。也就是说,「不同维度间的相互作用」这件事,已经由 \(W\) 解决了。激活函数不需要再重复做。
所以神经网络的分工是:\(W\) 负责「不同维度间的混合」(线性,跨元素),\(\sigma\) 负责「单个维度上的弯曲」(非线性,元素内)。两者各司其职,组合出强大的表达能力。
后面我们会看到,这个分工有一个例外——Softmax。Softmax 是非逐元素的非线性(它需要对整个向量归一化),所以它有特殊的地位。但常规激活函数都是逐元素的。
最早的激活函数是 Sigmoid(也叫 Logistic 函数):
\[\sigma(z) = \frac{1}{1 + e^{-z}}\]
它的形状是一条「S」曲线:当 \(z \to -\infty\) 时趋于 0,当 \(z \to +\infty\) 时趋于 1,在 \(z = 0\) 处等于 0.5,左右对称。
Sigmoid 为什么是第一代?因为它有几个让人喜欢的性质。
性质一:输出在 \((0, 1)\) 之间。这个区间天然像「概率」。所以 Sigmoid 在二分类的输出层用得很多——「这是猫的概率」之类。
性质二:处处可导。Sigmoid 处处光滑,没有任何尖角。它的导数还有一个漂亮的形式:
\[\sigma'(z) = \sigma(z)(1 - \sigma(z))\]
也就是说,导数可以由函数值本身算出来——前向算了 \(\sigma(z)\),反向算梯度时直接乘 \(\sigma(z)(1-\sigma(z))\),不用重新算 exp。这在没有自动微分的早期是个工程优势。
性质三:受生物启发。神经元有「激发阈值」——刺激不够就不发放,刺激够了就饱和发放。Sigmoid 的形状刚好像「阈值激发函数的光滑版」。早期人们觉得这种「生物味」是好事。
但 Sigmoid 也有几个致命的缺点。这些缺点直到 2010 年代才被深度学习社区集体抛弃。
Sigmoid 的导数 \(\sigma'(z) = \sigma(z)(1 - \sigma(z))\),最大值出现在 \(z = 0\) 处,等于 \(0.5 \times 0.5 = 0.25\)。最大值只有 0.25。当 \(|z|\) 大一点(比如 \(z = 5\)),\(\sigma(z) \approx 0.993\),导数 \(\approx 0.993 \times 0.007 \approx 0.007\)——基本上等于 0。
这意味着什么?意味着 Sigmoid 在「饱和区」(\(|z|\) 较大)的梯度极小。
而梯度在反向传播时是「逐层相乘」的。链式法则告诉我们:
\[\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial \mathbf{h}_L} \cdot \prod_{l} \frac{\partial \mathbf{h}_{l+1}}{\partial \mathbf{h}_l}\]
每一层都要乘一次激活函数的导数。如果每层导数最大才 0.25,10 层之后梯度被乘了 \(0.25^{10} \approx 10^{-6}\)。再乘点权重矩阵,基本就是 0 了。
这就是著名的梯度消失问题(vanishing gradients)。早期网络的浅层(靠近输入)几乎学不到东西——梯度还没传到那里就消失了。1990 年代到 2000 年代,整个深度学习领域被这个问题困住了十几年。
我以前读这段历史,觉得不可思议——一个看起来这么显然的问题,居然困住了一代人。但仔细想想,「显然」是事后的视角。当时人们没有 PyTorch 的可视化工具,没有 TensorBoard 的梯度直方图。他们只能盯着训练曲线发呆——「为什么深网络比浅网络还差?」直到 Hinton 等人在 2006 年用「逐层预训练」绕开这个问题,再到 2012 年 ReLU 直接消解这个问题,整个领域才打开局面。
Sigmoid 的输出范围是 \((0, 1)\),全是正数。这看起来无害,其实有一个微妙的问题——梯度更新会有 zigzag。
简单解释:假设第 \(l\) 层的激活是 \(\mathbf{h}_l = \sigma(\cdot)\),全是正数。那么下一层 \(W\) 的梯度更新方向,会受 \(\mathbf{h}_l\) 的符号约束——所有 \(W\) 的元素的梯度同号(要么都正、要么都负)。这意味着参数只能朝某些「象限」移动,要去其他象限就得绕路,形成 zigzag 的更新轨迹。
这听起来抽象,但实际表现就是收敛慢、训练不稳定。
这个问题被叫做「非零中心化」(not zero-centered)问题。要解决它,激活函数的输出最好以 0 为中心,左右对称分布。
为了解决「非零中心化」,人们找到了 Tanh:
\[\tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}}\]
它和 Sigmoid 是孪生兄弟——形状几乎一模一样,只是范围从 \((0, 1)\) 拉到了 \((-1, 1)\),并且关于原点对称。事实上:
\[\tanh(z) = 2\sigma(2z) - 1\]
Tanh 解决了 Sigmoid 的「非零中心化」问题。RNN 时代(90 年代到 2010 年代初),Tanh 是 LSTM 和 GRU 内部的标准激活——你今天打开任何一个 RNN 实现,里面到处都是 tanh。
但 Tanh 没有解决梯度消失问题。它的导数 \(\tanh'(z) = 1 - \tanh^2(z)\),最大值是 1(在 \(z = 0\) 处),但两端饱和时仍然趋于 0。所以 Tanh 比 Sigmoid 好一点,但深度网络仍然训不深。
到这里,「梯度消失」已经像一座大山压在深度学习的头上。整个领域亟需一个全新的激活函数。
2010 年前后,一个看似「大道至简」的激活函数横空出世——
\[\text{ReLU}(z) = \max(0, z)\]
「整流线性单元」(Rectified Linear Unit)。它的形状简单到不像「现代神经网络」该有的东西:负数砍成零,正数原样保留。一条折线,仅此而已。
但就是这样一个朴素的函数,彻底改变了深度学习。
ReLU 的优势:
第一,正区间梯度恒为 1。在 \(z > 0\) 时,\(\text{ReLU}'(z) = 1\)。梯度不会衰减。这意味着深层网络的梯度可以原样传到底层。一记神来之笔。
第二,计算极快。max(0, z)
是一个 if-else 分支,比 exp 快几十倍。这在 GPU
上是巨大优势。
第三,稀疏性。负输入直接归零,意味着每次前向时,约一半神经元处于「不激活」状态。稀疏激活对生物神经元而言是常态,对网络的容量也有帮助。
第四,不饱和。Sigmoid/Tanh 在 \(|z|\) 大时饱和(输出贴近边界,梯度趋零);ReLU 在正区间永不饱和。
ReLU 不是 2010 年才被发明的。最早 Hahnloser 在 2000 年的论文里就提到过它。但真正让它流行起来的,是 2010 年 Glorot & Bengio 的《Deep Sparse Rectifier Neural Networks》,以及 2012 年 Krizhevsky 用 ReLU 训出的 AlexNet——一举打破了 ImageNet 的所有纪录,深度学习从此进入「ReLU 时代」。
ReLU 这么好,有缺点吗?有。最有名的叫Dying ReLU(死亡 ReLU)。
如果某个神经元的输入 \(z\) 始终为负,那么 ReLU 输出始终为 0,对应的梯度也是 0。梯度为 0 意味着参数不会更新。这个神经元就「死了」——再也不会激活,再也不会学习。
死亡 ReLU 在训练初期最容易发生:参数初始化不好,或者学习率太大,导致某些神经元的输入被推到一直为负,从此再也救不回来。研究发现,一个训练完的 ReLU 网络里,可能有 10%-40% 的神经元已经死了。
这是 ReLU 的「副作用」,但通常不致命——其他 60%-90% 的神经元仍然在工作,整体性能仍然很好。但在一些极端情况下,死亡 ReLU 会显著降低模型容量。
为了缓解这个问题,人们提出了一系列「ReLU 变种」。
第一个补丁是 Leaky ReLU:
\[\text{LeakyReLU}(z) = \begin{cases} z & z > 0 \\ \alpha z & z \le 0 \end{cases}\]
这里 \(\alpha\) 是个小常数,通常取 0.01。负数区间也有一个小斜率,梯度不再为零。即使神经元的输入长期为负,参数也仍然在更新——它有机会「复活」。
PReLU(Parametric ReLU)把 \(\alpha\) 也变成可学习参数,每个神经元自己学一个最合适的负斜率。He 等人的工作(2015)显示这在某些任务上略有提升。
Leaky/PReLU 实战中的表现……不算特别好。它们解决了 Dying ReLU 的理论问题,但实际收益不太明显。多数大模型仍然用普通的 ReLU 或后面要讲的 GELU,而不是 Leaky 系。
ELU(Exponential Linear Unit, Clevert et al. 2015)走得更远:
\[\text{ELU}(z) = \begin{cases} z & z > 0 \\ \alpha(e^z - 1) & z \le 0 \end{cases}\]
ELU 在负区间用 \(\alpha(e^z - 1)\),让输出能取到接近 \(-\alpha\) 的负值。这有两个好处:
SELU(Scaled ELU, Klambauer et al. 2017)在 ELU 基础上加了一个缩放因子 \(\lambda\):
\[\text{SELU}(z) = \lambda \cdot \text{ELU}(z)\]
并精心选取 \(\lambda \approx 1.0507\) 和 \(\alpha \approx 1.6733\),使得网络在前向传播时自归一化——激活的均值和方差自动保持在 0 和 1 附近。理论很漂亮,工程上也省了 BatchNorm。
但 SELU 的限制很多(要求特定权重初始化、特定层结构),使用门槛高。最终它没能成为主流。
ELU/SELU 的故事告诉我们一件事——激活函数的「数学优雅」不一定等于「工程实用」。一个函数能不能流行,要看它在大量真实任务上的综合表现,不只是看它有多少漂亮的性质。
2017 年,Google 用神经架构搜索(NAS)寻找更好的激活函数。算法跑了几天,吐出了一个让人意外的结果:
\[\text{Swish}(z) = z \cdot \sigma(z)\]
其中 \(\sigma\) 是 Sigmoid。这个函数也叫 SiLU(Sigmoid Linear Unit)。
它的形状像「光滑版的 ReLU」——大于 0 时趋近 \(z\),小于 0 时趋近 0,中间过渡平滑、非单调(在某个负值附近有个小凹陷)。
Swish 的优势是「软」——不像 ReLU 在 0 处有尖角,Swish 处处光滑。这对优化曲面有微妙的好处,让训练更稳。
更妙的是,Swish 的形式非常简单——只是 \(z\) 和 \(\sigma(z)\) 的乘积,几乎不增加计算量。
Swish 在 EfficientNet 等模型里效果不错,逐渐流行起来。但更广为人知的是它的「亲戚」——GELU。
GELU(Gaussian Error Linear Unit, Hendrycks & Gimpel, 2016)的形式是:
\[\text{GELU}(z) = z \cdot \Phi(z)\]
其中 \(\Phi(z)\) 是标准正态分布的累积分布函数(CDF)。
直观地讲,GELU 把 \(z\) 乘以「\(z\) 是正的概率」(在标准正态假设下)。如果 \(z\) 很大,概率接近 1,GELU 输出 \(\approx z\);如果 \(z\) 很负,概率接近 0,GELU 输出 \(\approx 0\)。它和 ReLU 的差别在中间——ReLU 是「硬开关」(要么 0 要么 1),GELU 是「软概率」(连续从 0 过渡到 1)。
GELU 的精确公式比较麻烦,所以工程实现常用近似:
\[\text{GELU}(z) \approx 0.5 z \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}}(z + 0.044715 z^3)\right)\right)\]
PyTorch 提供了精确版(用 erf 函数)和 tanh 近似版两种。
GELU 的真正分量在哪里?——它是 Transformer 的标准激活函数。从原始的 BERT 论文(2018)开始,几乎所有后续 Transformer 模型(GPT-2、T5、ViT、CLIP 等)都用 GELU。它和 Swish/SiLU 形状几乎一样,效果也接近,但在 Transformer 上 GELU 的实证表现略好,于是成为 de facto 标准。
我自己的理解:GELU 在 ReLU 和 Swish 中间找到一个甜蜜点——它是「带概率解释的 Swish」「光滑版的 ReLU」。它没有 ReLU 的尖角问题,又有 ReLU 的稀疏激活倾向,还多了一个「概率」的诠释(虽然这个诠释主要是事后归因)。
Transformer 的 FFN 部分原本是这样:
\[\text{FFN}(x) = W_2 \cdot \text{GELU}(W_1 x + b_1) + b_2\]
这是一个「线性 → 激活 → 线性」的标准结构。
LLaMA(2023)和它的后继者用了一个不一样的结构——SwiGLU(Shazeer 2020 提出,LLaMA 2 普及):
\[\text{SwiGLU}(x) = (\text{Swish}(W_1 x) \odot (W_3 x)) W_2\]
这里 \(\odot\) 是逐元素相乘。
注意它有三个权重矩阵 \(W_1, W_2, W_3\),比标准 FFN 多了一个 \(W_3\)。整体结构是:
这种「用一路当门控乘另一路」的设计叫门控线性单元(GLU, Gated Linear Unit)。最早出现在 Dauphin 等人的 LSTM 改造里,2017 年被引入 Transformer,2020 年 Shazeer 系统比较了各种 GLU 变种(GeGLU、ReGLU、SwiGLU 等),SwiGLU 综合表现最好。
LLaMA 把 SwiGLU 用了之后,开源社区基本一边倒地跟上——Mistral、Qwen、DeepSeek、Yi 都用 SwiGLU。它已经替代 GELU 成为现代 LLM 的 FFN 标准。
为什么 SwiGLU 比 GELU 好?没有非常完整的理论解释。Shazeer 在论文最后一段写了一句很有名的话:「我们没有理由解释为什么这些架构有效。我们把它归因于神圣的恩赐(divine benevolence)」——这是激活函数研究里的一句神回复,意思是「就是 work,问就是玄学」。
但有一些直觉可以建立:
我的看法:SwiGLU 的成功很可能是「容量增加」+「门控形式」+「Swish 激活」三者的合力。其中哪个占主因,研究界还没共识。
整理一下整个家族:
| 时代 | 激活函数 | 主要优势 | 主要问题 |
|---|---|---|---|
| 1990s | Sigmoid | 概率解释、光滑 | 梯度消失、非零中心 |
| 1990s-2010s | Tanh | 零中心 | 仍然梯度消失 |
| 2012- | ReLU | 简单、不饱和、稀疏 | Dying ReLU、非光滑 |
| 2014- | Leaky/PReLU | 缓解 Dying ReLU | 实证收益小 |
| 2015- | ELU/SELU | 自归一化(SELU) | 限制太多 |
| 2017- | Swish/SiLU | 光滑、轻微非单调 | 略有计算开销 |
| 2018- | GELU | Transformer 标配 | 和 Swish 实质等价 |
| 2020- | SwiGLU/GeGLU | LLM 时代标配 | 参数量多 50% |
这条演化线有几个有意思的观察:
第一,前期演化由「梯度消失」驱动。从 Sigmoid 到 Tanh 到 ReLU,每一步都在让梯度更好传播。
第二,中期演化由「Dying ReLU」驱动。从 ReLU 到 Leaky/ELU/Swish,每一步都在让负区间也有梯度。
第三,后期演化由「Transformer + LLM」驱动。从 GELU 到 SwiGLU,是为了在大模型上挤出最后一点性能。这个阶段的进步不是「修补已知问题」,而是「在已经很好的基础上找更好的」——所以提升幅度小、解释难。
第四,最简单的方案不是最优的。ReLU 简单,但不是终点;GELU/SwiGLU 比 ReLU 复杂得多,但确实更好。这驳斥了「最简单的就是最好的」这种朴素观点——在工程上,一点复杂换一点性能经常是值得的。
上一篇我们口头说「线性的复合还是线性」。这里给一个严格推导,让结论无可辩驳。
定义两层无激活的全连接:
\[\mathbf{h} = W_1 \mathbf{x} + \mathbf{b}_1\] \[\mathbf{y} = W_2 \mathbf{h} + \mathbf{b}_2\]
代入:
\[\mathbf{y} = W_2 (W_1 \mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2 = W_2 W_1 \mathbf{x} + W_2 \mathbf{b}_1 + \mathbf{b}_2\]
记 \(W' = W_2 W_1\),\(\mathbf{b}' = W_2 \mathbf{b}_1 + \mathbf{b}_2\),则:
\[\mathbf{y} = W' \mathbf{x} + \mathbf{b}'\]
这就是单层线性。归纳法可以推出 n 层无激活的复合也等价于单层。
数学上严谨多了。插入非线性 \(\sigma\) 之后,因为 \(\sigma\) 不可交换出来(\(\sigma(W\mathbf{x}) \ne W \sigma(\mathbf{x})\) 一般情况下),整个表达就不再能折叠成单层了。这就是非线性的本质作用——打破线性变换的「可交换+可吸收」性质。
激活函数和参数初始化是耦合的。同一个网络,初始化变了,激活函数的合适选择也会变。
具体说,初始化的目标是让前向传播时每层激活的方差稳定(不爆炸、不消失),同时让反向传播时梯度方差稳定。不同激活函数对这两个目标的影响不同:
这些是经典论文里推出来的。有兴趣可以读 He et al. 2015《Delving Deep into Rectifiers》——里面把 ReLU 初始化的方差推导得很清楚。
初始化错了,再好的激活函数也救不了你。这是新手常踩的坑——下载了别人代码,把 ReLU 换成 Sigmoid 就完蛋,因为初始化没跟着换。
激活函数还和归一化(Normalization)层紧密相关。
BatchNorm(2015)和 LayerNorm(2016)的出现,让激活函数饱和的问题得到了缓解——归一化把每一层的输入拉到一个合理范围,避免输入过大导致 Sigmoid/Tanh 饱和。
有趣的是,归一化层的存在让一些「老」激活函数重新焕发生机——比如在 Transformer 里,Pre-LayerNorm + GELU 工作良好;在 ViT 里,Pre-LayerNorm + GELU 也是标配。如果没有 LayerNorm,单纯的 GELU 也很难训稳大型 Transformer。
激活 + 归一化是「深度学习训练稳定性的双轮」。讨论激活函数时,不能脱离归一化谈。
激活函数主要解决梯度消失。但爆炸呢?
爆炸通常不是激活函数的问题,是权重过大或循环结构(RNN)的问题。激活函数有界(Sigmoid/Tanh)能从源头限制爆炸,但代价是梯度消失。无界激活(ReLU)不限制爆炸,但配合归一化和权重初始化通常没事。
实战中对抗梯度爆炸的工具:
激活函数本身能做的有限。它的主战场仍然是梯度消失。
我们之前说「激活函数都是逐元素的」。唯一的例外是 Softmax。
\[\text{Softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_j e^{z_j}}\]
它把一个向量映射到「概率分布」——所有输出非负、和为 1。它是非逐元素的(每个输出依赖整个输入向量)。
Softmax 在 Transformer 里有两个关键位置:
Softmax 我们留到「第八篇·Softmax 与概率分布」专门讲。这里只是提一句它的特殊地位——它不算狭义的「激活函数」,但它做的事情类似(引入非线性、把一个向量「整形」成有特定语义的输出)。
激活函数的导数决定了梯度怎么流。简单整理:
| 激活 | 函数 | 导数 |
|---|---|---|
| Sigmoid | \(\sigma(z) = 1/(1+e^{-z})\) | \(\sigma'(z) = \sigma(z)(1-\sigma(z))\) |
| Tanh | \(\tanh(z)\) | \(1 - \tanh^2(z)\) |
| ReLU | \(\max(0, z)\) | \(\mathbb{1}(z > 0)\) |
| Leaky ReLU | \(\max(\alpha z, z)\) | \(\mathbb{1}(z > 0) + \alpha \mathbb{1}(z \le 0)\) |
| ELU | \(z\) 或 \(\alpha(e^z - 1)\) | \(1\) 或 \(\alpha e^z\) |
| Swish | \(z \cdot \sigma(z)\) | \(\sigma(z) + z \sigma(z)(1-\sigma(z))\) |
| GELU | \(z \cdot \Phi(z)\) | \(\Phi(z) + z \phi(z)\) |
注意 Swish/GELU 的导数都不是「函数本身的简单变换」——它们包含 \(z\) 和 \(\sigma(z)/\Phi(z)\) 的乘积。这意味着反向传播时既要算 \(\sigma\) 又要算 \(\sigma'\),比 ReLU 贵一点。但在 GPU 上这点开销几乎可忽略。
「梯度流」(gradient flow)是分析深度网络训练的一个视角。我们关心:每过一层,梯度的「典型大小」会变成多少?如果大于 1,可能爆炸;小于 1,可能消失;接近 1,最稳。激活函数选得好,能让梯度流尽量保持「接近 1」。这就是从 Sigmoid 到 ReLU 到 GELU 演化的内在逻辑。
文字讲多了,回到代码感受一下。一个最小的两层 MLP:
import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self, in_dim, hidden, out_dim, act='relu'):
super().__init__()
self.fc1 = nn.Linear(in_dim, hidden)
self.fc2 = nn.Linear(hidden, out_dim)
self.act = {
'sigmoid': nn.Sigmoid(),
'tanh': nn.Tanh(),
'relu': nn.ReLU(),
'leaky': nn.LeakyReLU(0.01),
'elu': nn.ELU(),
'gelu': nn.GELU(),
'silu': nn.SiLU(),
}[act]
def forward(self, x):
return self.fc2(self.act(self.fc1(x)))用同一份训练代码、同一个数据集(比如 MNIST 或 fashion-MNIST),跑 Sigmoid / Tanh / ReLU / GELU 四个版本——你会清楚地看到:
我建议每个学深度学习的人都自己跑一遍这个实验。理论看得再多,不如自己跑一次的肌肉记忆深刻。Karpathy 的 makemore 系列有一节专门讲激活函数的实证对比,强烈推荐。
这是最近(2023 年开始)一个有趣的现象。GPT-3 还用 GELU,但 LLaMA 系列、Mistral、Qwen、DeepSeek、Yi 全用了 SwiGLU。这不是巧合。
主要原因有几条。
第一,效果实测稍好。Shazeer 2020 的论文《GLU Variants Improve Transformer》对比了多种 GLU 变体在 T5 上的表现,SwiGLU 略胜 GeLU。LLaMA 团队复现了这个结论,于是采用。
第二,参数效率。SwiGLU 多一个 \(W_3\) 矩阵,但 LLaMA 把 hidden_dim 略微缩小,使总参数量保持原样。同样参数预算下 SwiGLU 略好。
第三,惯性效应。LLaMA 是开源大模型的标杆,它怎么做后面的人就跟着做。这种「事实标准的传递」是开源社区的常见现象——一旦某个实践被证明有效且开源可见,其他人不会冒险换。
但 SwiGLU 也不是没缺点:
我的判断:SwiGLU 不是终点。未来可能有更好的 FFN 结构(比如 Mixture of Experts、LayerScale 等替代)。但短期内 SwiGLU 是大模型的实战标配。
说一段题外话。
我喜欢把激活函数想象成「邮局窗口」——输入是一封信,激活函数是窗口工作人员,决定这封信是「寄出去」还是「扔掉」。
这是个调皮的比喻,但帮我记住了不同激活的「精神」。读者可以自己创造比喻——让抽象概念在脑子里有形象,是学习深度学习的关键习惯。
激活函数这个领域,给我最大的启发是:
深度学习的进步,往往来自「把一个简单的细节做到极致」。
激活函数有什么大不了?不就是一个一维标量函数嘛。但从 Sigmoid 到 ReLU,整个深度学习领域换了几代——这是一个看似微小的细节,却带动了整个学科的变革。
类似的「小细节大革命」还有很多:
每一个细节,单独看都不起眼;合起来,把深度学习从「学术玩具」变成了「工业级技术」。
这给我们一个研究方法论:关注「不起眼」的小问题,往里面深挖。激活函数研究了三十年,仍然有人在 ICLR/NeurIPS 发新激活函数的论文。这不是「卷」,这是「重要的小事」。
回顾一下本篇的脉络。
为什么需要激活函数?因为线性的复合还是线性,单纯堆叠 \(W\mathbf{x} + \mathbf{b}\) 表达力极有限。激活函数引入非线性,把「分段线性」「光滑曲线」等表达能力带给神经网络。每一层的激活函数都是「弯曲」的来源。
激活函数家谱怎么看?早期 Sigmoid(光滑、概率解释),改进 Tanh(零中心),革命 ReLU(不饱和、训得动深),修补 Leaky/ELU(缓解 Dying),现代 Swish/GELU(光滑 ReLU),LLM 时代 SwiGLU(门控 + Swish)。每一代都解决了上一代的某个核心问题。
激活函数的「形状」决定什么?决定梯度怎么流。两端饱和会梯度消失;中段陡峭会梯度爆炸;恒等斜率(ReLU 正半轴)让梯度原样传递。深网络能不能训,根本上是「梯度流稳不稳」的问题。
激活函数与其他组件的耦合:和初始化耦合(不同激活配不同初始化);和归一化耦合(LayerNorm 让饱和问题缓解);和优化器耦合(不同激活的 loss 曲面不同,最佳学习率也不同)。孤立讨论激活函数没有意义,要把它当作整个系统的一部分。
Transformer 用什么?GELU 是经典选择;SwiGLU 是最新事实标准。两者实质都属于「光滑、近似 ReLU」家族。
把这五点串起来,你对激活函数的理解就完整了。
误解一:激活函数就是「神经元的开关」。
部分对。Sigmoid 和 Tanh 像「软开关」,ReLU 像「硬开关」。但 Swish/GELU 的「开关」是平滑过渡的,没有明显的「关」状态。激活函数本质是非线性函数,不必都按「开关」理解。
误解二:越复杂的激活函数效果越好。
错。ReLU 是最简单的之一,却是十年来最成功的激活函数。SwiGLU 比 GELU 复杂,但提升幅度有限。复杂不等于好——简单 + 工程实用,往往胜过复杂 + 数学优雅。
误解三:训练时用哪个激活函数差别不大。
大错。激活函数能让训练快 10 倍或慢 10 倍。Sigmoid 和 ReLU 在深网络上的差距是「能训」与「训不动」的差距。激活函数是深度学习能 work 的关键之一。
误解四:激活函数只在隐藏层有用,输出层不需要。
要看任务。回归任务输出层通常不加激活(让输出可以是任意实数);分类任务输出层加 Softmax(变概率);二分类输出层加 Sigmoid。所谓「输出层不需要激活」是回归的特例,不是通则。
误解五:Sigmoid 已经被淘汰了。
不完全。Sigmoid 仍然在二分类输出层、注意力门控(如 LSTM)、某些特殊场景里活跃。它在隐藏层确实被 ReLU 系替代,但不等于全面淘汰。
误解六:所有 Transformer 都用 GELU。
不对。原始 Transformer(2017)用 ReLU;BERT/GPT 系列用 GELU;LLaMA 系列用 SwiGLU。激活函数选择随时代演进,不要被「Transformer 用 X」一概而论。
误解七:激活函数「越接近 ReLU」越好。
部分对。形状像 ReLU(一边趋零、另一边趋恒等)的激活,确实在深网络上表现普遍较好。但「光滑」「零中心」「门控」等其他属性也重要。不要只盯一个维度优化。
误解八:激活函数是网络的「主要参数」。
错。激活函数本身是固定的非线性函数,几乎没有可学习参数(除了 PReLU 等少数变种)。可学习参数都在 \(W\) 和 \(\mathbf{b}\) 里。
误解九:换一个激活函数就能让模型大幅提升。
实际中,从 ReLU 换 GELU 的提升通常 < 1%。从 GELU 换 SwiGLU 类似。激活函数的边际收益有限——但如果你的模型在百亿参数级别,这 1% 就是值钱的。
误解十:激活函数研究已经做完了。
未必。每年仍有新的激活函数论文。比如 Mish、Hardswish、SquaredReLU、Geglu 等等。虽然「大革命」可能不会再有,但「小优化」一直在继续。
下一篇(第六篇)我们讲梯度下降与反向传播。
我们会从最朴素的梯度下降开始,逐步讲到 SGD、Momentum、Adam,看看「怎么用数据找好参数」这件事的工程实现。然后我们会讲反向传播——链式法则的工程化版本,让自动微分成为可能。
之后第七篇是 Softmax 与概率分布——它是注意力机制的核心齿轮之一。第八篇之后我们就开始接触向量空间、嵌入、位置编码,正式踏上注意力机制的核心。
激活函数这一篇,是「神经网络的非线性来源」。Softmax 那一篇,是「注意力的概率化齿轮」。这两个非线性合起来,构成了 Transformer 的全部非线性来源。记住一句话:Transformer 的非线性,在 FFN 的激活(GELU/SwiGLU)和 Attention 的 Softmax 这两个地方。
上一篇:04. 从函数到神经网络 下一篇:06. 梯度下降与反向传播
写完正文,我想抒情一下。
激活函数是我学深度学习时最早「惊艳到我」的概念之一。它的设计不靠数学推导,不靠理论证明,全靠实证选优——人们试了很多个,看哪个 work,就用哪个。这种「实验科学」的色彩,和我之前接触的纯数学非常不同。
后来我发现整个深度学习都是这种风格。先做实验,再补理论;先 work,再 understand。激活函数只是这种风格最浓缩的体现。
这种风格让一些纯数学背景的人不舒服。他们觉得「不优雅」「不严格」。但我觉得这恰恰是深度学习有意思的地方——它逼迫我们承认:复杂系统的某些性质,可能永远超出现有的数学工具。我们能做的,是先把它跑起来,再慢慢理解它为什么 work。这是科学研究的另一种形态。
如果你正在写代码、纠结选哪个激活函数,下面是一份实战清单:
MLP / 简单分类:用 ReLU。无脑选,省得纠结。
CNN:用 ReLU 或它的变种(Leaky/PReLU)。ReLU 仍然是 CNN 的标配。
Transformer 隐藏层:用 GELU 或 SwiGLU。GELU 经典,SwiGLU 现代。
RNN(如果你还在用):用 Tanh。LSTM 内部的门控用 Sigmoid,cell 状态用 Tanh,沿用经典。
输出层: - 回归:不加激活。 - 二分类:Sigmoid。 - 多分类:Softmax。
调试 Dying ReLU:换 Leaky ReLU 或 ELU 试试。通常治标不治本,根本解决在初始化和学习率。
模型很深、训练不稳:先检查 LayerNorm/BatchNorm 是不是加对了;激活函数本身换不大可能解决问题。
到这一篇为止,「数学与基础」部分(第 1-8 篇)完成了一半多。接下来:
之后我们会进入「注意力机制原理」篇章(第 11-18 篇),这是整个系列的核心。我会保持「长篇叙述 + 直觉为先」的风格,希望大家能跟着走完。
激活函数这一篇是「细节但关键」。下一篇梯度下降会更工程化一点——我们会真正面对优化器、学习率、动量这些训练时最常碰到的概念。
下一篇见。
最后再多絮叨几句。
关于本系列的定位:我希望本系列既能让初学者「建立直觉」,也能让有经验的人「重温细节」。所以正文偏向叙述和直觉,每篇配若干 SVG 直观图;后记和参考文献则偏向深入和工程。读者可以根据自己的水平选择性阅读。
关于「篇幅长」的辩护:本系列每一篇都接近一万字。这不是为了凑字数,而是因为深度学习的概念之间高度耦合——讲一个概念时不可避免会牵出旁系。我宁可一篇长一点把背景讲透,也不愿写五篇短的让读者跳来跳去。
关于「不卖弄」的承诺:我尽量避免「炫技式」的表述——故意用难懂的术语显得自己专业。我希望本系列读起来像「学长在跟你聊天」,而不是「教授在台上讲课」。如果某些段落让你觉得「在卖弄」,欢迎指出,我会改。
关于「错误」的免责:本系列我自己一遍一遍校对,但不可避免会有小错(公式手误、年份记错、术语用错)。如果你发现,请告诉我。AI 时代,唯一不变的是「人会犯错」——但人也会改。
那么,激活函数这一篇就到这里。
下一篇见。
讲完原理和家谱,我想专门挑出一个小坑——数值稳定性。
激活函数在数学上是良好定义的,但在浮点运算里会翻车。最经典的例子是 Sigmoid:
\[\sigma(z) = \frac{1}{1 + e^{-z}}\]
当 \(z\) 很大(比如 \(z = 1000\))时,\(e^{-z} \approx 0\),\(\sigma(z) \approx 1\),没问题。
但当 \(z\) 很负(比如 \(z = -1000\))时,\(e^{-z} = e^{1000}\)——浮点数会溢出(overflow)。结果是 NaN 或 inf。
工程实现里,Sigmoid 常用 torch.sigmoid
或者数值稳定的写法:
def stable_sigmoid(z):
return torch.where(z >= 0,
1 / (1 + torch.exp(-z)),
torch.exp(z) / (1 + torch.exp(z)))类似的,Softmax 必须减去最大值再做 exp:
def stable_softmax(z):
z = z - z.max(dim=-1, keepdim=True).values
e = torch.exp(z)
return e / e.sum(dim=-1, keepdim=True)否则 \(e^z\) 容易溢出。
GELU 和 Swish 也有类似的稳定性问题。PyTorch 的内置函数都已经处理好了,但如果你手写实现,要小心。
这些坑通常在 fp16/bf16 训练时更严重——半精度的范围更小,更容易溢出。所以训练大模型时,激活函数的数值实现细节非常重要。
混合精度训练(mixed precision training)是大模型时代的必备技能。它在 fp16 或 bf16 下做矩阵乘法,节省一半显存和加快一倍速度,同时在 fp32 下做 loss 计算和参数更新,保证精度。
激活函数在这种训练里要特别小心:
Sigmoid/Tanh:在 fp16 下饱和区会精度损失。极端值附近导数接近零,fp16 表示不出来差异。 ReLU:基本无影响,因为它只判断符号。 GELU/Swish:要看具体实现。tanh 近似版本在 fp16 下可能略不稳定;erf 精确版本一般稳定。 Softmax:必须用稳定写法,否则 fp16 下溢出概率非常高。
实际经验:Transformer 在 bf16 下训练时,GELU/SwiGLU 都没问题;在 fp16 下偶尔会出现 NaN,需要 loss scaling 或者切换到 bf16。
这些不是激活函数本身的设计问题,而是它和硬件、框架配合时的问题。但作为工程实践者,必须知道。
如果你想直观理解激活函数,最好的方式是画图。
我给学生上课时常做一个练习:让学生用
matplotlib
画出每个激活函数的「函数曲线」和「导数曲线」,并并排对比。这个练习极其有效——画完之后,学生对各激活的形状一辈子忘不掉。
代码极简:
import torch
import matplotlib.pyplot as plt
z = torch.linspace(-5, 5, 200)
fns = {
'Sigmoid': torch.sigmoid,
'Tanh': torch.tanh,
'ReLU': torch.relu,
'GELU': torch.nn.functional.gelu,
'SiLU': torch.nn.functional.silu,
}
fig, axes = plt.subplots(2, len(fns), figsize=(15, 5))
for i, (name, fn) in enumerate(fns.items()):
z_grad = z.clone().requires_grad_(True)
y = fn(z_grad)
y.sum().backward()
axes[0, i].plot(z.numpy(), y.detach().numpy())
axes[0, i].set_title(name)
axes[1, i].plot(z.numpy(), z_grad.grad.numpy())
axes[1, i].set_title(name + " derivative")
plt.tight_layout()
plt.show()跑一下,你会看到 Sigmoid 的导数像个钟形(峰值 0.25),Tanh 的导数像个钟形(峰值 1),ReLU 的导数是阶跃,GELU/SiLU 的导数是「光滑阶跃」。视觉化让抽象概念变具体。
最后讲一个高级一点的视角——微分几何。
神经网络可以看作 \(\mathbb{R}^n \to \mathbb{R}^m\) 的映射。激活函数让这个映射「弯曲」。从微分几何看,网络的每一层都在变形输入空间的几何结构——把曲线变直,把直线变曲,把流形变形。
线性层做「线性变形」(保持平直,只做拉伸/旋转);激活函数做「逐点弯曲」(把每个点单独弯一下)。两者交替,整个网络逐层把输入空间「揉」成对任务有用的形状。
这个视角在「几何深度学习」(Geometric Deep Learning)和「信息几何」(Information Geometry)领域有专门的研究。如果你对数学感兴趣,强烈推荐 Bronstein 等人的综述《Geometric Deep Learning: Grids, Groups, Graphs, Geodesics, and Gauges》。
我在这里只能蜻蜓点水。激活函数的「几何效果」,是深度学习理论最迷人的方向之一。
最后一个题外话——激活函数在硬件上的实现。
CPU/GPU 上,激活函数是软件实现的——一个数学函数,调用 exp/erf/max 等指令算出来。但有些专用硬件(TPU、神经形态芯片、模拟计算芯片)会把激活函数固化到电路里。
固化的好处是速度极快——不用走指令解码,电路直接给结果。坏处是不灵活——一旦想换激活,硬件要重新设计。
这就是为什么很多专用芯片只支持 ReLU 或者 GELU 的近似版本——这些激活足够稳定、足够普及,硬件厂商敢押宝。激活函数的选择,会反过来影响硬件设计。这是一个软件和硬件互相塑造的有趣例子。
未来如果有了「神经形态计算」的大规模应用,激活函数甚至可能不再是「函数」——而是某种模拟电路的物理特性。这是个值得关注的方向。
终于到末尾了。本篇我从「为什么需要非线性」一路讲到 SwiGLU,从原理讲到工程,从数学讲到硬件。希望你读完之后,对激活函数这个看似不起眼的小角色有了全新的尊重。
激活函数是深度学习里最简单的概念之一,也是最容易被低估的概念之一。一个一维标量函数,背后是三十年的演化、无数次的实验、整个学科的范式转移。
下一篇我们讲梯度下降与反向传播。如果激活函数是「让网络能弯曲」,那梯度下降就是「让网络能学习」。两者合起来,就是神经网络的「形 + 神」。
谢谢你又一次读到这里。我们下一篇见。
正文里我们讲了主流激活函数。这里再聊几个「不太主流但值得知道」的变种。
Mish(Misra, 2019):
\[\text{Mish}(z) = z \cdot \tanh(\ln(1 + e^z))\]
形状非常像 Swish,但导数稍微不同。在 YOLOv4 等目标检测模型里被使用,效果略好。但在 LLM 上没普及。
Hardswish / HardSigmoid(MobileNetV3):
\[\text{Hardswish}(z) = z \cdot \frac{\text{ReLU}(z + 3)}{6}\]
这是 Swish 的「分段线性近似」。在移动端部署时,分段线性比 Swish 的 sigmoid 计算便宜很多。是「精度与速度的权衡」的典型例子。
SquaredReLU:
\[\text{SquaredReLU}(z) = \max(0, z)^2\]
把 ReLU 的输出再平方一下。在某些 Transformer 变体里被用过,效果略有差异。
Softplus:
\[\text{Softplus}(z) = \ln(1 + e^z)\]
「光滑版的 ReLU」,但梯度下降不如 ReLU 高效,所以没流行起来。
Maxout(Goodfellow 2013):
\[\text{Maxout}(z) = \max(W_1 z + b_1, W_2 z + b_2, \cdots, W_k z + b_k)\]
它是「可学习的分段线性」。理论上很强(可以近似任何凸函数),但参数量是普通激活的 k 倍,没法 scale,被遗忘。
Snake(Liu et al. 2020):
\[\text{Snake}(z) = z + \frac{1}{a}\sin^2(az)\]
试图引入周期性。在某些时序信号建模任务里有用。
这些「小变种」大多数没成为主流,但每一个都解决了某个具体问题,值得作为「激活函数的多样性」的注脚记下来。
激活函数研究的状态:主流稳定(ReLU 系),但小变种层出不穷。每年几篇新激活函数的论文,但真正能推翻 ReLU/GELU/SwiGLU 三巨头的,至今没出现。
正文里我提到激活函数和初始化耦合。这里展开一点。
He 初始化的推导大致如下。考虑一层 \(y = Wx\),假设 \(W\) 的元素独立同分布、\(x\) 的元素独立同分布、均值为 0。则:
\[\text{Var}(y_i) = \sum_j W_{ij}^2 \cdot \text{Var}(x_j) = n \cdot \text{Var}(W) \cdot \text{Var}(x)\]
如果想让 \(\text{Var}(y) = \text{Var}(x)\)(前向方差稳定),需要 \(\text{Var}(W) = 1/n\),也就是 Xavier 初始化。
但这个推导假设 \(x\) 没有经过激活。如果有 ReLU,\(x\) 一半被砍成 0,方差减半。要补偿这个减半,应该让 \(W\) 的方差加倍——\(\text{Var}(W) = 2/n\)。这就是 He 初始化。
如果有 Tanh/Sigmoid,因为它们近似线性(在 0 附近),方差几乎不变,所以仍用 Xavier。
这种推导在更深的网络上还要考虑「方差的链式累积」。He 论文里证明:用 Xavier + ReLU 训 30 层 CNN,方差会指数衰减,无法训练;用 He + ReLU 则方差稳定,可以训得动。初始化与激活函数必须配套。
后来的工作(如 LSUV、深度残差网络)让初始化的重要性稍微降低(因为 LayerNorm/BatchNorm 自动归一化了激活),但「激活与初始化的相互作用」始终是一个核心议题。
最近 LLM 流行 Mixture of Experts (MoE) 架构(如 Mixtral、DeepSeek-MoE)。MoE 里有一个「路由器」决定 token 走哪个 expert。这个路由器本身用了激活函数——通常是 Softmax + Top-K 选择。
每个 expert 内部仍然是 SwiGLU 结构。所以 MoE 模型里有两层激活:
激活函数不仅出现在 FFN 里,只要涉及非线性映射或概率分布的地方,都有它的影子。注意力的 Softmax、Embedding 的某些归一化、损失的 Sigmoid/Softmax、输出的最终归一化……一个 LLM 里至少有几十种「激活」类操作。
这给我一个新的视角:激活函数不是某一种「层」,而是「贯穿模型的非线性原语」。理解它,就是理解模型的「弯曲」如何在不同层次发生。
最后辩护一下篇幅。
激活函数表面是个「一维标量函数」,但它牵涉到:
每一个都值得展开。把它们打包到一篇里,当然就长了。但也只有打包,读者才能看到「激活函数」作为一个概念,是怎么和深度学习的其他部分纠缠在一起的。
我希望你读完之后的感受不是「学到了 ReLU 是 max(0, z)」,而是「激活函数是深度学习的一个缩影——简单到极致,又深远到惊人」。
如果有这种感受,本篇就值了。
下一篇见。
本篇内容大量参考了:
向所有把这些精彩工作分享出来的研究者致敬。本系列只是站在他们的肩膀上的二次创作。
下一篇见。
把工程经验再细化一下。下面列几种「训练出问题,可能和激活有关」的典型场景,以及怎么排查。
场景 1:loss 一直不降,几乎是常数。
可能:激活全部饱和或全部死亡。Sigmoid 的输入值过大导致 \(\sigma' \approx 0\);ReLU 的所有神经元输入都为负。
排查:打印每层激活的均值、方差、min、max。如果均值都接近 0 或接近边界,就是饱和/死亡。 对策:换激活(ReLU → GELU 之类);调小学习率;改初始化;加 LayerNorm。
场景 2:loss 突然变 NaN。
可能:激活溢出。Sigmoid/Softmax 在极大输入下会 inf,进而 NaN。
排查:开启
torch.autograd.set_detect_anomaly(True);打印梯度统计。
对策:加梯度裁剪;用稳定版的 Softmax/Sigmoid;用 bf16 替代
fp16。
场景 3:浅层学得快,深层学不动。
可能:梯度消失。激活的导数累乘到深层时太小。
排查:打印每层的梯度范数。如果从输出到输入梯度逐层减小到接近 0,就是消失。 对策:加残差连接(Residual);改 ReLU 系激活;改初始化;加 LayerNorm。
场景 4:激活值分布不正常(部分维度全 0 或全 max)。
可能:Dying ReLU 或某些维度饱和。
排查:可视化每层激活的直方图(用 TensorBoard 或类似工具)。 对策:换 LeakyReLU/GELU;调初始化;加 dropout 让激活更分散。
场景 5:训练正常但推理时模型行为奇怪。
可能:你忘了 model.eval(),BatchNorm 和
Dropout 仍在训练模式;或者激活函数有非确定性(极少见)。
排查:检查 model.train() vs model.eval() 切换。 对策:永远在推理前调 .eval()。
这些场景全是我自己踩过的坑。激活函数虽然是一个「小细节」,但在调试时是「第一战场」之一。希望这些经验对你有用。
行了,这次真的最后一段。
本篇 28 节正文 + 17 节后记,写下来已经接近书的章节体量。激活函数这个题目,如果让我再写,我可能还能扩展出十几节——它真的太「纠缠」了,牵一发而动全身。
但适可而止。读者要消化、我也要喘口气。剩下的细节,留给下一次系统化的「深度学习系统手册」类的写作(如果有那一天)。
激活函数到此为止。下一篇梯度下降与反向传播,我们继续。
谢谢读到这里的每一个你。下一篇见。
到这里整个第 5 篇就完整了。从「为什么需要激活函数」到「激活函数的家谱」,从「数学性质」到「工程细节」,从「Sigmoid 的过去」到「SwiGLU 的现在」,希望对你深入理解深度学习有一些帮助。
再见。
写完十七篇后记,我自己回看也乐了——「后记比正文还长」是作者最不该犯的毛病。但激活函数这个话题真的容易勾出闲话。每写一段就想到一个相关的故事或经验,刹不住车。
为了不再失控,给本篇收一个真正的尾。
激活函数到底是什么?——一个让网络具备「弯曲表达力」的逐元素非线性函数。
为什么它重要?——没有它,网络再深也只能拟合超平面;有它,两层就能逼近任意连续函数(理论上)。
怎么选?——隐藏层用 ReLU 系(ReLU/GELU/SwiGLU),输出层根据任务选(无激活/Sigmoid/Softmax)。
演化方向?——从「光滑」到「不饱和」到「门控」,每一步都解决了上一代的某个核心痛点。
未来呢?——大变革可能不会再有,但小优化(针对特定任务、特定硬件)会持续。
把这五点记住,本篇的核心就拿到了。剩下的都是细节。
下一篇我们正式进入梯度下降。从「网络的形」(结构 + 激活)转到「网络的神」(学习 + 优化)。这是另一座大山,等你来爬。
再再再见。
写完这篇时已经是深夜,窗外飘了点小雪。我合上笔记本电脑,给自己倒了杯茶。
回想我第一次接触激活函数,是在大学三年级一门人工智能选修课上。老师在黑板上画 Sigmoid 曲线,说「这个 S 形,是神经网络非线性的来源」。当时我觉得「不就是一条 S 嘛,有什么了不起」。
十几年过去了,我从来没想到这条 S 形——以及它的兄弟姐妹们——会带给我这么多思考。简单的东西,往往是深邃的入口。激活函数让我看到了这一点。
如果本篇对你有所启发,那就把这种启发用在你接下来要学的东西上。保持对「简单细节」的敬畏,不要轻易跳过。深度学习里没有真正不重要的细节——只是有些细节,重要性藏得深一点,需要你慢慢发掘。
茶喝完了。明天继续写第六篇梯度下降。
晚安。
完。
亲爱的读者:
如果你顺序读到了这一篇,意味着你已经走过了向量、点积、矩阵乘法、神经网络、激活函数这五块「砖头」。这些都是 Transformer 的最基础积木。
接下来我们要进入「学习的机制」——梯度下降、反向传播、Softmax、嵌入、位置编码。这些会更工程化、更实战。等到第 11 篇我们会正式进入「注意力机制」——这是整个系列的核心,也是我个人最想讲清楚的部分。
如果你觉得到目前为止的内容慢、节奏拖沓——我承认有这种风险。本系列追求的是「让你建立直觉」,而不是「快速过完知识点」。如果你想快,强烈推荐 Karpathy 的视频——他三小时能讲完我五篇文章的内容,效率高很多。但慢有慢的好处:你会记得更牢。
如果你觉得到目前为止的内容快、跟不上——也欢迎反馈。我会在后续篇章里调整节奏。
总之:慢慢来,比较快。深度学习不是百米冲刺,是马拉松。一年的扎实学习胜过三个月的速成。本系列陪你走得稍远一点。
下一篇见。
——ltl
最后留一段虚构的小对话,算彩蛋。
——「老师,为什么不直接用最复杂的激活函数?比如三次多项式 + 周期项 + 指数衰减?」
——「你试过吗?」
——「没试过,我觉得理论上更复杂的应该更强。」
——「这就是问题。深度学习里,没试过就不算证据。你试一下就会发现,复杂激活的训练很不稳定,而且收益寥寥。」
——「但理论上……」
——「理论上 ReLU 不应该比 Sigmoid 好。但实际上 ReLU 大杀四方。理论是地图,实证是道路。」
——「那 SwiGLU 呢?为什么它就 work?」
——「因为有人试过了,并且发现 work。Shazeer 在论文里说『divine benevolence』,半开玩笑半认真——它就是 work,深层原因还在研究。」
——「我不太能接受这种『就是 work』的研究方式。」
——「这是大多数从纯数学过来的人最初的反应。但科学有两条路——演绎(从公理推导)和归纳(从实验提炼)。物理学起步靠归纳(伽利略、开普勒),后来才有牛顿做演绎。深度学习现在还在「伽利略阶段」——大量做实验、归纳规律。理论体系会慢慢建立,但不要等理论建好再用工具。」
——「我懂了。所以激活函数的研究,是一种实验科学。」
——「对。这也是它的魅力——任何人,只要敢做实验,都有机会发现新东西。你不必是数学天才,也能为这个领域做贡献。」
——「我可以试试发明一个新激活函数?」
——「试试看。但记住三件事:1)证明它在多个任务上稳定优于 ReLU/GELU;2)解释它为什么 work(哪怕只是直觉);3)保持谦虚——大多数新激活只是「和现有的差不多」,不是「革命」。」
——「好的,我去试了。」
——「祝你好运。如果发明出来了,记得让我用上。」
这段对话浓缩了我对激活函数研究态度的总结。深度学习是「敢做实验的人的乐园」——不要被「我不会数学」「我不是天才」吓住。实验本身就是一种思考。
下一篇真的真的见。
把当前热点继续串成多页阅读,而不是停在单篇消费。
2026-04-15 · transformer
把 Transformer block 里那个看起来最不起眼的两层 MLP 真正讲清楚——4 倍扩张比的来历、逐位置而不是跨位置的设计、Geva 等人 2021 年提出的「键值记忆」视角、SwiGLU/GLU/GeGLU 的现代变体、参数量分布、可解释性研究、量化时的瓶颈,以及它和 MoE 的关系。
2026-04-15 · transformer
embedding 是把离散的词变成稠密向量的桥梁。从 one-hot 的痛苦出发,经过 Firth 的分布假设、word2vec、GloVe、ELMo、BERT,一路走到现代 LLM 的 embedding 矩阵,本文把这条 70 年的演化讲清楚。
2026-04-15 · transformer
RNN 三难(长程依赖、梯度稳定、训练并行)的系统分析;attention 如何作为补丁逐步把 RNN 推向极限;Vaswani 2017 抛弃循环的范式革命
2026-04-15 · transformer
很多人第一次读 Vaswani 2017 的公式时,都会卡在那一个 √dk 上。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。