在上一节中,我们介绍了 TF-IDF 的基本原理:它通过结合词频(TF)和逆文档频率(IDF)来衡量每个词在文档中的重要性。理解了原理之后,我们可以用 Python 将文本转换为 TF-IDF 向量,从而实现关键词提取、文本相似度计算等实际应用。
一、TF-IDF 应用场景概览
TF-IDF 的典型应用包括:
关键词提取:从文档中提取最能代表内容的高权重词。
文本相似度计算:用向量表示文档,计算余弦相似度,衡量文档之间的内容相似性。
搜索与推荐:搜索引擎通过 TF-IDF 评估文档与查询关键词的匹配程度。
通过这些应用,TF-IDF 可以帮助我们把文本“量化”,用于分析、搜索和推荐系统。
二、动手玩转 TF-IDF:从文本到向量
在理论理解之后,我们可以用 Python 直接将文本转换为 TF-IDF 向量,方便进行关键词提取和文本相似度计算。
2.1 示例代码
假设有两句英文:
sentence_1="This is a good job.I will not miss it for anything"sentence_2="This is not good at all" 我们希望使用TfidfVectorizer将它们转换为 TF-IDF 特征向量:
fromsklearn.feature_extraction.textimportCountVectorizer,TfidfVectorizer# define tf-idftf_idf_vec=TfidfVectorizer(use_idf=True,# 使用逆文档频率smooth_idf=True,# 平滑 IDF,防止除零或负值ngram_range=(1,1),# 只使用单个词 (Unigram)stop_words='english'# 去掉英文停用词)# to use only bigrams ngram_range=(2,2)- 参数解释
use_idf=True:使用逆文档频率smooth_idf:是否平滑处理smooth_idf=True:使用平滑 IDF,避免除零问题(推荐)smooth_idf=False:不使用平滑,极少数情况下会导致 IDF 不稳定
ngram_range=(1,1):只考虑单个词(Unigram)stop_words='english':去掉英文停用词(如 “the”、“is”)
2.2 步骤说明
(1) 统一词汇表
- 扫描整个语料库,收集所有词(去除停用词)
- 每个词对应向量中的一列索引
(2) 生成TF-IDF向量
- 每篇文档的向量长度 = 词汇表大小
- 文档未出现的词 → 对应列为 0
- 因此无论文档长度或内容如何,每篇向量维度都一致
2.3 拟合语料fit()
在将文本转换为 TF-IDF 向量之前,需要先**“学习”语料库的词汇表**,这一步由fit()完成。
核心作用:扫描语料库中的所有文档,构建固定的词汇表(Vocabulary),记录每个词在向量中的索引位置。
注意:
fit()只学习词表,不返回向量。- 可以通过
get_feature_names_out()查看词表。 - 停用词(如英文的 “the”, “is” 等)会被自动过滤(如果在
TfidfVectorizer中指定stop_words='english')。
示例代码:
# 拟合语料tf_idf_vec.fit([sentence_1,sentence_2])# 查看词汇表print("Vocabulary:",tf_idf_vec.get_feature_names_out())输出:
Vocabulary: ['good' 'job' 'miss']说明:
- 在两条示例句子中,去掉英文停用词后,语料库中出现的有效词是
"good"、"job"和"miss"。 - 词汇表顺序对应向量列的索引,也就是说向量的第一列对应
"good",第二列对应"job",第三列对应"miss"。 - 这一步是后续 transform() 或 fit_transform() 的基础:所有文档向量的维度都是固定的,与词汇表大小一致。
- 在两条示例句子中,去掉英文停用词后,语料库中出现的有效词是
如果语料库很大,词汇表会非常长(可能有几十万甚至上百万个词),大部分文档向量元素仍然是 0 → 体现 TF-IDF 的稀疏性。
fit()不会计算每篇文档的 TF-IDF 值,仅仅是生成固定的向量空间。
2.4 转换文本为向量transform()
在完成对语料库的拟合 (fit()) 后,我们可以使用transform()方法,将任意文本转换为TF-IDF 向量。
核心作用:将文本映射到和训练语料一致的固定维度向量空间,保证每篇文档向量长度与词汇表大小一致。
特点:
- 只使用已拟合的词表(Vocabulary)中的词。
- 文本中未出现的词对应列值为 0。
- 新文本中未在原语料中出现的词会被忽略,TF-IDF = 0,不会改变向量维度。
(1) 示例代码
# 使用已有向量器进行 transformtf_idf_sentence=tf_idf_vec.transform([sentence_1,sentence_2])tf_idf_dataframe=pd.DataFrame(tf_idf_sentence.toarray(),columns=tf_idf_vec.get_feature_names_out())print(f"transform for sentences:\n{tf_idf_dataframe}")- 输出:
transformforsentences: good job miss00.4494360.6316670.63166711.0000000.0000000.000000- 可以看到:
- 第一条句子
"This is a good job. I will not miss it for anything"对应向量中"good"、"job"、"miss"的 TF-IDF 值都有非零值。 - 第二条句子
"This is not good at all"中只包含"good"→ 其他列为 0。
- 第一条句子
(2) 对新句子进行转换
对于未在训练语料中出现的新句子,transform()同样适用:
new_sentence=["This job is good but not perfect"]tf_idf_new_sentence=tf_idf_vec.transform(new_sentence)tf_idf_dataframe=pd.DataFrame(tf_idf_new_sentence.toarray(),columns=tf_idf_vec.get_feature_names_out())print(f"transform for sentences:\n{tf_idf_dataframe}")transformforsentences: good job miss00.5797390.8148020.0
说明:
"good"和"job"出现在训练语料中 → 有对应的 TF-IDF 值。"perfect"未在训练语料中出现 → 被忽略,TF-IDF = 0。- 保证了向量维度与训练语料一致,便于后续相似度计算或模型输入。
💡 总结:
transform()的核心是将任意文本映射到固定的向量空间,保证新文本和训练语料的特征维度一致,同时保持 TF-IDF 的稀疏性特点。
三、TF-IDF 计算示例:从公式到数值结果
为了更直观地理解 TF-IDF 的计算过程,我们用一个简单的中文语料库作为示例:
"机器学习是人工智能的分支""深度学习是机器学习的分支""自然语言处理需要机器学习" 我们希望计算词语机器学习在文档1中的 TF-IDF 值。
3.1 计算词频(TF)
首先计算词“机器学习”在文档1中的词频(Term Frequency, TF):
- “机器学习” 在文档1中出现1 次
- 文档1的总词数为6
因此:
TF机器学习=16 \mathcal{TF}_{\text{机器学习}} = \frac{1}{6}TF机器学习=61
这一步反映的是:这个词在当前文档中的重要程度。
3.2 计算逆文档频率(IDF)
接下来计算逆文档频率(Inverse Document Frequency, IDF),衡量这个词在整个语料库中的稀有程度。
语料库文档总数:N=3N=3N=3,“机器学习” 出现在3 篇文档中(即DF=3DF = 3DF=3)
使用平滑版本 IDF 公式:
IDF机器学习=log1+N1+DF+1=log1+31+3+1=1 \mathcal{IDF}_{\text{机器学习}} =\log \frac{1+\text{N}}{1 + \text{DF}} + 1= \log \frac{1+3}{1+3}+1=1IDF机器学习=log1+DF1+N+1=log1+31+3+1=1
因为“机器学习”出现在所有文档中,它并不是区分文档的关键词,所以 IDF 权重最低(=1)。
3.3 计算TF-IDF
最后,将 TF 和 IDF 相乘:
TF−IDF机器学习=TF机器学习×IDF机器学习=16×1=16 \mathcal{TF-IDF}_{\text{机器学习}} = \mathcal{TF}_{\text{机器学习}} \times \mathcal{IDF}_{\text{机器学习}} = \frac{1}{6} \times 1 = \frac{1}{6}TF−IDF机器学习=TF机器学习×IDF机器学习=61×1=61
3.4 直观理解
- TF:词在当前文档中出现得多不多
- IDF:这个词是不是“通用词”,能否区分文档
- TF-IDF:既在当前文档中重要,又能区分文档的词权重最高
在这个例子中,“机器学习”虽然在文档1中出现,但它在所有文档中都出现,因此区分度不高,最终权重也不高。
假设 “人工智能” 只出现在文档1,那么它的DF=1DF=1DF=1,IDF 会更大,因此 TF-IDF 权重会更高。
四、应用场景
TF-IDF 是经典文本表示方法,在信息检索、文本挖掘和 NLP 工程实践中被广泛使用。下面介绍两个最典型的应用场景。
4.1 关键词提取
TF-IDF 的核心思想是:在当前文档中频繁出现,但在整个语料库中较少出现的词,更可能是关键词。
因此,可以通过 TF-IDF 权重排序来提取文档关键词。
(1) 示例语料
假设我们的语料库如下,其中每个元素是一篇文档。
corpus=["this is a good book","this book is great","good book recommend","I really love this great book"] 每个字符串代表一篇文档,语料库中包含 4 篇文档。
(2) 计算TF-IDF
fit_transform()会完成两步操作:
- 构建词表(Vocabulary)
- 计算每个文档的 TF-IDF 向量表示
fromsklearn.feature_extraction.textimportTfidfVectorizer vectorizer=TfidfVectorizer()X=vectorizer.fit_transform(corpus)(3) 定义关键词提取函数
该函数对每篇文档,按 TF-IDF 权重排序,选取权重最高的top_n个词作为关键词
importnumpyasnpdefextract_keywords(tfidf_matrix,feature_names,top_n=3):keywords=[]foriinrange(tfidf_matrix.shape[0]):# 取出第 i 篇文档的 TF-IDF 向量row=np.squeeze(tfidf_matrix[i].toarray())# 按 TF-IDF 值排序,取前 top_n 个top_indices=row.argsort()[-top_n:][::-1]# 提取对应的关键词keywords.append([feature_names[j]forjintop_indices])returnkeywords(4) 查看结果
keywords=extract_keywords(X,vectorizer.get_feature_names_out())fori,kwinenumerate(keywords):print(f"文档{i+1}的关键词:{kw}")运行结果:
文档1的关键词:['good','is','this']文档2的关键词:['great','is','this']文档3的关键词:['recommend','good','book']文档4的关键词:['love','really','great']
- 结果分析
"recommend"、"love"等稀有词被识别为关键词- 高频通用词(this, is)仍然可能被选中,因此实际工程中通常需要去除停用词(stopwords)
- TF-IDF 是自动摘要、搜索关键词高亮、内容推荐的基础技术
4.2 文本相似度计算Document Similarity
使用TF-IDF向量计算余弦相似度
TF-IDF 向量可以看作是文档在高维空间中的坐标,因此可以使用向量相似度衡量文档语义相似性。
(1) 基本思想
- 每篇文档 → 一个 TF-IDF 向量
- 在向量空间中计算文档之间的距离或夹角
- 距离越近 / 角度越小 → 文档越相似
(2) 余弦相似度(Cosine Similarity)
在文本领域,最常用的是余弦相似度,它衡量两个向量之间的夹角:
similarity(di,dj)=cos(θij)=vi⋅vj∥vi∥∥vj∥ \text{similarity}(d_i,d_j) = \cos(\theta_{ij})= \frac{\mathbf{v}_i\cdot \mathbf{v}_j}{\|\mathbf{v}_i\| \|\mathbf{v}_j\|}similarity(di,dj)=cos(θij)=∥vi∥∥vj∥vi⋅vj
其中,
vi\mathbf{v}_ivi:文档did_idi的 TF-IDF 向量
vi⋅vj\mathbf{v}_i \cdot \mathbf{v}_jvi⋅vj:两个文档共享词汇的加权重叠
∥vi∥\|\mathbf{v}_i\|∥vi∥:向量长度,用于归一化
为什么使用余弦相似度?
- 文档长度不同会影响欧几里得距离
- 但长度不会影响主题方向
- 余弦相似度只关注词分布方向,更适合文本语义比较
取值范围:[0,1][0,1][0,1],越接近 1 表示两篇文档越相似,越接近 0 表示越不相似。
- 当两个文档共享更多高权重(TF-IDF 值高)的词时,它们的余弦相似度会更接近 1。
(3) 代码示例
fromsklearn.feature_extraction.textimportTfidfVectorizerfromsklearn.metrics.pairwiseimportcosine_similarity corpus=["this is a good book","this book is great","good book recommend","I really love this great book"]vectorizer=TfidfVectorizer()X=vectorizer.fit_transform(corpus)similarities=cosine_similarity(X[0:1],X[1:])fori,scoreinenumerate(similarities[0],start=2):print(f"文档 1 与文档{i}的相似度:{score:.3f}")这里使用
cosine_similarity()函数计算余弦相似度。fromsklearn.metrics.pairwiseimportcosine_similarity similarities=cosine_similarity(X[0:1],X[1:])cosine_similarity(A, B)计算矩阵A与B中各行向量之间的余弦相似度。
范围为[0, 1],越接近 1 表示文本语义越相似。
输出:
文档1与文档2的相似度:0.677 文档1与文档3的相似度:0.468 文档1与文档4的相似度:0.270
- 结果分析:
- 文档 1 与 文档 2 最相似,因为共享大量词(this, book, is)
- 文档 3 次之,部分关键词重叠
- 文档 4 语义差异较大,因此相似度最低
4.3 工业应用总结
| 应用领域 | TF-IDF 作用 |
|---|---|
| 搜索引擎 | 文档相关性排序 |
| 推荐系统 | 内容相似度计算 |
| 文本聚类 | 向量化输入 |
| 关键词提取 | 自动摘要 |
| 传统机器学习 | 特征工程 |
总结
TF-IDF 是传统 NLP 的工业基石。即使在大模型盛行的今天,它依然是计算成本最低、可解释性最强、工程实践中最稳定可靠的文本表示方法之一。