🎯 目标:深入理解Transformer架构(整个大模型的基石),掌握BERT/GPT等预训练模型,熟练使用HuggingFace生态进行NLP任务开发。 📋 前置要求:阶段二(神经网络、CNN、RNN/LSTM、PyTorch、注意力机制基础)
本阶段知识依赖图
flowchart TD
A["阶段二基础<br/>RNN/LSTM + 注意力机制"]
A --> T["Transformer架构(最核心)"]
T --> T1[Self-Attention] --> T1a[Multi-Head Attention]
T --> T2[Positional Encoding]
T --> T3[Feed-Forward Network]
T --> T4["Add & Norm<br/>残差连接 + 层归一化"]
T --> T5[Encoder Block] --> T5a["Encoder × N"]
T --> T6[Decoder Block] --> T6a["Decoder × N"]
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[评估指标]
模块一:Transformer架构——大模型的基石 ⭐⭐⭐
这是整个课程中最重要的一个模块。 Transformer是GPT、BERT、LLaMA、ChatGPT等所有现代大模型的基础架构。理解了Transformer,就理解了大模型的"骨架"。
1.1 从宏观视角理解Transformer
Transformer之前的问题——为什么需要它?
RNN/LSTM的两大致命缺陷:
- 串行计算(效率低):处理第5个词时,必须等第1-4个词全部处理完 → 无法利用GPU的并行能力 → 一个1000词的句子,需要串行计算1000步。
- 长距离依赖(效果差):虽然LSTM缓解了梯度消失,但信息仍然需要"逐步传递"。第1个词的信息要经过999步才能到达第1000个词 → 每一步都有信息损失 → 超长序列效果仍然不好。
Transformer的革命性解决方案:
- 完全抛弃循环结构,用Self-Attention替代 → 所有位置同时计算(并行),训练速度提升10-100倍。
- 任意两个位置直接相连(不需要逐步传递) → 第1个词和第1000个词之间只有一步之遥 → 天然支持长距离依赖。
Transformer的整体结构——先看大局
类比:翻译任务就像"理解+写作"两个步骤。
Encoder(编码器) = “理解” —— 读懂原文。输入:“I love AI”,输出:每个词的"理解向量"(包含上下文信息)。
Decoder(解码器) = “写作” —— 根据理解写出译文。输入:已生成的部分译文 + Encoder的理解,输出:下一个词的概率分布。
原论文标题:“Attention Is All You Need”(2017年,Google)
核心洞察:不需要循环(RNN),不需要卷积(CNN),只需要注意力机制就够了!
1.2 Self-Attention——Transformer的灵魂
直觉理解:Self-Attention到底在做什么?
类比:一场会议讨论
想象一个6人会议,讨论"The cat sat on the mat"这个句子的含义:
每个人(词)都要弄清楚"我和谁的关系最密切"。
“cat"发言时:
- 看了看"The”:嗯,你是我的限定词,关系一般(权重0.05)
- 看了看自己"cat":我当然了解自己(权重0.15)
- 看了看"sat":你是我做的动作!关系最紧密(权重0.60)
- 看了看"on":介词,关系不大(权重0.05)
- 看了看了看"the":和我没关系(权重0.03)
- 看了看了看"mat":我坐在你上面,有点关系(权重0.12)
$"cat"的新表示 = 0.05 \times \text{The} + 0.15 \times \text{cat} + 0.60 \times \text{sat} + 0.05 \times \text{on} + 0.03 \times \text{the} + 0.12 \times \text{mat}$
现在"cat"的向量不仅包含自己的信息,还融合了所有相关词的信息!特别是"sat"的信息(权重最大),所以"cat"现在"知道"自己是句子的主语。
Self-Attention的数学计算:Query, Key, Value——完整推导
为什么要引入Q、K、V三个概念?
类比:图书馆找书
- 你(Query):想找一本关于"深度学习"的书
- 书架上的书(Key):每本书的标签/标题
- 书的内容(Value):每本书的实际内容
匹配过程:
- 你的需求(Query)和每本书的标签(Key)做匹配 → 得到相关性分数
- 根据相关性分数,从书的内容(Value)中提取信息
- 相关性高的书,多提取一些;相关性低的书,少提取一些
具体计算步骤(带维度标注):
假设:句子长度 seq_len=4,模型维度 d_model=512,头数 n_heads=8,每头维度 $d_k = 64$
输入矩阵 $X$:(4, 512) — 4个词,每个词512维
Step 1:生成Q、K、V(通过3个不同的线性变换)
$$ \begin{aligned} Q &= X \cdot W_Q \quad (4, 512) \times (512, 64) = (4, 64) \quad \text{← 每个词的"需求"} \\ K &= X \cdot W_K \quad (4, 512) \times (512, 64) = (4, 64) \quad \text{← 每个词的"特征标签"} \\ V &= X \cdot W_V \quad (4, 512) \times (512, 64) = (4, 64) \quad \text{← 每个词的"实际内容"} \end{aligned} $$为什么需要3个不同的变换?因为一个词的"我需要什么"(Q)、“我能提供什么”(K)、“我的内容”(V)是不同的。
Step 2:计算注意力分数
$$ \text{scores} = Q \cdot K^T \quad (4, 64) \times (64, 4) = (4, 4) \quad \text{← 每对词之间的相关性} $$Step 3:缩放(除以 $\sqrt{d_k} = \sqrt{64} = 8$)
$$ \text{scaled\_scores} = \text{scores} / 8 $$为什么要缩放?当 $d_k$ 很大时,$Q \cdot K^T$ 的值可能很大。大的输入值会导致softmax输出接近one-hot(梯度接近0)。除以 $\sqrt{d_k}$ 让方差回到1,softmax的梯度正常。
Step 4:Softmax归一化
$$ \text{attention\_weights} = \text{softmax}(\text{scaled\_scores}) \quad (4, 4) $$现在每行都是一个概率分布,表示"每个词应该关注谁"。
Step 5:加权求和
$$ \text{output} = \text{attention\_weights} \cdot V \quad (4, 4) \times (4, 64) = (4, 64) $$每个词的输出 = 所有词的Value的加权和(权重=注意力分数)。
一句话总结:Self-Attention让每个词都能"看到"句子中的所有其他词,并根据相关性加权聚合信息,生成包含上下文信息的新表示。
1.3 位置编码——告诉模型"词的顺序"
为什么需要位置编码?
核心问题:Self-Attention是"位置无关"的。
“狗咬人"和"人咬狗”——如果只看Self-Attention的计算,两个句子包含的词相同,Q、K、V的计算方式相同 → Self-Attention无法区分这两个句子!但它们的意思完全不同!→ 必须额外告诉模型"每个词在哪个位置"。
正弦位置编码——为什么用sin/cos?
$$ \begin{aligned} PE(pos, 2i) &= \sin(pos / 10000^{2i/d_{\text{model}}}) \\ PE(pos, 2i+1) &= \cos(pos / 10000^{2i/d_{\text{model}}}) \end{aligned} $$其中 $pos$ 是词在句子中的位置(0, 1, 2, …),$i$ 是编码维度的索引(0, 1, 2, …, $d_{\text{model}}/2-1$),$d_{\text{model}}$ 是模型维度。
为什么选择正弦/余弦?三个巧妙的原因:
- 每个位置有唯一的编码:sin和cos的组合可以唯一标识每个位置,就像每个GPS坐标是唯一的一样。
- 相对距离可以用线性变换表示:$PE(pos+k)$ 可以用 $PE(pos)$ 的线性变换来表示(因为 $\sin(a+b) = \sin(a)\cos(b) + \cos(a)\sin(b)$)→ 模型可以学到"距离关系"(相邻、隔一个、隔两个…)。
- 可以外推到更长的序列:正弦函数是周期性的,可以计算任意位置的编码 → 即使训练时没见过1000个词的句子,推理时也能处理。
词嵌入 + 位置编码——两者如何结合?
$$ \text{最终输入} = \text{Token Embedding} + \text{Positional Encoding} $$- Token Embedding:词本身的语义信息(可学习的参数矩阵)。“猫” → [0.2, 0.8, -0.1, …](512维向量)
- Positional Encoding:词的位置信息(固定的正弦函数)。位置0 → [0.0, 1.0, 0.0, …](512维向量)
相加后:[0.2, 1.8, -0.1, …]。模型同时知道"这个词是猫"(语义)和"它在第1个位置"(位置)。
类比:就像给每个词发了一张"身份证",上面写着姓名(Token Embedding)和地址(Positional Encoding)。
1.4 多头注意力与Encoder Block
多头注意力——为什么要"多头"?
类比:多角度分析
单头注意力 = 一个人看问题,只能从一个角度理解。多头注意力 = 多个人同时看问题,每人从不同角度理解,最后综合。
例如理解"The animal didn’t cross the street because it was too tired":
- Head 1 可能学到:“it"指代"animal”(指代关系)
- Head 2 可能学到:“tired"修饰"animal”(修饰关系)
- Head 3 可能学到:“didn’t cross"和"tired"有因果关系(逻辑关系)
单头很难同时学到这三种关系,但多头可以!
数学实现——维度追踪:
假设 $d_{\text{model}} = 512$,$n_{\text{heads}} = 8$,$d_k = d_{\text{model}} / n_{\text{heads}} = 64$
输入 $X$:(batch, seq_len, 512)
$$ \text{head}_i = \text{Attention}(X \cdot W_{Q_i}, X \cdot W_{K_i}, X \cdot W_{V_i}) $$$$ = \text{softmax}\left(\frac{(X \cdot W_{Q_i})(X \cdot W_{K_i})^T}{\sqrt{64}}\right) \cdot (X \cdot W_{V_i}) $$每个head的输出:(batch, seq_len, 64)
合并所有头:
$$ \text{MultiHead} = \text{Concat}(\text{head}_1, \ldots, \text{head}_8) \cdot W_O = (batch, \text{seq\_len}, 8 \times 64) \cdot (512, 512) = (batch, \text{seq\_len}, 512) $$输出维度和输入维度相同(512),方便堆叠多层!
Encoder Block的完整结构——逐层理解
输入 $x$:(batch, seq_len, 512)
- Multi-Head Self-Attention(x, x, x) → 输出:(batch, seq_len, 512)
- Add & LayerNorm:$\text{norm}_1(x + \text{attn\_output})$。残差连接:保留原始信息 + 新学到的信息。层归一化:稳定数值范围。
- Feed-Forward Network:Linear(512 → 2048) → ReLU → Linear(2048 → 512)
- Add & LayerNorm:$\text{norm}_2(h + \text{ffn\_output})$
输出:(batch, seq_len, 512) ← 维度不变,可以堆叠N层。
Feed-Forward Network(FFN)——每个位置的"独立思考”
$$ \text{FFN}(x) = \max(0, x \cdot W_1 + b_1) \cdot W_2 + b_2 $$维度变化:512 → 2048 → 512。先升维4倍(512→2048),增加表达能力,再降回原维度(2048→512),方便后续处理。
关键:FFN对每个位置是独立计算的!位置1的FFN不看位置2、3、4的输出。FFN的作用是"独立思考":在Attention已经收集了全局信息后,每个位置独立地对这些信息做非线性变换。
Attention vs FFN的分工:
- Attention:收集信息(“看看别人说了什么”)→ 类比:看参考资料
- FFN:处理信息(“想想自己该怎么理解”)→ 类比:独立答题
1.5 Masked Self-Attention与Decoder
为什么Decoder需要Mask?
类比:考试时不能偷看答案
训练时,我们知道完整的译文:“我 爱 大 模型”。但我们不能让模型"偷看"未来的词!
- 预测"爱"时:只能看到 “<start>” 和 “我”
- 预测"大"时:只能看到 “<start>"、“我” 和 “爱”
- 预测"模型"时:只能看到 “<start>"、“我”、“爱” 和 “大”
如果不加Mask,模型会直接抄答案,什么都学不到!
Mask的实现——下三角矩阵
注意力分数矩阵(4个词):
| start | 我 | 爱 | 大 | |
|---|---|---|---|---|
| start | ✓ | ✗ | ✗ | ✗ |
| 我 | ✓ | ✓ | ✗ | ✗ |
| 爱 | ✓ | ✓ | ✓ | ✗ |
| 大 | ✓ | ✓ | ✓ | ✓ |
✓ = 可以看到(正常计算注意力),✗ = 不能看到(设为 $-\infty$,softmax后变为0)
$$ \text{scores} = \text{scores.masked\_fill}(\text{mask} == 0, -10^9) $$Encoder-Decoder Cross-Attention——连接两个世界的桥梁
Decoder的第二个Attention层(Cross-Attention):
- Query来自Decoder(“我在找什么信息?")
- Key和Value来自Encoder(“源语言提供了什么信息?")
类比:翻译时的"参考原文”。Query = 你正在翻译的当前词的需求(“我现在需要翻译’爱’,原文中哪里有相关信息?")。Key/Value = 原文中每个词的信息。
Cross-Attention让Decoder在生成每个译文词时,都能"回头看"原文的相关部分 → 这就是"注意力对齐”:自动学到"哪个译文词对应哪个原文词”。
1.6 Transformer代码实现
import torch
import torch.nn as nn
import math
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, n_heads):
super().__init__()
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads
self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)
self.W_O = nn.Linear(d_model, d_model)
def forward(self, Q, K, V, mask=None):
batch_size = Q.size(0)
Q = self.W_Q(Q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
K = self.W_K(K).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
V = self.W_V(V).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attn_weights = torch.softmax(scores, dim=-1)
context = torch.matmul(attn_weights, V)
context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
output = self.W_O(context)
return output
class TransformerBlock(nn.Module):
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
self.attention = MultiHeadAttention(d_model, n_heads)
self.norm1 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.ReLU(),
nn.Linear(d_ff, d_model)
)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
attn_output = self.attention(x, x, x, mask)
x = self.norm1(x + self.dropout(attn_output))
ffn_output = self.ffn(x)
x = self.norm2(x + self.dropout(ffn_output))
return x
1.7 核心总结
Transformer的五大核心创新
- Self-Attention:让每个位置都能直接访问其他所有位置 → 解决了RNN的"逐步传递"问题
- Multi-Head Attention:从多个角度并行建模关系 → 同时捕获语法、语义、位置等多种关系
- Positional Encoding:用正弦/余弦函数注入位置信息 → 让模型知道"词的顺序”
- 残差连接 + LayerNorm:稳定深层网络训练 → 来自ResNet的智慧,让梯度可以"跳过"层
- 并行计算:所有位置同时计算 → 训练速度比RNN快10-100倍
Transformer vs RNN 对比
| 特性 | RNN | Transformer |
|---|---|---|
| 计算方式 | 串行(逐步) | 并行(所有位置同时) |
| 长距离依赖 | 困难(梯度消失) | 容易(直接连接) |
| 计算复杂度 | $O(n \cdot d^2)$ | $O(n^2 \cdot d)$ |
| 训练速度 | 慢 | 快(10-100倍) |
| 序列长度限制 | 无理论限制 | 受限于显存($n^2$ 注意力) |
| 参数量 | 少 | 多(但效果更好) |
| 实际表现 | 较好 | 显著更好(大模型时代的基石) |
模块二:BERT与GPT——预训练语言模型
2.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的两个预训练任务——从海量文本中学习语言
任务1:Masked Language Model(MLM)——完形填空
训练方式:随机遮盖15%的词,让模型预测被遮盖的词。输入:“The [MASK] sat on the mat”,目标:预测 [MASK] = “cat”。
为什么遮盖15%而不是100%?遮盖太多:上下文信息不足,模型猜不准。遮盖太少:训练效率低(每个句子只学一个词)。15%是实验验证的最佳比例。
为什么用[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亿参数
输入表示(三种嵌入相加):Token Embedding + Segment Embedding(句子A/B标记)+ Position Embedding(位置信息)= 最终输入。
特殊标记:[CLS] 放在句子开头,用于分类任务的输出。[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已经"理解"了语言,只需要在上面加一个简单的分类头。
2.2 GPT——自回归生成的大模型
GPT的核心思想——预测下一个词
GPT = Generative Pre-trained Transformer = Transformer Decoder
训练目标极其简单:给定前文,预测下一个词。
- “今天” → 预测"天气"
- “今天天气” → 预测"很好"
- “今天天气很好” → 预测"。"
这就是自回归(Autoregressive)生成:每次只生成一个词,然后把这个词加入输入,继续生成下一个词。就像"滚雪球"一样,越滚越大。
GPT vs BERT——核心区别对比
| 特性 | BERT | GPT |
|---|---|---|
| 架构 | Transformer Encoder | Transformer Decoder |
| 方向 | 双向(同时看前后文) | 单向(只看前面的词) |
| 训练目标 | 遮盖词预测(MLM) | 预测下一个词(自回归) |
| 注意力 | 没有Mask,所有位置互相看 | 有Mask,只能看前面的位置 |
| 擅长任务 | 理解类(分类、NER、问答) | 生成类(文本生成、对话) |
| 代表模型 | BERT, RoBERTa, ALBERT | GPT-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):多模态,推理能力大幅提升。可以理解图片、代码、数学。
2.3 BERT实战——微调中文分类
from transformers import BertTokenizer, BertForSequenceClassification
import torch
# 1. 加载预训练模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
# 2. 数据预处理
text = "这部电影真的很好看"
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
# 3. 前向传播
outputs = model(**inputs, labels=torch.tensor([1]))
loss = outputs.loss
logits = outputs.logits # shape: (1, 2)
# 4. 预测
prediction = torch.argmax(logits, dim=1) # 0=负面,1=正面
print(f"预测:{'正面' if prediction == 1 else '负面'}")
2.4 预训练语言模型总结对比
| 模型 | 架构 | 方向 | 预训练任务 | 擅长任务 |
|---|---|---|---|---|
| BERT | Encoder | 双向 | MLM + NSP | 分类、NER、问答 |
| GPT | Decoder | 单向 | 预测下一个词 | 文本生成、对话 |
| T5 | Encoder-Decoder | 双向 | 文本到文本 | 翻译、摘要、所有任务 |
| LLaMA | Decoder | 单向 | 预测下一个词 | 通用大模型 |
| ChatGLM | Prefix LM | 双向 | 混合 | 对话、理解 |
模块三:HuggingFace生态——NLP开发的瑞士军刀
3.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优点:
- 词表大小适中(通常3万-5万)
- 能处理任何未登录词(总能拆成子词)
- 保留了语义信息(“play"和"playing"共享"play”)
3.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)
3.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的魔力:它自动帮你完成所有的预处理、模型加载、后处理。一行代码 = 完整的推理流程。
3.4 Trainer——标准化的训练框架
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
warmup_steps=500,
weight_decay=0.01,
logging_dir='./logs',
evaluation_strategy='epoch',
save_strategy='epoch',
load_best_model_at_end=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
results = trainer.evaluate()
trainer.save_model('./my_model')
3.5 实战——中文情感分类完整流程
from datasets import load_dataset
from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments
from transformers import pipeline
# 1. 加载数据
dataset = load_dataset('csv', data_files='ChnSentiCorp.csv')
# 2. 分词
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
def tokenize_function(examples):
return tokenizer(examples['text'], padding='max_length', truncation=True, max_length=128)
tokenized = dataset.map(tokenize_function, batched=True)
# 3. 加载预训练模型
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
# 4. 训练
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=32,
learning_rate=2e-5,
evaluation_strategy='epoch',
)
trainer = Trainer(model=model, args=training_args,
train_dataset=tokenized['train'], eval_dataset=tokenized['test'])
trainer.train()
# 5. 预测
classifier = pipeline('sentiment-analysis', model='./results/checkpoint-best')
print(classifier("这部电影太棒了!"))
模块四:NLP实战与综合复习
4.1 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': '我喜欢深度学习'}]
4.2 AI写诗项目——综合实战
项目流程:
- 数据准备:收集古诗数据集(如唐诗三百首)
- 数据预处理:分词、构建词表、转为数字序列
- 模型选择:使用GPT2-Chinese进行微调
- 训练:在古诗数据上微调模型
- 生成:给定开头(如"春风"),让模型生成完整诗歌
关键代码:
from transformers import GPT2LMHeadModel, BertTokenizer
model = GPT2LMHeadModel.from_pretrained('uer/gpt2-chinese-poem')
4.3 阶段总结——核心知识地图
- Transformer = Self-Attention + FFN + 残差连接 + LayerNorm
- Self-Attention = $Q \cdot K^T / \sqrt{d_k}$ → softmax → $\cdot V$
- Multi-Head = 多组Q/K/V并行计算,从不同角度理解
- 位置编码 = 正弦/余弦函数(注入顺序信息)
- BERT = Transformer Encoder × N(双向,擅长理解)
- GPT = Transformer Decoder × N(单向,擅长生成)
- HuggingFace = Tokenizer + Datasets + Pipeline + Trainer
- 微调 = 预训练模型 + 下游任务分类头 + 少量标注数据
📚 推荐补充资源
| 知识点 | 推荐资源 | 说明 |
|---|---|---|
| Transformer | Jay Alammar《The Illustrated Transformer》 | 图解Transformer,必看 |
| BERT | Jay Alammar《The Illustrated BERT》 | 图解BERT |
| GPT | Andrej Karpathy《Let’s build GPT》 | 从零实现GPT |
| HuggingFace | HuggingFace官方课程 | 免费的NLP实战课程 |
| 注意力机制 | 斯坦福CS224n | NLP经典课程 |
| Tokenizer | HuggingFace Tokenizers文档 | 分词器详解 |