一言
老鼠不知道自己吃的东西是偷来的,只是在小心翼翼地活着。——网易云音乐
RELU到SwiGLU:LLM中FFN层的演变

原始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

其中:

  • :信息分支,负责产生特征;
  • :门控分支,负责控制信息是否通过;
  • :逐元素乘法。

他的思想就是将信息提取和信息筛选分开。模型可以学习一个专门的门控函数,动态控制不同特征的重要程度。
论文中还研究了各种门控函数的变体

20260820173738

20260820173745

最后效果比较好的就是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))

代码很简单,这里不再阐述

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇