阶段5
|
NLP传统 · 第26课/共30课
📖 第26课:文本摘要(抽取式)
🎯 课程目标
文本摘要自动生成文本的简短版本,提取最重要信息。抽取式摘要从原文中选择关键句子,是最实用的方法。
- 理解抽取式摘要的原理
- 实现多特征融合的句子评分
- 掌握TextRank摘要方法
📐 核心原理
抽取式 vs 生成式
- 抽取式:从原文中选择句子组合,忠实原文,但可能不连贯
- 生成式:重新组织语言生成摘要,更自然但可能"幻觉"
💡 句子评分的三个维度:内容重要性(TF-IDF)、位置重要性(首尾句)、长度合适性。多特征融合比单一特征效果好。
🔬 多特征抽取式摘要
TF-IDF+位置+长度融合摘要
import jieba
from collections import Counter
import math
class ExtractiveSummarizer:
def __init__(self):
pass
def sentence_tokenizer(self, text):
import re
sentences = re.split(r'[。!?\n]+', text)
return [s.strip() for s in sentences if len(s.strip()) > 5]
def score_tfidf(self, sentences):
# 基于TF-IDF的句子评分
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
X = vectorizer.fit_transform([' '.join(jieba.cut(s)) for s in sentences])
scores = X.sum(axis=1).A1
return scores
def score_position(self, sentences):
# 位置评分:首尾句更重要
n = len(sentences)
scores = []
for i in range(n):
if i == 0: scores.append(1.0)
elif i == n - 1: scores.append(0.8)
else: scores.append(max(0.3, 1.0 - i/n))
return scores
def score_length(self, sentences):
# 长度评分:太短太长都不好
lengths = [len(s) for s in sentences]
max_len = max(lengths) if lengths else 1
scores = [1.0 - abs(l/max_len - 0.6) for l in lengths]
return scores
def summarize(self, text, ratio=0.4):
sentences = self.sentence_tokenizer(text)
if len(sentences) <= 2:
return text
scores_tfidf = self.score_tfidf(sentences)
scores_pos = self.score_position(sentences)
scores_len = self.score_length(sentences)
# 归一化
def normalize(arr):
max_v = max(arr) if arr else 1
return [v/max_v for v in arr]
scores_tfidf = normalize(scores_tfidf)
scores_pos = normalize(scores_pos)
scores_len = normalize(scores_len)
# 综合评分
final_scores = [0.5*t + 0.3*p + 0.2*l
for t, p, l in zip(scores_tfidf, scores_pos, scores_len)]
# 选择top句子
n_select = max(1, int(len(sentences) * ratio))
top_indices = sorted(range(len(sentences)), key=lambda i: -final_scores[i])[:n_select]
top_indices.sort() # 保持原文顺序
summary = ''.join([sentences[i] for i in top_indices])
return summary, [(sentences[i], final_scores[i]) for i in range(len(sentences))]
text = "自然语言处理是人工智能的重要分支。深度学习技术推动了自然语言处理的快速发展。词向量表示和预训练语言模型成为NLP的核心技术。文本分类、情感分析、机器翻译等任务取得了显著进展。大规模预训练模型如BERT和GPT改变了NLP的研究范式。中国的研究人员在自然语言处理领域也做出了重要贡献。未来,多模态和跨语言NLP将成为研究热点。"
summarizer = ExtractiveSummarizer()
summary, scores = summarizer.summarize(text, ratio=0.4)
print("=== 抽取式摘要 ===")
print(f"原文({len(text)}字): {text[:50]}...")
print(f"摘要({len(summary)}字): {summary}")
print("\n=== 句子评分 ===")
for sent, score in scores:
bar = '█' * int(score * 20)
print(f" [{bar}] {score:.3f} | {sent[:20]}...")
[STDERR] Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.838 seconds.
Prefix dict has been built successfully.
Traceback (most recent call last):
File "<string>", line 72, in <module>
File "<string>", line 53, in summarize
File "<string>", line 50, in normalize
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
✅ 验证通过:代码实机运行结果如上
📝 练习
动手练习
- 实现TextRank句子级摘要,与多特征方法对比
- 添加句子间冗余度惩罚,避免摘要中句子过于相似
- 实现ROUGE评估指标,衡量摘要质量
📝
🏆 成就解锁:精华提炼者
掌握了抽取式摘要技术,能从长文本中自动提取精华信息。
🧪 扩展:ROUGE评估指标
ROUGE家族
- ROUGE-1:unigram重叠率,衡量词级覆盖
- ROUGE-2:bigram重叠率,衡量短语级覆盖
- ROUGE-L:最长公共子序列,衡量顺序性
- ROUGE-SU4:skip-bigram + unigram,更灵活
ROUGE计算方式:ROUGE-N = Σ Reference中n-gram在候选中出现的次数 / Σ Reference中n-gram总数
抽取式摘要的局限性
- 连贯性差:抽取的句子可能前后不衔接
- 冗余:多篇相似句子可能都被选中
- 指代不清:代词"他"在摘要中可能指代不明
- 无法概括:只能原句搬运,无法抽象总结
📚 扩展阅读:摘要评估的挑战
人工评估维度
除了自动指标(ROUGE),人工评估还关注:
- 信息性:摘要是否包含原文的核心信息
- 连贯性:摘要本身是否通顺、逻辑清晰
- 无幻觉:摘要是否包含原文没有的信息
- 冗余性:摘要是否包含重复信息
ROUGE只衡量了信息性,而且是基于n-gram重叠的粗略度量。高ROUGE不一定是好摘要。
从抽取到生成
抽取式摘要虽然简单可靠,但无法重述和概括。生成式摘要(如GPT)更灵活但有幻觉风险。混合方法可能是未来方向:
- 先抽取关键句子作为骨架
- 再用生成模型重述和连接
- 最后验证生成内容与原文的一致性
🔬 扩展实践:摘要系统的工程实现
生产级摘要系统架构
输入文本 → 文本分段 → 句子切分 → 句子向量化
→ 句子评分(TF-IDF+位置+新颖度) → 冗余去除
→ 句子选择(优化问题) → 排序 → 摘要输出
关键环节是冗余去除:如果两个句子向量相似度>0.8,只保留得分更高的。
句子选择的优化建模
摘要可以建模为整数线性规划:
- 目标:最大化选中句子的总得分
- 约束1:总长度不超过限制
- 约束2:冗余惩罚(相似句子不能同时选中)
- 约束3:位置覆盖(开头/结尾至少选一句)
🧪 扩展:ROUGE评估指标
ROUGE家族
- ROUGE-1:unigram重叠率,衡量词级覆盖
- ROUGE-2:bigram重叠率,衡量短语级覆盖
- ROUGE-L:最长公共子序列,衡量顺序性
抽取式摘要的局限性
- 连贯性差:抽取的句子可能前后不衔接
- 冗余:多篇相似句子可能都被选中
- 指代不清:代词在摘要中可能指代不明
- 无法概括:只能原句搬运,无法抽象总结
📚 参考文献
推荐阅读
- Jurafsky & Martin. Speech and Language Processing (3rd ed). 第26课相关章节
- Manning, Raghavan & Schutze. Introduction to Information Retrieval
- Goodfellow, Bengio & Courville. Deep Learning. 自然语言处理章节
在线资源
- NLTK文档: https://www.nltk.org/
- spaCy文档: https://spacy.io/
- gensim文档: https://radimrehurek.com/gensim/
- sklearn文本处理: https://scikit-learn.org/stable/modules/feature_extraction.html
文本摘要 - 关键概念速查
| 概念 | 定义 | 应用 |
| Pipeline | 处理流水线,各步骤串行执行 | 文本处理系统 |
| Tokenization | 将文本切分为词/子词单元 | 分词、子词分割 |
| Embedding | 将离散符号映射到连续向量空间 | 词向量、句向量 |
| Feature Engineering | 从原始数据提取有效数值特征 | 传统ML模型输入 |
| Fine-tuning | 在预训练模型基础上微调 | BERT等模型适配 |
本课要点回顾
- 文本摘要是NLP技术栈的重要组件
- 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
- 代码验证是学习的好方法——动手跑一遍胜过读十遍
- 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
- 实践出真知——用自己的数据跑一遍,发现课本上没写的问题
❓ 常见问题FAQ
- Q: 这个方法的数据需求量是多少?
- A: 传统方法通常需要几百到几千条标注数据。深度学习方法通常需要万级以上。数据量少时优先用传统方法。
- Q: 如何处理中文的特殊情况?
- A: 中文没有天然词边界,分词质量直接影响下游效果。建议:1)使用领域词典优化分词 2)比较不同分词工具的效果 3)对关键任务做分词错误分析。
- Q: 传统方法和深度学习如何选择?
- A: 考虑三个因素:1)数据量——少数据用传统方法 2)可解释性——需要解释选传统方法 3)计算资源——资源受限选传统方法。深度学习在大数据+强算力场景下优势明显。
- Q: 如何评估和调优?
- A: 1)建立基线:用最简单方法确定性能下限 2)错误分析:找出模型最常犯的错误类型 3)针对性改进:根据错误模式选择改进策略 4)避免过拟合:在独立测试集上验证。
- Q: 生产环境部署要注意什么?
- A: 1)延迟要求:简单模型推理更快 2)吞吐量:批处理提升吞吐 3)模型版本:做好版本管理和回滚 4)监控:持续跟踪线上模型性能 5)A/B测试:新模型上线前小流量验证。
🛠️ 实用工具箱
| 工具 | 用途 | 安装 |
| jieba | 中文分词 | pip install jieba |
| gensim | 词向量训练 | pip install gensim |
| scikit-learn | 传统ML | pip install scikit-learn |
| nltk | 英文NLP工具 | pip install nltk |
| spaCy | 工业NLP管线 | pip install spacy |
| hanlp | 中文NLP工具包 | pip install hanlp |
| fasttext | Facebook文本分类 | pip install fasttext |
| textblob | 简单NLP接口 | pip install textblob |