1. SwiGLU激活函数原理:从GLU到SwiGLU的演进
说起SwiGLU,我得先坦白一件事。几年前我第一次在论文里看到它时,心里想的是:「又来个新激活函数?ReLU不够用吗?」结果后来在部署大模型时,发现几乎所有主流模型都在用SwiGLU。嗯,真香。
这一节,咱们就把它彻底搞明白。从GLU开始,一步步推到SwiGLU,再跟ReLU、GELU做个对比。你想想看,搞懂这些,后面做算子融合心里才有底。
1.1 从GLU说起:门控机制的直觉
GLU的全称是Gated Linear Unit,门控线性单元。它最早在NLP领域被提出,核心思想很简单:让网络自己决定「哪些信息该通过,哪些该过滤掉」。
数学上,GLU的定义是这样的:
GLU(x) = (x * W1 + b1) ⊗ σ(x * W2 + b2)
其中⊗是逐元素乘法,σ是sigmoid函数。你看,它把输入分成两路:一路做线性变换,另一路经过sigmoid变成0到1之间的门控信号。两路相乘,门控信号决定了信息保留多少。
核心直觉:GLU相当于给神经网络装了个「智能阀门」。sigmoid输出的值接近1时,信息几乎无损通过;接近0时,信息被阻断。这种动态调节能力,比固定激活函数灵活得多。
我在项目中遇到过一个问题:用GLU做语言模型时,训练收敛确实快,但推理时sigmoid的计算开销不小。尤其端侧芯片上,sigmoid的指数运算是个瓶颈。这就引出了后面的改进。
1.2 SwiGLU的诞生:用Swish替换Sigmoid
2020年,Google在PaLM论文里提出了SwiGLU。说白了,就是把GLU里的sigmoid换成了Swish激活函数。
Swish长这样:
Swish(x) = x * σ(βx)
其中β是可学习的参数,通常设为1。当β=1时,Swish(x) = x * sigmoid(x)。
那么SwiGLU的公式就是:
SwiGLU(x) = (x * W1 + b1) ⊗ Swish(x * W2 + b2)
为什么这么改?我个人习惯从两个角度理解:
- 梯度特性更好:Swish在负半轴不是完全饱和的,梯度不会像sigmoid那样趋近于0。这意味着深层网络的梯度消失问题得到缓解。
- 表达能力更强:Swish的非线性比sigmoid更丰富。它保留了sigmoid的「软门控」特性,同时增加了负值区域的微小响应——这有点像给阀门加了「微调旋钮」。
我的经验:在实际部署中,SwiGLU相比GLU通常能提升0.5-1个百分点的准确率。代价是计算量略有增加,但端侧推理引擎可以通过算子融合来弥补。
1.3 数学公式的直观推导
咱们把SwiGLU拆开来看。假设输入是x,经过两个线性层得到a和b:
a = x * W1 + b1
b = x * W2 + b2
然后:
output = a ⊗ Swish(b) = a ⊗ (b * sigmoid(b))
写成完整形式:
SwiGLU(x) = (xW1 + b1) ⊗ [(xW2 + b2) * σ(xW2 + b2)]
你发现没有?这里其实隐含了一个「门控+非线性」的组合。a是线性变换后的主信号,b经过Swish后变成门控信号。两者相乘,既保留了GLU的门控机制,又引入了Swish的平滑非线性。
关键点:SwiGLU的参数量是普通激活函数的两倍(因为需要两个线性层),但换来的是更强的表达能力。在端侧部署时,这个「参数量翻倍」的问题需要通过算子融合来缓解。
1.4 与ReLU的对比:非线性与门控
ReLU太经典了,但它的局限性也很明显:
| 特性 | ReLU | SwiGLU |
|---|---|---|
| 非线性形式 | 分段线性 | 门控+平滑非线性 |
| 负半轴响应 | 完全抑制(0) | 微小负响应(通过Swish) |
| 参数量 | 无额外参数 | 2个线性层参数 |
| 梯度特性 | 负半轴梯度为0 | 负半轴有非零梯度 |
| 端侧部署 | 极简单,零开销 | 需算子融合优化 |
我曾经在部署一个语音识别模型时,把SwiGLU换成ReLU,结果WER(词错误率)直接涨了3%。这说明在某些任务上,SwiGLU的门控机制确实不可或缺。
1.5 与GELU的对比:相似但不同
GELU(Gaussian Error Linear Unit)是另一种流行的激活函数,公式是:
GELU(x) = x * Φ(x)
其中Φ(x)是标准正态分布的累积分布函数。近似计算时常用:
GELU(x) ≈ 0.5x * (1 + tanh(√(2/π) * (x + 0.044715x³)))
GELU和SwiGLU看起来有点像——都是「输入乘以一个门控值」。但区别在于:
- GELU的门控是输入本身的函数:Φ(x)只依赖于x本身。
- SwiGLU的门控来自另一个线性变换:Swish(b)中的b是x经过独立线性层得到的。
说白了,SwiGLU多了一个「可学习的门控分支」,而GELU的门控是固定的(只依赖输入值)。这意味着SwiGLU能学到更复杂的门控策略。
避坑指南:我曾经在端侧芯片上直接使用GELU的精确公式,结果发现tanh和幂运算在定点推理时精度损失严重。后来改用近似公式,精度损失控制在0.1%以内。SwiGLU也有类似问题——Swish中的sigmoid在低比特量化时容易出问题,需要做校准。
1.6 知识体系结构图
下面这张图帮你理清SwiGLU在整个激活函数家族中的位置:
从图中你能看到,SwiGLU站在两个「巨人」的肩膀上:GLU提供了门控框架,Swish提供了更好的非线性。最终在端侧部署时,我们需要通过算子融合来弥补它参数量翻倍带来的开销。
1.7 小结:为什么端侧需要关注SwiGLU
总结一下这节的核心:
- SwiGLU = GLU + Swish,用Swish替换sigmoid,梯度更好、表达更强
- 相比ReLU:SwiGLU有门控机制,负半轴有响应,但参数量翻倍
- 相比GELU:SwiGLU的门控来自独立分支,更灵活,但计算更重
- 端侧挑战:两个线性层 + sigmoid/Swish,计算量和内存访问都是瓶颈
我个人觉得,SwiGLU是「用计算换精度」的典型代表。在服务器端这不是问题,但在端侧,我们必须想办法把多出来的计算「藏」起来。下一节我会详细讲怎么通过算子融合做到这一点。
一句话记住:SwiGLU就是给GLU换了个更聪明的门控函数,让模型学得更好,但让端侧工程师更忙了。