深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU

上一篇我们看了现代大模型对归一化的改造。

RMSNorm 去掉了均值中心化,只保留均方根缩放:一个沿用多年的标准组件,拆开一看,其中一部分工作在现代整体架构中已经有些多余了。

本篇来看第二个改动:Transformer 架构中的 FFN (MLP) 层的重构 ,而其具体内容,需要先从激活函数说起。

1. 激活函数的发展历程

很早之前,我们就展开过激活函数的相关内容:激活函数。其本质是给神经网络引入非线性。 这里再简单复述一遍:

Sigmoid 是最早被广泛使用的激活函数:

\\\sigma(x) = \\frac{1}{1 + e\^{-x}} \\

Sigmoid 将任意实数压缩到 \((0,1)\) 区间,在隐藏层可以理解为"激活概率",但现在更常见在分类头等输出层最后表示概率。

但它有两个问题:

  1. 输出不是零中心的,会导致后一层接收的输入始终为正,导致反向传播时权重梯度容易同号,梯度更新效率低。
  2. 两端饱和严重 ,\(x\) 较大或较小时梯度几乎为零,深层网络中容易梯度消失。

Tanh 缓解了第一个问题:

\\\tanh(x) = \\frac{e\^x - e\^{-x}}{e\^x + e\^{-x}} \\

很明显它把输出范围压缩到 \((-1,1)\),实现了零中心化 。但梯度饱和的问题仍然存在,在 \(x\) 绝对值较大的区域梯度依然趋近于零。

真正的转折点是 2012 年前后 ReLU(Rectified Linear Unit) 的普及:

\\\text{ReLU}(x) = \\max(0, x) \\

ReLU 的形式极简:正半轴保持梯度为 1,负半轴直接归零。

但这种看似不合理的设计却在隐藏层传播中带来了极大优势:正半轴梯度恒为 1,梯度消失问题被大幅缓解,同时计算更是简单到只需要一个比较操作。

ReLU 对训练深层网络起到积极贡献(此外还有残差网络),因此也是很长一段时间里的默认选择。

但 ReLU 也有自己的问题:负半轴完全归零会导致死神经元:当某个神经元对所有训练样本都输出负值时,其梯度将始终为 0,参数无法继续更新,从而永久停留在负半区。

因此:出现了一批试图修复这个问题的变体:

  1. Leaky ReLU :\(\max(\alpha x, x)\),负半轴改为一个小斜率 \(\alpha\)(比如 0.01),让信息仍然可以流动。
  2. PReLU :把 \(\alpha\) 变成可学习的参数。
  3. ELU :负半轴用指数曲线,让输出均值接近零。

    这些变体各自有一些效果,但都没有真正取代 ReLU 的主流地位。

再往后就来到了 16 年的 Gaussian Error Linear Units(GELU):

\\\text{GELU}(x) = x \\cdot \\Phi(x) \\

我们在之前的 ConvNeXt中也简单展开过它, \(\Phi(x)\) 是标准正态分布的累积分布函数。

GELU 在 ReLU 的基础上做了平滑,负半轴有一个非零的"尾巴",在 \(x=0\) 附近也是光滑可导的。

它在 NLP 领域很受欢迎,BERT 和 GPT 系列都用了它,它也逐渐成为 Transformer 系列中 FFN 的默认激活函数。

此外,17 年 Google 在 Searching for Activation Functions 提出了 Swish 激活函数,它看起来和 GELU 十分相似:

\\\text{Swish}(x) = x \\cdot \\sigma(x) \\

Swish 是 Google 实验室在同一个网络中进行只换激活函数的大规模 A/B Test 得到的,并没有从理论推导,而是纯粹实验发现的优解。

从函数形状上看,Swish 与 GELU 几乎等价,二者性能差距通常很小。

历史上 NLP 社区由于 BERT 的成功更偏向 GELU,而 CV 领域曾广泛采用实现更简单的 Swish。

这种难分伯仲的情况也让研究者开始跳出 "用哪种激活函数" 去发现其他逻辑,这也是下部分的内容:

2. 标准 FFN 的结构局限

简单过了过激活函数后,现在我们再来看 Transformer Block 中的 FFN 层:

\\\text{FFN}(\\mathbf{x}) = \\mathbf{W}_2 \\cdot \\sigma(\\mathbf{W}_1 \\mathbf{x} + \\mathbf{b}_1) + \\mathbf{b}_2 \\

其中 \(\sigma\) 是激活函数,原版 Transformer 使用 ReLU,后续则大多换成了 GELU。

这条信息流路径非常直接:

\输入 \\rightarrow 升维 \\rightarrow 激活 \\rightarrow 降维 \\rightarrow 输出 \\

原理也很清晰:FFN 可以学习特征的重要性,在训练过程中,权重矩阵 \(\mathbf{W}_1\) 和 \(\mathbf{W}_2\) 会不断调整,从而让网络自动学会哪些模式更值得关注。

但以事后诸葛亮的视角来看,它仍然存在一个隐含的限制:

完成训练后,FFN 学到的是固定的重要性,而不是针对当前输入的动态重要性。

也就是完成训练后,某个隐藏神经元在训练过程中学会了识别某种特征,那么无论输入什么样本,它都会使用同一套权重进行计算。

这与注意力机制形成了鲜明对比,在 Attention 逻辑中,不同输入会产生不同的注意力分布,因此模型能够动态决定哪些信息应该被重点关注、哪些信息应该被弱化甚至忽略。

这种根据当前样本实时调整权重的能力,我们称之为动态门控思想

既然 Attention 可以动态选择重要信息,那么 FFN 能否也拥有类似的能力?

而答案正是后来逐渐流行起来的门控机制,我们之前在 RNN 中介绍过,例如 GRULSTM都利用门控控制信息流动。

不久前的 通道注意力系列等其实也是这种门控思想的应用。

那么如何实现在 FFN 中实现门控逻辑?就是本篇的主角:

3. SwiGLU

3.1 GLU:门控线性单元

GLU(Gated Linear Unit) 来自 17 年的论文 Language Modeling with Gated Convolutional Networks。它在标准全连接基础上增加了一个分支:

\\\text{GLU}(\\mathbf{x})= (\\mathbf{x}\\mathbf{W}+\\mathbf{b}) \\odot \\sigma(\\mathbf{x}\\mathbf{V}+\\mathbf{c}) \\

其中:

  • 左边的是 Value 分支。
  • 右边是 Gate 分支,使用 Sigmoid 函数激活。

其实逻辑和之前的SE是十分相似的,将输入同步聚合为一组权重再作用在输出上,实现门控逻辑。

这便是最基础的 GLU 逻辑。

3.2 SwiGLU

Sigmoid 门控本身是很符合我们的现实逻辑的,但在后续的实践中,研究者们却发现它其实不一定是最优选择。

20 年,论文 GLU Variants Improve Transformer 中做了一个系统的实验:把标准 FFN 替换成各种 GLU 变体,看门控激活函数选哪个最好,而 SwiGLU 表现最好,也由此在后续的实践中逐渐发扬光大。

SwiGLU 其实就是把 GLU 中的 Sigmoid 激活函数更换为了 Swish 函数:(DL 论文里经常忽略偏置,Transformer 架构里偏置甚至经常在实现中被直接删除。)

\\\text{SwiGLU}(x)= (xW) \\odot \\text{Swish}(xV) \\

而对于为什么使用 Swish 要强于 sigmoid 比较主流的认知主要集中在两点:

  1. sigmoid 的 0-1 取值范围只能实现抑制和保留,而 Swish 却不受限于 0-1,可以实现放大。
  2. sigmoid 在网络传播中的梯度问题仍然存在,Swish 更好优化。

3.3 一个应用 SwiGLU 的 FFN 子层

最终,Transformer block 在引入 SwiGLU 后,原本的 FFN 子层本的升维层被拆成了两条并行分支:

\\\text{SwiGLU}(x)= (xW_v) \\odot \\text{Swish}(xW_g) \\

随后再正常通过原本的降维层,整体表示如下:

\\\text{FFN}_{\\text{SwiGLU}}(x) = W_o \\Big( (xW_v) \\odot \\text{Swish}(xW_g) \\Big) \\

显然,其改进就在于利用额外的门控分支实现输入相关的动态调制。

这样,整个 Transformer block 就都拥有了动态调制输入的能力。

如今的开源大语言模型中,SwiGLU 也已经基本成为 FFN 模块的默认选择。

4. SwiGLU 的参数问题

在具体实现中,一个需要展开的点是 SwiGLU 的参数问题。

很明显的是,标准 FFN 用了 2 个权重矩阵,而 SwiGLU 由于门控分支增加到了 3 个。凭空多出一个矩阵,参数量必然上涨。

于是,为了保持参数量持平,LLaMA 将中间维度,也就是升维维度从 \(4d\) 压缩到了 \(\frac{8}{3}d \approx 2.67d\)。

对比一下:

  1. 标准 FFN:

\\\text{Params} = 2 \\times (d \\times 4d) = 8d\^2 \\

  1. SwiGLU FFN:

\\\text{Params} = d \\times \\frac{8}{3}d + d \\times \\frac{8}{3}d + \\frac{8}{3}d \\times d = 3 \\times \\frac{8}{3}d\^2 = 8d\^2 \\

刚好相等。这是刻意的参数预算分配。

因此,SwiGLU 的性能提升并不能简单归因于堆参数,而是在相同参数预算下,通过重新分配容量获得了更好的效果。

简单看几个例子:

模型 \(d_{\text{model}}\) \(d_{\text{ff}}\)(标准 4×) \(d_{\text{ff}}\)(SwiGLU 的 8/3×)
LLaMA 7B 4096 16384 11008
LLaMA 13B 5120 20480 13824
LLaMA 70B 8192 32768 28672

从 \(d_{\text{ff}}\) 实际取值来看,LLaMA 选择了比 \(\frac{8}{3}d\) 更规整的实际数字(通常取 8 的倍数),接近但不完全等于理论值。

相关推荐
coderwei1232 小时前
从OpenAI到Strip:用六大支柱读懂Harness Engineering的生产实践
python·ai·ai编程
春日见2 小时前
五分钟入门强化学习DDPG
大数据·人工智能·算法·机器学习·计算机视觉
小真zzz2 小时前
当“虚构的解决方案”成为试金石:搜极星如何将市场幻想变为可验证的现实?
搜索引擎·ai·大模型·deepseek
土星云SaturnCloud2 小时前
基于边缘计算的商场智慧运营架构设计与AI落地实践
服务器·人工智能·ai·边缘计算
启途AI3 小时前
当营销话术超越产品实体:GEO市场的诚信挑战
大数据·人工智能·搜索引擎·ai·chatgpt
todoitbo3 小时前
TimechoAI 上手:从一段时序数据跑到预测分析
人工智能·ai·aigc·timechoai
云和数据.ChenGuang3 小时前
openEuler下NLP模型的部署和推理
人工智能·深度学习·机器学习·自然语言处理·数据挖掘·边缘计算
虎冯河3 小时前
Token 知识文档, 什么是 Token?
人工智能·ai
YDS8294 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— 动态决策策略的接口对接
java·spring boot·ai·agent·spring ai·deepseek