打造完整 Transformer 编码器:逐步实现高效深度学习模块
11. encoder
打造完整 Transformer 编码器:逐步实现高效深度学习模块
在深入理解了编码器块的核心结构后,下一步就是实现一个完整的 Transformer 编码器。该编码器将输入序列转换为高级语义向量,并为后续的解码或其他任务模块提供高质量的特征表示。今天我们将详细解析编码器的每一部分,并附上代码示例,助你轻松掌握 Transformer 的编码器构建。
Transformer 编码器的主要组成部分
一个完整的 Transformer 编码器通常包含以下几个步骤:
- 输入嵌入层(Embedding Layer):将输入的词索引转换为高维向量表示。
- 位置编码(Positional Encoding):为每个词加上位置信息,使模型能够捕捉词序关系。
- 多个编码器块(Encoder Blocks):编码器块堆叠以提取深层次特征,通常包括 6-12 层,视任务而定。
- 输出:编码器最终输出的特征向量,将传递给解码器或下游任务模块。
实现完整的 Transformer 编码器类
以下代码实现了一个 TransformerEncoder
类,其中包含输入嵌入、位置编码、多个编码器块和 Dropout 层:
import torch
import torch.nn as nn
import math
class PositionalEncoding(nn.Module):
def __init__(self, embed_size, max_length=100):
super(PositionalEncoding, self).__init__()
self.encoding = torch.zeros(max_length, embed_size)
position = torch.arange(0, max_length, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, embed_size, 2).float() * (-math.log(10000.0) / embed_size))
self.encoding[:, 0::2] = torch.sin(position * div_term)
self.encoding[:, 1::2] = torch.cos(position * div_term)
self.encoding = self.encoding.unsqueeze(0) # Shape: (1, max_length, embed_size)
def forward(self, x):
return x + self.encoding[:, :x.size(1), :].to(x.device)
class TransformerEncoder(nn.Module):
def __init__(self, src_vocab_size, embed_size, num_layers, heads, forward_expansion, dropout, max_length):
super(TransformerEncoder, self).__init__()
# 输入嵌入层
self.word_embedding = nn.Embedding(src_vocab_size, embed_size)
self.position_encoding = PositionalEncoding(embed_size, max_length)
# 堆叠编码器层
self.layers = nn.ModuleList(
[EncoderBlock(embed_size, heads, forward_expansion, dropout) for _ in range(num_layers)]
)
# Dropout 层
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask):
# 1. 添加词嵌入和位置编码
out = self.word_embedding(x)
out = self.position_encoding(out)
out = self.dropout(out)
# 2. 逐层通过编码器块
for layer in self.layers:
out = layer(out, mask)
return out
代码解析:逐步了解 Transformer 编码器
1. 输入嵌入和位置编码
self.word_embedding = nn.Embedding(src_vocab_size, embed_size)
self.position_encoding = PositionalEncoding(embed_size, max_length)
word_embedding
:将输入的词(以整数索引表示)转换成嵌入向量。position_encoding
:为每个词嵌入向量加上位置编码,帮助模型识别词的顺序。
2. 堆叠多个编码器块
self.layers = nn.ModuleList(
[EncoderBlock(embed_size, heads, forward_expansion, dropout) for _ in range(num_layers)]
)
- 使用
ModuleList
创建多个EncoderBlock
。每个EncoderBlock
包含多头自注意力层、前馈神经网络层、残差连接和正则化。 num_layers
控制编码器块的数量。通常的设置是 6 层,但可以根据任务需求进行调整。
3. Dropout 层
self.dropout = nn.Dropout(dropout)
- 使用 Dropout 增强泛化能力,通过随机丢弃一些神经元的输出来防止过拟合。
前向传播过程解析
-
词嵌入和位置编码
out = self.word_embedding(x) out = self.position_encoding(out) out = self.dropout(out)
- 将输入序列转换为嵌入向量。
- 添加位置编码,保留输入序列的顺序信息。
- 使用 Dropout 防止过拟合。
-
通过编码器块层层提取特征
for layer in self.layers: out = layer(out, mask)
- 将嵌入后的输出依次传递给每一个编码器块。
mask
参数用于在注意力机制中屏蔽掉填充符(padding)等不相关部分,避免模型关注无关信息。
测试 Transformer 编码器
为了确保我们的编码器可以正常工作,编写一些简单的测试代码:
# 设置测试参数
src_vocab_size = 10000 # 假设词汇表大小
embed_size = 512
num_layers = 6
heads = 8
forward_expansion = 4
dropout = 0.1
max_length = 100
seq_length = 20
batch_size = 2
# 输入序列
x = torch.randint(0, src_vocab_size, (batch_size, seq_length)) # (batch_size, seq_length)
mask = None # 暂不使用 mask
# 实例化 Transformer 编码器并进行前向传播
encoder = TransformerEncoder(src_vocab_size, embed_size, num_layers, heads, forward_expansion, dropout, max_length)
out = encoder(x, mask)
print("编码器的输出形状:", out.shape) # 预期输出: (batch_size, seq_length, embed_size)
- 输出形状:
(batch_size, seq_length, embed_size)
,例如(2, 20, 512)
。
接下来的步骤
- 实现解码器块(Decoder Block):
- 解码器块和编码器类似,但会增加编码器-解码器注意力层,用于从编码器的输出中提取信息。
- 实现完整的解码器(Decoder):
- 将多个解码器块堆叠,构成完整的解码器结构。
- 组装完整的 Transformer 模型:
- 结合编码器和解码器,实现完整的 Transformer 模型。
通过这篇文章,我们构建了一个完整的 Transformer 编码器,并了解了编码器的每个模块如何协同工作以提取输入序列的深层次特征。希望这些知识帮助你在 Transformer 的实现和理解上更进一步!如果你对解码器或 Transformer 其他部分感兴趣,欢迎继续阅读或留言讨论!