Qwen2.5-VL 视觉编码器的SwiGLU
Qwen2.5-VL 视觉编码器的SwiGLU
flyfish
视觉编码器在多模态大模型中扮演着至关重要的角色。我们从头开始训练了一个原生动态分辨率的 ViT,包括 CLIP、视觉-语言模型对齐和端到端训练等阶段。为了解决多模态大模型在训练和测试阶段 ViT 负载不均衡的问题,我们引入了窗口注意力机制,有效减少了 ViT 端的计算负担。在我们的 ViT 设置中,只有四层是全注意力层,其余层使用窗口注意力。最大窗口大小为 8x8,小于 8x8 的区域不需要填充,而是保持原始尺度,确保模型保持原生分辨率。此外,为了简化整体网络结构,我们使 ViT 架构与 LLMs 更加一致,采用了 RMSNorm 和 SwiGLU 结构。
这里就是介绍 SwiGLU
一、激活函数的本质定义
- 数学层面:激活函数是作用于神经元输入的非线性函数,将神经元的输入信号转换为输出信号。
若神经元输入为 z z z,输出为 a a a,则 a = f ( z ) a = f(z) a=f(z),其中 f f f 即为激活函数。 - 生物启发:模拟生物神经元的“激活”机制——只有当输入信号强度超过阈值时,神经元才会传递信息。
二、为什么神经网络需要激活函数?
-
线性模型的局限性
若没有激活函数,多层神经网络等价于单层线性模型,只能拟合线性关系。例如:- 单层神经网络: y = W x + b y = Wx + b y=Wx+b(线性)。
- 两层神经网络(无激活函数): y = W 2 ( W 1 x + b 1 ) + b 2 = ( W 2 W 1 ) x + ( W 2 b 1 + b 2 ) y = W_2(W_1x + b_1) + b_2 = (W_2W_1)x + (W_2b_1 + b_2) y=W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2),本质仍是线性变换。
结论:无激活函数的神经网络无法解决非线性问题(如异或运算、图像识别中的特征提取)。
-
非线性的必要性
激活函数通过引入非线性,使神经网络能够拟合任意复杂的函数(根据通用近似定理,含非线性激活函数的多层神经网络可近似任何连续函数)。
案例:异或问题(XOR)无法用线性模型解决,但含ReLU激活函数的两层神经网络可轻松拟合。
Sigmoid
Sigmoid激活函数的公式为 σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+e−z1,其核心特点是将输入映射到(0,1)区间,输出可视为概率或激活强度。该函数早期广泛应用于神经网络隐藏层和二分类输出层,其生物神经元模拟特性使其具备直观的解释性,但存在明显缺陷:当 ∣ z ∣ |z| ∣z∣较大时梯度趋近于0,导致深层网络训练时出现梯度消失问题;输出恒为正值,会使梯度更新方向单一,可能引发参数震荡;此外,指数运算的计算开销较高,效率低于现代激活函数。如今Sigmoid主要用于二分类任务的输出层(如逻辑回归),在隐藏层已较少使用。
Tanh
Tanh(双曲正切)函数公式为 tanh ( z ) = e z − e − z e z + e − z \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} tanh(z)=ez+e−zez−e−z,输出范围为(-1,1),解决了Sigmoid的非零中心化问题,使梯度更新方向更灵活。相比Sigmoid,Tanh的非线性表达能力更强,但仍未摆脱梯度消失的困扰,尤其在输入绝对值较大时梯度趋近于0,导致深层网络训练困难。其计算复杂度与Sigmoid相当,效率较低,目前主要应用于早期序列模型(如RNN)的隐藏层,或需要输出对称区间的特定任务,在现代主流架构中已逐渐被ReLU等函数取代。
ReLU
ReLU(修正线性单元)的公式为 ReLU ( z ) = max ( 0 , z ) \text{ReLU}(z) = \max(0, z) ReLU(z)=max(0,z),是现代神经网络中最常用的激活函数之一。其核心优势在于计算效率极高,仅需判断输入正负,无需复杂运算,非常适合GPU/TPU等硬件加速;在正数区域梯度恒为1,有效缓解了梯度消失问题,使深层网络训练更稳定;约50%的输入会被映射为0,形成稀疏激活,增强了特征选择性。但ReLU存在“死神经元”问题——输入为负时梯度为0,神经元可能永久不更新(尤其在大学习率下),且在 z = 0 z=0 z=0处不可微,可能影响优化器收敛。目前ReLU广泛应用于CNN、Transformer等架构的隐藏层,是默认首选激活函数。
Leaky ReLU
Leaky ReLU的公式为 Leaky ReLU ( z ) = { z , z ≥ 0 α z , z < 0 \text{Leaky ReLU}(z) = \begin{cases} z, & z \geq 0 \\ \alpha z, & z < 0 \end{cases} Leaky ReLU(z)={z,αz,z≥0z<0( α \alpha α为小常数,如0.01),其设计初衷是解决ReLU的“死神经元”问题。通过为负数区域赋予小斜率 α \alpha α,Leaky ReLU确保了全域梯度存在,避免神经元永久失活,同时计算开销可忽略(仅多一次乘法)。然而, α \alpha α需手动设定,部分场景下调参成本较高,因此其应用场景主要集中在ReLU表现不佳的任务中,如图像生成模型(如GAN)或某些NLP任务,相比ReLU更适合需要保留负数区域信息的场景。
PReLU
PReLU(参数化ReLU)与Leaky ReLU结构类似,但 α \alpha α为可学习参数而非固定常数,公式形式相同但 α \alpha α通过反向传播自动优化。这一改进使PReLU能够自适应调整负数区域斜率,更贴合数据分布,避免手动调参的局限性。尽管每个通道或神经元需要额外学习一个 α \alpha α参数,增加了少量模型复杂度,但在大型图像模型(如ResNet变种)中,PReLU的自适应特性显著提升了模型性能,尤其在数据分布复杂的场景中优势明显,是需要自适应激活函数时的优选方案。
ELU
ELU(指数线性单元)的公式为 ELU ( z ) = { z , z ≥ 0 α ( e z − 1 ) , z < 0 \text{ELU}(z) = \begin{cases} z, & z \geq 0 \\ \alpha (e^z - 1), & z < 0 \end{cases} ELU(z)={z,α(ez−1),z≥0z<0( α \alpha α通常取1),其特点是负数区域呈指数型平滑过渡,输出均值更接近0。ELU通过平滑的负数区域设计缓解了梯度消失问题(该区域梯度非零且连续),同时输出均值趋近于0的特性类似Batch Normalization,能加速训练收敛。但ELU包含指数运算,计算效率略低于ReLU,主要应用于对收敛速度要求高的任务,如图像分类、语音识别等,在需要兼顾梯度稳定性和收敛效率的场景中表现优异。
GLU
GLU(门控线性单元)于2016年由Meta团队提出,公式为 GLU ( z ) = ( W 1 z + b 1 ) ⊗ σ ( W 2 z + b 2 ) \text{GLU}(z) = (W_1z + b_1) \otimes \sigma(W_2z + b_2) GLU(z)=(W1z+b1)⊗σ(W2z+b2),其中 ⊗ \otimes ⊗为逐元素相乘, σ \sigma σ为Sigmoid门控函数。该设计首次将门控机制引入神经网络,通过将输入分为两部分——一部分经线性变换,另一部分生成门控信号,实现信息流的动态筛选,有效解决长序列依赖问题。GLU适合处理序列建模任务(如NLP中的句子生成),但由于使用Sigmoid作为门控,在极端值区域存在梯度消失风险,且计算开销高于传统激活函数,后续衍生出的变体(如SwiGLU)对其进行了优化。
Swish
Swish由Google Brain于2017年提出,公式为 Swish ( z ) = z ⋅ σ ( z ) \text{Swish}(z) = z \cdot \sigma(z) Swish(z)=z⋅σ(z),是自门控激活函数的里程碑。该函数结合线性变换与Sigmoid门控,通过 σ ( z ) \sigma(z) σ(z)动态调节输入权重,形成平滑非线性激活,全域可导且梯度稳定性显著优于ReLU。Swish的自门控机制模拟了神经元的自适应激活特性,增强了模型对复杂模式的拟合能力,尤其在大模型中表现突出。尽管包含Sigmoid运算导致计算开销略高,但通过硬件优化(如Tensor Cores)可有效缓解,目前应用于PaLM、Gato等大模型及图像生成任务中。
SwiGLU
SwiGLU(Swish-Gated Linear Unit)是GLU的改进版,2020年由Google研究员Noam Shazeer提出,公式为 SwiGLU ( z ) = ( W 1 z + b 1 ) ⊗ Swish ( W 2 z + b 2 ) \text{SwiGLU}(z) = (W_1z + b_1) \otimes \text{Swish}(W_2z + b_2) SwiGLU(z)=(W1z+b1)⊗Swish(W2z+b2)。其核心创新是用Swish替换GLU中的Sigmoid门控,利用Swish的平滑特性解决了原GLU的梯度消失问题,同时通过分块矩阵运算保持计算效率接近ReLU。SwiGLU在LLaMA、PaLM-2等大语言模型中成为标准配置,允许前馈网络维度从4倍降至3倍,参数量减少25%但性能无损,是当前大模型提升参数效率的关键技术之一,广泛应用于长文本生成、多模态融合等场景。
GELU
GELU(高斯误差线性单元)的公式为 GELU ( z ) = z ⋅ Φ ( z ) \text{GELU}(z) = z \cdot \Phi(z) GELU(z)=z⋅Φ(z),其中 Φ ( z ) \Phi(z) Φ(z)为标准正态分布的累积分布函数,其设计基于概率理论,模拟神经元的随机失活特性(类似Dropout的平滑版本)。GELU通过平滑非线性提升模型拟合能力,梯度稳定性优于ReLU,理论上更接近人类神经元的激活分布。由于直接计算 Φ ( z ) \Phi(z) Φ(z)复杂度高,实际常用近似公式 0.5 z ( 1 + tanh ( 2 / π ( z + 0.044715 z 3 ) ) ) 0.5z(1 + \tanh(\sqrt{2/\pi}(z + 0.044715z^3))) 0.5z(1+tanh(2/π(z+0.044715z3)))实现,目前广泛应用于BERT、GPT系列等Transformer模型的隐藏层,是NLP任务中的主流激活函数选择。
Softmax
Softmax是多分类任务专用的激活函数,公式为 Softmax ( z i ) = e z i ∑ j = 1 k e z j \text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^k e^{z_j}} Softmax(zi)=∑j=1kezjezi,作用是将k维输入向量归一化为概率分布,输出和为1。该函数适合作为多分类任务的最后一层,将模型输出转换为类别概率,例如图像分类、文本情感分析等场景。Softmax的计算涉及指数运算和归一化,计算量较大,但作为输出层激活函数不可替代,其结果可直接用于概率预测和交叉熵损失计算,是多分类模型的标准组件。
激活函数名称 | 公式 |
---|---|
Sigmoid | σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+e−z1 |
Tanh | tanh ( z ) = e z − e − z e z + e − z \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} tanh(z)=ez+e−zez−e−z |
ReLU | ReLU ( z ) = max ( 0 , z ) \text{ReLU}(z) = \max(0, z) ReLU(z)=max(0,z) |
Leaky ReLU | Leaky ReLU ( z ) = { z , z ≥ 0 α z , z < 0 \text{Leaky ReLU}(z) = \begin{cases} z, & z \geq 0 \\ \alpha z, & z < 0 \end{cases} Leaky ReLU(z)={z,αz,z≥0z<0 |
PReLU | PReLU ( z ) = { z , z ≥ 0 α z , z < 0 \text{PReLU}(z) = \begin{cases} z, & z \geq 0 \\ \alpha z, & z < 0 \end{cases} PReLU(z)={z,αz,z≥0z<0( α \alpha α为可学习参数) |
ELU | ELU ( z ) = { z , z ≥ 0 α ( e z − 1 ) , z < 0 \text{ELU}(z) = \begin{cases} z, & z \geq 0 \\ \alpha (e^z - 1), & z < 0 \end{cases} ELU(z)={z,α(ez−1),z≥0z<0 |
GLU | GLU ( z ) = ( W 1 z + b 1 ) ⊗ σ ( W 2 z + b 2 ) \text{GLU}(z) = (W_1z + b_1) \otimes \sigma(W_2z + b_2) GLU(z)=(W1z+b1)⊗σ(W2z+b2) |
Swish | Swish ( z ) = z ⋅ σ ( z ) \text{Swish}(z) = z \cdot \sigma(z) Swish(z)=z⋅σ(z) |
SwiGLU | SwiGLU ( z ) = ( W 1 z + b 1 ) ⊗ Swish ( W 2 z + b 2 ) \text{SwiGLU}(z) = (W_1z + b_1) \otimes \text{Swish}(W_2z + b_2) SwiGLU(z)=(W1z+b1)⊗Swish(W2z+b2) |
GELU | GELU ( z ) = z ⋅ Φ ( z ) \text{GELU}(z) = z \cdot \Phi(z) GELU(z)=z⋅Φ(z)( Φ ( z ) \Phi(z) Φ(z)为标准正态分布累积函数) |
Softmax | Softmax ( z i ) = e z i ∑ j = 1 k e z j \text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^k e^{z_j}} Softmax(zi)=∑j=1kezjezi |
SwiGLU 详细说明
从2016年GLU首次将门控机制引入CNN,到2017年Swish以自门控特性突破梯度瓶颈,再到2020年SwiGLU在Transformer中验证优势,直至2022年后在PaLM、LLaMA等大模型中实现工程化落地,SwiGLU的发展脉络贯穿了深度学习从理论探索到工程实践的渐进式创新。
理论奠基:门控机制与自门控激活的诞生
-
门控线性单元(GLU)的原始设计(2016年)
2016年,Meta(原Facebook)团队在《Language Modeling with Gated Convolutional Networks》中提出GLU,旨在解决传统RNN在序列建模中的并行计算瓶颈。当时研究者尝试用CNN替代RNN处理长序列,而GLU的核心公式为 GLU ( z ) = ( W 1 z + b 1 ) ⊗ σ ( W 2 z + b 2 ) \text{GLU}(z) = (W_1z + b_1) \otimes \sigma(W_2z + b_2) GLU(z)=(W1z+b1)⊗σ(W2z+b2),其中 σ \sigma σ为Sigmoid门控函数,通过逐元素相乘实现信息流的动态筛选。这一设计首次将门控机制引入CNN,使模型能够通过门控信号自适应过滤无效信息,有效解决长序列依赖问题,为后续Transformer架构中前馈网络(FFN)的激活函数优化提供了关键思路,奠定了门控激活函数在序列处理任务中的基础地位。 -
Swish的革命性突破(2017年)
2017年,Google Brain团队在《Swish: A Self-Gated Activation Function》中提出Swish,公式为 Swish ( z ) = z ⋅ σ ( z ) \text{Swish}(z) = z \cdot \sigma(z) Swish(z)=z⋅σ(z),通过Sigmoid的自门控特性,在保留ReLU计算高效性的同时解决了梯度消失问题。其优势在于具有平滑非线性特性,全域可导,梯度稳定性显著优于ReLU,且自门控机制能模拟神经元的自适应激活,增强模型对复杂模式的拟合能力。这一突破直接推动了后续门控激活函数的发展,成为SwiGLU等创新设计的核心基石。
技术突破:SwiGLU的提出与优化
-
SwiGLU的首次理论验证(2020年)
2020年,Google研究员Noam Shazeer在《GLU Variants Improve Transformer》中针对Transformer架构中FFN层使用ReLU等激活函数存在的表达瓶颈,提出将GLU中的Sigmoid替换为Swish,形成SwiGLU,其公式为 SwiGLU ( z ) = ( W 1 z + b 1 ) ⊗ Swish ( W 2 z + b 2 ) \text{SwiGLU}(z) = (W_1z + b_1) \otimes \text{Swish}(W_2z + b_2) SwiGLU(z)=(W1z+b1)⊗Swish(W2z+b2)。实验表明,SwiGLU在GLUE、SuperGLUE等NLP基准任务上性能显著优于ReLU和GELU,其创新点在于通过Swish的平滑特性提升了梯度稳定性,缓解了原GLU中Sigmoid在极端值区域梯度消失的问题,同时通过分块矩阵运算优化计算效率,使实际计算量接近ReLU,适配大规模模型训练需求。 -
工程化落地与模型适配(2022年后)
2022年后,SwiGLU在PaLM、LLaMA等大模型中实现规模化应用。Google的PaLM模型将FFN层激活函数替换为SwiGLU,在参数量减少25%的情况下保持性能持平;Meta的LLaMA(2023年)进一步优化,通过去除偏置项( b 1 , b 2 b_1, b_2 b1,b2)和调整矩阵维度降低计算复杂度。在硬件适配方面,SwiGLU的分块运算特性与GPU/TPU的并行计算架构高度契合,成为现代大模型的标准配置,同时量化框架(如TensorRT)针对其门控机制进行定制优化,大幅提升推理效率,推动SwiGLU从理论走向工程实践。
应用与范式转变
-
大语言模型的标配选择
当前LLaMA 2、PaLM-2、Qwen-1.8B等新一代大语言模型均默认使用SwiGLU。实验数据显示,在长文本生成任务(如代码补全、多轮对话)中,SwiGLU相比GELU可使困惑度(Perplexity)降低3-5%,性能优势显著。此外,SwiGLU推动了参数效率革命,允许FFN层维度从传统的4倍降至3倍(如LLaMA将中间层维度从 4 d 4d 4d改为 3 d 3d 3d),在参数量减少25%的情况下保持模型性能,为万亿级参数大模型的训练提供了关键支持。 -
跨领域迁移与改进
在计算机视觉领域,Vision Transformer(ViT)的MLP层引入SwiGLU后,在ImageNet分类任务中top-1准确率提升1.2%,轻量级模型(如MobileViT)通过HardSwish变体进一步优化计算量,适配边缘设备;在多模态融合领域,Gato(DeepMind)、Flamingo(Google)等模型在跨模态交互层使用SwiGLU,增强不同模态语义的对齐能力。学术研究中,SwiGLU衍生出诸多变体,如GeGLU(将Swish替换为GELU)在LaMDA中提升表达能力,ReGLU(用ReLU替代门控函数)在特定场景下优化计算效率,研究者还从信息论角度证明其门控机制能最大化信息流互信息,提升模型泛化能力。
import numpy as np
import matplotlib.pyplot as plt# 设置图片清晰度
plt.rcParams['figure.dpi'] = 300# 定义输入范围
x = np.linspace(-6, 6, 1000)# 定义激活函数
def sigmoid(x):return 1 / (1 + np.exp(-x))def tanh(x):return np.tanh(x)def relu(x):return np.maximum(0, x)def leaky_relu(x, alpha=0.01):return np.where(x >= 0, x, alpha * x)def prelu(x, alpha=0.1):return np.where(x >= 0, x, alpha * x)def elu(x, alpha=1.0):return np.where(x >= 0, x, alpha * (np.exp(x) - 1))def glu(x, W1=1, W2=1, b1=0, b2=0):return (W1 * x + b1) * sigmoid(W2 * x + b2)def swish(x):return x * sigmoid(x)def swiglu(x, W1=1, W2=1, b1=0, b2=0):return (W1 * x + b1) * swish(W2 * x + b2)def gelu(x):return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))# 激活函数列表(不含Softmax)
functions = [(sigmoid, 'Sigmoid'),(tanh, 'Tanh'),(relu, 'ReLU'),(leaky_relu, 'Leaky ReLU (α=0.01)'),(prelu, 'PReLU (α=0.1)'),(elu, 'ELU (α=1.0)'),(swish, 'Swish'),(gelu, 'GELU'),(glu, 'GLU (W=1)'),(swiglu, 'SwiGLU (W=1)')
]# 颜色列表(使用10种高对比度颜色)
colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']# 创建画布(调整尺寸为更紧凑的比例)
plt.figure(figsize=(4, 3))# 绘制每个激活函数
for i, (func, name) in enumerate(functions):y = func(x)plt.plot(x, y, label=name, color=colors[i], linewidth=1, zorder=3)# 添加图例(调整为2列布局,放在图内底部)
plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.15), fancybox=False, shadow=False, ncol=2, fontsize=4)# 添加标题和标签(英文)
plt.title('Activation Functions', fontsize=4)
plt.xlabel('Input (x)', fontsize=4,loc='left')
plt.ylabel('Output (f(x))', fontsize=4)# 设置坐标轴范围
plt.xlim(-5, 5)
plt.ylim(-6, 6)# 添加网格线和参考线
plt.grid(True, linestyle='--', alpha=0.5, zorder=0)
plt.axhline(y=0, color='k', linestyle='-', alpha=0.3, zorder=1)
plt.axvline(x=0, color='k', linestyle='-', alpha=0.3, zorder=1)# 调整布局
plt.tight_layout(rect=[0, 0, 1, 0.9]) # 为图例留出空间
plt.show()