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. 余弦相似度:衡量方向相似性,不受向量长度影响

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

相关推荐
合作小小程序员小小店1 个月前
web开发,在线%高校舆情分析%系统demo,基于python,flaskweb,echart,nlp,ida,tf-idf,多爬虫源,数据库mysql
人工智能·python·flask·html5·tf-idf
nju_spy1 个月前
牛客网 AI题(二)机器学习 + 深度学习
人工智能·机器学习·笔试·tf-idf·pca·位置编码·k-means
nju_spy3 个月前
机器学习 - Kaggle项目实践(8)Spooky Author Identification 作者识别
人工智能·深度学习·机器学习·nlp·tf-idf·glove·南京大学
nju_spy3 个月前
机器学习 - Kaggle项目实践(7)NLP with Disaster Tweets 灾难消息
人工智能·深度学习·自然语言处理·bert·tf-idf·glove·南京大学
2202_756749693 个月前
自然处理语言NLP:One-Hot编码、TF-IDF、词向量、NLP特征输入、EmbeddingLayer实现、word2vec
人工智能·深度学习·自然语言处理·tf-idf·word2vec
nju_spy3 个月前
机器学习 - Kaggle项目实践(4)Toxic Comment Classification Challenge 垃圾评论分类问题
人工智能·深度学习·自然语言处理·tf-idf·南京大学·glove词嵌入·双头gru
fsnine3 个月前
机器学习——TF-IDF算法
tf-idf
qqxhb4 个月前
零基础数据结构与算法——第七章:算法实践与工程应用-搜索引擎
算法·搜索引擎·tf-idf·倒排索引·pagerank·算法库
赴3354 个月前
机器学习 TF-IDF提取关键词,从原理到实践的文本特征提取利器
人工智能·机器学习·tf-idf·sklearn