TF-IDF的计算过程和搜索过程

场景设定

我们有一个微型搜索引擎,索引了3个文档:

  • 文档1"苹果 苹果 手机"
  • 文档2"苹果 电脑"
  • 文档3"香蕉 水果"

用户搜索:"苹果 手机"


第一部分:TF-IDF计算过程(索引阶段)

步骤1:构建词汇表

扫描所有文档,得到所有不同的词:

  • 文档1:苹果, 手机
  • 文档2:苹果, 电脑
  • 文档3:香蕉, 水果

词汇表[苹果, 手机, 电脑, 香蕉, 水果]

步骤2:计算每个文档的词频(TF)

TF公式词在文档中出现的次数 / 文档总词数

文档 总词数 苹果 手机 电脑 香蕉 水果
文档1 3 2/3=0.667 1/3=0.333 0 0 0
文档2 2 1/2=0.500 0 1/2=0.500 0 0
文档3 2 0 0 0 1/2=0.500 1/2=0.500

步骤3:计算逆文档频率(IDF)

IDF公式log(文档总数 / 包含该词的文档数)

文档总数 N = 3

出现在哪些文档 文档数(DF) IDF = log(3/DF)
苹果 文档1, 文档2 2 log(3/2)=log(1.5)≈0.405
手机 文档1 1 log(3/1)=log(3)≈1.099
电脑 文档2 1 log(3/1)=log(3)≈1.099
香蕉 文档3 1 log(3/1)=log(3)≈1.099
水果 文档3 1 log(3/1)=log(3)≈1.099

步骤4:计算TF-IDF矩阵

TF-IDF = TF × IDF

文档\词 苹果 手机 电脑 香蕉 水果
文档1 0.667×0.405=0.270 0.333×1.099=0.366 0×1.099=0 0×1.099=0 0×1.099=0
文档2 0.500×0.405=0.203 0×1.099=0 0.500×1.099=0.550 0×1.099=0 0×1.099=0
文档3 0×0.405=0 0×1.099=0 0×1.099=0 0.500×1.099=0.550 0.500×1.099=0.550

步骤5:向量归一化(实际中重要的一步)

归一化公式向量中每个值 / 向量长度

复制代码
文档1向量:[0.270, 0.366, 0, 0, 0]
向量长度 = √(0.270² + 0.366²) = √(0.073 + 0.134) = √0.207 ≈ 0.455
归一化后:[0.270/0.455≈0.593, 0.366/0.455≈0.805, 0, 0, 0]

文档2向量:[0.203, 0, 0.550, 0, 0]  
向量长度 = √(0.203² + 0.550²) = √(0.041 + 0.303) = √0.344 ≈ 0.587
归一化后:[0.203/0.587≈0.346, 0, 0.550/0.587≈0.938, 0, 0]

文档3向量:[0, 0, 0, 0.550, 0.550]
向量长度 = √(0.550² + 0.550²) = √(0.303 + 0.303) = √0.606 ≈ 0.778
归一化后:[0, 0, 0, 0.550/0.778≈0.707, 0.550/0.778≈0.707]

最终TF-IDF矩阵(归一化后)

文档\词 苹果 手机 电脑 香蕉 水果
文档1 0.593 0.805 0 0 0
文档2 0.346 0 0.938 0 0
文档3 0 0 0 0.707 0.707

现在每个文档都有自己的"数字指纹"!


第二部分:搜索过程(查询阶段)

用户输入查询:"苹果 手机"

步骤1:将查询也转换为TF-IDF向量

1.1 计算查询的词频(TF)

查询:"苹果 手机"(2个词)

  • 苹果:出现1次 → TF = 1/2 = 0.5
  • 手机:出现1次 → TF = 1/2 = 0.5
1.2 使用相同的IDF值

关键:使用索引阶段计算好的IDF值!

  • 苹果的IDF:0.405
  • 手机的IDF:1.099
1.3 计算查询的TF-IDF
  • 苹果:0.5 × 0.405 = 0.203
  • 手机:0.5 × 1.099 = 0.550

查询向量:[0.203, 0.550, 0, 0, 0]

1.4 归一化查询向量
复制代码
向量长度 = √(0.203² + 0.550²) = √(0.041 + 0.303) = √0.344 ≈ 0.587
归一化后:[0.203/0.587≈0.346, 0.550/0.587≈0.938, 0, 0, 0]

最终查询向量 Q[0.346, 0.938, 0, 0, 0]

步骤2:计算查询与每个文档的相似度

使用余弦相似度相似度 = (向量A·向量B) / (|A|×|B|)

注意 :我们的向量已经归一化,长度都是1,所以:
相似度 = A·B = 对应位置相乘后求和

2.1 查询Q vs 文档1
复制代码
Q = [0.346, 0.938, 0, 0, 0]
文档1 = [0.593, 0.805, 0, 0, 0]

相似度 = 0.346×0.593 + 0.938×0.805 + 0+0+0
      = 0.205 + 0.755 = 0.960
2.2 查询Q vs 文档2
复制代码
Q = [0.346, 0.938, 0, 0, 0]
文档2 = [0.346, 0, 0.938, 0, 0]

相似度 = 0.346×0.346 + 0.938×0 + 0×0.938 + 0+0
      = 0.120 + 0 + 0 + 0 + 0 = 0.120
2.3 查询Q vs 文档3
复制代码
Q = [0.346, 0.938, 0, 0, 0]
文档3 = [0, 0, 0, 0.707, 0.707]

相似度 = 0×0.346 + 0×0.938 + 0×0 + 0×0.707 + 0×0.707 = 0

步骤3:排序并返回结果

文档 相似度 排名
文档1 0.960 🥇 第1名
文档2 0.120 🥈 第2名
文档3 0.000 🥉 第3名

搜索结果

  1. 文档1:"苹果 苹果 手机"(最相关!)
  2. 文档2:"苹果 电脑"(有一点相关)
  3. 文档3:"香蕉 水果"(完全不相关)

第三部分:深入分析"为什么这样排"

为什么文档1最相关?

复制代码
文档1:既有"苹果"(0.593)又有"手机"(0.805)
查询:既有"苹果"(0.346)又有"手机"(0.938)

完美匹配!相乘相加得到高分。

为什么文档2只有一点点相关?

复制代码
文档2:有"苹果"(0.346)但没有"手机"(0)
查询:有"苹果"(0.346)和"手机"(0.938)

只在"苹果"上匹配,但"手机"完全没匹配。
相似度 = 0.346×0.346 = 0.120

为什么文档3完全不相关?

复制代码
文档3:只有"香蕉"和"水果",没有查询中的任何词
所有对应位置相乘都是0

IDF的关键作用观察:

  • "手机"的IDF(1.099) > "苹果"的IDF(0.405)
  • 这意味着"手机"比"苹果"更有区分度
  • 文档1同时有"苹果"和"手机",所以得分最高
  • 如果只有TF没有IDF,权重会不同

第四部分:Python代码验证

python 复制代码
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer

# 1. 文档集
documents = [
    "苹果 苹果 手机",  # 文档1
    "苹果 电脑",      # 文档2
    "香蕉 水果"       # 文档3
]

# 2. 创建TF-IDF向量器
vectorizer = TfidfVectorizer()

# 3. 训练(索引阶段)
tfidf_matrix = vectorizer.fit_transform(documents)

print("词汇表:", vectorizer.get_feature_names_out())
print("\nTF-IDF矩阵(文档向量):")
print(tfidf_matrix.toarray())

# 4. 处理查询(搜索阶段)
query = "苹果 手机"
query_vector = vectorizer.transform([query])

print("\n查询向量:", query_vector.toarray())

# 5. 计算相似度(余弦相似度)
from sklearn.metrics.pairwise import cosine_similarity

similarities = cosine_similarity(query_vector, tfidf_matrix)
print("\n查询与每个文档的相似度:")
for i, sim in enumerate(similarities[0]):
    print(f"文档{i+1}: {sim:.3f}")

输出结果

复制代码
词汇表: ['手机' '水果' '电脑' '苹果' '香蕉']

TF-IDF矩阵:
[[0.70710678 0.         0.         0.70710678 0.        ]
 [0.         0.         0.89442719 0.4472136  0.        ]
 [0.         0.70710678 0.         0.         0.70710678]]

查询向量: [[0.70710678 0.         0.         0.70710678 0.        ]]

查询与每个文档的相似度:
文档1: 1.000  # 完美匹配!
文档2: 0.316  # 部分匹配
文档3: 0.000  # 完全不匹配

注:sklearn的TF-IDF计算与我们的手工计算略有不同(平滑处理不同),但结果趋势一致。


总结:TF-IDF搜索的核心流程

离线阶段(建索引):

复制代码
原始文档 → 分词清洗 → 计算TF-IDF矩阵 → 存储文档向量

在线阶段(处理查询):

复制代码
用户查询 → 同款分词清洗 → 计算查询向量 → 与所有文档向量计算相似度 → 按相似度排序 → 返回结果

关键洞察:

  1. TF-IDF将文字转换为数字,让计算机能计算相似度
  2. IDF是核心:让"手机"这种有区分度的词权重更高
  3. 归一化很重要:保证公平比较,不受文档长度影响
  4. 余弦相似度:衡量方向相似性,不受向量长度影响

这个简单例子展示了从文本到数学,再到排名的完整过程。实际搜索引擎就是这个原理的百万倍规模扩展!

相关推荐
光羽隹衡6 天前
机器学习——自然语言处理之关键词提取任务(TF-IDF)
机器学习·自然语言处理·tf-idf
Pyeako6 天前
机器学习--TF-IDF&红楼梦案例
机器学习·nlp·tf-idf·红楼梦·自然语言学习
子夜江寒9 天前
基于 TF-IDF 的《红楼梦》分卷文本关键词提取分析
tf-idf
薛不痒9 天前
机器学习算法之TF-idf
人工智能·算法·机器学习·tf-idf
啊巴矲10 天前
小白从零开始勇闯人工智能:机器学习初级篇(TF-IDF)
人工智能·机器学习·tf-idf
一招定胜负10 天前
基于TF-IDF完成《红楼梦》关键词提取(制作搜索引擎)
tf-idf
爱打代码的小林10 天前
机器学习(TF-IDF)
人工智能·tf-idf
郝学胜-神的一滴12 天前
机器学习特征提取:TF-IDF模型详解与实践指南
开发语言·人工智能·python·程序人生·机器学习·tf-idf·sklearn
子夜江寒18 天前
了解 TF-IDF
tf-idf
Pyeako18 天前
机器学习--K-means聚类&DBSCAN&TF-IDF
python·机器学习·kmeans·tf-idf·聚类·dbscan