趋近智
Transformer 编码器层包括用于捕获上下文关系的多头自注意力机制、用于转换表示的位置维度前馈网络,以及用于稳定性和梯度流动的加和归一化步骤。这些部分将使用 Python 和 PyTorch 或 TensorFlow 等深度学习框架组装成一个能工作的编码器层。
本次实践练习假设您已经有了多头注意力机制的实现(可能是在前一章的实践部分完成的),并且了解您所选框架中的基本类定义和张量操作。我们将侧重于构建 EncoderLayer 模块本身。
在编写代码之前,让我们快速回顾一下单个编码器层内的子层:
在多头注意力输出和 FFN 输出之后,通常也会应用 Dropout 以防止训练期间的过拟合。
这是一个直接的组成部分。它由两次线性变换组成,中间夹着一个非线性激活函数。通常,第一个线性层会扩展维度,而第二个线性层会将其压缩回原始的模型维度 (dmodel)。常见的扩展因子是 4。
我们用类似 PyTorch 的语法来表示它:
import torch
import torch.nn as nn
class PositionWiseFeedForward(nn.Module):
def __init__(self, d_model, d_ff, dropout=0.1):
"""
初始化位置维度前馈网络。
参数:
d_model (int):输入和输出的维度。
d_ff (int):内部层的维度。
dropout (float):Dropout 概率。
"""
super().__init__()
self.linear_1 = nn.Linear(d_model, d_ff)
self.activation = nn.ReLU() # 或 nn.GELU()
self.dropout = nn.Dropout(dropout)
self.linear_2 = nn.Linear(d_ff, d_model)
def forward(self, x):
"""
FFN 的前向传播。
参数:
x (torch.Tensor):形状为 (batch_size, seq_len, d_model) 的输入张量。
返回:
torch.Tensor:形状为 (batch_size, seq_len, d_model) 的输出张量。
"""
x = self.linear_1(x)
x = self.activation(x)
x = self.dropout(x) # Dropout 通常在激活后应用
x = self.linear_2(x)
return x
这个 PositionWiseFeedForward 模块接收一个形状为 (batch_size, seq_len, d_model) 的张量,并返回一个相同形状的张量,它在每个序列位置独立应用了变换。
现在,我们将多头自注意力机制(我们假设它作为一个名为 MultiHeadAttention 的模块是可用的)、上面定义的位置维度前馈网络、层归一化、残差连接和 Dropout 组合成一个 EncoderLayer。
class EncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
"""
初始化一个 Transformer 编码器层。
参数:
d_model (int):输入/输出特征(嵌入)的维度。
num_heads (int):注意力头的数量。
d_ff (int):前馈网络的内部维度。
dropout (float):Dropout 概率。
"""
super().__init__()
self.self_attn = MultiHeadAttention(d_model, num_heads) # 假定已实现
self.feed_forward = PositionWiseFeedForward(d_model, d_ff, dropout)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
def forward(self, x, mask):
"""
编码器层的前向传播。
参数:
x (torch.Tensor):形状为 (batch_size, seq_len, d_model) 的输入张量。
mask (torch.Tensor):注意力掩码(可选,用于填充)。
形状通常为 (batch_size, 1, 1, seq_len) 或类似
取决于 MultiHeadAttention 的实现。
返回:
torch.Tensor:形状为 (batch_size, seq_len, d_model) 的输出张量。
"""
# 1. 多头自注意力机制 + 加和归一化
attn_output = self.self_attn(query=x, key=x, value=x, mask=mask)
# 残差连接 1:将输入 'x' 添加到注意力输出
# 在添加之前对注意力输出应用 Dropout
x = self.norm1(x + self.dropout1(attn_output))
# 2. 位置维度前馈网络 + 加和归一化
ff_output = self.feed_forward(x)
# 残差连接 2:将输入到 FFN 的数据 ('x') 添加到 FFN 输出
# 在添加之前对 FFN 输出应用 Dropout
x = self.norm2(x + self.dropout2(ff_output))
return x
在这个 EncoderLayer 中,输入 x 首先经过多头自注意力机制。注意力机制的输出接着经过 Dropout (dropout1),然后加回到原始输入 x(第一个残差连接),其和进行归一化 (norm1)。这个归一化后的输出随后作为位置维度前馈网络的输入。FFN 的输出经过 Dropout (dropout2),然后添加到进入 FFN 的输入(第二个残差连接),这个和进行归一化 (norm2),从而生成编码器层的最终输出。
下面的图表描绘了我们刚刚定义的 EncoderLayer 内的数据流。
单个 Transformer 编码器层内部的数据流,描绘了多头注意力机制和前馈子层,每个子层都接着 Dropout、残差连接(相加)和层归一化。
这种结构被多次重复(在原始 Transformer 论文中通常重复 6 次或 12 次),形成了完整的编码器堆栈。每个层都将前一层的输出作为其输入,使得模型能够构建输入序列越来越复杂的表示。
您现在有了一个 Transformer 核心组成部分的实践实现。在下一章中,我们将介绍如何有效地训练这些模型。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造