前馈网络与激活函数

Transformer中的Position-wise FFN 的具体结构是怎样的?为什么是两层全连接?¶
标准 Transformer 的 FFN 对每个位置独立作用(Position-wise),结构为两层全连接:
其中 W1 将输入维度 d_model 映射到中间维度 d_ff(通常 4 倍),σ 是非线性激活函数(如 GELU、ReLU),W2 再映射回 d_model。整个过程可以概括为“升维→非线性→降维”。
为什么是两层?
单层全连接加激活其实只能对输入做一个线性变换后的非线性映射,表达的是原始特征空间中的固定模式组合。而两层结构引入了一个“高维中间层”,让不同位置的特征在这个高维空间里进行充分的非线性交互,然后再压缩回原维度。这类似核方法中的升维思想:在更高维的空间中,原本线性不可分的模式可能变得可分。两层是能实现通用函数逼近的最简结构(万能逼近定理),兼顾了表达能力和计算效率。更深层当然可以,但两层已足够提供丰富的特征变换,并且不会引入过多参数和训练难度。
FFN 中间的隐层维度通常设为输入维度的几倍?这个比例有什么经验依据?¶
通常设为 4 倍。例如 d_model=512,则 d_ff=2048。这个比例最早在 Transformer 原论文中被采用,后来几乎成为默认配置。
经验依据:
-
在最初的机器翻译实验中,4 倍扩展提供了足够的容量来存储和组合从注意力层传来的上下文信息。过小(如 2 倍)会限制模型表达能力,导致下游任务性能下降;过大(如 8 倍)虽然容量更大,但参数量剧增,容易过拟合,且计算开销显著增加,而边际收益递减。
-
4 倍是一个在精度、参数量和训练速度之间的“甜点”,被大量后续工作(BERT、GPT 等)验证和延续。当然,并非绝对,有些大型模型为了在固定总参数量下加深网络,会适当减小
d_ff的比例(如 3.5 倍或更小),也有模型使用 8/3 倍等变体,但 4 倍是最稳健的起点。
GELU 激活函数和 ReLU 的核心区别是什么?为什么 Transformer 中常用 GELU?¶
核心区别:
ReLU(Rectified Linear Unit)是一个确定性的分段线性函数:ReLU(x) = max(0, x)。它在输入大于 0 时保留原值,小于等于 0 时直接置零,形成硬截断。
GELU(Gaussian Error Linear Unit)则是一个平滑的随机正则化函数,可以近似理解为“期望的 ReLU”,公式为 GELU(x) = x · Φ(x),其中 Φ(x) 是标准高斯分布的累积分布函数。实际计算常用近似:
GELU 没有硬截断,而是在输入为负时给予一个逐渐趋近于零的小梯度,正值时接近线性但保持平滑。
为什么 Transformer 中常用 GELU?
-
平滑性:GELU 处处可微且曲率平滑,梯度流动更稳定,尤其适合深层网络的训练。
-
自正则化效应:GELU 根据输入的大小随机性地“关闭”部分神经元(当 x 较小时Φ(x)接近 0),这类似于一种输入驱动的 Dropout,有助于防止过拟合,提升泛化能力。
-
性能更好:在大量 NLP 预训练任务中,GELU 相比 ReLU 取得了更优的困惑度和下游微调性能,特别是在 BERT、GPT 等模型中。ReLU 的硬截断在表示空间中可能丢弃了部分有用的微弱负信号,而 GELU 保留了这部分信息。
什么是 SwiGLU 激活函数?它的公式是什么?相比 ReLU 带来了什么改进?¶
SwiGLU 是 Swish-activated Gated Linear Unit 的简称,属于门控线性单元(GLU)家族。它将 GLU 的门控思想与 Swish 激活函数结合。
公式:
或者写成两步:
其中 ⊙ 表示逐元素乘法,σ 为 sigmoid。SwiGLU 对 values 还常另加偏置,这里简写。
相比 ReLU 的改进:
-
门控机制:SwiGLU 引入了一个独立的门(gate),由输入本身线性变换后通过 Swish 非线性生成,对另一个变换后的值进行逐元素加权。这使得网络能够学习哪些信息应该通过、哪些应该被抑制,比直接使用 ReLU 过滤更灵活。
-
梯度质量:Swish(SiLU)是光滑非单调函数,没有硬饱和区,梯度可以更均匀地传播,缓解了 ReLU 的“死神经元”问题,也避免了梯度消失。
-
更强的表示能力:由于门的存在,SwiGLU 可以视作对输入的条件线性变换,相当于根据上下文动态调整特征映射,使得相同参数量的模型能拟合更复杂的函数。实验表明,SwiGLU 在语言模型预训练中优于 ReLU、GELU 等传统激活,是实现更优困惑度的关键组件。
使用 SwiGLU 的门控机制是如何实现的?与标准 FFN 相比,参数量有何变化?¶
实现方式:
标准 FFN 有两组权重:W1(升维)和 W2(降维),中间加非线性。
SwiGLU 需要三个权重矩阵,因为门控需要额外的线性变换:
-
W_gate:将输入映射到d_ff维,产生门控信号。 -
W_value:将输入映射到d_ff维,产生待门控的值。 -
W_out:将门控后的结果(维度仍为d_ff)映射回d_model。
公式可写为:
实际中常把 W_gate 和 W_value 合并为一个更大的矩阵,然后分成两半,以一次矩阵乘法完成。
参数量变化:
标准 FFN 参数量:d_model × d_ff + d_ff × d_model = 2 · d_model · d_ff。
SwiGLU 有三个权重矩阵,但为了保持整体参数量不变,通常会将中间维度 d_ff 设为原来的 2/3 左右(详细计算:原 FFN 的参数总量约为 2·d_model·d_ff。SwiGLU 的参数量为 2·d_model·d_ff_swiglu + d_ff_swiglu·d_model(因为 gate 和 value 各需要 d_model × d_ff_swiglu,输出投影 d_ff_swiglu × d_model)。若要保持参数量一致,需 3·d_ff_swiglu = 2·d_ff_old,即 d_ff_swiglu ≈ (2/3) d_ff_old。这样在同等参数预算下,SwiGLU 通过门控增强了表达能力,实验表明这种参数重新分配优于直接增大标准 FFN 的宽度。
SwiGLU 中的“门”是如何计算的?为什么门控能提升模型效果?¶
门 gate 的计算:
其中 SiLU(x) = x · σ(x)。注意这里的门不是像 LSTM 那样由另一个序列或历史状态决定,而是完全基于当前输入。所以这是一种自门控(self-gating):模型根据当前 token 自身的表示,决定哪些特征维度应该放大、缩小或置零。
为什么门控能提升效果?
-
动态特征选择:门控允许模型对每个 token 的不同维度进行非线性的条件过滤。同样的 FFN 权重,面对不同输入时可以产生不同的激活模式,等于让 FFN 变成输入依赖的“条件计算”。这极大地增加了模型的容量和灵活性。
-
克服线性瓶颈:标准 FFN 是两层全连接+固定非线性,隐层表达是输入的一个确定性变换。门控引入了乘法交互,可以更精细地控制信息流,使模型更容易学习到稀疏的特征激活,类似于对隐层神经元进行软性的、上下文相关的 Dropout 或剪枝。
-
梯度流动更优:门控的 SiLU 激活函数及其导数平滑,且门控结构天然为梯度提供了两条通路(通过 gate 和 values),有助于缓解梯度消失问题。
FFN 可以看作是一种“记忆”模块,这个说法的依据是什么?它与注意力如何分工?¶
依据:
-
FFN 的两层结构可以视为存储了海量的键值对记忆。有研究将 FFN 的隐层神经元解释为“键”(key),输出权重解释为“值”(value)。输入激活了哪些键,就会检索出对应的值并叠加到输出中。从这个角度看,FFN 实际上实现了一个大规模的联想记忆网络:它记住了训练数据中大量的特征组合模式,并在推理时根据输入的模式进行检索和组合。
-
实验表明,FFN 的参数量巨大(占 Transformer 总参数的三分之二以上),且这些参数具有高度冗余性,去除部分 FFN 权重后模型仍能工作,这符合记忆系统的特征:容量大、可压缩。
与注意力的分工:
-
注意力负责捕获上下文依赖和序列级别的信息路由:它根据当前 token 与其他 token 的关系,动态地从序列中聚合信息。注意力解决的是“我应该从哪些位置取信息”。
-
FFN负责对每个位置的表示进行独立、非线性的特征变换,可以看作是在处理“拿到信息后怎么加工”。它将注意力输出的混合信息进行升维处理,在这个高维空间中完成特征的重新组合、筛选和存储。
-
形象比喻:注意力是“查资料”,FFN 是“消化资料并作答”。注意力负责信息流动,FFN 负责信息加工和记忆。
如果去掉 Transformer 中的 FFN 层,只用注意力层,模型能力会怎样?¶
去掉 FFN 层后,模型将完全由线性投影和注意力组成,失去了非线性特征变换和记忆存储的能力。
具体影响:
-
表示容量急剧下降:注意力本质上是基于输入相似度的加权求和,是输入表示的线性组合(不算 softmax 的非线性)。多层注意力叠加若没有 FFN 中的非线性,整个模型将退化为输入的一个复杂线性函数,无法拟合非线性决策边界。
-
无法独立处理 token 内部信息:注意力只能混合不同位置的信息,但无法对一个 token 自身的表示做丰富的特征抽取(比如将词性、语义、句法角色等组合起来)。FFN 给每个位置提供了独立“思考”的机会。
-
记忆缺失:如上一问所述,FFN 存储了大量训练数据中学习到的特征模式。去掉 FFN 后,模型将失去这种“知识记忆”能力,完全依赖注意力权重中隐式编码的信息,导致下游任务(尤其是需要事实知识的任务)性能暴跌。
-
实验上,如果移除所有 FFN 层,Transformer 在语言建模、翻译等任务上的性能会降至极低水平,甚至无法收敛。
所以,FFN 是 Transformer 不可或缺的组件,它提供了非线性、特征变换和知识存储三大核心能力。
GLU 的变体(如 ReGLU, GEGLU)各有什么特点?如何在实践中选择?¶
GLU(Gated Linear Unit)及其变体通过在门控路径上采用不同的激活函数得到不同特性:
-
ReGLU:门控路径使用 ReLU,即
gate = ReLU(x W_g + b_g),values 可以是线性或带激活。特点:计算简单,但 ReLU 的硬截断会丢失负值信息,可能限制表达。 -
GEGLU:门控路径使用 GELU。GELU 平滑,能保留部分负信号,往往比 ReGLU 表现好。
-
SwiGLU:门控路径使用 SiLU(Swish),被证明在大量语言模型预训练中效果最优,是目前主流。
选择依据:
-
优先考虑 SwiGLU,因为它在扩展实验中表现最优,已成为 LLaMA、PaLM 等模型的标准配置。
-
如果对推理速度有极致要求,且损失可接受,可考虑 ReGLU 或直接使用 GELU(标准 FFN)。
-
实践中,很多框架已提供方便切换的选项,可通过小规模消融实验对比不同激活函数的验证损失,选择适合自己数据和模型的变体。
注意:使用 GLU 变体时需调整隐层维度以保持参数量不变(见第5问)。
FFN 中的激活函数需要具备哪些特性,才能保证训练的稳定性?¶
为保证深层 Transformer 的稳定训练,FFN 中的激活函数最好具备:
-
平滑性:处处可导且导数变化平缓,避免突变导致梯度震荡。GELU、SiLU 等满足此条件,而 ReLU 在 0 点不可导(实践中可用次梯度)。
-
避免硬饱和:激活函数的导数在大部分输入区域内不为零,以缓解梯度消失。ReLU 在负半轴导数为零,可能造成“死神经元”;GELU 和 SiLU 在负区仍有微小梯度,更稳定。
-
适当的非线性:提供足够的非线性以提高模型容量,但不宜过度扭曲特征(如 sigmoid/tanh 容易饱和)。
-
有限的输出范围或自正则化:如 GELU 可以看作自动调节神经元输出幅度,有助于控制激活值的规模,防止前向爆炸。
-
计算高效:前向和反向传播的实现要快,以支持大规模训练。近似 GELU、SiLU 均可高效计算。
实践中,GELU 和 SiLU 很好地满足了这些要求,因此成为主流。
为什么 LLaMA 系列使用 SiLU(Swish)激活?它与 SwiGLU 是什么关系?¶
首先澄清:LLaMA 的 FFN 使用的是 SwiGLU,而不是单独的 SiLU。SwiGLU 的门控路径用了 SiLU 作为激活函数,values 路径无额外激活(线性)。所以 LLaMA 中广泛使用 SiLU 是作为 SwiGLU 的一部分。
但 LLaMA 论文中同时也提到,他们在 GLU 之外的标准 FFN 里并没有使用 SiLU(那些位置通常还是用 ReLU 或 GELU?实际上 LLaMA 完全采用 SwiGLU,所以没有单独用 SiLU 的 FFN)。关系:SiLU 是 SwiGLU 中门控函数的激活,SwiGLU = 门控(SiLU)+ 值变换。
为什么用 SiLU(SwiGLU):
-
SiLU 光滑非单调,在深层网络中梯度传播更优。
-
SwiGLU 作为整体,相比 GELU 等标准激活提供了更强的表达能力,这在 Meta 的扩展实验中得到了验证。
-
在 LLaMA 这种追求高效、高性能的模型中,SwiGLU 被选为默认配置,成为后续开源模型的重要参考。
能否使用线性激活函数代替非线性激活?对模型容量有什么影响?¶
可以,但模型将失去非线性,退化为一个线性映射的组合。多层线性变换叠加仍等价于单层线性变换,无法拟合任何非线性函数。这会导致模型容量极限受限:即使堆叠很深,模型也只能学习线性决策边界,完全无法处理语言中复杂的非线性关系(如语法组合、语义多义性等)。实验上,这种模型在语言建模等任务上的性能极差,等同于放弃了深度学习的基本优势。
所以,非线性激活是必需的,它赋予网络拟合任意复杂函数的能力。FFN 中的激活函数正是提供这种非线性的关键环节。
SwiGLU 的门控机制为什么需要两个线性变换?如果只用一个线性变换再分半,会怎样?¶
SwiGLU 的标准实现需要两个独立的线性变换:一个生成门控信号(gate),另一个生成待门控的值(values)。写成公式就是 gate = SiLU(x · W_g),values = x · W_v,然后输出 (gate ⊙ values) · W_o。这里 W_g 和 W_v 是两个独立的权重矩阵。
为什么需要两个独立的线性变换?
门控和值通路承担着不同的角色。门控负责“筛选”——决定哪些特征维度应该通过、放大或抑制。值通路负责“内容”——承载需要被筛选的信息本身。如果两者共享完全相同的输入特征和变换,它们之间的交互会非常受限。独立的 W_g 和 W_v 让模型可以将输入分别投影到两个不同的子空间中,一个专门学习“筛选规则”,另一个专门学习“被筛选的内容”。这种不对称性极大地增强了 FFN 的表达能力,让门控可以根据输入的不同方面做出精细的条件化决策。
如果只用一个线性变换再分半,会怎样?
“一个线性变换分半”的做法是:先计算 U = x · W,其中 W 的宽度是 2 × d_ff,然后将 U 沿特征维度切成两半,一半作为 gate,一半作为 values。这种做法在工程上更简单,只需一次矩阵乘法,而且在训练时常见(比如 PyTorch 的 nn.Linear 可以一次完成)。但从表示能力的角度看,它等同于强制 W_g 和 W_v 共享同一个输入投影的列空间。这意味着门控信号和值信号必须从输入特征的同一个线性组合中派生出来。虽然还是可以学习到有用的门控,但灵活性打了折扣。实验上,参数完全独立的双线性变换通常效果更好,因为模型可以学习到更丰富的门控-值交互模式。不过,为了保持参数量和计算量不变(见第3问),实践中往往将中间维度调整为原来的 2/3,使得两个独立的线性变换的总参数量与原来的一次大投影相当。
在 FFN 中,使用 SwiGLU 与使用 GELU 相比,训练时的激活值分布有何不同?¶
标准 FFN 使用 GELU 时,激活值分布大致是对称的,中心在零附近,负值被平滑地压缩至接近零但保留微小梯度,正值近似线性。整体分布比较紧凑,方差相对可控。
SwiGLU 引入了门控乘法运算 gate ⊙ values,这会使激活值分布发生显著变化:
-
分布形状更复杂:gate 经过 SiLU 后,正值区接近线性,负值区接近零且平滑。values 是线性投影的输出,未经过任何非线性。两者逐元素相乘后,结果的分布是这两个分布的结合。正值区域的 values 会被线性缩放,负值区域的 values 被强烈衰减。因此最终的激活值分布不再是简单的对称分布,而是一种“正偏态”且尾部更重的形状。
-
稀疏性增强:SwiGLU 的门控倾向于将许多神经元的部分输出置零或接近零(尤其是当 gate 为负值时),这导致了比 GELU 更明显的稀疏激活。在深层 Transformer 中,SwiGLU 层的输出中零值比例通常更高。
-
方差可能更大:由于门控的缩放效应,激活值在正值方向的波动可能比 GELU 更剧烈。这要求更好的初始化策略和归一化(如 RMSNorm)来防止前向激活值爆炸。
-
对混合精度更友好?:SwiGLU 的这种稀疏性和可控的正值分布实际上有利于 FP16 等低精度训练。GELU 在负值区有细微的梯度,对下溢出敏感;而 SwiGLU 的负值区直接输出接近零,并且梯度也接近零,这在 FP16 中表示为精确零而非微小非正规数,更不容易产生异常值。不过门控乘法仍可能产生极值,需要合理的缩放。
FFN 的中间维度有时会设定为 8/3×d_model,这个数字是怎么来的?和 SwiGLU 有关吗?¶
这个比例确实与 SwiGLU 直接相关。标准的 FFN(用 ReLU 或 GELU)的中间维度通常设为 4 × d_model。参数量为:两个线性层 W1 和 W2,总参数量 ≈ 2 × d_model × d_ff = 2 × d_model × (4 × d_model) = 8 × d_model²。
SwiGLU 需要三个权重矩阵:W_g、W_v(各自升维)和 W_o(降维)。如果保持中间维度仍为 4 × d_model,则总参数量变成 3 × d_model × (4 × d_model) = 12 × d_model²,比标准 FFN 多了 50%。为了在相同参数预算下对比,研究者将 SwiGLU 的中间维度调低,使得总参数量与标准 FFN 持平。设中间维度为 d_ff',则有 3 × d_model × d_ff' = 2 × d_model × (4 × d_model),解得 d_ff' = 8/3 × d_model ≈ 2.67 × d_model。这就是 8/3 倍的来历。
因此,8/3 × d_model 并非任意选择,而是为了让 SwiGLU 的参数量恰好等于标准 4 倍中间维度的 FFN,从而进行公平的对比。实践证明,在这种等参数条件下,SwiGLU 的性能显著优于 ReLU 或 GELU。后来很多模型(如 LLaMA)就直接采用这个比例作为 SwiGLU 的默认配置。
能否用 MoE 的思想替换标准 FFN?这时的“专家”就是单独的 FFN,路由如何设计?¶
完全可以,这正是 MoE(Mixture of Experts)Transformer 的核心思路。它将标准 FFN 层替换为多个并行的“专家 FFN”,并由一个门控路由器动态选择每个 token 应该激活哪些专家。
结构设计:
-
一个 MoE 层包含
N个专家,每个专家本身就是一个完整的 FFN(通常是两层结构,如 SwiGLU)。 -
输入 token
x会先经过一个轻量级的路由器(通常是一个线性层),输出N个分数。经过 softmax 得到每个专家的概率分布。 -
实际中通常使用 Top-K 路由:只选取概率最高的 K 个专家(K 通常为 1 或 2),并将 token 路由给它们。
-
每个被选中的专家独立计算输出,然后根据路由权重(softmax 概率)进行加权求和,得到最终的 FFN 输出。如果 K=1,就直接取那个专家的输出。
路由设计的考量:
-
负载均衡:为了防止所有 token 都涌向同一个专家,训练时通常会加入辅助损失,惩罚专家之间的负载不均衡。这确保每个专家都有机会被训练,提升整体利用率。
-
容量因子:每个专家可以设置一个容量上限(capacity factor),限制一个 batch 内最多处理多少 token。超出的 token 会被“溢出”,可能直接通过残差连接绕过,或者由额外的共享专家处理。
-
专家选择策略:除了 Top-K,还有基于哈希的路由、随机路由等。Top-K 最常用,因为简单高效,且在推理时可以利用稀疏性只激活少数专家,大幅节省计算。
效果与挑战:MoE 可以在不显著增加推理计算量的前提下,将模型容量(总参数量)扩大数倍甚至数十倍。但专家之间的通信开销、负载均衡、以及微调时的专家坍塌等问题仍需仔细处理。Switch Transformer、GShard、Mixtral 等都是成功的 MoE 实践。
FFN 的位置为什么在注意力之后?如果放在前面,或者与注意力并行,实验效果如何?¶
标准 Transformer 块顺序是:注意力 → FFN,每层内部有残差连接和层归一化。
为什么注意力在前?
从信息流的角度看,注意力负责“聚合上下文”:每个 token 先去序列中搜集其他位置的信息,形成一个富含上下文的综合表示。然后 FFN 对这个综合表示进行深度的特征加工和记忆检索。如果反过来(FFN 在注意力之前),token 各自先做独立变换,然后再去和其他 token 交互。独立变换可能会改变 token 的表示,使得后续注意力的匹配变得不稳定。实验上,有些早期探索发现“注意力在前”通常效果更好,收敛更稳定。
如果放在前面:一些研究尝试了 FFN 先于注意力的变体,发现性能略有下降,但并非不可接受。这可能是因为 FFN 先处理会过早地将 token 映射到另一个空间,使得注意力对位置和语义的匹配受到干扰。但经过仔细调优后,这种顺序也可以工作。
如果与注意力并行:也就是在同一层内同时计算注意力和 FFN,然后将两者输出相加。这种结构被称为 Parallel Transformer Block,在一些模型(如 PaLM)中被采用。它的好处是可以在硬件上更好地并行调度:注意力计算和 FFN 计算可以同时进行,减少了串行等待时间,提升训练和推理速度。实验结果表明,并行结构的性能与串行结构相当,甚至在某些配置下略优,同时显著提高了硬件利用率。目前,并行块已成为许多大模型的标准配置。
在超大模型中,FFN 的参数量往往超过注意力部分,为什么?这是必须的吗?¶
原因:Transformer 总参数量主要由两部分构成:注意力层的 Q、K、V、O 投影矩阵(形状均为 d_model × d_model,对标准注意力总参数量约为 4 × d_model²),以及 FFN 的两个(或三个)线性层(标准 FFN 参数量约为 2 × d_model × d_ff)。当 d_ff 设为 4 × d_model 时,FFN 的参数量是 8 × d_model²,而注意力部分只有约 4 × d_model²。因此 FFN 的参数量大约是注意力的两倍。
为什么这样设计?
实验表明,在给定总参数预算下,将更多参数分配给 FFN(即增大 d_ff 或使用更深的 FFN)比增加注意力头的数量或注意力层的维度更能有效提升模型容量和下游性能。FFN 充当了模型的“知识记忆”部分,存储海量的特征模式;而注意力负责动态路由信息。知识的存储需要大量参数,而信息路由本身相对轻量。所以这种参数分配策略是合理的。
这是必须的吗?
不一定。这是一个经验上的优化选择,并非理论必须。如果改变比例,比如让注意力拥有更多参数,可能会在某些需要复杂推理的任务上更好,但通常整体表现不如当前分配。有些模型为了在特定硬件上提高效率,会调整比例。此外,使用 MoE 时,FFN 的参数量被极度放大(因为有多组专家),这时 FFN 参数占比更高,但实际激活的参数仅是一小部分。因此,参数分配比例是灵活可调的,取决于对容量、计算效率和任务需求的权衡。
激活函数的选择会如何影响混合精度训练中的数值稳定性?哪些激活函数对 FP16 更友好?¶
混合精度训练(通常指 FP16 权重和激活,FP32 主权重)对数值范围非常敏感。FP16 的表示范围大约在 6×10⁻⁸ 到 65504 之间,超出则溢出为 Inf,低于最小正规数则可能变成零或产生非正规数,导致梯度消失。
激活函数的影响:
-
ReLU:在 FP16 下相对安全,因为负半轴直接输出 0,正半轴线性。但正半轴如果输入值很大(超过 65504),仍然会溢出。更隐蔽的是,ReLU 的梯度在正半轴为 1,稳定;负半轴梯度为 0,不会产生微小梯度,避免了非正规数下溢问题。
-
GELU:负值区输出不是零,而是约
0.5x(1+erf(x/√2)),当 x 很负时,输出接近零但非零。在 FP16 下,这些微小值可能落入非正规数范围或直接下溢为 0。同时,GELU 的梯度在负值区有微小值,也容易下溢,可能影响训练稳定性。因此使用 GELU 时通常需要配合 loss scaling,将梯度放大后再裁剪,以保持有效位数。 -
SiLU(Swish):在负值区输出也是负值,接近 0,梯度变化平滑。同样有微小梯度下溢的风险。但 SiLU 的形状使得它在很多实现中与 GELU 有类似的行为。
-
SwiGLU 中的门控乘法:输出在负值区被门控抑制为接近 0,正值区可能被放大。乘法操作会放大数值范围,一旦 gate 较大且 values 也较大,乘积可能溢出 FP16 范围。这需要门控投影和值投影的初始化和缩放(如使用小初始权重或合理的方差)来控制激活值规模。
哪些对 FP16 更友好?
-
理论上,ReLU 是最简单的,没有微小梯度问题,只有正半轴的溢出需要注意。
-
GELU 和 SiLU 都需要更细致的梯度缩放。实践中,几乎所有大模型都使用 BF16(Brain Float 16)来替代 FP16,因为 BF16 的指数位与 FP32 相同,动态范围极大,几乎不会溢出,且对微小值下溢的容忍度更高。因此,在现代 GPU(如 A100、H100)上,激活函数的选择对数值稳定性的影响被大大降低。
-
如果必须使用 FP16,则推荐使用 GELU 或 SiLU 的近似实现,并在训练中开启 loss scaling,同时监控梯度的直方图。
总结:在 BF16 环境下,激活函数的选择更多取决于任务性能而非数值稳定性;在 FP16 下,需要额外关注激活值的动态范围并配合 loss scaling。ReLU 最稳健,但表达能力受限;GELU/SiLU 表达能力更强,但需要更谨慎的数值管理。