更新于 2026年7月12日

今天要和大家介绍的是大模型当中常用的一个激活函数,不过真要是细究一下,这个激活函数都不能看做是一个激活函数了,因为它居然还引入了门控(类似于 LSTM 中的遗忘门,详见 「LSTM原理:长短期记忆网络与门控机制详解」 )的概念。

在研究 LLaMA 和 DeepSeek 的相关论文中发现,大家都不约而同地使用到了一个名叫 SwiGLU 的激活函数。实话说,这个激活函数我之前既没有听过也没有用过,看来还是论文看少了。既然大家都这么喜欢用,那自然是要给大家去扒一扒它到底是个什么东西。

结果扒完以后发现,SwiGLU 压根就不是一个激活函数,要命!

那到底什么是 SwiGLU 呢?要说清楚 SwiGLU 那还得从两篇论文说起,一篇介绍 Swish 激活函数的,一篇则是研究 Transformer 中到不同激活函数对于模型结果的影响。

下面带着大家来逐一进行了解!

关键字:Swish、SwiGLU、ReLU、激活函数、Transformer、DeepSeek、LLaMA、大模型

1. Swish 动机#

我们都知道,在深度神经网络中激活函数的选择对于模型结果的影响可谓是至关重要的,同样的网络结构但是选择不同的激活函数完全可以带来大相径庭的结果。在此之前,大家熟知的激活函数 ReLU 几乎已经成为了各个模型的标配,因为它具有稳定的性能,不管什么场景用它几乎都能取得一个较好的效果。

尽管一直以来也有很多研究者尝试手工设计新的激活函数来取代 ReLU,但是这些方案在不同的任务或模型上表现不稳定,有时候有效,有时候无效,所以自然也就无法取代现有的 ReLU 激活函数。

因此,作者就发出猜想,既然手动靠逻辑分析去设计无法相处一个好的激活函数,那搞一个模型让它自己去搜索寻找怎么样?

这就有点类似于在机器学习中侧重于人工设计特征,其前提就是人工设计特征的时候一定会在意它所代表的含义,换句话说没有含义的特征是不会被设计出来的,因为人也压根儿想不到那儿去,但凡能想到一定是有逻辑也就是有意义的。因此,这也就成为了催生深度学习出现的一个原因,让网络自己去提取特征,不管在人类眼里是否具有意义,只要能提高模型效果那就采用。

基于这样的动机,在2017年的时候 Google Brain 发表了一篇论文 Searching for activation functions 来搜寻合适的激活激活函数。顺便说一句,论文的作者之一 Quoc V. Le 还是谷歌大脑的联合创始人之一,他共同参与发明了自然语言处理领域中的 Doc2vec 和 Seq2Seq 模型。

最后,作者结合穷举搜索和基于强化学习的搜索方法,发现了多种新颖的激活函数。最后,还真就找出了一个能够全面替代 ReLU 的激活函数,作者将其称之为 Swish.

Using a combination of exhaustive and reinforcement learning-based search, we discover multiple novel activation functions. Our experiments show that the best discovered activation function, $f(x) = x · \text{sigmoid}(\beta x)$, which we name Swish, tends to work better than ReLU on deeper models across a number of challenging datasets.

经过试验对比,在 ImageNet 数据集上将如果 ReLU 替换为 Swish 激活函数,那么在 Mobile NASNet-A 和 Inception-ResNet-v2 模型上分别提升了 0.9% 和 0.6% 的 top-1 分类准确率。虽然看起来没有太大的提升,但是如果考虑到从 Inception V3 到 Inception-ResNet-v2 通过一年时间的架构优化仅提升了 1.3% 的准确率这个角度来看,那么此时仅由激活函数带来的准确率提升就是非常显著的。

具体的搜索方法在这里就不再介绍了,有兴趣的朋友可以直接去阅读论文原文。下面我们直接来看看 Swish 的一些特性。

2. Swish 原理#

对于 Swish 的原理可以用一句话来总结,那就是:披着马甲的 ReLU !

为什么这么说呢?因为在 Swish 眼里,ReLU 就是特殊情况下的 Swish 激活函数,也就是说我们可以通过调整 Swish 中的超参数使其接近于 ReLU 但同时有还能体现出自己的独到之处。

2.1 Swish 表达式#

对于 Swish 激活函数来说,它的函数表达式如下所示

$$ f(x) = x \cdot \text{sigmoid}(\beta x)\tag{1} $$

其中 $\beta$可以是一个常数,也可以定义成一个可训练的权重参数。

当然,我们还可以通过下面这张图来形象化表示式(1)中的过程,让你一眼记住这个激活函数。

图 1. Swish 示意图

进一步,从 Swish 的表达式可以看出,当 $\beta >0$ 时,其值越大则 $f(x)$ 将无限趋于 ReLU,如图2所示。

图 2. Swish 和 ReLU 函数图像

从图2可以看出当 $\beta=3$ 时,其函数图像便几乎接近于 ReLU 了,并且当 $\beta$ 越接近于 0 时,$f(x)$ 便退化成了直线 $\frac{x}{2}$。

2.2 Swish 为何有效#

那为什么 Swish 会略好于 ReLU 呢? 作者从两个方面给出了解释。

根据式(1)可知,Swish 激活函数对应的导数为

$$ \begin{aligned} f'(x) &= \text{sigmoid}(\beta x)+x\cdot\text{sigmoid}'(\beta x)\\[3ex] &=\text{sigmoid}(\beta x)+x\cdot\left[\text{sigmoid}(\beta x)(1-\text{sigmoid}(\beta x))\cdot\beta\right] \end{aligned} \tag{2} $$

2.2.1 从图示分析#

因此,Swish 激活函数对应导数的函数图像如图3所示。

图 3. Swish 导数图像

从图3可以看出,$\beta$ 的大小将会控制 Swish 一阶导数接近于0的速速,$\beta$ 越大导数越陡峭接近于0越快,也就越近似于 ReLU 的导数。当 $\beta = 1$ 时,Swish 的导数在输入小于约 1.25 时,其导数绝对值小于 1。这意味着 Swish 的梯度在小于约 1.25 的输入区间内都比较“温和”,不像 ReLU 那样恒为 1(当$ x > 0$时),这种特性有助于抑制梯度爆炸,但也可能减缓信息传播。同时,在 $x<0$ 的部分依旧存在部分梯度,这也是 ReLU 所不具备的。

因此 Swish 的优点是,首先它能够保证梯度不会消失;其次它能够通过参数 $\beta$来控制在不同输入范围下梯度的尺度,因此它相比于 ReLU 更加灵活。

2.2.2 从实验验证#

进一步,作者还通过实验来对上述猜想进行了验证。作者认为,Swish 与 ReLU 最显著的区别是,Swish 在 $x < 0$ 时存在一个非单调区域,先下降后略有回升。

图 3. 激活前输入值分布情况

如图 6 所示, 为 ResNET-32 模型训练结束后,激活前输入值的分布情况。可以明显看出,存在大量激活值落在区域 $(−5 \leq x \leq 0)$ 内,说明这个区域的性质对网络性能有显著影响,而 Swish 激活函数在这个区域恰好存在梯度,并且还可以通过 $\beta$ 来控制范围。

同时,虽然在实际情况中将 $\beta$ 固定为 1 已经有效,但作者发现在某些模型中,把 $\beta$ 设置为可训练权重还能进一步提升性能。

图 4. 可训练参数 $\beta$ 分布情况

如图4所示,展示了 Mobile NASNet-A 模型中训练得到的 $\beta$ 值的分布情况,可以发现 $\beta$ 值不是集中在某一个固定值,而是在一个范围内分布($0 $ 到 $1.5$ 附近),这显示出网络为了适应不同特征图,自动选择了不同的 $\beta$ 值。

图 5. 不同激活函数表现结果

如图5所示便是3个模型(ResNet-164 (RN) 、Wide ResNet 28-10 (WRN)和 DenseNet 100-12 (DN))分别使用不同激活函数时的模型的在两个数据集上的表现情况。从结果来看,Swish 稍好于其它激活函数。

2.3 从零实现 Swish 激活函数#

根据式(1)可知, Swish 的实现过程比较简单,示例代码如下:

1 class Swish(nn.Module):
2     def __init__(self, gamma=1.):
3         super(Swish, self).__init__()
4         self.gamma = gamma
5 
6     def forward(self, x):
7         return x * torch.sigmoid(self.gamma * x)

对于 Swish 来说,它的用法和一个普通的网络层类似:

1 if __name__ == '__main__':
2     x = torch.randn(2, 128)
3     net = nn.Sequential(nn.Flatten(),nn.Linear(128, 256),
4               Swish(), nn.Dropout(0.5),nn.Linear(256, 10))
5     result = net(x)
6     print(result)

当然,我们也可以直接使用 torch 中的 nn.SiLU() 层进行代替,区别在于 nn.SiLU() 不能自定义 $\gamma$ 取值, 即只能是 $\gamma = 1$ 。

以上内容就是关于 Swish 的介绍,下面我们再来看真正的主角 SwiGLU !

3. SwiGLU 动机#

我们都知道 Transformer 的多头注意力机制中有两个全连接层(position-wise feed-forward networks, FFN),并且我们还在文章「逐元素非线性:没全连接层行不行?90%的人忽略了 Transformer 中的这一点」中详细介绍了两个全连接层的作用,四个字总结——不可或缺

图 6. Transformer 网络结构图(红色方框部分便是 FFN)

既然这两个全连接层对于多头注意力来说如此重要,于是就有好事者开始研究,到底应该选择什么样的激活函数能够使得 Transformer 达到一个更好的效果。最后,Attention is All you Need 的作者之一,Noam Shazeer 在2020年就一个人专门写了一篇文章 GLU Variants Improve Transformer [2] 来研究 Transformer 中的这个问题。

同 Swish 的研究过程类似,作者 Noam 也是筛选和构造了一系列的激活函数,并将其替换掉原有的 ReLU 激活函数然后进行效果上的验证。

最后发现真的有效,但就是不知道为什么!

4. SwiGLU 原理#

根据先前对于 Transformer 模型(本专栏第10.3节内容)的介绍,Transformer 中的 FFN 的前向传播过程如下

$$ \text{FFN}(x,W_1,W_2,b_1,b_2)=\text{ReLU}(xW_1+b_1)W_2+b_2=\text{max}(0,xW_1+b_1)W_2+b_2\tag{3} $$

不过作者延续了谷歌 T5 模型中做法,去掉了偏置项,即

$$ \text{FFN}(x,W_1,W_2)_{\text{ReLU}}=\text{max}(0,xW_1)W_2\tag{4} $$

4.1 SwiGLU 表达式#

最后,作者结合2016年 Dauphin 等人提出门控线性单元 Gated Linear Units (GLU) 筛选出了如下候选 Transformer FFN 进行实现对比

$$ \begin{aligned} \text{FFN}_{\text{GLU}}(x,W,V,W_2)&=(\sigma(xW)\odot xV)W_2\\[3ex] \text{FFN}_{\text{Bilinear}}(x,W,V,W_2)&=(xW\odot xV)W_2\\[3ex] \text{FFN}_{\text{ReGLU}}(x,W,V,W_2)&=(\text{ReLU}(xW)\odot xV)W_2\\[3ex] \text{FFN}_{\text{GEGLU}}(x,W,V,W_2)&=(\text{GELU}(xW)\odot xV)W_2\\[3ex] \text{FFN}_{\text{SwiGLU}}(x,W,V,W_2)&=(\text{Swish}_1(xW)\odot xV)W_2 \end{aligned}\tag{5} $$

这里需要注意的是,对于式(5)中的所有表达式来说,新的 FFN 中有三个权重矩阵,而式(4)中的 FFN 只有两个权重矩阵。同时,为了使得新的 FFN 保持参数量和计算开销上的量级不变,作者将 FFN 中隐藏层的维度减少为了原始的 $\frac{2}{3}$,不过这个在实际中也是可以调整的。

进一步,根据上面介绍的内容可知

$$ \text{FFN}_{\text{SwiGLU}}(x,W,V,W_2)= ((xW\odot\sigma(1\cdot xW))\odot xV)W_2\tag{6} $$

4.2 SwiGLU 从零实现#

根据式(6)的计算过程,其实现过程如下所示:

 1 class FFN(nn.Module):
 2     def __init__(self, dim, hidden_dim):
 3         super().__init__()
 4         self.linear1 = nn.Linear(dim, hidden_dim)
 5         self.linear2 = nn.Linear(dim, hidden_dim)
 6         self.linear3 = nn.Linear(hidden_dim, dim)
 7 
 8     def forward(self, x):
 9         x_gate = self.linear1(x)  # [batch_size, hidden_dim]
10         x_gate = x_gate * torch.sigmoid(x_gate)  # [batch_size, hidden_dim]
11         y = self.linear2(x)  # [batch_size, hidden_dim]
12         y = x_gate * y  # [batch_size, hidden_dim]
13         output1 = self.linear3(y)  # [batch_size, dim]
14         output2 = self.linear3(F.silu(self.linear1(x)) * self.linear2(x))
15         return output1, output2
16 
17 
18 if __name__ == '__main__':
19     x = torch.randn(2, 6)
20     ffn = FFN(6, int(2 * 6 / 3))
21     result = ffn(x)

在上述代码中,第4~5行是定义式(6)中对应的3个线性变换;第9~13行则是我们自己动手实现的整个计算过程,相关维度变换都进行了表示大家可以仔细阅读。同时,我们还可以通过第14行这样一行代码来完成整个计算过程。

上述代码运行结束后,将会得到如下结果

tensor([[-0.0118, -0.2936,  0.3152, -0.1862, -0.0641, -0.3700],
        [ 0.0397, -0.2450,  0.4108, -0.3064, -0.1887, -0.5593]],
       grad_fn=<AddmmBackward0>)
tensor([[-0.0118, -0.2936,  0.3152, -0.1862, -0.0641, -0.3700],
        [ 0.0397, -0.2450,  0.4108, -0.3064, -0.1887, -0.5593]],
       grad_fn=<AddmmBackward0>)

以上就是整个 SwiGLU 的由来和实现过程。

最后再给大家分享一下这篇论文总结里比较有趣的两句话。

These architectures are simple to implement, and have no apparent computational drawbacks. We offer no explanation as to why these architectures seem to work; we attribute their success, as all else, to divine benevolence.

这些架构实现起来都很简单,并且在计算开销上也没有明显的缺点。不过我们无法解释这些结构为何如此有效;我们将它们的成功,和其他一切一样,归功于“神的恩赐”。

引用#

[1] Ramachandran P, Zoph B, Le Q V. Searching for activation functions[J]. arXiv preprint arXiv:1710.05941, 2017.

[2] Shazeer N. Glu variants improve transformer[J]. arXiv preprint arXiv:2002.05202, 2020.

[3] Yann N. Dauphin, Angela tional networks. CoRR, Fan, Michael Auli, and David Grangier. Language modeling with gated convolu- abs/1612.08083, 2016. URL http://arxiv.org/abs/1612.08083.

阅读 --

3.12 激活函数

在本节内容中,我们首先回顾了在深度学习中为什么我们需要进行非线性变换;然后介分别介绍了4种常见激活函数Sigmoid、Tanh、ReLU和LeakyReLU的原理和计算过程。最后详细介绍了各个激活函数的实现过程和使用示例。

3.1 线性回归

在本节内容中,我们首先以房价预测为例引入了单变量线性回归以及如何转换模型的求解思路;然后通过梯形面积预测的实例引入了什么是多项式回归,并进一步引出了抽象特征提取的概念;

7.3 LSTM网络

在前面两节内容中,我们详细介绍了RNN模型的原理以及在PyTorch框架中的使用方法。虽然理论上RNN模型在处理序列数据方面具有着很好的效果,但在处理长序列数据时RNN模型可能会出现梯度消失或爆炸的情况,进而导致模型无法学习到长期依赖的关系 …