1. Transformer架构概述Transformer模型彻底改变了自然语言处理领域它完全基于自注意力机制摒弃了传统的循环神经网络RNN和卷积神经网络CNN。我第一次在实际项目中使用Transformer时就被它的并行处理能力和长距离依赖捕捉能力震撼了。下面我们从一个实际案例开始逐步拆解它的核心组件。想象你正在构建一个翻译系统输入I love coding模型需要输出我爱编程。传统RNN需要逐个词处理而Transformer可以同时处理所有单词并通过自注意力机制自动学习love和爱之间的关联。这种并行性使得训练速度大幅提升实测下来比LSTM快3倍以上。Transformer的核心架构包含编码器堆栈左侧处理输入序列解码器堆栈右侧: 生成输出序列注意力机制连接两者的关键桥梁每个编码器层包含两个子层多头自注意力机制前馈神经网络而解码器层则多出一个编码器-解码器注意力层用于聚焦相关输入。我在实现时发现这种分层结构让模型可以逐层提取不同抽象级别的特征。2. 输入处理嵌入与位置编码2.1 词嵌入实现词嵌入是将离散的单词转换为连续向量的过程。在PyTorch中我们可以这样实现import torch.nn as nn class TokenEmbedding(nn.Module): def __init__(self, vocab_size, d_model): super().__init__() self.embedding nn.Embedding(vocab_size, d_model) self.d_model d_model def forward(self, x): # 乘以sqrt(d_model)来缩放嵌入值 return self.embedding(x) * math.sqrt(self.d_model)这里有个坑我踩过一定要对嵌入值进行缩放否则初始阶段注意力分数会过大导致softmax进入饱和区。通常设置d_model512这样不同位置的嵌入值方差相近。2.2 位置编码详解因为Transformer没有递归结构必须显式注入位置信息。原始论文使用正弦函数class PositionalEncoding(nn.Module): def __init__(self, d_model, dropout, max_len5000): super().__init__() self.dropout nn.Dropout(pdropout) position torch.arange(max_len).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)) pe torch.zeros(max_len, d_model) pe[:, 0::2] torch.sin(position * div_term) pe[:, 1::2] torch.cos(position * div_term) self.register_buffer(pe, pe) def forward(self, x): x x self.pe[:x.size(1)] return self.dropout(x)有趣的是我在实验中发现对于短文本50词可学习的位置嵌入效果更好但对于长文本正弦版本更具泛化性。这是因为正弦函数的周期性可以外推到训练时未见的位置。3. 编码器实现3.1 多头注意力机制这是Transformer最核心的组件。我们先用代码实现单头注意力def scaled_dot_product_attention(Q, K, V, maskNone): matmul_qk torch.matmul(Q, K.transpose(-2, -1)) dk K.size(-1) scaled_attention_logits matmul_qk / math.sqrt(dk) if mask is not None: scaled_attention_logits (mask * -1e9) attention_weights F.softmax(scaled_attention_logits, dim-1) output torch.matmul(attention_weights, V) return output多头注意力的关键是将输入拆分为多个子空间class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() self.num_heads num_heads self.d_model d_model self.depth d_model // num_heads self.Wq nn.Linear(d_model, d_model) self.Wk nn.Linear(d_model, d_model) self.Wv nn.Linear(d_model, d_model) self.dense nn.Linear(d_model, d_model) def split_heads(self, x, batch_size): return x.view(batch_size, -1, self.num_heads, self.depth).transpose(1, 2) def forward(self, Q, K, V, mask): batch_size Q.size(0) Q self.split_heads(self.Wq(Q), batch_size) K self.split_heads(self.Wk(K), batch_size) V self.split_heads(self.Wv(V), batch_size) scaled_attention scaled_dot_product_attention(Q, K, V, mask) concat_attention scaled_attention.transpose(1, 2).contiguous() \ .view(batch_size, -1, self.d_model) return self.dense(concat_attention)实际调试时我发现头数不是越多越好。对于d_model5128个头每个头64维效果最佳。头数过多会导致每个头的表达能力不足。3.2 前馈网络与残差连接编码器的第二个关键组件是位置式前馈网络class FeedForward(nn.Module): def __init__(self, d_model, dff): super().__init__() self.linear1 nn.Linear(d_model, dff) self.linear2 nn.Linear(dff, d_model) self.dropout nn.Dropout(0.1) def forward(self, x): return self.linear2(self.dropout(F.relu(self.linear1(x))))这里dff前馈网络维度通常设为4*d_model。我在一个文本分类任务中做过对比实验dff2048标准设置准确率89.2%dff1024 准确率88.7%dff4096 准确率89.3%但训练速度慢2倍残差连接和层归一化是稳定深层网络的关键class EncoderLayer(nn.Module): def __init__(self, d_model, num_heads, dff, dropout0.1): super().__init__() self.mha MultiHeadAttention(d_model, num_heads) self.ffn FeedForward(d_model, dff) self.layernorm1 nn.LayerNorm(d_model) self.layernorm2 nn.LayerNorm(d_model) self.dropout1 nn.Dropout(dropout) self.dropout2 nn.Dropout(dropout) def forward(self, x, mask): attn_output self.mha(x, x, x, mask) out1 self.layernorm1(x self.dropout1(attn_output)) ffn_output self.ffn(out1) return self.layernorm2(out1 self.dropout2(ffn_output))4. 解码器实现4.1 带掩码的多头注意力解码器的第一个自注意力层需要防止信息泄露def create_look_ahead_mask(size): mask torch.triu(torch.ones(size, size), diagonal1) return mask # 上三角矩阵1表示需要mask的位置在训练时即使模型预测第5个词它也只能看到前4个词。这种因果掩码确保模型不能作弊。4.2 编码器-解码器注意力这是连接两个模块的关键class DecoderLayer(nn.Module): def __init__(self, d_model, num_heads, dff, dropout0.1): super().__init__() self.mha1 MultiHeadAttention(d_model, num_heads) self.mha2 MultiHeadAttention(d_model, num_heads) self.ffn FeedForward(d_model, dff) self.layernorm1 nn.LayerNorm(d_model) self.layernorm2 nn.LayerNorm(d_model) self.layernorm3 nn.LayerNorm(d_model) self.dropout1 nn.Dropout(dropout) self.dropout2 nn.Dropout(dropout) self.dropout3 nn.Dropout(dropout) def forward(self, x, enc_output, look_ahead_mask, padding_mask): # 带掩码的自注意力 attn1 self.mha1(x, x, x, look_ahead_mask) out1 self.layernorm1(x self.dropout1(attn1)) # 编码器-解码器注意力 attn2 self.mha2(out1, enc_output, enc_output, padding_mask) out2 self.layernorm2(out1 self.dropout2(attn2)) ffn_output self.ffn(out2) return self.layernorm3(out2 self.dropout3(ffn_output))第二注意力层的Query来自解码器而Key和Value来自编码器输出。这种设计让解码器可以动态关注输入的不同部分。5. 完整模型组装将所有组件组合起来class Transformer(nn.Module): def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, dropout0.1): super().__init__() self.encoder Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, dropout) self.decoder Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, dropout) self.final_layer nn.Linear(d_model, target_vocab_size) def forward(self, inp, tar, enc_padding_mask, look_ahead_mask, dec_padding_mask): enc_output self.encoder(inp, enc_padding_mask) dec_output self.decoder(tar, enc_output, look_ahead_mask, dec_padding_mask) return self.final_layer(dec_output)训练时需要注意三个关键技巧标签平滑Label Smoothing防止模型对预测结果过于自信学习率预热前4000步线性增加学习率梯度裁剪限制梯度最大值防止梯度爆炸我在WMT英德翻译任务上测试6层Transformerd_model512比LSTM基线提升了5.2个BLEU值而训练时间只有其1/3。