正文
目录会跟随阅读位置移动。
阅读进度

Encoderlayerclass Encoderlayer(nn.Module):
def __init__(self,d_model,n_heads,d_ff,dropout=0.1):
super().__init__()
#多头注意力机制
self.attn = MultiHeadAttention(d_model,n_heads,dropout)
#前馈神经网络
self.ffn = FeedForward(d_model,d_ff,dropout)
def forward(self,src,src_mask,mask=None):
# src 输入序列向量,形状 batch ,seq_len,d_model
# src_mask 屏蔽padding的位置,避免模型关注无效的token,在decoder中 masker用来防止看到未来的词,以提高模型思考能力
# Q,K,V +src 对输入序列本身进行自注意力计算
out,attn = self.attn(src,src,src,mask)
# 经过 MoE 前馈层,让门控网络选择专家参与计算
out = self.ffn(out)
return out
作用:
表示编码器中的一层。
由“自注意力 + 前馈网络”组成。
它做了什么:
src 先经过自注意力:
out, attn = self.attn(src, src, src, mask)
out = self.ffn(out)
这个模块的核心意义:
DecoderLayerclass DecoderLayer(nn.Module):
def __init__(self,d_model,n_heads,d_ff,dropout=0.1):
super().__init__()
# 先加上带有掩码的掩码自注意力机制,让模型逐渐知道信息的关系
# mask多头自注意力机制,输入tgt
self.attn = MultiHeadAttention(d_model, n_heads, dropout)
# 交叉注意力,和encoder做交互
# 输入 Q=当前注意力的输出,K和V来自编码器的memory(原序列上下文信息)
self.ctoss_attn = MultiHeadAttention(d_model, n_heads, dropout)
# 前馈神经网络,提升模型的表达能力
self.ffn = FeedForward(d_model, d_ff, dropout)
def forward(self,tgt,memory,tgt_mask = None,memory_mask = None):
# tgt:目标序列 memory:编码器输出
# tgt——mask:屏蔽未来的token,memory_mask:PAD做掩码
out, attn = self.attn(tgt, tgt, tgt, tgt_mask)
out, _ = self.ctoss_attn(out, memory, memory, memory_mask)
out = self.ffn(out)
return out
作用:
表示解码器中的一层。
由“带 mask 的自注意力 + 交叉注意力 + 前馈网络”组成。
它做了什么:
out, attn = self.attn(tgt, tgt, tgt, tgt_mask)
memory 做交叉注意力:
out, _ = self.ctoss_attn(out, memory, memory, memory_mask)
out = self.ffn(out)
这个模块的核心意义:
PositionalEncoding#创建位置编码
class PositionalEncoding(nn.Module):
def __init__(self,d_model,max_len):
super().__init__()
# d_model 每个词向量的维度 :max_len:句子的最大长度
# 初始化位置编码矩阵 形状为max_len,d_model
pe = torch.zeros(max_len,d_model)
# 定义记录每一个token项链改动位置索引 从0到max_len-1
# [max_1,1] 方便和缩放因子进行相处
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
# div_term 每个维度得到缩放因子,torch.arange(0, d_model, 2):生成偶数索引 0,2,4,对应公式就是2i
# torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model) = (2i/d_model)*-ln(10000.0)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
# 对偶数进行sin(pos * 缩放因子)得到位置编码值
pe[:, 0::2] = torch.sin(position * div_term)
# 对奇数进行cos(pos * 缩放因子)得到位置编码值
pe[:, 1::2] = torch.cos(position * div_term)
#增加batch维度.1,max_len,d_model,方便后续与输入embedding进行相加
pe = pe.unsqueeze(0)
# 注册为buffer,把位置编码pe加载到模型里面,但是不参与训练
self.register_buffer('pe', pe)
def forward(self, x):
# X: 输入的embedding 形状batch,seq_len, d_model
seq_len = x.size(1)
# 每个token的embedding加上对应的位置编码
# self.pe[;,;seq_len,;]:取出前seq_len个位置, 形状会变成1,seq_len,d_model可以和输入x对齐
return self.pe[:, :seq_len, :] + x
作用:
给 embedding 加上位置信息。
因为注意力本身不带顺序感,所以需要额外注入“第几个 token”。
它做了什么:
用 sin 和 cos 生成不同位置的编码向量。
再把位置编码加到 embedding 上。
这个模块的核心意义:
Encoderclass Encoder(nn.Module):
def __init__(self,vocab_size,d_model,n_heads,num_layer,d_ff,dropout=0.1,max_len=5000):
super().__init__()
# 词嵌入层,vocab_size:词表大小,包含不同的token总数
# 将输入的token ID(对原始文本分词得到词表, 不同词对应不同ID)准换成连续向量, 维度为d_model
self.embedding = nn.Embedding(vocab_size,d_model)
# 位置编码加入序列中的token的位置信息
self.pos_encoding = PositionalEncoding(d_model,max_len)
# 构建编码器的堆叠结构
self.layers = nn.ModuleList([
Encoderlayer(d_model,n_heads,d_ff,dropout)for _ in range(num_layer)
])
def forward(self,src,src_mask=None):
# 将输入 token ID 转换成 embedding 向量
# 输出结构 shape batch,seq_len,d_model
# 加上 sqrt(d_model),进行缩放,让后续注意力计算更稳定
out = self.embedding(src) * math.sqrt(self.embedding.embedding_dim)
# 经过位置编码
out = self.pos_encoding(out)
# 逐层经过encoderlayer
for layer in self.layers:
out = layer(out,src_mask,src_mask)
return out
作用:
它做了什么:
token id 先经过 embedding。
乘以 sqrt(d_model) 做缩放。
加上位置编码。
逐层通过多个 Encoderlayer。
输出:
memory,供解码器使用。Decoderclass Decoder(nn.Module):
def __init__(self,vocab_size,d_model,n_heads,num_layer,d_ff,dropout=0.1,max_len=5000):
super().__init__()
# 将目标序列的 token id 转换为向量 维度为 d_model
self.embedding = nn.Embedding(vocab_size,d_model)
# 经过位置编码
self.pos_encoding = PositionalEncoding(d_model,max_len)
# 定义解码器列表
self.layers = nn.ModuleList([
DecoderLayer(d_model,n_heads,d_ff,dropout)for _ in range(num_layer)
])
# 最后一个全连接输出映射回原词汇表的大小, 从而得到每个token推测分布(最后的softmax自动有了所以不用加)
self.fc = nn.Linear(d_model,vocab_size)
def forward(self,tgt,memory,tgt_mask = None,memory_mask = None):
# tgt 目标序列 解码器的输入 memory编码器的输出 也叫上下文信息
# tgt_mask 目标序列的mask 用来屏蔽未来的位置 memory_mask:用来屏蔽pad
out = self.embedding(tgt) * math.sqrt(self.embedding.embedding_dim)
#添加位置编码
out = self.pos_encoding(out)
#逐层经过 decoderlayer
for layer in self.layers:
out = layer(out,memory,tgt_mask,memory_mask)
return self.fc(out)
作用:
它做了什么:
tgt 先经过 embedding。
加上位置编码。
通过多个 DecoderLayer。
最后通过线性层 fc 投影到词表大小。
输出形状通常是:
(batch_size, seq_len, vocab_size)
Transformerclass Transformer(nn.Module):
def __init__(self,
src_size, # 原语言词表大小
tgt_size, #目标语言词表大小
d_model=512, #embedding向量维度
n_heads=8, #多头注意力头数
num_encoder_layers=6, #编码器层数(循环次数)
num_decoder_layers=6, #解码器层数(循环次数)
d_ff=2048, #前馈神经网络隐藏维度
dropout=0.1,
max_len=5000):
super().__init__()
self.encoder = Encoder(
src_size,d_model,n_heads,num_encoder_layers,d_ff,dropout,max_len
)
self.decoder = Decoder(
tgt_size,d_model,n_heads,num_decoder_layers,d_ff,dropout,max_len
)
def forward(self,src,tgt,src_mask=None,tgt_mask=None,memory_mask=None):
memory = self.encoder(src,src_mask)
out = self.decoder(tgt,memory,tgt_mask,memory_mask)
return out
作用:
它做了什么:
src:
memory = self.encoder(src, src_mask)
tgt:
out = self.decoder(tgt, memory, tgt_mask, memory_mask)
这个模块的核心意义:
FeedForward作用:
对每个 token 的表示单独做非线性变换。
不负责 token 和 token 之间的信息交互,而是增强单个 token 的特征表达能力。
结构:
Linear(d_model -> d_ff)
ReLU
Linear(d_ff -> d_model)
Dropout
Residual + LayerNorm
这个模块的核心意义: