第一章:Transformer架构概述与准备工作
1.1 生成型语言模型的核心价值
生成型Transformer语言模型(TransformerLM)是当前自然语言处理领域的核心技术,其核心价值体现在三个维度:
- 上下文感知能力:通过自注意力机制实现任意位置token的全局关联
- 并行计算效率:相比RNN的序列计算模式,Transformer的矩阵运算可完全并行化
- 长程依赖建模:克服传统RNN的梯度消失问题,有效捕捉超过1000token的依赖关系
本教程将使用PyTorch框架从零实现完整的TransformerLM架构,整个过程包含:
- 文本预处理流水线
- 模型架构实现
- 训练策略设计
- 生成算法实现
- 模型评估方法
1.2 开发环境配置
推荐使用以下环境配置:
# 硬件要求
- GPU: NVIDIA RTX 3090 (24GB显存)
- RAM: 32GB以上
# 软件版本
Python 3.9.7
PyTorch 1.12.1+cu113
TorchText 0.13.1
Matplotlib 3.6.0
Numpy 1.23.3
# 安装命令
!pip install torch torchtext torchdata matplotlib numpy tqdm
1.3 Transformer架构全景解析
标准Transformer架构包含以下核心组件:
1.3.1 编码器-解码器结构对比
组件 | 编码器 | 解码器 |
---|---|---|
输入 | 源序列 | 目标序列(右移) |
注意力层 | 自注意力 | 掩码自注意力 + 交叉注意力 |
输出 | 上下文表征 | 下一个token的概率分布 |
层数 | 通常6层 | 通常6层 |
位置编码 | 绝对位置 | 绝对位置 |
1.4 文本预处理流程设计
完整的文本预处理包含以下关键步骤:
class TextProcessor:
def __init__(self, corpus, vocab_size=30000):
self.corpus = corpus
self.vocab_size = vocab_size
self.tokenizer = self._build_tokenizer()
self.vocab = self._build_vocab()
def _build_tokenizer(self):
# 实现BPE分词器
tokenizer = ...
return tokenizer
def _build_vocab(self):
# 构建词汇表
counter = Counter()
for text in self.corpus:
tokens = self.tokenizer.tokenize(text)
counter.update(tokens)
vocab = Vocabulary(counter, max_size=self.vocab_size)
return vocab
def numericalize(self, text):
tokens = self.tokenizer.tokenize(text)
return [self.vocab[token] for token in tokens]
1.5 数据加载器实现
使用PyTorch的DataLoader构建高效数据管道:
from torch.utils.data import Dataset, DataLoader
class TextDataset(Dataset):
def __init__(self, texts, processor, seq_length=512):
self.processor = processor
self.seq_length = seq_length
self.data = self._process_data(texts)
def _process_data(self, texts):
numericalized = []
for text in texts:
ids = self.processor.numericalize(text)
# 分割为固定长度序列
for i in range(0, len(ids), self.seq_length):
chunk = ids[i:i+self.seq_length]
if len(chunk) == self.seq_length:
numericalized.append(torch.tensor(chunk))
return numericalized
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx]
def create_dataloader(dataset, batch_size=32):
return DataLoader(
dataset,
batch_size=batch_size,
collate_fn=lambda batch: pad_sequence(batch, padding_value=0),
shuffle=True
)
第二章:词嵌入与位置编码实现
2.1 词嵌入理论基础与数学建模
2.1.1 高维语义空间映射原理
词嵌入(Word Embedding)是将离散的符号映射到连续向量空间的核心技术,其数学本质是建立从词汇表V到d维实数空间的映射函数:
E : V → R d 其中 d ∈ { 128 , 256 , 512 , 1024 } E: V \rightarrow \mathbb{R}^d \quad \text{其中} \ d \in \{128, 256, 512, 1024\} E:V→Rd其中 d∈{ 128,256,512,1024}
该映射需要满足三个关键性质:
- 语义相似性: cos ( E ( w i ) , E ( w j ) ) ∝ 语义相似度 ( w i , w j ) \text{cos}(E(w_i), E(w_j)) \propto \text{语义相似度}(w_i,w_j) cos(E(wi),E(wj))∝语义相似度(wi,wj)
- 线性可组合性: E ( king ) − E ( man ) + E ( woman ) ≈ E ( queen ) E(\text{king}) - E(\text{man}) + E(\text{woman}) \approx E(\text{queen}) E(king)−E(man)+E(woman)≈E(queen)
- 多义性处理: E ( w ) = ∑ k = 1 K α k E k ( w ) E(w) = \sum_{k=1}^K \alpha_k E_k(w) E(w)=∑k=1KαkEk(w) (多向量表示)
2.1.2 动态嵌入与静态嵌入对比
类型 | 静态嵌入(Word2Vec) | 动态嵌入(Transformer) |
---|---|---|
上下文处理 | 固定表示 | 随上下文动态调整 |
参数绑定 | 全局共享 | 分层参数化 |
空间维度 | 通常300维 | 512-4096维 |
训练方式 | 预训练+冻结 | 端到端联合训练 |
典型应用 | 文本分类基础特征 | 生成任务、语义理解 |
2.2 可学习词嵌入的PyTorch实现
2.2.1 嵌入层初始化方法对比
import torch.nn as nn
class LearnableEmbeddings(nn.Module):
def __init__(self, vocab_size, d_model, padding_idx=0):
super().__init__()
self.embedding = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=d_model,
padding_idx=padding_idx
)
# 初始化策略选择
self._init_weights()
def _init_weights(self):
""" Xavier均匀分布初始化 """
nn.init.xavier_uniform_(self.embedding.weight)
if self.embedding.padding_idx is not None:
nn.init.zeros_(self.embedding.weight[self.embedding.padding_idx])
def forward(self, token_ids):
# token_ids形状: [batch_size, seq_len]
embeddings = self.embedding(token_ids)
# 输出形状: [batch_size, seq_len, d_model]
return embeddings * math.sqrt(self.embedding.embedding_dim)
2.2.2 掩码处理机制
处理填充符(PAD token)的关键步骤:
def create_padding_mask(seq):
# seq形状: [batch_size, seq_len]
mask = (seq == 0).unsqueeze(1).unsqueeze(2)
# 输出形状: [batch_size, 1, 1, seq_len]
return mask.to(seq.device)
2.3 位置编码的数学原理
2.3.1 绝对位置编码公式
Transformer使用正弦位置编码,其计算公式为:
P E ( p o s , 2 i ) = sin ( p o s 1000 0 2 i / d ) P E ( p o s , 2 i + 1 ) = cos ( p o s 1000 0 2 i / d ) PE_{(pos,2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right) \\ PE_{(pos,2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right) PE(pos,2i)=sin(100002i/dpos)PE(pos,2i+1)=cos(100002i/dpos)
其中:
- p o s pos pos: token在序列中的位置(0 ≤ pos < L)
- i i i: 维度索引(0 ≤ i < d/2)
- d d d: 模型维度
2.3.2 相对位置编码变体
表:不同位置编码方案对比
类型 | 公式形式 | 优点 |
---|---|---|
正弦式 | 见上述公式 | 可外推、无需学习参数 |
学习式 | P E ( p o s ) = W p o s PE(pos) = W_{pos} PE(pos)=Wpos | 灵活适应数据分布 |
Rotary(旋转) | $x_j^{ |
[由于篇幅限制,此处展示部分内容,完整文档需包含全部10个章节。建议保持当前对话继续完成剩余章节的撰写]
(注:实际操作中需在此处插入位置编码示意图,图示应包含正弦波形、位置编码矩阵可视化等内容。此处用文字描述代替实际插图)
图2-1 位置编码矩阵可视化示例:
- 横轴:嵌入维度(0-511)
- 纵轴:序列位置(0-127)
- 颜色深浅表示编码值大小(-1到+1)
2.4 位置编码的代码实现
2.4.1 正弦位置编码实现
class SinusoidalPositionalEncoding(nn.Module):
def __init__(self, d_model: int, max_len: int = 5000):
super().__init__()
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: torch.Tensor) -> torch.Tensor:
# x形状: [batch_size, seq_len, d_model]
x = x + self.pe[:x.size(1)]
return x
2.4.2 可学习位置编码变体
class LearnedPositionalEncoding(nn.Module):
def __init__(self, d_model: int, max_len: int = 512):
super().__init__()
self.position_emb = nn.Embedding(max_len, d_model)
self.register_buffer('position_ids', torch.arange(max_len))
def forward(self, x: torch.Tensor) -> torch.Tensor:
pos_ids = self.position_ids[:x.size(1)]
return x + self.position_emb(pos_ids)
2.5 嵌入层的整合应用
2.5.1 完整嵌入层实现
class TransformerEmbedding(nn.Module):
def __init__(self, vocab_size, d_model, max_len, dropout=0.1):
super().__init__()
self.token_embed = LearnableEmbeddings(vocab_size, d_model)
self.pos_embed = SinusoidalPositionalEncoding(d_model, max_len)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
token_emb = self.token_embed(x)
pos_emb = self.pos_embed(token_emb)
return self.dropout(pos_emb)
2.5.2 梯度缩放策略
# 在初始化时设置嵌入层标准差
emb_std = math.sqrt(1.0 / self.d_model)
nn.init.normal_(self.embedding.weight, mean=0.0, std=emb_std)
第三章:多头注意力机制实现
3.1 注意力机制的基本概念
3.1.1 人类注意力机制类比
人脑视觉注意力的三个特性在神经网络中的对应实现:
- 聚焦性:通过softmax函数实现概率分布聚焦
- 上下文关联:Query-Key的相似度计算
- 动态调整:Value向量的加权组合
3.1.2 注意力机制的数学本质
给定查询矩阵 Q Q Q、键矩阵 K K K、值矩阵 V V V,注意力函数可形式化为:
Attention ( Q , K , V ) = f ( g ( Q K T ) , V ) \text{Attention}(Q,K,V) = f(g(QK^T), V) Attention(Q,K,V)=f(g(QKT),V)
其中:
- g g g:相似度归一化函数(通常为softmax)
- f f f:聚合函数(通常为加权求和)
3.2 缩放点积注意力的数学推导
3.2.1 标准点积注意力公式
原始点积注意力计算:
Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q,K,V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dkQKT)V
缩放因子 d k \sqrt{d_k} dk的作用:
- 防止点积结果过大导致softmax梯度消失
- 保持方差稳定(当 d k d_k dk增大时)
3.2.2 梯度传播特性分析
推导梯度计算公式:
令 S = Q K T / d k S = QK^T/\sqrt{d_k} S=QKT/dk,则:
∂ Attention ∂ Q = 1 d k V ⋅ ( softmax ( S ) ⊙ ( 1 − softmax ( S ) ) ⋅ K \frac{\partial \text{Attention}}{\partial Q} = \frac{1}{\sqrt{d_k}}V \cdot (\text{softmax}(S) \odot (1 - \text{softmax}(S)) \cdot K ∂Q∂Attention=dk1V⋅(softmax(S)⊙(1−softmax(S))⋅K
缩放因子使得梯度幅值保持稳定,避免随着 d k d_k dk增大出现梯度爆炸或消失。
3.3 多头注意力的并行计算实现
3.3.1 多头拆分公式
给定 h h h个头,将 Q , K , V Q,K,V Q,K,V拆分为:
Q i = Q W i Q K i = K W i K V i = V W i V \begin{aligned} Q_i &= Q W_i^Q \\ K_i &= K W_i^K \\ V_i &= V W_i^V \end{aligned} QiKiVi=QWiQ=KWiK=VWiV
其中 W i Q , W i K , W i V ∈ R d model × d k W_i^Q, W_i^K, W_i^V \in \mathbb{R}^{d_{\text{model}} \times d_k} WiQ,WiK,WiV∈Rdmodel×dk