Appearance
章节4:NLP 下游任务
学习目标
- 掌握使用 BERT 进行文本分类与情感分析的微调流程
- 理解序列标注框架下的命名实体识别与 CRF 解码
- 了解 Transformer 在机器翻译与文本生成中的应用
- 区分检索式与生成式问答系统的原理
- 了解对话系统的基本架构与知识图谱的结合方式
4.1 文本分类与情感分析——BERT 微调
4.1.1 微调范式
预训练(通用语料)→ 保存权重 → 加载 + 任务头(分类层)→ 下游微调4.1.2 完整微调代码
python
from transformers import (BertTokenizer, BertForSequenceClassification,
Trainer, TrainingArguments)
from datasets import Dataset
import torch
# 1. 准备数据
train_texts = ["这部电影太精彩了", "剧情无聊透顶", "特效很棒,故事一般"]
train_labels = [1, 0, 1] # 1=正面, 0=负面
# 2. 加载预训练模型 + 分类头
model_name = "bert-base-chinese"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 3. 编码
def tokenize(batch):
return tokenizer(batch["text"], padding="max_length",
truncation=True, max_length=128)
dataset = Dataset.from_dict({
"text": train_texts,
"label": train_labels
}).map(tokenize, batched=True)
# 4. 配置训练参数
training_args = TrainingArguments(
output_dir="./bert_sentiment",
per_device_train_batch_size=8,
num_train_epochs=3,
learning_rate=2e-5,
save_strategy="no",
logging_steps=10,
)
# 5. 训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
)
trainer.train()
# 6. 推理
def predict_sentiment(text: str) -> dict:
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128)
outputs = model(**inputs)
probs = torch.nn.functional.softmax(outputs.logits, dim=-1)
label = "正面" if probs[0][1] > 0.5 else "负面"
return {"text": text, "情感": label, "置信度": probs[0][1].item():.4f}
print(predict_sentiment("这个产品物超所值"))4.1.3 微调关键参数
| 参数 | 推荐值 | 说明 |
|---|---|---|
| learning_rate | 2e-5 ~ 5e-5 | 预训练模型学习率不宜过大 |
| batch_size | 8 ~ 32 | 显存允许范围内尽量大 |
| epochs | 2 ~ 4 | 微调 epoch 较少,防止过拟合 |
| weight_decay | 0.01 | L2 正则化 |
4.2 命名实体识别与关系抽取
4.2.1 序列标注框架
NER 是典型的序列标注问题:
输入: "李华毕业于清华大学"
标签: B-PER E-PER O B-ORG I-ORG I-ORG O标签体系: BIO(Begin / Inside / Outside)
4.2.2 BERT + CRF 模型
python
from transformers import BertForTokenClassification
import torch.nn as nn
from torchcrf import CRF # pip install torchcrf
class BertNER(nn.Module):
def __init__(self, model_name="bert-base-chinese", num_labels=7):
super().__init__()
self.bert = BertForTokenClassification.from_pretrained(
model_name, num_labels=num_labels
)
self.crf = CRF(num_labels, batch_first=True)
def forward(self, input_ids, attention_mask, labels=None):
outputs = self.bert(
input_ids,
attention_mask=attention_mask,
output_hidden_states=True,
return_dict=True
)
emissions = outputs.logits # [batch, seq_len, num_labels]
if labels is not None:
# 使用 CRF 计算负对数似然损失
loss = -self.crf(emissions, labels, mask=attention_mask.bool())
return loss
else:
# 解码:维特比算法求最优路径
return self.crf.decode(emissions, mask=attention_mask.bool())
# 关系抽取(RE)是在 NER 基础上的管道任务:
# 1. 识别实体 → 2. 检测实体间的关系类别
# 常用方法:基于 BERT [CLS] 向量的关系分类4.2.3 CRF 解码的优势
纯 Softmax: B-PER(0.4) I-PER(0.4) O(0.2) ← 可能输出 B-PER→I-PER→I-PER
CRF + Viterbi: B-PER→I-PER→O ← 强制约束标签转移合法性CRF 在解码时学习标签之间的转移约束(如 I-PER 前面必须是 B-PER 或 I-PER)。
4.3 机器翻译与文本生成
4.3.1 Transformer 在翻译中的应用
机器翻译使用 Encoder-Decoder 架构:
编码器(Encoder): 读取源语言句子,生成上下文表示
解码器(Decoder): 基于编码器输出 + 已生成词,预测下一个目标词python
from transformers import MarianTokenizer, MarianMTModel
# 中译英模型
model_name = "Helsinki-NLP/opus-mt-zh-en"
tokenizer = MarianTokenizer.from_pretrained(model_name)
model = MarianMTModel.from_pretrained(model_name)
def translate(text: str) -> str:
inputs = tokenizer(text, return_tensors="pt", truncation=True)
translated = model.generate(**inputs, max_length=128)
return tokenizer.decode(translated[0], skip_special_tokens=True)
print(translate("自然语言处理是人工智能的重要分支"))
# 输出: Natural language processing is an important branch of artificial intelligence.4.3.2 文本生成策略
python
from transformers import GPT2LMHeadModel, GPT2Tokenizer
model = GPT2LMHeadModel.from_pretrained("gpt2")
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
prompt = "The future of AI is"
inputs = tokenizer(prompt, return_tensors="pt")
# 不同生成策略
strategies = {
"greedy": model.generate(**inputs, max_length=30, do_sample=False),
"beam_search": model.generate(**inputs, max_length=30, num_beams=5),
"sampling": model.generate(**inputs, max_length=30, do_sample=True, temperature=0.8),
"top_k": model.generate(**inputs, max_length=30, do_sample=True, top_k=50),
"top_p": model.generate(**inputs, max_length=30, do_sample=True, top_p=0.92),
}
for name, output_ids in strategies.items():
print(f"{name}: {tokenizer.decode(output_ids[0], skip_special_tokens=True)}\n")生成策略对比:
| 策略 | 多样性 | 确定性 | 适用场景 |
|---|---|---|---|
| Greedy | ❌ | ✅ | 确定性任务(翻译) |
| Beam Search | ❌ | ✅ | 翻译、摘要 |
| Temperature Sampling | ✅ | ❌ | 故事生成、对话 |
| Top‑K / Top‑P | ✅ | ✅ 可控 | 通用生成 |
4.4 问答系统与阅读理解
4.4.1 检索式 QA(Retrieval-Based)
用户问题 → 检索(BM25/向量检索)→ 候选答案 → 排序 → 返回最佳答案python
from sentence_transformers import SentenceTransformer, util
# 使用 Sentence-BERT 做语义检索
model = SentenceTransformer("all-MiniLM-L6-v2")
corpus = [
"北京是中国的首都",
"Python 是一种编程语言",
"地球是太阳系的行星"
]
query = "中国的首都是哪里?"
# 编码
corpus_emb = model.encode(corpus, convert_to_tensor=True)
query_emb = model.encode(query, convert_to_tensor=True)
# 检索
scores = util.cos_sim(query_emb, corpus_emb)[0]
best_idx = scores.argmax().item()
print(f"最佳答案: {corpus[best_idx]} (相似度: {scores[best_idx]:.4f})")4.4.2 生成式 QA(Generative QA / 抽取式 QA)
使用 BERT 做抽取式阅读理解:
python
from transformers import pipeline
# 使用预训练阅读理解模型
qa_pipeline = pipeline(
"question-answering",
model="bert-large-uncased-whole-word-masking-finetuned-squad"
)
context = """The Transformer model was introduced in 2017 in the paper
"Attention Is All You Need" by Vaswani et al. It has become the foundation
for many NLP models including BERT, GPT, and T5."""
result = qa_pipeline(
question="When was the Transformer model introduced?",
context=context
)
print(f"答案: {result['answer']} (置信度: {result['score']:.4f})")检索式 vs 生成式 QA:
| 维度 | 检索式 | 生成式 |
|---|---|---|
| 答案来源 | 已有文档库 | 模型生成 |
| 灵活度 | 有限(仅返回原文片段) | 高(可重组/总结) |
| 可解释性 | ✅ 可定位原文 | ❌ 黑盒生成 |
| 典型模型 | BM25 + BERT Reader | T5, GPT, ChatGPT |
4.5 对话系统与知识图谱
4.5.1 对话系统架构
用户输入
↓
[自然语言理解 NLU] → 意图识别 + 槽位填充
↓
[对话管理 DM] → 状态跟踪 + 策略决策
↓
[自然语言生成 NLG] → 系统回复
↓
输出给用户python
import re
class SimpleNLU:
"""简单的意图识别 + 槽位提取"""
def __init__(self):
self.intents = {
"查询天气": ["天气", "温度", "下雨", "晴天"],
"预订机票": ["机票", "航班", "订票", "飞往"],
"咨询时间": ["几点", "时间", "什么时候"]
}
def extract_intent(self, text: str) -> str:
for intent, keywords in self.intents.items():
for kw in keywords:
if kw in text:
return intent
return "闲聊"
def extract_slots(self, text: str) -> dict:
slots = {}
# 简单规则提取
city_match = re.search(r"([\u4e00-\u9fa5]{2,3}市?)", text)
if city_match:
slots["城市"] = city_match.group(1)
date_match = re.search(r"(\d{1,2}月\d{1,2}日?)", text)
if date_match:
slots["日期"] = date_match.group(1)
return slots
nlu = SimpleNLU()
text = "明天北京天气怎么样?"
print(f"意图: {nlu.extract_intent(text)}")
print(f"槽位: {nlu.extract_slots(text)}")4.5.2 知识图谱(Knowledge Graph)
知识图谱 = 实体(节点)+ 关系(边)
(北京) --首都--> (中国)
(Python) --是编程语言--> (Python)
(地球) --属于--> (太阳系)结合知识图谱的对话系统:
python
import networkx as nx
# 构建简易知识图谱
kg = nx.DiGraph()
kg.add_edge("北京", "中国", relation="首都")
kg.add_edge("上海", "中国", relation="直辖市")
kg.add_edge("Python", "编程语言", relation="属于")
kg.add_edge("TensorFlow", "深度学习框架", relation="属于")
def kg_lookup(entity: str) -> list:
"""查询知识图谱中的三元组"""
results = []
for _, neighbor, data in kg.edges(entity, data=True):
results.append(f"{entity} 的{data['relation']}是 {neighbor}")
return results
print("\n".join(kg_lookup("北京")))
# 输出: 北京的首都是 中国小结
- BERT 微调是文本分类的标准范式,只需在预训练模型上加一个分类头。
- NER 与关系抽取使用序列标注(BIO)+ CRF 解码,确保标签转移合规。
- 机器翻译使用 Encoder-Decoder 架构,文本生成可采用 Greedy / Beam Search / Sampling 等策略。
- QA 系统分检索式和生成式,前者可解释性强,后者更灵活。
- 对话系统由 NLU → DM → NLG 组成,可结合知识图谱提供结构化知识。
练习
- 用 BERT 在 IMDB 影评数据集(或中文酒店评论)上训练情感二分类模型,报告准确率。
- 使用
torchcrf结合 BERT 实现一个简单的 NER 模型,在人民日报 NER 数据集上验证。 - 用 HuggingFace 的 MarianMT 实现英译中翻译,并用 BLEU 指标评估翻译质量。
- 构建一个基于 Sentence-BERT 的检索式 FAQ 系统,支持回答"入学条件""学费"等常见问题。
- 挑战题: 设计一个简单的"景点问答"对话系统,前端接收自然语言问题,后端通过知识图谱查询返回结构化答案。覆盖"门票""开放时间""地址"三类问题。