2017年,Google Brain团队在论文《Attention Is All You Need》中提出的Transformer架构,彻底改变了自然语言处理领域的格局。这个看似简单的架构仅基于注意力机制构建,却一举解决了困扰研究者多年的序列建模难题。
传统RNN架构在处理长序列时面临三大核心挑战:首先是梯度消失问题,随着序列长度增加,早期信息在反向传播过程中逐渐衰减;其次是计算效率低下,由于时间步间的强依赖性无法实现并行计算;最后是内存限制,长序列处理时批处理大小被迫缩减。Transformer通过自注意力机制完美解决了这些问题,在WMT2014英德翻译任务上取得28.4 BLEU值,比当时最佳模型提升超过2个点。
提示:理解Transformer的关键在于把握其"全局视野"特性——每个位置都能直接关注到序列中所有其他位置,而不像RNN需要逐步传递信息。
Transformer延续了经典的编码器-解码器框架,但用自注意力层替代了传统的循环单元。编码器由6个相同层堆叠而成,每层包含两个核心子层:
这两个子层都采用残差连接和层归一化,数学表示为:
code复制LayerNorm(x + Sublayer(x))
其中d_model=512是所有子层的统一维度,这种设计使得残差连接可以直接进行元素相加。
解码器在编码器结构基础上增加了第三个子层——编码器-解码器注意力层,用于建立源语言和目标语言间的关联。特别值得注意的是解码器的掩码机制,确保预测位置i时只能看到位置小于i的输出,维持自回归特性。
注意力函数的核心是将查询(Query)映射到一组键值对(Key-Value)的输出。具体计算过程可分为四步:
数学表达式为:
python复制Attention(Q, K, V) = softmax(QK^T/√d_k)V
这种设计使得模型能够动态聚焦于最相关的信息。例如在翻译"The animal didn't cross the street because it was too tired"时,模型能自动学习到"it"应该关注"animal"而非"street"。
单一注意力头只能学习一种关注模式,多头机制并行运行h个不同的注意力函数(论文中h=8),然后将结果拼接并线性变换:
code复制MultiHead(Q, K, V) = Concat(head_1, ..., head_h)W^O
where head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)
每个头都拥有独立的参数矩阵W_i^Q, W_i^K, W_i^V ∈ R^{d_model×d_k},这使得模型能够:
实际应用中,d_k = d_v = d_model/h = 64,保持计算量与单头注意力相当。
由于Transformer不含循环和卷积,必须显式注入位置信息。论文采用正弦函数生成位置编码:
code复制PE(pos,2i) = sin(pos/10000^{2i/d_model})
PE(pos,2i+1) = cos(pos/10000^{2i/d_model})
这种设计的优势在于:
注意:位置编码与词嵌入相加而非拼接,这要求二者维度必须相同。实验表明,简单的相加操作足以让模型有效区分位置信息。
论文在8台P100 GPU上训练了12小时达到最优效果,关键配置包括:
学习率预热(warmup)策略特别重要:在前4000步逐步提高学习率,避免早期训练不稳定。这种设计源于注意力层梯度范数较大的特性。
在WMT2014英德翻译任务上:
在英法翻译任务上:
这些结果证明,纯注意力架构不仅计算效率更高,在质量上也显著优于传统方法。
Transformer的计算效率主要来自三个方面:
实际编码时需要注意:
python复制# 伪代码示例:多头注意力实现
class MultiHeadAttention(nn.Module):
def __init__(self, h, d_model):
super().__init__()
self.d_k = d_model // h
self.linears = clones(nn.Linear(d_model, d_model), 4)
def forward(self, query, key, value, mask=None):
nbatches = query.size(0)
# 线性变换后分割为h个头
query, key, value = [
lin(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
for lin, x in zip(self.linears, (query, key, value))
]
# 计算缩放点积注意力
x, self.attn = attention(query, key, value, mask)
# 拼接多头结果
x = x.transpose(1, 2).contiguous().view(nbatches, -1, self.h * self.d_k)
return self.linears[-1](x)
原始Transformer发表后,研究者提出了多种改进:
这些变体主要在三个方面进行优化:
在实际生产环境中部署Transformer时:
例如,华为的TinyBERT通过蒸馏将BERT体积缩小7.5倍,速度提升9.4倍,同时保持96%以上的性能。
我在实际项目中发现,当训练数据不足时,可以先在类似领域的大规模语料上预训练注意力层,然后在小数据集上微调,这种方法通常能提升3-5个BLEU点。另一个实用技巧是在解码器第一个子层添加额外的局部注意力,帮助模型更好地捕捉短语级别的模式。