《动手学深度学习》Transformer 架构从零实现指南:自注意力、编码器-解码器与序列到序列训练实战
发布时间:2026/9/30 6:38:35来源:尧图网络
人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载导读Transformer 是首个完全摒弃卷积与循环结构、仅基于注意力机制的深度架构它同时具备了并行计算与最短的最大路径长度两大优势是现代大语言模型、视觉与语音模型的基石。本文以 chapter_attention-mechanisms/transformer_origin.md 为骨架结合仓库 d2l/torch.py 与 d2l/mxnet.py 的源码实现逐层讲解 Transformer 的编码器、解码器、位置前馈网络、残差连接与层规范化等核心组件并给出在英法机器翻译任务上的完整可运行训练与推理流程。读完本文你将能够独立使用 PyTorch 或 MXNet 从零搭建一个可训练的 Transformer理解掩码自注意力的自回归机制并学会用 BLEU 指标与注意力热图评估翻译质量。为什么是自注意力从 CNN、RNN 到 Transformer在对比 CNN、RNN 与自注意力self-attention时自注意力在两个方面表现突出并行计算自注意力对序列中所有位置同时计算不像 RNN 那样必须按时间步串行推进最短最大路径长度任意两个位置之间的信息传递只需一步注意力交互而 RNN 需要 O(n) 步、CNN 需要随感受野增长而增加层数。早期的自注意力模型如 Cheng et al. 2016、Lin et al. 2017、Paulus et al. 2017仍依赖 RNN 来获得输入表示而 TransformerVaswani et al. 2017则完全基于注意力机制不含任何卷积层或循环层。它最初为文本数据的序列到序列学习而设计后来被广泛用于语言、视觉、语音和强化学习等现代深度学习应用。模型总览编码器-解码器架构Transformer 是编码器-解码器架构的一个实例其整体结构如下与使用 Bahdanau 注意力的序列到序列模型不同Transformer 在输入序列源和输出序列目标的词嵌入上先叠加位置编码positional encoding再送入堆叠了基于自注意力的模块的编码器与解码器。编码器的高层结构Transformer 编码器由多个相同的层堆叠而成每一层包含两个子层sublayer多头自注意力池化multi-head self-attention pooling其查询、键、值均来自上一层编码器的输出位置前馈网络positionwise feed-forward network。受到 ResNet 设计的启发两个子层周围都使用了残差连接。对于序列中任意位置处的输入 $x \in \mathbb{R}^d$要求 $\mathrm{sublayer}(x) \in \mathbb{R}^d$以保证残差连接 $x \mathrm{sublayer}(x) \in \mathbb{R}^d$ 可行。残差连接的结果立即跟随层规范化layer normalization。因此Transformer 编码器为输入序列的每个位置输出一个 $d$ 维向量表示。解码器的高层结构Transformer 解码器同样由多个带有残差连接与层规范化的相同层堆叠而成。除编码器中的两个子层外解码器在两者之间插入了第三个子层——编码器-解码器注意力在该子层中查询来自解码器上一层的输出键和值来自 Transformer 编码器的输出在解码器自注意力中查询、键、值都来自解码器上一层的输出但解码器中的每个位置只允许关注到该位置之前含该位置的解码器位置。这种掩码masked注意力保留了**自回归auto-regressive**性质确保预测只依赖于已经生成的输出词元。前置组件多头注意力与位置编码文档假设读者已经掌握多头上缩放点积注意力见 chapter_attention-mechanisms/multihead-attention.md和位置编码见 chapter_attention-mechanisms/self-attention-and-positional-encoding.md后续将实现 Transformer 的其余部分。在仓库 d2l/torch.py 中MultiHeadAttention的实现要点如下通过W_q、W_k、W_v三个线性层将查询、键、值投影到num_hiddens维transpose_qkvd2l/torch.py将形状变换为(batch_size*num_heads, 查询个数, num_hiddens/num_heads)使多个头可以并行执行缩放点积注意力内部封装d2l.DotProductAttention完成缩放点积valid_lens会通过torch.repeat_interleave沿轴 0 复制num_heads次以匹配多头并行后的形状最后用W_o将拼接后的多头输出投影回num_hiddens维。PositionalEncodingd2l/torch.py使用正弦/余弦函数生成与位置相关的编码矩阵P在forward中直接与嵌入相加X X P[:, :X.shape[1]]并施加dropout。位置编码取值在 -1 到 1 之间因此后文会将词嵌入乘以嵌入维度的平方根进行缩放再相加。位置前馈网络Positionwise FFN位置前馈网络使用同一个 MLP对序列所有位置的表示进行变换因此称为positionwise按位置。输入X的形状为(batch size, 序列长度/时间步数, 特征维度)经过两层 MLP 后输出形状为(batch size, 时间步数, ffn_num_outputs)。PyTorch 实现d2l/torch.pyclass PositionWiseFFN(nn.Module): def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs, **kwargs): super(PositionWiseFFN, self).__init__(**kwargs) self.dense1 nn.Linear(ffn_num_input, ffn_num_hiddens) self.relu nn.ReLU() self.dense2 nn.Linear(ffn_num_hiddens, ffn_num_outputs) def forward(self, X): return self.dense2(self.relu(self.dense1(X)))MXNet 版本d2l/mxnet.py使用nn.Dense(ffn_num_hiddens, flattenFalse, activationrelu)与nn.Dense(ffn_num_outputs, flattenFalse)其中flattenFalse保证仅在最后一个轴上做线性变换。示例验证当X形状为(2, 3, 4)、输入维为 4、隐藏维为 4、输出维为 8 时输出张量内层维度变为 8。由于所有位置使用同一个 MLP当各位置输入相同时其输出也完全相同。残差连接与层规范化Add Norm层规范化与批规范化的区别在批规范化batch normalization中统计量跨小批量内的样本计算而层规范化则跨特征维度计算。尽管批规范化在计算机视觉中应用广泛但在自然语言处理任务中输入常常是变长序列层规范化通常经验上更有效。对比示例PyTorchln nn.LayerNorm(2) bn nn.BatchNorm1d(2) X d2l.tensor([[1, 2], [2, 3]], dtypetorch.float32) print(layer norm:, ln(X), \nbatch norm:, bn(X))AddNorm 实现AddNorm类封装了残差连接 层规范化并施加 Dropout 进行正则化d2l/torch.pyclass AddNorm(nn.Module): def __init__(self, normalized_shape, dropout, **kwargs): super(AddNorm, self).__init__(**kwargs) self.dropout nn.Dropout(dropout) self.ln nn.LayerNorm(normalized_shape) def forward(self, X, Y): return self.ln(self.dropout(Y) X)残差连接要求两个输入形状一致这样加法操作后输出张量形状保持不变。测试中两个(2, 3, 4)的全 1 张量经AddNorm([3, 4], 0.5)处理后输出形状仍为(2, 3, 4)。编码器实现单层 EncoderBlockEncoderBlock包含两个子层多头自注意力与位置前馈网络两者周围均采用残差连接 层规范化d2l/torch.pyclass EncoderBlock(nn.Module): def __init__(self, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, use_biasFalse, **kwargs): super(EncoderBlock, self).__init__(**kwargs) self.attention d2l.MultiHeadAttention( key_size, query_size, value_size, num_hiddens, num_heads, dropout, use_bias) self.addnorm1 AddNorm(norm_shape, dropout) self.ffn PositionWiseFFN( ffn_num_input, ffn_num_hiddens, num_hiddens) self.addnorm2 AddNorm(norm_shape, dropout) def forward(self, X, valid_lens): Y self.addnorm1(X, self.attention(X, X, X, valid_lens)) return self.addnorm2(Y, self.ffn(Y))可以看到编码器中的任意一层都不改变输入形状。例如输入X形状(2, 100, 24)、valid_lens [3, 2]EncoderBlock(24, 24, 24, 24, [100, 24], 24, 48, 8, 0.5)的输出形状仍为(2, 100, 24)。完整 TransformerEncoderTransformerEncoderd2l/torch.py堆叠num_layers个EncoderBlock。关键细节使用可学习的nn.Embedding(vocab_size, num_hiddens)将词元索引映射为嵌入向量由于固定位置编码的取值在 -1 到 1 之间嵌入值会乘以嵌入维度的平方根math.sqrt(self.num_hiddens)进行重新缩放再与位置编码相加前向过程中记录每一层的注意力权重blk.attention.attention.attention_weights供后续可视化使用。MXNet 版本见 d2l/mxnet.py。示例TransformerEncoder(200, 24, 24, 24, 24, [100, 24], 24, 48, 8, 2, 0.5)词表 200、2 层编码器在输入(2, 100)时输出形状为(2, 100, 24)即(batch size, 时间步数, num_hiddens)。解码器实现单层 DecoderBlockDecoderBlock包含三个子层解码器自注意力、编码器-解码器注意力、位置前馈网络每个子层均采用残差连接与层规范化。关键机制——掩码自注意力与状态管理训练时输出序列所有位置的词元都已知可同时处理但预测时输出序列逐词元生成任意解码时间步只能使用已生成的词元。为保持解码器的自回归特性其掩码自注意力指定dec_valid_lens使任何查询只能关注解码器中不超过其自身位置的所有位置。在forward中if self.training: batch_size, num_steps, _ X.shape # dec_valid_lens 形状为 (batch_size, num_steps)每行是 [1, 2, ..., num_steps] dec_valid_lens torch.arange( 1, num_steps 1, deviceX.device).repeat(batch_size, 1) else: dec_valid_lens None同时state[2][self.i]在训练时初始化为None预测时则保存该层截至当前时间步已解码输出的表示并通过torch.cat与新输入拼接作为自注意力的键值对实现逐词元自回归解码。三个子层的数据流为自注意力X2 attention1(X, key_values, key_values, dec_valid_lens)残差后得Y编码器-解码器注意力Y2 attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)残差后得Z位置前馈网络addnorm3(Z, ffn(Z))。为了便于编码器-解码器注意力中的缩放点积运算以及残差连接中的加法运算解码器的特征维度num_hiddens与编码器保持一致。完整 TransformerDecoderTransformerDecoder继承d2l.AttentionDecoder堆叠num_layers个DecoderBlock末尾使用全连接层nn.Linear(num_hiddens, vocab_size)计算所有vocab_size个可能输出词元的预测。其init_state返回[enc_outputs, enc_valid_lens, [None] * num_layers]前向过程中同时保存解码器自注意力权重与编码器-解码器注意力权重供可视化。预测时每步取Y.argmax(dim2)作为下一时间步输入遇到eos词元即停止生成见 d2l/torch.py 的predict_seq2seq。训练英法机器翻译实战按 Transformer 架构实例化编码器-解码器模型超参数设置为编码器与解码器各2 层、4 头注意力。超参数与数据加载num_hiddens, num_layers, dropout, batch_size, num_steps 32, 2, 0.1, 64, 10 lr, num_epochs, device 0.005, 200, d2l.try_gpu() ffn_num_input, ffn_num_hiddens, num_heads 32, 64, 4 key_size, query_size, value_size 32, 32, 32 norm_shape [32] train_iter, src_vocab, tgt_vocab d2l.load_data_nmt(batch_size, num_steps)num_hiddens32隐藏单元数也是编码器/解码器的特征维度ffn_num_hiddens64位置前馈网络隐藏层宽度num_heads4注意力头数每个头维度为num_hiddens/num_heads 8num_steps10序列截断长度batch_size64lr0.005、num_epochs200、dropout0.1d2l.load_data_nmt加载英法机器翻译数据集其默认样本数num_examples600见 d2l/torch.py。组装与训练encoder TransformerEncoder( len(src_vocab), key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout) decoder TransformerDecoder( len(tgt_vocab), key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, num_layers, dropout) net d2l.EncoderDecoder(encoder, decoder) d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)d2l.EncoderDecoderd2l/torch.py是编码器-解码器基类先运行编码器得到enc_outputs再调用解码器init_state初始化状态最后执行解码器前向。d2l.train_seq2seqd2l/torch.py的训练要点对线性层与 GRU 参数应用Xavier 初始化使用Adam 优化器lr0.005损失函数为MaskedSoftmaxCELossd2l/torch.py通过sequence_mask屏蔽填充词元只对有效词元计算交叉熵采用强制教学teacher forcing解码器输入为bos拼接真实目标序列去掉最后一个词元每轮按词元数归一化损失并做梯度裁剪d2l.grad_clipping(net, 1)。推理与 BLEU 评估训练完成后将若干英文句子翻译为法语并计算 BLEU 分数engs [go ., i lost ., he\s calm ., i\m home .] fras [va !, j\ai perdu ., il est calme ., je suis chez moi .] for eng, fra in zip(engs, fras): translation, dec_attention_weight_seq d2l.predict_seq2seq( net, eng, src_vocab, tgt_vocab, num_steps, device, True) print(f{eng} {translation}, , fbleu {d2l.bleu(translation, fra, k2):.3f})d2l.bleud2l/torch.py实现 BLEU 指标对 1-gram 到 k-gram此处 k2计算修正后的精度并乘以长度惩罚项exp(min(0, 1 - len_label/len_pred))。predict_seq2seq在推理时将模型置为评估模式net.eval()保存每步的解码注意力权重序列供可视化。注意力可视化热图解读编码器自注意力可视化翻译最后一句英文时编码器的自注意力权重。编码器自注意力权重的形状为(编码器层数, 注意力头数, num_steps 查询数, num_steps 键值对数)enc_attention_weights d2l.reshape( d2l.concat(net.encoder.attention_weights, 0), (num_layers, num_heads, -1, num_steps)) d2l.show_heatmaps( enc_attention_weights.cpu(), xlabelKey positions, ylabelQuery positions, titles[Head %d % i for i in range(1, 5)], figsize(7, 3.5))由于编码器自注意力的查询与键来自同一输入序列且通过valid_lens指定输入序列的有效长度任何查询都不会关注填充padding词元的位置。d2l.show_heatmapsd2l/torch.py按行展示 2 层 × 4 头的注意力热图每个头在查询、键、值的独立表示子空间上分别关注。解码器自注意力与编码器-解码器注意力解码器注意力的可视化需要更多数据处理将掩码注意力权重中的NaN用 0 填充利用pd.DataFrame(...).fillna(0.0)并重塑为(-1, 2, num_layers, num_heads, num_steps)其中第 1 维的 2 分别对应解码器自注意力与编码器-解码器注意力。dec_attention_weights_filled d2l.tensor( pd.DataFrame(dec_attention_weights_2d).fillna(0.0).values) dec_attention_weights d2l.reshape(dec_attention_weights_filled, (-1, 2, num_layers, num_heads, num_steps)) dec_self_attention_weights, dec_inter_attention_weights \ dec_attention_weights.permute(1, 2, 3, 0, 4)两个热图的解读要点解码器自注意力热图由于自回归特性任何查询都不会关注查询位置之后的键值对观察下三角结构切片[:, :, :, :len(translation.split()) 1]加 1 以包含序列开始bos词元编码器-解码器注意力热图与编码器自注意力类似通过输入序列的valid_lens输出序列的任何查询都不会关注输入序列中的填充词元。模型要点小结Transformer 是编码器-解码器架构的一个实例实践中编码器或解码器也可单独用于不同的深度学习任务Transformer 使用多头自注意力表示输入序列与输出序列解码器通过掩码版本保持自回归性质残差连接与层规范化对训练极深模型至关重要位置前馈网络用同一个 MLP 变换序列所有位置的表示。延伸思考练习题与进阶方向围绕 Transformer 的设计与局限可以进一步实验与思考更深模型训练更深的 Transformer观察训练速度与翻译性能如何变化注意力变体将缩放点积注意力替换为加性注意力是否合理原因是什么语言建模对于语言建模应使用 Transformer 编码器、解码器还是两者并用如何设计长序列挑战输入序列很长时 Transformer 面临哪些挑战为什么效率改进如何提升 Transformer 的计算与内存效率可参考稀疏注意力等方向的研究视觉应用如何在不使用 CNN 的情况下设计基于 Transformer 的图像分类模型如 Vision Transformer 的思路。进一步阅读多头上缩放点积注意力实现chapter_attention-mechanisms/multihead-attention.md位置编码详解chapter_attention-mechanisms/self-attention-and-positional-encoding.md编码器-解码器基类与序列到序列训练chapter_recurrent-modern/encoder-decoder.md、chapter_recurrent-modern/seq2seq.md库内完整实现d2l/torch.py 与 d2l/mxnet.py 中MultiHeadAttention、PositionalEncoding、PositionWiseFFN、AddNorm、EncoderBlock、TransformerEncoder等类的源码与注释赞分享人工智能深度学习机器学习教程【免费下载链接】d2l-zh《动手学深度学习》面向中文读者、能运行、可讨论。中英文版被70多个国家的500多所大学用于教学。项目地址https://gitcode.com/GitHub_Trending/d2/d2l-zh点击查看免费下载相关推荐《动手学深度学习》Bahdanau 注意力在 RNN 编解码器中对齐源序列的经典实现《动手学深度学习》Bahdanau 注意力在 RNN 编解码器中对齐源序列的经典实现 Bahdanau 注意力是首个将学习对齐learn to align人工智能深度学习机器学习教程编码器-解码器架构序列到序列学习的核心技术原理编码器 解码器架构序列到序列学习的核心技术原理 在当今人工智能领域编码器 解码器架构已成为序列到序列学习的核心技术广泛应用于机器翻译、文本摘要、对话系统等教程从零实现 Transformer 架构D2L 多框架PyTorch / TensorFlow / JAX / MXNet注意力编码器-解码器实战指南从零实现 Transformer 架构D2L 多框架PyTorch / TensorFlow / JAX / MXNet注意力编码器 解码器实战指南 Tr文档教程人工智能深度学习NLP计算机视觉强化学习上一篇mp-html 图片加载闪烁问题的分析与解决方案下一篇YimMenu中游戏通缉等级系统的技术分析与改进建议创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网