阶段2
|
NLP传统 · 第7课/共30课
📖 第07课:词频与TF-IDF
🎯 课程目标
TF-IDF是信息检索和文本挖掘的经典算法,衡量词对文档的重要性。理解它是理解更高级词表示的基础。
- 从零实现TF-IDF算法
- 理解TF、IDF各自的含义与变体
- 掌握sklearn的TfidfVectorizer使用
- 利用TF-IDF计算文档相似度
📐 核心原理
TF-IDF(t,d) = TF(t,d) × IDF(t)
IDF(t) = log(N / df(t)),其中N为文档总数,df(t)为包含词t的文档数
💡 直觉:"的"在每篇文档都出现(低IDF),对区分文档无帮助;"量子"只在少数文档出现(高IDF),是强信号词。
🔬 手动实现TF-IDF
手动TF-IDF
import math
from collections import Counter
documents = [
"自然语言处理 是 人工智能 的 分支",
"深度学习 推动 了 自然语言处理 的 发展",
"人工智能 和 机器学习 改变 了 世界",
"自然语言处理 包括 分词 词性标注 命名实体识别"
]
doc_tokens = [doc.split() for doc in documents]
def compute_tf(tokens):
tf = Counter(tokens)
total = len(tokens)
return {word: count/total for word, count in tf.items()}
def compute_idf(docs):
N = len(docs)
idf = {}
all_words = set(w for doc in docs for w in doc)
for word in all_words:
df = sum(1 for doc in docs if word in doc)
idf[word] = math.log((N + 1) / (df + 1)) + 1
return idf
def compute_tfidf(tf, idf):
return {word: tf_val * idf.get(word, 0) for word, tf_val in tf.items()}
idf = compute_idf(doc_tokens)
print("=== IDF值 ===")
for word, val in sorted(idf.items(), key=lambda x: -x[1])[:10]:
print(f" {word}: {val:.4f}")
print("\n=== 各文档TF-IDF Top 3 ===")
for i, tokens in enumerate(doc_tokens):
tf = compute_tf(tokens)
tfidf = compute_tfidf(tf, idf)
top3 = sorted(tfidf.items(), key=lambda x: -x[1])[:3]
print(f" 文档{i+1}: {[(w, f'{v:.4f}') for w, v in top3]}")
=== IDF值 ===
推动: 1.9163
是: 1.9163
机器学习: 1.9163
发展: 1.9163
命名实体识别: 1.9163
分词: 1.9163
世界: 1.9163
和: 1.9163
包括: 1.9163
分支: 1.9163
=== 各文档TF-IDF Top 3 ===
文档1: [('是', '0.3833'), ('分支', '0.3833'), ('人工智能', '0.3022')]
文档2: [('深度学习', '0.3194'), ('推动', '0.3194'), ('发展', '0.3194')]
文档3: [('和', '0.3194'), ('机器学习', '0.3194'), ('改变', '0.3194')]
文档4: [('包括', '0.3833'), ('分词', '0.3833'), ('词性标注', '0.3833')]
✅ 验证通过:代码实机运行结果如上
🔧 sklearn TF-IDF与文档相似度
sklearn TF-IDF
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import jieba
corpus = [
"自然语言处理是人工智能的分支",
"深度学习推动了自然语言处理的发展",
"人工智能和机器学习改变了世界",
"自然语言处理包括分词词性标注命名实体识别"
]
corpus_spaced = [' '.join(jieba.cut(doc)) for doc in corpus]
vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
tfidf_matrix = vectorizer.fit_transform(corpus_spaced)
print("=== sklearn TF-IDF ===")
print(f"词表大小: {len(vectorizer.get_feature_names_out())}")
print(f"矩阵形状: {tfidf_matrix.shape}")
feature_names = vectorizer.get_feature_names_out()
for i in range(len(corpus)):
row = tfidf_matrix[i].toarray()[0]
top_indices = row.argsort()[-5:][::-1]
top_words = [(feature_names[j], f"{row[j]:.4f}") for j in top_indices if row[j] > 0]
print(f" 文档{i+1}: {top_words}")
sim = cosine_similarity(tfidf_matrix)
print("\n=== 文档余弦相似度 ===")
for i in range(len(corpus)):
for j in range(i+1, len(corpus)):
print(f" 文档{i+1}↔文档{j+1}: {sim[i][j]:.4f}")
=== sklearn TF-IDF ===
词表大小: 22
矩阵形状: (4, 22)
文档1: [('分支', '0.4964'), ('是', '0.4964'), ('的', '0.3914'), ('人工智能', '0.3914'), ('处理', '0.3169')]
文档2: [('深度', '0.4196'), ('推动', '0.4196'), ('发展', '0.4196'), ('了', '0.3308'), ('学习', '0.3308')]
文档3: [('改变', '0.4129'), ('机器', '0.4129'), ('世界', '0.4129'), ('和', '0.4129'), ('了', '0.3256')]
文档4: [('词性', '0.3577'), ('识别', '0.3577'), ('标注', '0.3577'), ('包括', '0.3577'), ('分词', '0.3577')]
=== 文档余弦相似度 ===
文档1↔文档2: 0.2992
文档1↔文档3: 0.1274
文档1↔文档4: 0.1447
文档2↔文档3: 0.2154
文档2↔文档4: 0.1223
文档3↔文档4: 0.0000
✅ 验证通过:代码实机运行结果如上
📝 练习
动手练习
- 对比不同IDF平滑公式对结果的影响
- 用TF-IDF实现一个简单的文档检索系统
- 探索TF-IDF的变体:BM25等
⚖️
🏆 成就解锁:词权大师
掌握了TF-IDF这一经典加权方案。
🧪 扩展:TF-IDF变体与BM25
TF变体
- 原始TF:直接计数
- 对数TF:1 + log(tf),防止高频词过度影响
- 增强TF:0.5 + 0.5 * tf / max_tf,归一化到[0.5, 1]
IDF变体
- 标准IDF:log(N/df)
- 平滑IDF:log((N+1)/(df+1)) + 1,sklearn默认
- 概率IDF:log((N-df)/df),更理论化
从TF-IDF到BM25
BM25是TF-IDF的进化版,解决了两个问题:
- TF饱和:第10次出现的词不应该比第1次重要10倍
- 文档长度归一化:长文档不一定更相关
BM25公式:score = IDF * (tf * (k1+1)) / (tf + k1 * (1-b+b*dl/avgdl))
📚 扩展阅读:向量空间模型
Salton的向量空间模型(1975)
将文档表示为词频向量,是信息检索的奠基工作。核心思想:
- 文档 = 词频空间中的点
- 相似度 = 向量夹角的余弦
- 查询也是向量,检索 = 找最近的文档向量
这个模型统治了信息检索30年,直到深度学习方法出现。但BM25(TF-IDF的进化版)至今仍是搜索引擎的基石。
TF-IDF的理论解释
TF-IDF可以从信息论的角度理解:
- TF反映了词在文档中的信息量
- IDF反映了词的区分度——越稀有的词区分度越高
- 乘积相当于"在文档中重要且全局稀有的词",这正是关键词的特征
🔬 扩展实践:TF-IDF的工程实现
sklearn TfidfVectorizer参数调优
| 参数 | 默认值 | 说明 |
| max_features | None | 限制特征数,防止维度爆炸 |
| min_df | 1 | 词至少在N个文档中出现,去低频词 |
| max_df | 1.0 | 词在超过比例的文档中出现则忽略 |
| ngram_range | (1,1) | n-gram范围,(1,2)包含unigram+bigram |
| sublinear_tf | False | True时用1+log(tf)替代原始tf |
| norm | 'l2' | 归一化方式,l2使余弦相似度=点积 |
TF-IDF的在线更新
新文档到来时,需要更新IDF值。两种策略:
- 全量重算:简单但慢,适合批量更新
- 增量近似:新文档的IDF用旧IDF近似,定期全量重算
🧪 扩展:TF-IDF变体与BM25
TF变体
- 原始TF:直接计数
- 对数TF:1 + log(tf),防止高频词过度影响
- 增强TF:0.5 + 0.5 * tf / max_tf,归一化到[0.5, 1]
IDF变体
- 标准IDF:log(N/df)
- 平滑IDF:log((N+1)/(df+1)) + 1,sklearn默认
sklearn TfidfVectorizer参数调优
| 参数 | 默认值 | 说明 |
| max_features | None | 限制特征数,防止维度爆炸 |
| min_df | 1 | 词至少在N个文档中出现 |
| max_df | 1.0 | 词在超过比例的文档中出现则忽略 |
| sublinear_tf | False | True时用1+log(tf)替代原始tf |
| norm | 'l2' | 归一化方式 |
📚 参考文献
推荐阅读
- Jurafsky & Martin. Speech and Language Processing (3rd ed). 第7课相关章节
- 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
TF-IDF - 关键概念速查
| 概念 | 定义 | 应用 |
| Pipeline | 处理流水线,各步骤串行执行 | 文本处理系统 |
| Tokenization | 将文本切分为词/子词单元 | 分词、子词分割 |
| Embedding | 将离散符号映射到连续向量空间 | 词向量、句向量 |
| Feature Engineering | 从原始数据提取有效数值特征 | 传统ML模型输入 |
| Fine-tuning | 在预训练模型基础上微调 | BERT等模型适配 |
本课要点回顾
- TF-IDF是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 |