系列目录-大模型学习篇
问题场景(Problems)
上一个章节,我们介绍了几个具有代表性的基础大模型。但是在上一个章节的内容中,我们还缺少对大模型使用的激活函数改进和attention机制优化进行介绍。这一章节,我们补充激活函数和attention机制优化的内容。
激活函数(Activation Function)
激活函数是将线性近似改变为非线性的重要计算单元,如果采用纯线性层进行叠加,那么整个模型无论如何增加深度,都可以写作线性表示的函数,那么模型对复杂特征和信息的表征与拟合能力会受到限制。
(1)ReLU:
f
(
x
)
=
m
a
x
(
0
,
x
)
f(x)=max(0,x)
f(x)=max(0,x)
特点: 只保留正值,负值直接置为 0。
优点: 简单高效,计算速度快,不会出现梯度消失问题。
缺点: 存在 “Dead ReLU” 问题,当大量负值出现时,神经元可能失效且无法恢复。
(2)Leaky ReLU:
y
=
{
x
i
f
x
≥
0
α
x
i
f
x
<
0
y=\begin{cases}x&ifx\geq0\\\alpha{x}&ifx<0\end{cases}
y={xαxifx≥0ifx<0
其中α 是一个小的常数,通常为 0.01。
特点: 与 ReLU 类似,但对负值引入了一个小的斜率,以避免死神经元。
优点: 解决了神经元死亡问题,允许小部分负值信息通过。
缺点: 可能会引入一些噪音,性能提升有限。
(3)GeLU:
f
(
x
)
=
x
⋅
Φ
(
x
)
f(x)=x⋅Φ(x)
f(x)=x⋅Φ(x)
其中 Φ(𝑥)是正态分布的累积分布函数,近似表示为:
f
(
x
)
≈
0.5
⋅
x
∗
(
1
+
t
a
n
h
(
2
/
π
∗
(
x
+
0.044715
∗
x
3
)
)
)
f(x)≈0.5⋅x*(1+tanh(2/{\pi}*(x+0.044715*x3)))
f(x)≈0.5⋅x∗(1+tanh(2/π∗(x+0.044715∗x3)))
特点: 通过高斯分布以概率方式激活输入,允许负值在特定条件下通过。
优点: 平滑且具备更强的表达能力,能更好地处理复杂模式。
缺点: 计算复杂度高于 ReLU 和 Leaky ReLU,但在现代硬件上问题不大。
(4)Swish:
f
(
x
)
=
x
⋅
σ
(
x
)
f(x)=x⋅σ(x)
f(x)=x⋅σ(x)
其中
σ
(
x
)
σ(x)
σ(x)是 Sigmoid 函数。
特点: 类似于 ReLU,但在负值区间有部分激活效果。输出是输入的缩放版本,由 sigmoid 函数控制。
优点: 在一些任务中表现优于 ReLU,尤其是深层模型。平滑且连续,能改善梯度流动。
缺点: 相比 ReLU 计算复杂度稍高。
(5)GLU:
G
L
U
(
x
)
=
x
1
⋅
σ
(
x
2
)
GLU(x)=x_1 ⋅σ(x_2)
GLU(x)=x1⋅σ(x2)
特点: 一种门控机制,输入被分为两个部分
x
1
x_1
x1和
x
2
x_2
x2 ,使用 sigmoid 激活函数
σ
(
x
2
)
σ(x_2)
σ(x2) 作为门控信号,控制
x
1
x_1
x1的输出。
优点: 通过门控机制实现更灵活的控制,增强了网络的建模能力。
缺点: 增加了参数和计算量,适用于更复杂的任务。
(6)SwiGLU:
S
w
i
G
L
U
(
x
)
=
x
1
⋅
S
w
i
s
h
(
x
2
)
SwiGLU(x)=x_1⋅Swish(x_2)
SwiGLU(x)=x1⋅Swish(x2)
特点: GLU 的变体,使用 Swish 代替 sigmoid 作为门控机制。
优点: 结合了 Swish 的平滑激活和 GLU 的门控机制,进一步提升了模型的非线性表达能力和梯度流动。
缺点: 增加了计算复杂度,但在一些任务上表现优异,适合复杂模型。
(7)GeGLU:
G
e
G
L
U
(
x
)
=
x
1
⋅
G
e
L
U
(
x
2
)
GeGLU(x)=x_1⋅GeLU(x_2)
GeGLU(x)=x1⋅GeLU(x2)
特点: GLU 的另一个变体,使用 GeLU 作为门控函数代替传统的 sigmoid 函数。
优点: 结合了 GeLU 的非线性和 GLU 的门控机制,具有强大的建模能力,能够捕捉更加复杂的模式和特征。
缺点: 计算量进一步增加,但在深度学习任务中的表现往往更好。
MHA改进之KV cache
在聊KV cache之前,我们先引入decoder only模型的生成过程:
解码器以自回归方式工作,如 GPT-2 文本生成所示。这种自回归方式重复了一些操作,我们可以通过放大在解码器中计算注意力的过程来更好地理解这一点。
由于解码器是因果的(即,下一个生成Token的所需的注意力只需要计算当前token的注意力值,而不需要计算其他Token的注意力值),但是在每个生成步骤中,我们都在重新计算相同的先前的Token注意力。所以实际上我们可以考虑通过保存历史keys和values的值,只计算当前Token的注意力即可。
这就是KV cache解决问题的地方。通过缓存以前的keys和values,我们可以只关注计算新Token的attention。