使用Python从零开始构建生成型TransformerLM并训练

第一章:Transformer架构概述与准备工作

1.1 生成型语言模型的核心价值

生成型Transformer语言模型(TransformerLM)是当前自然语言处理领域的核心技术,其核心价值体现在三个维度:

  1. 上下文感知能力:通过自注意力机制实现任意位置token的全局关联
  2. 并行计算效率:相比RNN的序列计算模式,Transformer的矩阵运算可完全并行化
  3. 长程依赖建模:克服传统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架构包含以下核心组件:

Transformer
Encoder
Decoder
Embedding
Positional Encoding
Encoder Layer x6
Embedding
Positional Encoding
Decoder Layer x6
Multi-Head Attention
Feed Forward
Masked Multi-Head Attention
Multi-Head Attention
Feed Forward

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:VRd其中 d{ 128,256,512,1024}

该映射需要满足三个关键性质:

  1. 语义相似性 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)
  2. 线性可组合性 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)
  3. 多义性处理 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 人类注意力机制类比

人脑视觉注意力的三个特性在神经网络中的对应实现:

  1. 聚焦性:通过softmax函数实现概率分布聚焦
  2. 上下文关联:Query-Key的相似度计算
  3. 动态调整: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(dk QKT)V

缩放因子 d k \sqrt{d_k} dk 的作用:

  1. 防止点积结果过大导致softmax梯度消失
  2. 保持方差稳定(当 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 QAttention=dk 1V(softmax(S)(1softmax(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,WiVRdmodel×dk

3.3.2 并行计算架构