在这里插入图片描述

文章目录


学完即“无死角”:从宏观设计到微观细节,一次厘清所有关键概念
适合读者:初级/中级开发者、AI 爱好者、准备面试或复习 Transformer 核心知识的任何人
风格:系统性总结 + 易混淆点对比 + 实战决策指南


写在前面:为什么要专门做一次总结?

前面的 12 节课,我们系统学习了 Transformer 从底向上、从内到外的全部核心知识:

  • 第 2 课:数学基础(向量、矩阵、Softmax、激活函数)
  • 第 3 课:整体架构(编码器-解码器,宏观视角)
  • 第 4 课:注意力机制入门(Q, K, V,基本流程)
  • 第 5 课:自注意力机制(Self-Attention)
  • 第 6 课:多头注意力(Multi-Head Attention)
  • 第 7 课:编码器深度解析
  • 第 8 课:解码器深度解析
  • 第 9 课:输入处理机制(分词、位置编码)
  • 第 10 课:预训练-微调范式
  • 第 11 课:MLM vs CLM
  • 第 12 课:训练优化(梯度、正则、优化器)

这些知识点就像搭积木,单看每一块都很清楚,但合在一起就容易混淆。例如:

  • 编码器和解码器都有自注意力,但一个无掩码,一个有掩码,为什么?
  • 多头注意力是“把 Q/K/V 切分后分别做注意力,再拼接”,和“单头做多次”有什么区别?
  • MLM 和 CLM 训练出的模型分别适用于什么任务?能不能互换?

本节课的目标就是:把所有知识点串成一张网,把常见的坑填平,并给出选择架构/任务的决策方法。

学完这节课,你将能够:

  • 清晰画出 Transformer 完整的数据流图,包括张量形状变化。
  • 准确回答面试中最常问的 10 个“对比类”问题(如区别、优缺点)。
  • 面对一个新任务(如文档摘要、情感分析),能合理选择仅编码器/仅解码器/编码器-解码器,以及对应的预训练任务。

一、核心架构知识点总结——一张图串起所有模块

1.1 原始 Transformer(编码器-解码器)全景回顾

我们从输入到输出,以 机器翻译 为例,绘制完整数据流(文字描述 + 代码模拟形状):

源句子(中文): "我 爱 你"   →  分词 + 词嵌入 + 位置编码
    ↓
编码器(6 层):
    每层: 多头自注意力(无掩码) → 残差+LN → FFN → 残差+LN
    输出: encoder_output (batch, src_len, d_model)
    ↓
解码器(6 层):
    输入: 目标句子起始符 "<SOS>" + 已生成词(训练时用 teacher forcing)
    每层: 
      1) 掩码多头自注意力(因果掩码)→ 残差+LN
      2) 交叉注意力(Q 来自解码器,K,V 来自 encoder_output)→ 残差+LN
      3) FFN → 残差+LN
    ↓
输出: 线性层 + Softmax → 下一个词的概率分布

核心的形状变化(批大小为 1,方便理解)

# 伪代码形状
src_len, tgt_len = 5, 4
d_model = 512

# 输入
src_tokens = (1, src_len)
tgt_tokens = (1, tgt_len)

# 嵌入后
src_emb = (1, src_len, 512)
tgt_emb = (1, tgt_len, 512)

# 加位置编码后,形状不变
src_input = (1, 5, 512)
tgt_input = (1, 4, 512)

# 编码器输出
encoder_out = (1, 5, 512)

# 解码器输出(经过线性层前)
decoder_out = (1, 4, 512)

# Logits
logits = (1, 4, vocab_size)  # 每个目标位置预测下一个词

1.2 三大变体架构速查

架构 组件 注意力掩码 典型预训练任务 代表作 核心用途
仅编码器 只堆叠编码器 无(双向) MLM ± NSP BERT, RoBERTa, ALBERT 文本理解、分类、序列标注
仅解码器 只堆叠解码器 因果掩码(单向) CLM(自回归) GPT, LLaMA, PaLM 文本生成、对话、代码生成
编码器-解码器 编码器 + 解码器 编码器无掩码,解码器内部掩码 + 交叉注意力 降噪自编码、Span Corruption T5, BART,原始Transformer 序列到序列(翻译、摘要)

1.3 注意力机制家族对比

这是最容易混淆的部分,我们用一个表格清晰区分:

注意力类型 Q来源 K/V来源 掩码 用在哪
自注意力 (Self-Attention) X X 编码器、解码器的交叉注意力之前(解码器内部第一个子层需加因果掩码)
掩码自注意力 (Masked Self-Attention) X X 因果掩码(未来屏蔽) 解码器第一个子层
交叉注意力 (Cross-Attention) 解码器上一层的输出 编码器输出 无(但 K/V 的 padding 需掩码) 解码器第二个子层
多头注意力 (Multi-Head) 上述任意一种,但 Q/K/V 先切分再计算 同上 同上 所有注意力子层(实际实现时用 MultiHeadAttention 封装)

关键区分

  • “自注意力”强调 Q/K/V 来自同一序列;“多头”强调把特征维度拆成多个头并行计算。
  • “掩码”是附加在注意力得分矩阵上的约束,不是独立的注意力类型。

1.4 训练优化要点速记

问题 解决方案 常用值/做法
梯度爆炸 梯度裁剪 clip_grad_norm_,max_norm=1.0
过拟合 Dropout + L2正则化 Dropout=0.1, weight_decay=0.01
收敛缓慢 自适应优化器 + 学习率预热 AdamW, lr=5e-5, warmup 10%
训练不稳定 学习率预热 + 合理的初始化 预热步数 总步数的 5%~10%

二、易错点拆解——从“我以为”到“我确认”

2.1 误区一:编码器的自注意力是“双向”,解码器的自注意力是“单向”,但解码器的交叉注意力也是双向的吗?

正确理解

  • 编码器自注意力:双向,可以看整个输入序列的所有位置。
  • 解码器掩码自注意力:单向(因果),只能看当前位置及之前的输出位置。
  • 解码器交叉注意力:Q 来自解码器,K/V 来自编码器。由于编码器的输出已经包含了完整的源序列信息,而且没有对未来目标位置的限制,所以交叉注意力允许解码器的每个位置 看到整个编码器输出。这本质上是“从解码器到编码器”的全双向连接。因此,我们说交叉注意力 没有因果限制

常见错误:认为交叉注意力也要加因果掩码,这会导致解码器无法看到后面的源词(实际上源词的顺序是固定的,不需要掩码)。

2.2 误区二:多头注意力就是多次调用单头注意力,然后取平均。

正确理解

  • 单头注意力:将整个 d_model 维度的 Q/K/V 一次性做注意力,输出 d_v 维。
  • 多头注意力:将 d_model 拆成 h 个 d_k 维的子空间,每个子空间独立计算注意力,然后将 h 个输出拼接,再经过一次线性变换得到最终输出。
  • 关键区别:每个头在不同的子空间学习不同的特征模式,拼接后通过 W_O 融合。这比简单平均多个单头结果要强大得多。

代码对比(伪)

# 错误的“平均多头”理解
heads = [single_head_attention(q, k, v) for _ in range(h)]
output = sum(heads) / h   # 不对

# 正确的多头
q_multi = split(q, h)  # (batch, h, seq, d_k)
k_multi = split(k, h)
v_multi = split(v, h)
attn_out = attention(q_multi, k_multi, v_multi)  # (batch, h, seq, d_v)
concat = reshape(attn_out)  # (batch, seq, h*d_v)
output = linear(concat)      # (batch, seq, d_model)

2.3 误区三:MLM 训练时,被随机替换成其他词的位置,模型预测的目标是那个随机词。

正确理解:无论该位置被替换成 [MASK]、随机词还是保留原词,标签始终是原始的真实 token。模型的目标是预测出正确答案,而不是预测被替换后的内容。

例如:原词“爱”,被随机替换成“恨”。模型需要输出“爱”,而不是“恨”。引入随机词是为了让模型学会:即使输入是错误词,也要通过上下文纠正。

2.4 误区四:位置编码只加在输入层,模型就能记住位置顺序。

正确理解:Transformer 的自注意力本身对位置不敏感,所以必须在输入嵌入中加入位置信号。但需要注意的是,这个位置信号会随着层数加深逐渐被淡化。因此深层的表示中,位置信息可能会被语义信息覆盖。对于需要强位置依赖的任务(如命名实体识别),往往需要额外的手段(如相对位置编码、RoPE)。

正弦编码的局限:绝对位置编码无法很好地外推到训练时未见过的序列长度。现代的模型(如 GPT-NeoX、LLaMA)普遍采用 RoPE(旋转位置编码)ALiBi 来改善外推性。

2.5 误区五:解码器的输入就是目标序列,训练时直接把整个目标序列喂进去就行。

正确理解:在 teacher forcing 下,解码器的输入的确是完整的正确目标序列(右移一位,开头添加 <SOS>),但必须配合因果掩码,防止第 i 个位置看到第 i+1 及之后的目标词。如果没有掩码,模型就会看到未来的答案,学不到自回归生成的能力。

推理时,输入是动态增长的:初始只有 <SOS>,每生成一个词就追加到输入末尾。

2.6 误区六:BERT 只用 MLM 预训练,GPT 只用 CLM 预训练,两者完全不可互换。

正确理解

  • BERT 采用 MLM + NSP,是因为它的目标是为下游理解任务提供双向表示。如果改成 CLM,BERT 就变成了一个生成模型,但它的架构(无因果掩码)不合适。
  • GPT 采用 CLM,是因为生成任务需要自回归。若改成 MLM,GPT 在生成时无法使用双向信息(因为未来词不存在)。
  • 然而,有些模型(如 UniLM、GLM)通过改变注意力掩码,可以同时支持双向和单向训练,甚至在同一模型上做多种预训练任务。

选择原则

  • 任务本质是“理解”(如分类、匹配) → 优先用双向模型(BERT)。
  • 任务本质是“生成”(如写故事、代码) → 优先用自回归模型(GPT)。
  • 需要同时理解输入并生成输出(如翻译、摘要) → 编码器-解码器(T5)或超大 GPT 通过 prompt 也可完成。

2.7 误区七:训练时使用 AdamW,dropout 和 weight_decay 越大越好。

正确理解

  • Dropout 和 weight_decay 是正则化手段,过大(如 dropout=0.5, wd=0.5)会使模型欠拟合,训练损失降不下去。
  • 预训练阶段:数据量巨大,正则化可以轻一些(dropout=0.1, wd=0.01)。
  • 微调阶段:数据量小,可适当增强正则化(dropout=0.2~0.3, wd=0.05),但要关注验证集性能。

调试建议:如果验证集 loss 不降,尝试降低 dropout 和 weight_decay;如果训练集 loss 远低于验证集,说明过拟合,可提高正则化。

2.8 易错概念速查表

易混淆点 正确辨析
Self-Attention vs Multi-Head Attention 自注意力是一种计算模式,多头是一种并行策略;多头注意力内部使用了自注意力。
编码器 vs 解码器 编码器无掩码双向,解码器有掩码自注意力 + 交叉注意力。
MLM vs CLM MLM 双向预测随机遮盖词;CLM 单向预测下一个词。
Q, K, V 的含义 Q 是查询,K 是被查询的键,V 是实际被加权的内容。
训练中的 dropout vs 推理中的 dropout 训练时启用 dropout,推理时关闭(或等价于保留所有神经元)。
LayerNorm vs BatchNorm LayerNorm 对每个样本的特征维度归一化,BatchNorm 对 batch 内的每个特征通道归一化;序列任务中 LayerNorm 更常用。
Teacher Forcing vs 自回归 Teacher forcing 是训练技巧,使用真实标签作为输入;自回归是推理方式,使用自己生成的词作为输入。

三、理论应用思考——如何为新任务设计 Transformer 方案?

3.1 决策树:选择架构和预训练任务

1. 任务输出是什么?
   ├── 一个类别/分数(分类/回归) → 仅编码器(BERT 类),取 [CLS] 或平均池化
   ├── 一个与输入等长的标签序列(NER, POS) → 仅编码器,每个 token 独立分类
   ├── 一段新文本(生成) → 仅解码器(GPT 类)或编码器-解码器
   └── 输入输出是不同序列且长度变化大(翻译、摘要) → 编码器-解码器

2. 如果选择了生成类:
   ├── 输入较短,生成较长 → 解码器(用 prompt 编码输入)
   ├── 输入很长,生成较短(摘要) → 编码器-解码器更佳
   └── 输入输出结构相似(如代码补全) → 解码器足够

3. 资源限制?
   ├── 只有少量标注数据(<1000条) → PEFT(LoRA)+ 大模型
   ├── 可以接受几小时训练 → 全参数微调 BERT-base/GPT-2
   └── 有大规模预算 → 从头预训练(极少情况)

3.2 案例实战演练

案例1:电商评论情感分类(正面/负面)
  • 选型:仅编码器(BERT-base)。
  • 理由:输出是固定类别,需要深度理解评论文本的语义,双向上下文至关重要。
  • 预训练任务:MLM(不涉及生成,CLM 不必要)。
  • 微调:在 5000 条标注评论上全参数微调,学习率 2e-5,dropout=0.1。
案例2:新闻标题自动生成(根据正文生成标题)
  • 选型:编码器-解码器(T5-small)或仅解码器(GPT-2 通过 prompt “标题:”)。
  • 理由:输入正文较长,输出标题较短,两者不同结构;编码器-解码器天然适配。
  • 预训练任务:T5 的 span corruption 预训练。
  • 微调:使用新闻-标题对,全参数微调。
案例3:Python 代码补全(根据上文预测下文代码)
  • 选型:仅解码器(CodeGPT, StarCoder)。
  • 理由:自回归生成,输入输出都是代码,且需要长程依赖。
  • 预训练任务:CLM 在大量代码上预训练。
  • 微调:在特定仓库代码上微调,或直接使用 few-shot prompting。

3.3 面试中常见的设计问题

:如果我想做一个智能客服对话系统,应该用 BERT 还是 GPT?

:对话系统需要生成回复,因此应选用生成式模型(GPT 系列)。BERT 只能做分类或提取,无法生成连贯对话。如果对话是检索式(从候选库中选最佳回复),BERT 也可以用来计算相似度,但端到端生成首选 GPT。

:为什么机器翻译可以用仅解码器模型(如 GPT-4)而不用编码器-解码器?

:对于大语言模型(如 GPT-4),可以通过构造 prompt 将翻译任务转化为“给定源语言句子,生成目标语言句子”的形式。模型内部的注意力虽然是单向的,但在生成过程中,源语言句子作为前缀,整个序列(包括源句)都在解码器的自注意力视野内(因为源句在前,目标词在后)。虽然理论上不如交叉注意力灵活,但在规模足够大时,效果已经超越传统编码器-解码器。


四、课后延伸任务

任务1:绘制思维导图

使用 XMind、Obsidian 或纸上手绘,画出 Transformer 知识体系,至少包括:

  • 输入处理(分词、嵌入、位置编码)
  • 编码器(自注意力、FFN、残差、LN)
  • 解码器(掩码自注意力、交叉注意力、FFN)
  • 注意力机制(Q/K/V、缩放点积、多头)
  • 预训练任务(MLM、CLM、NSP)
  • 训练优化(梯度裁剪、AdamW、预热)

任务2:为以下任务设计 Transformer 方案

任务A:医学病历命名实体识别(识别疾病、药物、症状等)。
任务B:诗歌生成(给定关键词生成四行七言绝句)。
任务C:中英文文本相似度计算(输出 0~1 相似度)。

对每个任务,回答:

  • 选什么架构(仅编码器/仅解码器/编码器-解码器)?为什么?
  • 选择什么预训练模型(如 BioBERT、GPT-2-Chinese、Sentence-BERT)?
  • 微调时需要调整哪些关键参数?

参考答案在文末附录中。

任务3:代码实践——用 Transformer 实现一个字符级预测(玩具 CLM)

下面代码实现了一个极简的字符级 CLM(GPT-like),帮你巩固因果掩码和自回归生成的概念:

import torch
import torch.nn as nn
import torch.nn.functional as F

# 超参数
vocab = list("abcdefghijklmnopqrstuvwxyz ")
char_to_id = {c: i for i, c in enumerate(vocab)}
id_to_char = {i: c for c, i in char_to_id.items()}
vocab_size = len(vocab)
d_model = 32
num_layers = 2
num_heads = 4
max_len = 50

class CausalSelfAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.d_k = d_model // num_heads
        self.qkv = nn.Linear(d_model, 3 * d_model)
        self.proj = nn.Linear(d_model, d_model)
    
    def forward(self, x):
        B, T, C = x.shape
        qkv = self.qkv(x).reshape(B, T, 3, self.num_heads, self.d_k).permute(2, 0, 3, 1, 4)
        q, k, v = qkv[0], qkv[1], qkv[2]
        # 因果掩码
        mask = torch.tril(torch.ones(T, T)).view(1, 1, T, T).to(x.device)
        att = (q @ k.transpose(-2, -1)) / (self.d_k ** 0.5)
        att = att.masked_fill(mask == 0, float('-inf'))
        att = F.softmax(att, dim=-1)
        y = (att @ v).transpose(1, 2).reshape(B, T, C)
        return self.proj(y)

class DecoderBlock(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.ln1 = nn.LayerNorm(d_model)
        self.attn = CausalSelfAttention(d_model, num_heads)
        self.ln2 = nn.LayerNorm(d_model)
        self.ff = nn.Sequential(
            nn.Linear(d_model, 4 * d_model),
            nn.GELU(),
            nn.Linear(4 * d_model, d_model)
        )
    def forward(self, x):
        x = x + self.attn(self.ln1(x))
        x = x + self.ff(self.ln2(x))
        return x

class TinyGPT(nn.Module):
    def __init__(self, vocab_size, d_model, num_layers, num_heads, max_len):
        super().__init__()
        self.token_embed = nn.Embedding(vocab_size, d_model)
        self.pos_embed = nn.Parameter(torch.zeros(1, max_len, d_model))
        self.blocks = nn.ModuleList([DecoderBlock(d_model, num_heads) for _ in range(num_layers)])
        self.ln_f = nn.LayerNorm(d_model)
        self.head = nn.Linear(d_model, vocab_size)
    
    def forward(self, idx):
        B, T = idx.shape
        tok_emb = self.token_embed(idx)
        pos_emb = self.pos_embed[:, :T, :]
        x = tok_emb + pos_emb
        for block in self.blocks:
            x = block(x)
        x = self.ln_f(x)
        logits = self.head(x)
        return logits

# 生成一个简单的训练数据:字符串 "the quick brown fox jumps over the lazy dog" 重复
text = "the quick brown fox jumps over the lazy dog " * 10
ids = torch.tensor([char_to_id[c] for c in text], dtype=torch.long)
model = TinyGPT(vocab_size, d_model, num_layers, num_heads, max_len)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)

# 训练(简单的 next token prediction)
epochs = 5
for epoch in range(epochs):
    for i in range(0, len(ids) - max_len, max_len):
        inp = ids[i:i+max_len].unsqueeze(0)
        tgt = ids[i+1:i+max_len+1].unsqueeze(0)
        logits = model(inp)  # (1, seq, vocab)
        loss = F.cross_entropy(logits.view(-1, vocab_size), tgt.view(-1))
        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        optimizer.step()
    print(f"Epoch {epoch+1}, loss: {loss.item():.4f}")

# 生成
model.eval()
start = torch.tensor([[char_to_id['t']]])  # 起始字符 't'
for _ in range(50):
    with torch.no_grad():
        logits = model(start)
    next_token = torch.argmax(logits[0, -1, :]).item()
    start = torch.cat([start, torch.tensor([[next_token]])], dim=1)
    if next_token == char_to_id[' ']:
        break
print("生成的文本:", ''.join([id_to_char[int(i)] for i in start[0].tolist()]))

运行这段代码,你会看到模型学会预测常见的英文单词序列。


附录:任务设计参考答案

任务A(医学NER)

  • 架构:仅编码器(如 BioBERT)。
  • 原因:NER 是 token 级分类,需要双向上下文。
  • 预训练模型:BioBERT(在生物医学语料上预训练)。
  • 关键参数:学习率 2e-5,batch size 16,max_len 128。

任务B(诗歌生成)

  • 架构:仅解码器(如 GPT-2-Chinese)。
  • 原因:自由生成,自回归。
  • 预训练模型:GPT-2-Chinese 或从头训练一个小型 GPT。
  • 关键参数:生成时温度 0.8(增加多样性),top-p=0.9。

任务C(文本相似度)

  • 架构:仅编码器(Sentence-BERT 双编码器)。
  • 原因:需要计算两段文本的表示向量,再计算余弦相似度。
  • 预训练模型:BERT 或 RoBERTa,使用双塔结构微调。
  • 关键参数:对比学习损失,学习率 2e-5。

下节课预告

所有基础已铺垫完毕。从下一节课开始,我们将进入 经典模型解析 系列,逐个解剖那些改变 NLP 历史的模型。

【第14课:BERT 模型全解析——预训练与微调的完美实践】

  • BERT 的架构(与原始 Transformer 的差异)
  • MLM + NSP 预训练任务的细节与效果
  • 如何进行微调(文本分类、问答、NER)
  • BERT 的变体(RoBERTa、ALBERT、DistilBERT)对比

你将看到一个“基础模型”如何通过精巧的设计,横扫 11 项 NLP 任务纪录。
我们第14课见!


祝贺你完成了 Transformer 核心理论的全部学习! 现在你已经具备了从零理解、修改、甚至设计新 Transformer 变体的能力。保持动手,保持思辨,经典模型系列等你来挑战。


🔗 Transformers模型架构系列课程导航

去专栏阅读

模块1:Transformers入门基础(第1-6课)
模块核心目标:帮助零基础读者快速入门,搭建Transformers的基础认知框架,了解其起源、发展背景及核心应用场景,掌握必备的前置知识,为后续核心原理学习奠定基础,降低入门门槛。
模块2:Transformers核心架构与原理(第7-13课)
模块核心目标:深入拆解Transformers的核心架构(编码器、解码器),掌握每个子模块的工作原理、作用及实现逻辑,理解各模块之间的协同工作机制,突破理论难点,为后续模型解析与实战奠定基础。
模块3:Transformers经典模型解析(第14-20节课)
模块核心目标:逐个拆解Transformers领域的经典模型(BERT、GPT、T5等),分析每个模型的核心改进、预训练任务、适用场景与优缺点,让读者掌握不同模型的差异,能根据实际任务选择合适的模型,兼顾理论深度与应用落地。
模块4:Transformers实战与优化(第21-26课)
模块核心目标:聚焦实战落地,从环境搭建、工具使用到具体任务实操,让读者掌握Transformers模型的训练、微调、部署方法,学习实战中的优化技巧,解决实际项目中的常见问题,确保每节课都有具体的实操案例,让读者“会应用、能落地”。
模块5:Transformers行业应用与前沿拓展(第27-30课)
模块核心目标:结合不同行业的实际应用场景,讲解Transformers的落地案例,让读者了解其行业应用价值;同时覆盖当前Transformers的前沿趋势,帮助读者把握技术发展方向,提升专栏的前沿性与实用性。


🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐