原始transformer的FFN层
原始transformerFFN层非常简单,就是一个升维+降维的操作,中间套一个RELU激活函数:
FFN层的作用是增加模型的非线性能力,升维再降维的操作也是老传统了,之前学从感知机到多层感知机就明白,一个低的维度的特征终归是有限,但是高的维度,能够组合更多的特征,学到更多的东西。
相关研究(Transformer Feed-Forward Layers Build Predictions by Promoting Concepts in the Vocabulary Space)发现 Transformer FFN 会编码词汇和概念信息,可以说FFN层是存储着世界知识。
ReLU的局限性
在传统深度学习中,就有老生常谈的"死亡ReLu"问题。当x<0的时候,Relu是输出是0,梯度为0,这个神经元就可以看作"死亡了",这在大规模的LLM训练中,这个问题更严重。
GLEU
后续很多基于transformer架构的模型,例如Bert、GPT1都是采用了GELU(Gaussian Error Linear Units)这个激活函数
其中:
表示标准正态分布的累积分布函数。
GELU相比ReLU改进在哪里?
GELU的核心思想是:不要像ReLU一样进行简单的二值化筛选,而是根据输入值的重要程度进行平滑地保留信息。
可以看作一个动态权重,决定当前输入应该保留多少。
当时:,
此时
信息基本保留。
当:时:
此时
信息被抑制。
ReLU 是硬门控(Hard Gate),直接判断保留或丢弃; GELU 是软门控(Soft Gate),通过连续函数平滑控制信息流。
这使得GELU在大规模Transformer训练中通常比ReLU更加稳定。
GLU——引入显式门控机制
虽然GELU可以根据输入调整激活程度,但是它仍然只有一条信息路径。输入特征既负责提供信息,又负责决定自己是否被保留。
门控机制很早就有了。2020年google的一项研究将他引入了transformer的FFN层:
GLU Variants Improve Transformer
其中:
:信息分支,负责产生特征; :门控分支,负责控制信息是否通过; :逐元素乘法。
他的思想就是将信息提取和信息筛选分开。模型可以学习一个专门的门控函数,动态控制不同特征的重要程度。
论文中还研究了各种门控函数的变体


最后效果比较好的就是SwiGLU了
GLU变体的胜出者:SwiGLU
门控的思想我们已经掌握了,就是将信息提取和信息筛选分开,模型可以学习一个专门的门控函数,动态控制不同特征的重要程度。各种变体无非就是这个门控函数的激活函数不同。
SwiGLU用的激活函数为:Swish(也叫 SiLU,Sigmoid Linear Unit):
可以看到Siwsh就是x乘以一个函数,只是比原始的GLU的激活函数多乘了一个
这会带来什么优势?
原始的GLU的门控范围为0~1,这意味着只能对通过的信息进行缩小或者不变,没法放大,当较大时,可以保留甚至增强正向信息;当为负值时,仍然保留部分负方向信息,而不是像ReLU一样直接截断。Swish相比sigmoid具有更加灵活的信息调节能力,既负责开关,又负责调节幅度
实现
以下摘自modeling_llama.py(已删除无关内容以及分支)
class LlamaMLP(nn.Module):
def __init__(self, config):
super().__init__()
self.config = config
self.hidden_size = config.hidden_size
self.intermediate_size = config.intermediate_size
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
self.act_fn = ACT2FN[config.hidden_act]#hidden_act="silu",
def forward(self, x):
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
代码很简单,这里不再阐述








