上一篇速通 AI(三):Transformer 架构详解 — Self-Attention、多头注意力、位置编码、Encoder/Decoder

目标:掌握 BERT/GPT 预训练模型的核心原理与实战,熟练使用 HuggingFace 生态进行 NLP 任务开发。

前置要求:上一篇(Transformer 架构:Self-Attention、Multi-Head、位置编码、Encoder/Decoder)


本阶段知识依赖图

flowchart TD
    A["Transformer 架构(上一篇)"]
    A --> L[预训练语言模型]
    L --> L1["BERT(Encoder-only)"] --> L1a[文本分类、NER、阅读理解]
    L --> L2["GPT(Decoder-only)"] --> L2a[文本生成、对话]
    L --> L3[Seq2Seq模型] --> L3a[翻译、摘要]
    A --> H["HuggingFace生态(实战工具)"]
    H --> H1[Tokenizer] --> H1a[文本编码/解码]
    H --> H2[Datasets] --> H2a[数据集加载与处理]
    H --> H3[Pipeline] --> H3a[一行代码完成推理]
    H --> H4[Trainer] --> H4a[模型训练框架]
    H --> H5[Evaluate] --> H5a[评估指标]

1. BERT 与 GPT——预训练语言模型

1.1 BERT——双向理解的大模型

BERT 的核心思想——为什么要"双向"?

类比:阅读理解

  • 单向(GPT):从左到右阅读,像"遮住后面的文字"。“I went to the bank to [???]” → 只知道前面是"bank",不知道后面是"deposit"还是"river" → 无法确定"bank"是银行还是河岸。
  • 双向(BERT):同时看前后文,像"通读全文后回答问题"。“I went to the [bank] to deposit money” → 看到了后面的"deposit",确定"bank"是银行。

双向理解 = 更好的语义理解能力。

BERT 双向的代价:BERT 不能做自回归生成——因为训练时每个位置都能看到全文,无法模拟"逐词生成"的过程。这就是为什么 GPT(单向)适合生成任务,BERT(双向)适合理解任务。

BERT 的两个预训练任务——从海量文本中学习语言

任务 1 : Masked Language Model (MLM)——完形填空

训练方式:随机遮盖 15% 的词,让模型预测被遮盖的词。输入:“The [MASK] sat on the mat”,目标:预测 [MASK] = “cat”。

MLM 的损失函数:

$$ \mathcal{L}_{\text{MLM}} = -\sum_{i \in \text{masked}} \log P(x_i | x_{\backslash i}) $$

直觉:只计算被遮盖位置的损失——让模型在这些位置"填空",填对了损失小,填错了损失大。

为什么遮盖 15% 而不是 100% ?遮盖太多:上下文信息不足,模型猜不准。遮盖太少:训练效率低(每个句子只学一个词)。 15% 是实验验证的最佳比例。

15% 被选中词的处理策略(80/10/10 规则)

处理方式比例例子为什么这样做
替换为 [MASK]80%“The [MASK] sat on the mat”让模型学习"根据上下文填空"的能力
替换为随机词10%“The happy sat on the mat”迫使模型不能只依赖 [MASK] 标记,要真正理解上下文
保持原词不变10%“The cat sat on the mat”让模型学习"判断每个词是否正确"的能力,保持输入分布接近真实

为什么需要混合策略? 如果 100% 用 [MASK],模型会学到"只在看到 [MASK] 时才预测"的捷径。混合策略让模型无法投机取巧,必须真正理解语言。

为什么用[MASK]而不是直接删除?删除后模型知道"这里缺了一个词"。用[MASK]替换,模型需要根据上下文推断。

任务 2 : Next Sentence Prediction (NSP)——判断句子关系

训练方式:给模型两个句子,判断它们是否是连续的。

  • 正例:“他去超市买了牛奶。” + “然后回家做早餐。” → IsNext
  • 负例:“他去超市买了牛奶。” + “今天天气很好。” → NotNext

为什么需要 NSP ?很多 NLP 任务需要理解句子之间的关系(问答、推理、自然语言推断)。 NSP 让模型学到"句子之间的逻辑关系"。

BERT 的架构——基于 Transformer Encoder

BERT = Transformer Encoder × 12 层(BERT-base)或 24 层(BERT-large)

  • BERT-base: 12 层, 768 维, 12 头, 1.1 亿参数
  • BERT-large: 24 层, 1024 维, 16 头, 3.4 亿参数

输入表示(三种嵌入相加):

$$ \text{input}_i = \text{TokenEmb}(x_i) + \text{SegEmb}(s_i) + \text{PosEmb}(i) $$
  • Token Embedding:词本身的语义信息
  • Segment Embedding:句子 A/B 标记(区分两个句子)
  • Position Embedding:位置信息(让模型知道词序)

特殊标记:[CLS] 放在句子开头,用于分类任务的输出(通过 Self-Attention 聚合了所有位置的信息,可代表整句语义)。[SEP] 分隔两个句子。[MASK] 遮盖标记。

BERT 微调——一个预训练模型解决多种 NLP 任务

BERT 的强大之处:预训练一次,微调多次。

文本分类:取[CLS]的输出 → 加一个分类头 → 完成。[CLS] 我 很 喜欢 这部 电影 → [CLS 的输出] → Linear → softmax → 正面/负面。

命名实体识别:取每个 token 的输出 → 序列标注。[CLS] 小 明 在 北京 上学 → B-PER I-PER O B-LOC I-LOC O 。

问答系统:取每个 token 的输出 → 预测答案的起止位置。问题:谁在北京上学?段落:小明在北京上学 → 预测答案起始位置=1 (“小”),结束位置=2 (“明”)→ 答案:“小明”。

核心思想: BERT 已经"理解"了语言,只需要在上面加一个简单的分类头。

为什么预训练+微调范式有效?

BERT 只需在少量标注数据上微调就能达到很好的效果,这背后有三个关键原因:

1. 预训练学到了通用语言表示

在海量无标注文本上做 MLM 预训练时,BERT 学到了:

  • 语法知识:词性、句法结构(“cat sat on the mat"中 cat 是主语)
  • 语义知识:词义、同义词关系(“happy"和"glad"含义接近)
  • 世界知识:事实性知识(“巴黎是法国的首都”)

这些知识是"通用的”——不管下游任务是情感分类还是命名实体识别,都需要理解语言。

2. 迁移学习的直觉

类比:一个学过英语的人去学法语,比一个没学过任何语言的人学法语容易得多——因为语言之间有共通的结构(语法、语序、时态)。

同理,BERT 在预训练阶段已经"学会了语言的结构”,微调时只需要"学一个新的任务规则"(如"判断情感=看[CLS]的输出"),而不是从零开始学语言。

3. 为什么微调学习率要很小?——灾难性遗忘

如果微调学习率太大,模型会"忘记"预训练学到的通用知识——这就是灾难性遗忘。类比:你学了 10 年英语,突然要学法语。如果学习速度太快,你可能会把英语语法忘掉。

解决方案:用很小的学习率(如 $2 \times 10^{-5}$),让模型在保留预训练知识的基础上,缓慢适应新任务。

面试要点:回答"预训练+微调为什么有效?“时,从通用语言表示、迁移学习直觉、灾难性遗忘三个角度展开。

1.2 GPT——自回归生成的大模型

GPT 的核心思想——预测下一个词

GPT = Generative Pre-trained Transformer = Transformer Decoder

训练目标极其简单:给定前文,预测下一个词。数学上,这等价于最大化序列的联合概率:

$$ P(x_1, x_2, \ldots, x_n) = \prod_{i=1}^{n} P(x_i | x_1, x_2, \ldots, x_{i-1}) $$

训练损失函数:

$$ \mathcal{L}_{\text{GPT}} = -\sum_{i=1}^{n} \log P(x_i | x_1, \ldots, x_{i-1}; \theta) $$

直觉:把一句话拆成"逐词预测"的条件概率乘积。“今天天气很好” = P(天气|今天) × P(很好|今天天气) × …,每个词的预测都依赖前面所有词。

  • “今天” → 预测"天气”
  • “今天天气” → 预测"很好"
  • “今天天气很好” → 预测"。"

这就是自回归(Autoregressive)生成:每次只生成一个词,然后把这个词加入输入,继续生成下一个词。就像"滚雪球"一样,越滚越大。

动手计算:GPT 自回归生成过程

假设 GPT 模型已经训练好,输入"I love",让我们手算它如何生成下一个词。

Step 1:输入 “I love” → 模型输出下一个词的概率分布

假设模型最后一层输出的 logits(未归一化分数)为:

候选词AIthecatdeeplearning
logits3.21.50.82.11.9

Step 2:Softmax 归一化 → 概率分布

$$ \text{softmax}([3.2, 1.5, 0.8, 2.1, 1.9]) = [0.532, 0.097, 0.048, 0.177, 0.145] $$

Step 3:采样(或取 argmax)→ 选择 “AI”(概率最高 53.2%)

Step 4:将 “AI” 拼接到输入 → “I love AI” → 再次前向传播 → 预测下一个词

候选词verysoand
概率0.450.200.150.120.08

选择 “。” → 生成完成:“I love AI。”

解读:GPT 每次只生成一个 token,但每次生成都要重新计算整个序列的注意力(因为新 token 需要看到前面所有 token)。这就是为什么生成 100 个 token 需要 100 次前向传播,比编码慢得多。

GPT vs BERT——核心区别对比

特性BERTGPT
架构Transformer EncoderTransformer Decoder
方向双向(同时看前后文)单向(只看前面的词)
训练目标遮盖词预测(MLM)预测下一个词(自回归)
注意力没有 Mask ,所有位置互相看有 Mask ,只能看前面的位置
擅长任务理解类(分类、 NER 、问答)生成类(文本生成、对话)
代表模型BERT, RoBERTa, ALBERTGPT-1/2/3/4, ChatGPT

核心区别: BERT 像"阅读理解"——读完全文再回答。 GPT 像"写作"——一个字一个字地写。

GPT 系列演进——从学术到改变世界

  • GPT-1 (2018): 1.17 亿参数, 12 层 Decoder 。证明了"预训练+微调"范式的有效性。
  • GPT-2 (2019): 15 亿参数, 48 层 Decoder 。展示了零样本能力(不需要微调就能做任务)。因为"太危险"而延迟发布。
  • GPT-3 (2020): 1750 亿参数, 96 层 Decoder 。展示了少样本学习能力(给几个例子就能做任务)。开启了"大模型时代"。
  • ChatGPT (2022): GPT-3.5 + RLHF (人类反馈强化学习)。通过人类反馈让模型更"对齐"人类意图。改变了整个 AI 行业。
  • GPT-4 (2023):多模态,推理能力大幅提升。可以理解图片、代码、数学。

1.3 BERT 实战——微调中文分类

from transformers import BertTokenizer, BertForSequenceClassification
import torch

# 1. 加载预训练模型和分词器(自动下载 ~400MB 的 bert-base-chinese 权重)
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
# num_labels=2:二分类(正面/负面)。模型会自动在 [CLS] 输出上加一个 768→2 的线性层

# 2. 数据预处理:文本 → token IDs → attention mask
text = "这部电影真的很好看"
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
# padding=True:短句子填充到相同长度;truncation=True:长句子截断到 512

# 3. 前向传播(labels 用于计算交叉熵损失,训练时需要,推理时可省略)
outputs = model(**inputs, labels=torch.tensor([1]))  # label=1 表示正面
loss = outputs.loss          # 交叉熵损失(训练时反向传播用)
logits = outputs.logits      # shape: (1, 2) — 两个类别的原始分数

# 4. 预测:取 logits 中较大的那个类别
prediction = torch.argmax(logits, dim=1)  # 0=负面,1=正面
print(f"预测:{'正面' if prediction == 1 else '负面'}")

1.4 预训练语言模型总结对比

模型架构方向预训练任务擅长任务
BERTEncoder双向MLM + NSP分类、 NER 、问答
GPTDecoder单向预测下一个词文本生成、对话
T5Encoder-Decoder双向文本到文本翻译、摘要、所有任务
LLaMADecoder单向预测下一个词通用大模型
ChatGLMPrefix LM双向混合对话、理解

三种 Transformer 架构对比

架构代表模型注意力方式擅长任务典型应用
Encoder-onlyBERT, RoBERTa双向(全连接)理解、分类、NER文本分类、问答
Decoder-onlyGPT, LLaMA单向(因果Mask)生成、对话文本生成、ChatGPT
Encoder-DecoderT5, BART编码双向 + 解码单向翻译、摘要Seq2Seq 任务

模块小结: BERT 与 GPT

你学到了什么为什么重要
BERT 双向理解理解类任务的基石
MLM + NSP 预训练任务从海量文本学习语言知识
GPT 自回归生成生成类任务的基石
BERT vs GPT 核心区别Encoder vs Decoder ,双向 vs 单向
预训练 + 微调范式一个模型解决多种 NLP 任务
理解了 Transformer 架构和 BERT/GPT 的预训练原理后,一个现实问题是:从零实现这些模型需要数千行代码。有没有更高效的方式?HuggingFace 生态正是为此而生——它提供了统一的 API,让你用几行代码就能加载、训练和部署最先进的 NLP 模型。本节将系统介绍 Tokenizer、Datasets、Pipeline、Trainer 四大核心工具。

2. HuggingFace 生态——NLP 开发的瑞士军刀

2.1 Tokenizer——文本与数字的桥梁

Tokenizer 到底在做什么?

人类理解文字(“我爱 AI”),计算机理解数字([101, 2023, 9932, 102])。 Tokenizer = 翻译官:把人类的文字翻译成计算机的数字,以及反过来。

完整流程:“我爱 AI” → 分词[“我”, “爱”, “AI”] → 查词表 → [101, 2023, 9932, 102] → 添加特殊标记[CLS]…[SEP] → 填充/截断到固定长度。

不同的分词算法——为什么分词方式很重要?

  • Word-level(按词分割):“I love AI” → [“I”, “love”, “AI”]。问题:词表太大(英文几十万词),无法处理未登录词(OOV)。
  • Character-level(按字符分割):“AI” → [“A”, “I”]。问题:序列太长(一个句子变成几百个字符),语义信息弱。
  • Subword-level (按子词分割)——主流方案:“playing” → [“play”, “##ing”](##表示这是词的后半部分)。“ChatGPT” → [“Chat”, “##G”, “##PT”]。

Subword 优点:

  1. 词表大小适中(通常 3 万-5 万)
  2. 能处理任何未登录词(总能拆成子词)
  3. 保留了语义信息(“play"和"playing"共享"play”)

BPE 分词算法数值示例

BPE(Byte Pair Encoding)是最常用的 Subword 算法。以训练语料为 [“low”, “lower”, “newest”, “widest”] 为例,展示它如何自动学习子词:

Step 1:初始化——每个字符为一个 token,并加上词尾标记 </w>

初始拆分
lowl o w
lowerl o w e r
newestn e w e s t
widestw i d e s t

Step 2:统计所有相邻字符对的出现频率

字符对频率
e s2(newest + widest)
s t2(newest + widest)
l o2(low + lower)
o w2(low + lower)

Step 3:合并频率最高的对(假设 e+s → es)

合并后
lowl o w
lowerl o w e r
newestn e w es t
widestw i d es t

Step 4:重复合并(es+t → est → new+est → newest…)

最终学到的词表可能包含:[l, o, w, </w>, lo, low, est, newest, es, er, ...]

解读:BPE 通过频率自动发现"low"是一个整体、“est"是常见后缀、“er"是另一个后缀。这样 “lowest”(训练时没见过)可以拆分为 low + est,“newer” 可以拆分为 new + er——完美解决了未登录词问题。

2.2 Datasets——数据集的统一接口

from datasets import load_dataset

# 加载数据集(自动下载和缓存)
dataset = load_dataset('imdb')

# 查看数据结构
print(dataset)

# 查看第一个样本
print(dataset['train'][0])

# 数据预处理(用map函数批量处理)
def preprocess(examples):
    return tokenizer(examples['text'], truncation=True, padding=True)

tokenized_dataset = dataset.map(preprocess, batched=True)

2.3 Pipeline——一行代码完成推理

from transformers import pipeline

# 情感分析——一行代码!
classifier = pipeline('sentiment-analysis')
result = classifier("I love this movie!")

# 文本生成
generator = pipeline('text-generation', model='gpt2')
result = generator("Once upon a time", max_length=50)

# 问答系统
qa = pipeline('question-answering')
result = qa(question="What is AI?", context="AI is artificial intelligence...")

# 命名实体识别
ner = pipeline('ner', grouped_entities=True)
result = ner("My name is John and I live in New York.")

# 零样本分类(不需要训练数据!)
zero_shot = pipeline('zero-shot-classification')
result = zero_shot("This is a great movie!", candidate_labels=["positive", "negative", "neutral"])

Pipeline 的魔力:它自动帮你完成所有的预处理、模型加载、后处理。一行代码 = 完整的推理流程。

2.4 Trainer——标准化的训练框架

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir='./results',              # checkpoint 和日志保存目录
    num_train_epochs=3,                  # 训练轮数(微调通常 2-5 轮,太多会过拟合)
    per_device_train_batch_size=16,      # 训练批大小(越大梯度越稳定,但显存消耗越大)
    per_device_eval_batch_size=64,       # 评估批大小(不需要反向传播,可以用更大的 batch)
    warmup_steps=500,                    # 预热步数(前 500 步学习率从 0 线性增长到设定值)
                                         # 为什么需要预热?训练初期梯度不稳定,大学习率会破坏预训练权重
    weight_decay=0.01,                   # 权重衰减(L2 正则化,防止权重过大,减少过拟合)
    logging_dir='./logs',                # TensorBoard 日志目录
    evaluation_strategy='epoch',         # 每个 epoch 结束时评估一次(也可设为 'steps')
    save_strategy='epoch',               # 每个 epoch 结束时保存 checkpoint
    load_best_model_at_end=True,         # 训练结束后自动加载最佳 checkpoint(而非最后一个)
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    compute_metrics=compute_metrics,     # 自定义评估函数(如准确率、F1)
)

trainer.train()                          # 开始训练(自动处理梯度裁剪、混合精度等)
results = trainer.evaluate()             # 在评估集上计算指标
trainer.save_model('./my_model')         # 保存最终模型(用于部署或后续推理)

2.5 实战——中文情感分类完整流程

from datasets import load_dataset
from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments
from transformers import pipeline

# 1. 加载数据(ChnSentiCorp 是中文情感分类数据集,约 1 万条酒店评论)
dataset = load_dataset('seamew/ChnSentiCorp')

# 2. 分词:文本 → token IDs(BERT 输入格式)
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
def tokenize_function(examples):
    return tokenizer(examples['text'], padding='max_length', truncation=True, max_length=128)
    # max_length=128:中文评论通常较短,128 足够(BERT 最大支持 512)
tokenized = dataset.map(tokenize_function, batched=True)  # batched=True:批量处理,速度快 10 倍

# 3. 加载预训练模型(在 BERT 上加一个 768→2 的分类头)
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)

# 4. 训练(微调 BERT 的所有参数,学习率很小 2e-5 以避免破坏预训练知识)
training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,                    # 3 个 epoch 通常足够(微调不需要太多轮)
    per_device_train_batch_size=32,        # 批大小 32(越大越稳定,但显存消耗越大)
    learning_rate=2e-5,                    # 微调学习率:比从头训练小 100 倍(避免灾难性遗忘)
    evaluation_strategy='epoch',           # 每个 epoch 结束时评估一次
)
trainer = Trainer(model=model, args=training_args,
                  train_dataset=tokenized['train'], eval_dataset=tokenized['test'])
trainer.train()  # 约 5-10 分钟(GPU),准确率可达 90%+

# 5. 预测(加载最佳 checkpoint,用 Pipeline 一行推理)
classifier = pipeline('sentiment-analysis', model='./results/checkpoint-best')
print(classifier("这部电影太棒了!"))  # [{'label': 'LABEL_1', 'score': 0.99}]

模块小结: HuggingFace 生态

你学到了什么为什么重要
Tokenizer 分词文本与数字的桥梁
Subword 分词算法平衡词表大小和表达能力
Datasets 数据加载统一的数据集接口
Pipeline 一行推理快速验证模型效果
Trainer 训练框架标准化的训练流程
有了 Transformer(骨架)、BERT/GPT(预训练模型)、HuggingFace(工具链),我们现在可以把三者组合起来,解决真实的 NLP 任务。本节通过文本摘要、机器翻译、命名实体识别等实战任务,串联前面所有知识点,展示"从原始文本到模型部署"的完整流程。

3. NLP 实战与综合复习

3.1 NLP 任务全景——从输入到输出

flowchart LR
    RAW["原始文本"] --> TOK["Tokenizer<br/>分词+编码"]
    TOK --> MODEL["预训练模型<br/>BERT/GPT/T5"]
    MODEL --> HEAD["任务头"]
    HEAD --> T1["分类<br/>[CLS]→Linear"]
    HEAD --> T2["序列标注<br/>每个token→标签"]
    HEAD --> T3["生成<br/>自回归解码"]
    HEAD --> T4["问答<br/>起止位置预测"]

每种 NLP 任务的区别在于任务头的设计,而不是模型本身。BERT 已经"理解"了语言,我们只需要在上面加不同的"帽子”。

3.2 NLP 高级实战任务

文本摘要——压缩信息

输入:长文本(如新闻文章)。输出:简短摘要。

  • 方法 1 :抽取式——从原文中挑选重要句子组合成摘要
  • 方法 2 :生成式——用模型重新"写"一段摘要(更灵活,但可能编造信息)

使用 HuggingFace :

summarizer = pipeline('summarization')
summary = summarizer(long_text, max_length=50)

机器翻译——跨语言理解

使用预训练翻译模型:

translator = pipeline('translation_en_to_zh')
result = translator("I love deep learning")
# [{'translation_text': '我喜欢深度学习'}]

命名实体识别(NER)——序列标注实战

NER 是信息提取的基础任务:从文本中识别出人名、地名、组织名等实体。

from transformers import pipeline

# 使用预训练 NER 模型(一行代码)
ner = pipeline('ner', grouped_entities=True, model='dslim/bert-base-NER')
result = ner("George Washington went to Washington D.C. last Monday.")
# [{'entity_group': 'PER', 'word': 'George Washington', 'score': 0.99},
#  {'entity_group': 'LOC', 'word': 'Washington D.C.', 'score': 0.98}]

# 中文 NER 示例
# 注意:bert-base-chinese 未经 NER 微调,效果较差。
# 生产环境应使用微调过的模型,如 ericje/bert-base-chinese-ner
ner_zh = pipeline('ner', grouped_entities=True, model='bert-base-chinese')
result_zh = ner_zh("小明在北京大学读书。")

NER 的工作原理:取 BERT 每个 token 的输出 → 加一个分类头 → 预测 BIO 标签(B-PER=人名开始,I-PER=人名延续,O=非实体)。

阅读理解——问答系统实战

给模型一个问题和一段文本,让它找出答案所在的片段:

from transformers import pipeline

qa = pipeline('question-answering', model='bert-large-uncased-whole-word-masking-finetuned-squad')
result = qa(
    question="What is the capital of France?",
    context="France is a country in Europe. Its capital is Paris, which is known for the Eiffel Tower."
)
# {'answer': 'Paris', 'score': 0.99, 'start': 45, 'end': 50}

问答系统的工作原理:BERT 同时编码问题和文本 → 预测答案的起始位置和结束位置 → 提取片段。

3.3 AI 写诗项目——综合实战

项目流程:

  1. 数据准备:收集古诗数据集(如唐诗三百首)
  2. 数据预处理:分词、构建词表、转为数字序列
  3. 模型选择:使用 GPT2-Chinese 进行微调
  4. 训练:在古诗数据上微调模型
  5. 生成:给定开头(如"春风”),让模型生成完整诗歌

关键代码:

from transformers import GPT2LMHeadModel, BertTokenizer
# uer/gpt2-chinese-poem 使用 BERT 词表训练,因此用 BertTokenizer
model = GPT2LMHeadModel.from_pretrained('uer/gpt2-chinese-poem')

3.4 阶段总结——核心知识地图

  1. Transformer = Self-Attention + FFN + 残差连接 + LayerNorm
  2. Self-Attention = $Q \cdot K^T / \sqrt{d_k}$ → softmax → $\cdot V$
  3. Multi-Head = 多组 Q/K/V 并行计算,从不同角度理解
  4. 位置编码 = 正弦/余弦函数(注入顺序信息)
  5. BERT = Transformer Encoder × N (双向,擅长理解)
  6. GPT = Transformer Decoder × N (单向,擅长生成)
  7. HuggingFace = Tokenizer + Datasets + Pipeline + Trainer
  8. 微调 = 预训练模型 + 下游任务分类头 + 少量标注数据

模块小结: NLP 实战与综合复习

你学到了什么为什么重要
NLP 任务全景所有任务的区别在于任务头,而非模型本身
文本摘要抽取式 vs 生成式
命名实体识别从文本中提取结构化信息
问答系统让模型"阅读理解"并定位答案
综合实战项目串联所有知识的实践

3.5 初学者常见踩坑与解决方案

踩坑症状原因解决方案
忘记设置 pad_tokenGPT2 训练时报错 “pad_token not found”GPT2 默认没有 pad_tokentokenizer.pad_token = tokenizer.eos_token
GPU 内存不足(OOM)训练时崩溃,报 CUDA out of memorybatch_size 太大或序列太长减小 batch_size、缩短 max_length、使用 fp16=True 混合精度
学习率过大训练 loss 不降反升,准确率 ~50%微调学习率应比从头训练小 100 倍使用 2e-5 ~ 5e-5,配合 warmup_steps
灾难性遗忘微调后模型在通用任务上表现下降学习率过大,破坏了预训练知识使用小学习率 + weight_decay + 早停
tokenizer 不匹配模型输出全是乱码用了错误的 tokenizer(如用英文 tokenizer 处理中文)确保 tokenizer 和模型来自同一个 from_pretrained()
评估时忘记 model.eval()每次推理结果不同Dropout 和 BatchNorm 在训练和推理时行为不同推理前调用 model.eval(),Trainer 自动处理

推荐补充资源

知识点推荐资源说明
TransformerJay Alammar《The Illustrated Transformer》图解 Transformer ,必看
BERTJay Alammar《The Illustrated BERT》图解 BERT
GPTAndrej Karpathy《Let’s build GPT》从零实现 GPT
HuggingFaceHuggingFace 官方课程免费的 NLP 实战课程
注意力机制斯坦福 CS224nNLP 经典课程
TokenizerHuggingFace Tokenizers 文档分词器详解

下一篇预告:本文掌握了预训练语言模型和 HuggingFace 实战。在速通 AI(五):现代大模型架构——LLaMA 系列中,你将看到 Transformer 如何演进为 LLaMA——开源大模型的事实标准,以及 RMSNorm、RoPE、SwiGLU、KV Cache、GQA 等关键改进。