SwiGLU原理:门控与参数量

黎 浩然/ 13 10 月, 2026/ 大语言模型/LARGELANGUAGEMODEL/LLM, 机器学习/MACHINELEARNING, 研究生/POSTGRADUATE, 计算机/COMPUTER/ 0 comments

SwiGLU 前馈层把两条线性投影逐元素相乘:一条经过 SiLU,另一条提供数值,再通过下投影回到模型维度。它多了一组矩阵,但可以缩小中间宽度,使参数量与普通前馈层相近;不能只按矩阵数量判断成本。

文章目录
  1. 从一条激活分支变成两条
  2. “门”不是零到一的开关
  3. 三组矩阵怎么与两组矩阵比较
  4. 用二维输入检查计算路径
  5. 它在哪一层工作
  6. 参考资料

从一条激活分支变成两条

普通无偏置前馈层可以写成 y=φ(xW₁)W₂。这里采用行向量记法:x 有 d 个分量,W₁ 为 d×m,W₂ 为 m×d。φ 对每个中间分量施加非线性,m 是中间层宽度。

把它改成无偏置 SwiGLU,得到 y=[SiLU(xWg)⊙(xWv)]Wo。其中 Wg 与 Wv 都为 d×h,Wo 为 h×d;⊙ 是逐元素乘法,不是矩阵乘法。每条分支必须产生相同的 h 维向量,才能对应相乘。

输入 x:d维
↓
门控分支:xWg → SiLU
数值分支:xWv
↓ × ↓
逐元素相乘:h维
↓
下投影 Wo → d维
原创数据流图:无偏置 SwiGLU 前馈层,两条分支通过逐元素乘法汇合,不是注意力分数。

“门”不是零到一的开关

SiLU(t)=t·σ(t),σ 为 sigmoid;此处使用 Swish 参数 β=1 的形式。虽然 sigmoid 在0与1之间,但再乘 t 后,SiLU 本身并不局限于这个范围。负输入可以给出负值,正输入也可以给出大于1的值。

所以“门控”是在描述输入相关的调制结构,并非保证每个门值都像开关或概率。这里也没有把候选归一化到总和1,不能把它当成Softmax。把输入经过学习到的两种投影,再相乘,是这一结构与单条激活路径的主要区别。

Noam Shazeer 的《GLU Variants Improve Transformer》于2020年2月首次提交 arXiv,比较了多种门控前馈变体。本文依据原始稿解释结构,不将预印本身份改写为已核验的期刊评审结论,也不把作者特定实验里的提升当成所有模型的保证。

三组矩阵怎么与两组矩阵比较

忽略偏置时,普通 FFN 的参数量为2dm,SwiGLU 为3dh。若希望这一层参数量相同,应设 h=2m/3。假如普通层 m=4d,对应 h≈8d/3;实际实现还可能为硬件友好的维度对齐作调整。

举一个整数教学例子:d=6、m=24,普通层有288个矩阵参数。门控层取 h=16,也有3×6×16=288个矩阵参数。这不是某个模型的配置,也不是整个 Transformer 的总参数。

单个 token 的三次密集投影乘加量约与3dh成正比,普通层约与2dm成正比。相同参数预算并不自动意味着相同墙钟速度:还有激活、逐元素乘法、中间张量与内核实现。本文没有测 GPU 延迟,不能给出加速比例。

用二维输入检查计算路径

import math

def silu(t):
    if t >= 0:
        sigmoid = 1.0 / (1.0 + math.exp(-t))
    else:
        e = math.exp(t)
        sigmoid = e / (1.0 + e)
    return t * sigmoid

def row_mm(x, matrix):
    if not matrix or len(x) != len(matrix):
        raise ValueError("Input dimension mismatch")
    width = len(matrix[0])
    if width == 0 or any(len(row) != width for row in matrix):
        raise ValueError("Expected rectangular nonempty matrix")
    return [sum(x[i] * matrix[i][j] for i in range(len(x)))
            for j in range(width)]

def swiglu(x, gate, value, down):
    g = row_mm(x, gate)
    v = row_mm(x, value)
    if len(g) != len(v):
        raise ValueError("Branch widths differ")
    hidden = [silu(a) * b for a, b in zip(g, v)]
    return row_mm(hidden, down)

identity = [[1.0, 0.0], [0.0, 1.0]]
value = [[1.0, 0.0], [0.0, -1.0]]
y = swiglu([1.0, 2.0], identity, value, identity)
assert math.isclose(y[0], silu(1.0))
assert math.isclose(y[1], -2.0 * silu(2.0))
assert swiglu([0., 0.], identity, value, identity) == [0., 0.]
assert silu(-1.0) < 0 and silu(2.0) > 1
assert math.isfinite(silu(-1000.0))
try:
    row_mm([1.], identity)
except ValueError:
    pass
else:
    raise AssertionError("Dimension mismatch accepted")
assert 2 * 6 * 24 == 3 * 6 * 16 == 288
print([round(a, 6) for a in y])

这是原创 Python 教学实现,没有依赖训练框架。x=[1,2],门控矩阵和下投影都为单位矩阵,数值分支把第二个分量取负。输出约 [0.731059,-3.523188]。负输出与“门是概率开关”的直觉不同,却符合公式。

代码已执行,检查手算对应关系、零输入、SiLU 的值域边界、较大负输入的 sigmoid 计算,以及维度不匹配。参数量示例也通过断言。此处只处理一个 token,不含批次、反向传播、偏置或残差连接。

它在哪一层工作

FFN 通常对序列中每个位置分别应用同一组权重。SwiGLU 的两条分支调制的是这个位置的特征;它本身不负责查询另一个 token。跨位置的信息交互与注意力应分开理解。

读模型配置或源码时,先找门控、上投影与下投影的实际形状,再核对中间宽度、偏置、激活和残差所在位置。不同命名约定并不影响上述行向量推导,但框架存储的权重可能转置。

更换一层的激活结构涉及权重形状与训练兼容性,不是把已有检查点里的名字替换一下就能获得论文效果。这里提供的是结构和预算的可验证解释;真实质量、吞吐与显存取舍仍需在明确的模型、精度和硬件条件下测量。

Read the English version

参考资料

Noam Shazeer — GLU Variants Improve Transformer, arXiv v1, February 12, 2020

支持

如果这篇文章对你有帮助,欢迎支持本站。

微信支持二维码,点击查看大图
微信
支付宝支持二维码,点击查看大图
支付宝
Buy Me a Coffee,点击支持本站
Buy Me a Coffee

二维码可点击放大。更多支持方式见支持页面。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*