阶段2 | NLP传统 · 第7课/共30课

📖 第07课:词频与TF-IDF

🎯 课程目标

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

✅ 验证通过:代码实机运行结果如上

📝 练习

动手练习

  1. 对比不同IDF平滑公式对结果的影响
  2. 用TF-IDF实现一个简单的文档检索系统
  3. 探索TF-IDF的变体:BM25等
⚖️

🏆 成就解锁:词权大师

掌握了TF-IDF这一经典加权方案。

🧪 扩展:TF-IDF变体与BM25

TF变体

IDF变体

从TF-IDF到BM25

BM25是TF-IDF的进化版,解决了两个问题:

  1. TF饱和:第10次出现的词不应该比第1次重要10倍
  2. 文档长度归一化:长文档不一定更相关

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的工程实现

sklearn TfidfVectorizer参数调优

参数默认值说明
max_featuresNone限制特征数,防止维度爆炸
min_df1词至少在N个文档中出现,去低频词
max_df1.0词在超过比例的文档中出现则忽略
ngram_range(1,1)n-gram范围,(1,2)包含unigram+bigram
sublinear_tfFalseTrue时用1+log(tf)替代原始tf
norm'l2'归一化方式,l2使余弦相似度=点积

TF-IDF的在线更新

新文档到来时,需要更新IDF值。两种策略:

  1. 全量重算:简单但慢,适合批量更新
  2. 增量近似:新文档的IDF用旧IDF近似,定期全量重算

🧪 扩展:TF-IDF变体与BM25

TF变体

IDF变体

sklearn TfidfVectorizer参数调优

参数默认值说明
max_featuresNone限制特征数,防止维度爆炸
min_df1词至少在N个文档中出现
max_df1.0词在超过比例的文档中出现则忽略
sublinear_tfFalseTrue时用1+log(tf)替代原始tf
norm'l2'归一化方式

📚 参考文献

推荐阅读

在线资源

TF-IDF - 关键概念速查

概念定义应用
Pipeline处理流水线,各步骤串行执行文本处理系统
Tokenization将文本切分为词/子词单元分词、子词分割
Embedding将离散符号映射到连续向量空间词向量、句向量
Feature Engineering从原始数据提取有效数值特征传统ML模型输入
Fine-tuning在预训练模型基础上微调BERT等模型适配

本课要点回顾

  1. TF-IDF是NLP技术栈的重要组件
  2. 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
  3. 代码验证是学习的好方法——动手跑一遍胜过读十遍
  4. 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
  5. 实践出真知——用自己的数据跑一遍,发现课本上没写的问题

❓ 常见问题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传统MLpip install scikit-learn
nltk英文NLP工具pip install nltk
spaCy工业NLP管线pip install spacy
hanlp中文NLP工具包pip install hanlp
fasttextFacebook文本分类pip install fasttext
textblob简单NLP接口pip install textblob