注意力机制是Transformer模型的标志性组件,但并非唯一构建模块。线性层与激活函数同样至关重要。本文将介绍:
本文分为三部分:
注意力层是Transformer的核心功能,它对序列元素进行对齐并将输入序列转换为输出序列。注意力层对输入进行仿射变换,即输出是每个序列元素输入的加权和。
神经网络的能力不仅来自线性层,更源于引入非线性的激活函数。在Transformer中,注意力层后需要非线性组件以学习复杂模式,这是通过在每个注意力层后添加前馈网络(FFN)或多层感知机网络(MLP)实现的。典型Transformer块结构如下:
图中灰色框在Transformer中多次重复。每个块中(除归一化层外),输入先经过注意力层,再通过前馈网络(PyTorch中实现为nn.Linear)。前馈网络内的激活函数为变换添加非线性。
前馈网络使模型能学习更复杂模式。通常包含多个线性层:首层扩展维度以探索不同表示,末层压缩回原始维度。激活函数通常应用于首层线性输出。
因此,通常将块的前半部分称为“注意力子层”,后半部分称为“MLP子层”。
在BERT模型中,MLP子层实现如下:
import torch.nn as nn
class BertMLP(nn.Module):
def __init__(self, dim, intermediate_dim):
super().__init__()
self.fc1 = nn.Linear(dim, intermediate_dim)
self.fc2 = nn.Linear(intermediate_dim, dim)
self.gelu = nn.GELU()
def forward(self, hidden_states):
hidden_states = self.fc1(hidden_states)
hidden_states = self.gelu(hidden_states)
hidden_states = self.fc2(hidden_states)
return hidden_statesMLP子层包含两个线性模块。输入序列进入MLP子层时,首线性模块扩展维度,随后应用GELU激活函数。结果通过第二线性模块压缩回原始尺寸。
中间维度通常为原始维度的4倍——这是Transformer模型的常见设计模式。
激活函数为神经网络引入非线性,使其能学习复杂模式。传统神经网络常用双曲正切(tanh)、Sigmoid和整流线性单元(ReLU),而Transformer模型通常采用GELU和SwiGLU激活函数。
以下是一些常见激活函数的数学定义:
ReLU(整流线性单元)在现代深度学习中流行,因其避免梯度消失问题且计算简单。
GELU(高斯误差线性单元)因使用标准正态分布累积分布函数Φ(x)而计算更复杂。存在如上所示的近似公式。GELU非单调,如下图所示。
单调激活函数通常更受青睐,因其确保梯度方向一致,可能带来更快收敛。但单调性非严格必需——仅需更长的训练时间。这是在模型复杂度与训练时长间的权衡。
Swish是另一种非单调激活函数,参数β控制x=0处的斜率。当β=1时称为SiLU(Sigmoid线性单元)。
SwiGLU(Swish门控线性单元)是现代Transformer模型常见的新激活函数。它是Swish函数与线性函数的乘积,参数在训练中学习。其流行源于复杂性:展开公式可见分子中的二次项,帮助模型无需额外层即可学习复杂模式。
上图展示了这些激活函数的曲线。所示SwiGLU函数为f(x)=SiLU(x)⋅(x+1)。
在Python代码中切换激活函数很简单。PyTorch提供内置的nn.Sigmoid、nn.ReLU、nn.Tanh和nn.SiLU。但SwiGLU需要特殊实现。以下是Llama模型中使用的PyTorch代码:
import torch.nn as nn
class LlamaMLP(nn.Module):
def __init__(self, dim, intermediate_dim):
super().__init__()
self.gate_proj = nn.Linear(dim, intermediate_dim)
self.up_proj = nn.Linear(dim, intermediate_dim)
self.down_proj = nn.Linear(intermediate_dim, dim)
self.act = nn.SiLU()
def forward(self, hidden_states):
gate = self.gate_proj(hidden_states)
up = self.up_proj(hidden_states)
swish = self.act(up)
output = self.down_proj(swish * gate)
return output此实现使用两个线性层处理输入hidden_states。一个输出通过SiLU函数,然后与另一输出相乘,最后通过线性层处理。线性层根据维度扩展/压缩命名为“up”或“down”,而与SiLU连接的层因门控机制称为“gate”。门控是神经网络的设计,意味一个线性层输出与权重的逐元素相乘,此处权重由Swish激活函数产生。
Llama模型架构如下所示,呈现了MLP模块的双分支结构:
本文探讨了Transformer模型中的线性层与激活函数。具体内容包括:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。