SwiGLU
SwiGLU 将经过 SiLU 激活的投影与第二个线性投影相乘,构成 Llama、PaLM 和 Qwen 中的门控前馈层。附有代码。
SwiGLU(Swish 门控线性单元)是 Transformer中前馈层的一种门控变体。它将每个词元向量投影两次,将其中一个投影结果传入 SiLU (Swish)激活函数,再将两者按元素相乘,并将乘积投影回模型维度:FFN(x) = (SiLU(xW) ⊙ xV)W₂。Noam Shazeer 在 2020 年的论文 GLU Variants Improve Transformer中提出了这种方法,此后它成为 PaLM、Llama、Mistral 和 Qwen 等大型语言模型 (LLM)的前馈模块。与 ReLU或 GELU这类逐个处理数值的激活函数不同,SwiGLU 包含可学习权重,因此本身就是一个小型层。
SwiGLU 的工作原理#
标准 Transformer 前馈网络 (FFN) 使用一个权重矩阵扩展每个词元向量,应用激活函数,再通过第二个矩阵将其压缩回去。SwiGLU 使用门控线性单元 (GLU) 替换第一个矩阵和激活函数;GLU 会对相同输入进行两次并行投影:
- 门控投影:xW 经过 SiLU,即 x · σ(x),得到的值在输入强烈为负时接近零,在输入为正时则接近输入值。
- 数值投影:xV 保持线性并承载内容。
- 按元素相乘:将两者相乘后,门控可以根据输入放大、缩小或近乎归零每个隐藏特征,输出投影 W₂ 再将结果映射回模型维度。
门控理念源自 Dauphin 等人提出的 GLU。他们在 2016 年的门控卷积语言模型中使用了 sigmoid门。Shazeer 的论文测试了结构相同但门函数不同的版本,并用各自的激活函数为每种变体命名:GLU 使用 sigmoid,ReGLU 使用 ReLU,GEGLU 使用 GELU,SwiGLU 使用 Swish,而双线性层则不使用激活函数。沿用 T5 代码库的做法,这些层省去了偏置项。
SwiGLU 为何取代 LLM 中的 ReLU 和 GELU#
门控前馈层包含三个权重矩阵而非两个,因此 Shazeer 将隐藏层宽度缩小为原来的 2/3,以保持参数量和计算量相同;在 T5-base 模型中,单元数从 3,072 降至 2,048。在相同规模下,SwiGLU 经过 524,288 个训练步骤后,留出集上的对数困惑度达到 1.636,而 ReLU 为 1.677,GELU 为 1.679;它在 GLUE 和 SuperGLUE 语言理解基准上的平均分数也更高。论文没有对性能提升给出理论解释;其结论将这些架构的成功归因于“与其他一切一样,是神恩所致”。
这一成果也适用于大型模型。Google 的 PaLM 于 2022 年采用 SwiGLU,Meta 的 LLaMA 则于 2023 年用 SwiGLU 取代 ReLU,将隐藏层宽度设为 2/3 × 4d 并向上取整至 256 的倍数,使 4,096 维的 7B 模型拥有 11,008 个隐藏单元。Llama 2、Llama 3、Mistral 和 Qwen 均沿用了这一设计;在 Mixtral 等混合专家 (MoE)模型中,每个专家都是一个 SwiGLU 前馈块。
在 PyTorch 中实现 SwiGLU#
下面的 PyTorch代码片段为 512 维模型构建标准 ReLU 前馈层和 SwiGLU 层,并比较两者的规模:
import torch
import torch.nn.functional as F
from torch import nn
d_model = 512
x = torch.randn(4, d_model) # four token vectors
# Standard transformer FFN: two matrices around a 4x wider ReLU hidden layer
relu_ffn = nn.Sequential(nn.Linear(d_model, 2048, bias=False), nn.ReLU(), nn.Linear(2048, d_model, bias=False))
# SwiGLU FFN: three matrices, hidden width cut to about 2/3 of 2048
w_gate = nn.Linear(d_model, 1365, bias=False)
w_value = nn.Linear(d_model, 1365, bias=False)
w_out = nn.Linear(1365, d_model, bias=False)
y = w_out(F.silu(w_gate(x)) * w_value(x)) # (SiLU(xW) * xV) W2
relu_params = sum(p.numel() for p in relu_ffn.parameters())
swiglu_params = sum(m.weight.numel() for m in (w_gate, w_value, w_out))
print(f"ReLU FFN: output {tuple(relu_ffn(x).shape)}, {relu_params:,} parameters")
print(f"SwiGLU FFN: output {tuple(y.shape)}, {swiglu_params:,} parameters")它会输出 ReLU FFN: output (4, 512), 2,097,152 parameters 和 SwiGLU FFN: output (4, 512), 2,096,640 parameters。SwiGLU 的隐藏单元数为 1,365,约为 2,048 的 2/3;其三个矩阵所含权重数量几乎与 ReLU 的两个矩阵完全相同,因此两者的质量差异来自门控。许多实现会将门控矩阵和值矩阵融合为一次投影,并将其输出一分为二;这样只需执行一次较大的矩阵乘法,而非两次较小的矩阵乘法。
SwiGLU 与相关术语的比较#
- SiLU (Swish):SiLU 是门控内部按元素应用的函数。SwiGLU 则是包含三个投影的完整门控层。
- GLU 和 GEGLU:这两种同类变体分别使用 sigmoid 门和 GELU 门。在 Shazeer 的测试中,GEGLU 与 SwiGLU 的表现相当,对数困惑度为 1.633;Google 的 Gemma 模型使用了 GEGLU。
- 注意力:注意力也会生成依赖于数据的权重,但它会在词元之间混合信息。SwiGLU 则独立地门控每个词元内部的特征。
- 视觉模型:门控前馈层也会出现在视觉和多模态模型中。YOLOE由清华大学研究人员开发,是一款可在 Ultralytics 包中运行的开放词汇检测器。它使用残差 SwiGLU 块来优化类别提示的文本嵌入,该块在每次
set_classes()调用时运行一次,并会在导出时折叠,因此不会增加每帧的成本。










