简单常用的激活函数
隐藏层:tanh优于sigmoid,形状相似,取值在-1到1之间,有中心化效果。
输出层:sigmoid优于tanh,因为我们希望输出概率在0-1之间。
实际应用:深层网络更多用relu,因为tanh和sigmoid会在端值趋于饱和,导致训练速度减慢。从导数角度看,sigmoid和tanh在x趋向于正负无穷时,导数趋向于0,一旦落入饱和区,梯度过小会发生梯度消失,而relu不会存在这个问题,但是lr不能设置过大,否则会出现40%的神经元坏死。
gelu
bert使用的激活函数,在梯度临近原点时不为0,减少梯度消失的问题,导函数光滑无间断,容易反向传播,计算复杂度低。
SwiGLU
llama&qwen2.5激活函数
from:https://blog.csdn.net/qinduohao333/article/details/131085549
2019年提出 ,结合了Swish和GLU,用于Transformer的FFN层以提升性能
Swish
平滑性。σ(x)是sigmoid函数。Swish函数光滑且非单调,x>0时f(x)无上线,x<0时有下限。
Swish(x) = x*sigmoid(ßx)
一次求导和二次求导。
GLU
门控机制。把矩阵拆分成两部分A和B,计算A和sigmoid(B)的哈达玛积。
input_split = torch.split(input, 1, dim=-1)
sigmoid = nn.Sigmoid()
output = input_split[0] * sigmoid(input_split[1])
SwiGLU
为了提升Transformer中FFN层的实现。
选择 SwiGLU 作为大语言模型的激活函数,主要是因为它综合了非线性能力、门控特性、梯度稳定性和可学习参数等方面的优势。
# FFN层原始定义(ReLU激活):
FFN(x, W1, W2, b1, b2) = max(0, xW1 + b1)W2 + b2
# 不带GLU的变种
## T5使用不带bias的ReLU
FFN_ReLU(x, W1, W2) = max(xW1, 0)W2
## bert系列使用GELU实现
FFN_GELU(x, W1, W2) = GELU(xW1)W2
FFN_Swish(x, W1, W2) = Swish1(xW1)W2
# 带GLU的变种
GLU(x, W, V, b, c) = σ(xW + b) ⊗ (xV + c)
SwiGLU(x, W, V, b, c, β) = Swishβ(xW + b) ⊗ (xV + c)
FFN_SwiGLU(x, W, V, W2) = (Swish1(xW) ⊗ xV )W2
class SwiGLU(nn.Module):
def __init__(self, w1, w2, w3) -> None:super().__init__()
self.w1 = w1
self.w2 = w2
self.w3 = w3
def forward(self, x):
x1 = F.linear(x, self.w1.weight)
x2 = F.linear(x, self.w2.weight)
hidden = F.silu(x1) * x2 # F.silu与β=1时的Swish相同
return F.linear(hidden, self.w3.weight)