LlamaIndex 系列【18】关键词检索(Keyword Search):TF-IDF 算法
2026/9/7 16:17:59 网站建设 项目流程

文章目录

  • 1. 算法介绍
  • 2. 整体流程
    • 2.1 📦 阶段一:离线预处理
    • 2.2 🔍 阶段二:在线实时检索
  • 3. 核心概念
    • 3.1 系统词汇表
    • 3.2 稀疏向量
    • 3.3 文档-词项矩阵
  • 4. 打分算法迭代演进
    • 4.1 版本一:简单命中计数打分
    • 4.2 版本二:TF 原始词频打分
    • 4.3 版本三:TF‑IDF(词频‑逆文档频率)
      • 步骤 1:计算 DF(文档频率)
      • 步骤 2:权重反转
      • 步骤 3:对数平滑得到标准 IDF
      • 步骤 4:TF‑IDF 加权计算
    • 5. TF-IDF 致命短板

1. 算法介绍

关键词检索Keyword Search)属于信息检索技术的基础方法之一,是一种通过输入关键字获取文档信息的查询方式。

这种技术几十年来一直驱动着数据库和搜索引擎的检索。它的简单性和高效性,使其成为现代RAG系统检索的核心组成部分。

基本思路统计文档与查询重叠词数量,重叠越多原始分数越高,但不理解语义,只看字面词共现。

示例,当前有三个做菜相关的文档:

  • Doc1:做川味水煮牛肉,首选牛里脊,搭配豆芽莴笋打底。
  • Doc2:这家川味菜馆的水煮牛肉支持外卖,可以在家点单食用。
  • Doc3:这家川味老店生意火爆,很多食客会在家点麻辣火锅。

用户输入提问:

怎么自制川味水煮牛肉?

输入会进行关键词拆分为:怎么、自制、川味、水煮牛肉,根据关键词三个文档都会被召回:

文档文档真实语义命中关键词字面命中数量
Doc1讲解自制水煮牛肉做法,匹配用户意图川味、水煮牛肉2
Doc2介绍餐馆外卖服务,并非做菜教程川味、水煮牛肉2
Doc3介绍自制麻辣火锅,与水煮牛肉无关川味1

其中Doc1Doc2字面命中数量一致,但语义相关性天差地别,简单的命中计数无法区分真实相关性。

2. 整体流程

TF‑IDF的完整链路分为知识库离线构建流程用户在线检索流程两大阶段:

  1. 离线阶段(预处理):对全部文档分词 → 构建全局词表 → 统计TF/DF→ 计算IDF,生成TF‑IDF稀疏向量库
  2. 在线阶段(实时查询):用户输入Query→ 分词 → 使用离线保存的IDF表计算QueryTF‑IDF稀疏向量 → 积分排序 → 返回Top‑K文档

2.1 📦 阶段一:离线预处理

一次性执行,知识库更新时重新跑:

  1. 原始文档输入:收集知识库全部非结构化文本。
  2. 文本清洗:去除标点、特殊符号、过滤无意义停用词(的、是、怎么)。
  3. 分词:将连续句子切割成独立词语列表。
  4. 构建全局词汇表:合并所有文档词语,去重,给每个词分配唯一固定下标,保证所有向量维度对齐。
  5. 计算 TF(词频 Term Frequency):针对每一篇文档,统计每个词汇在本文档内出现多少次。
  6. 计算 DF(文档频率 Document Frequency):统计整个知识库中,包含该词语的文档一共有多少篇。
  7. 计算 IDF(逆文档频率 Inverse Document Frequency)
    I D F ( w o r d ) = log ⁡ ( N D F ( w o r d ) ) IDF(word)=\log\left(\frac{N}{DF(word)}\right)IDF(word)=log(DF(word)N)
    N NN= 知识库文档总数;D F DFDF越大(词语越通用),I D F IDFIDF分值越低。
  8. 生成 TF‑IDF 加权向量
    T F ‑ I D F = T F × I D F TF‑IDF = TF × IDFTFIDF=TF×IDF
    每个词语最终权重 = 局部词频 × 全局稀有度权重
  9. 持久化存储:保存所有文档TF‑IDF稀疏向量,以及全局IDF字典,供线上查询复用。

2.2 🔍 阶段二:在线实时检索

每次用户提问执行:

  1. 用户输入自然语言Query
  2. Query使用和文档完全一致的清洗、分词规则
  3. 统计Query内每个词汇的局部词频TF
  4. 读取离线预计算的全局IDF字典,得到查询词语的加权分值。
  5. 通过倒排索引快速筛选出包含目标关键词的候选文档;对候选文档累加词语的TF‑IDF权重作为最终相关性得分。
  6. 按相关性分数降序排序,截取前 K 个文档作为检索结果返回。

3. 核心概念

3.1 系统词汇表

系统词汇表:整个知识库提前固定好的、不变化的全局词语清单,它给每一个词语分配唯一固定下标,用来统一所有文档向量的维度。

把全部文档里所有不重复的词收集、去重,整理成一份全局词列表,这份列表就是系统词汇表

上面的Doc1Doc2Doc3三个做菜文档,收集去重后的系统词汇表(固定顺序,全局唯一):

下标词语
0川味
1水煮牛肉
2牛里脊
3豆芽
4莴笋
5菜馆
6外卖
7麻辣火锅

3.2 稀疏向量

为了让知识库能够被检索,每个文档都会生成一个稀疏向量。

这些词频会被存储在一个向量中,这个向量为系统词汇表中的每个词都分配一个固定位置,向量中的每个数字,表示该词语在文本里出现的次数。

上面那套词汇表规则中,每个文档词频对应的向量为:

  • Doc1:[1, 1, 1, 1, 1, 0, 0, 0]
  • Doc2:[1, 1, 0, 0, 0, 1, 1, 0]
  • Doc3:[1, 0, 0, 0, 0, 0, 0, 1]

向量的维度很高,可能有数万个位置,由于绝大多数位置存放的值都是0,这类向量叫做稀疏向量Sparse Vectors)。

稠密向量Dense Vector)对比:

对比项TF‑IDF 向量(稀疏)Embedding嵌入向量(稠密)
生成方式分词+统计公式,无神经网络大模型/预训练Encoder推理输出
向量维度等于词表大小(上万~几十万维)固定小维度(512/768/1024)
向量特征大量0,稀疏数值连续非0,稠密
语义能力仅字面匹配,不理解语义承载语义,支持同义模糊匹配
相似度计算余弦/点积余弦相似度
检索定位稀疏检索(关键词召回)稠密检索(语义召回)

3.3 文档-词项矩阵

每一篇文档生成稀疏向量后,全部向量组合形成二维网格结构,这个表格叫文档-词项矩阵Document-Term Matrix)。

结构定义:

  • 行:词项(系统词汇表中)
  • 列:单篇文档

关于单元格的取值,可以有多种形式,比如:

  • One‑Hot形式:1代表词语在该文档出现,0代表未出现,只标记存在与否,不关心词汇出现多少次。
  • TF词频)形式:单元格填入该词在文档中的实际出现次数。数值越大,说明这个词在当前文档里反复出现,主题关联性更强。

上面的Doc1Doc2Doc3词项‑文档矩阵One‑Hot形式):

词项\文档Doc1Doc2Doc3
川味111
水煮牛肉110
牛里脊100
豆芽100
莴笋100
菜馆010
外卖010
麻辣火锅001

有了这个矩阵之后,我们就把非结构化的自然语言文本,转换成了计算机可以直接运算的数值化结构化数据

可以很方便地从一个词查找包含该词的所有文档,一般也叫倒排索引。因为通常我们是从文档出发,思考它包含哪些词(正向索引),这里是给定一个词语,列出所有包含这个词的文档。

词语对应文档列表
川味Doc1,Doc2,Doc3
水煮牛肉Doc1,Doc2
牛里脊Doc1
豆芽Doc1
莴笋Doc1
菜馆Doc2
外卖Doc2
麻辣火锅Doc3

4. 打分算法迭代演进

至此,文档与查询语句各自都拥有对应的稀疏向量,检索器接收查询语句后,接下来便可对全部文档执行打分与排序操作。

三代矩阵对比总结:

  • One‑Hot矩阵:仅标记是否出现,无权重差异
  • TF词频矩阵:仅统计文档内部频次,无法抑制通用词汇
  • TF‑IDF矩阵:全局加权,稀有业务词汇获得高分,无区分度的高频词权重归零

4.1 版本一:简单命中计数打分

最基础的打分规则:每命中一个关键词,文档获得对应积分,关键词只要出现一次即得1分,重复出现不会额外加分,后续可基于累计分值完成相关性排序。

2.5章节中的示例,查询文本:自制川味水煮牛肉?,提取全部关键词:自制、川味、水煮牛肉,命中文档:

词语对应文档列表
自制无匹配文档
川味Doc1,Doc2,Doc3
水煮牛肉Doc1,Doc2

每命中1个关键词得1分,这样就会得到一个得分排序:

  • Doc1:川味 + 水煮牛肉 =2 分
  • Doc2:川味 + 水煮牛肉 =2 分
  • Doc3:川味 =1 分

种基础打分机制存在短板:它仅判断关键词是否存在于文档中,不会统计关键词在文本内的出现次数。但现实场景里,同一个关键词反复出现,往往代表这份文档和用户查询的相关性更强,该简单算法无法捕捉这层信号。

4.2 版本二:TF 原始词频打分

一种简易优化方案:文档中每出现一次关键词,就累加一次分值。

假设三篇文档内部词汇出现频次如下:

关键词Doc1词频Doc2词频Doc3词频
自制200
川味312
水煮牛肉310
TF总分822

打分计算过程:

  • Doc1:自制(2) + 川味(3) + 水煮牛肉(3) =8 分
  • Doc2:自制(0) + 川味(1) + 水煮牛肉(1) =2 分
  • Doc3:自制(0) + 川味(2) + 水煮牛肉(0) =2 分

TF词频只统计单个文档内部词语出现多少次,但它存在一个明显缺陷:无法区分词语本身的珍贵程度。有些高频通用词(如自制)哪怕多次出现,对判断文档主题几乎没有区分价值;而小众专属词汇,才是判断相关性的核心线索。

4.3 版本三:TF‑IDF(词频‑逆文档频率)

TF-IDF的全称是Term Frequency-Inverse Document Frequency,中文叫词频 - 逆文档频率。它的核心思想很简单:如果一个词在某篇文章里出现很多次,但在其他文章里很少出现,那这个词对这篇文章就很重要 。‌‌

这个技术由两部分组成:

  • TF(词频)‌:衡量一个词在当前文档中出现的频率,出现越多越重要。
  • IDF(逆文档频率)‌:衡量一个词在整个文档集合中的稀有程度,越稀有的词权重越高。‌‌

IDF这个概念最早由英国学者Karen Spärck Jones1972年提出,后来与TF结合形成了现在广泛使用的TF-IDF算法 。‌‌是一种用于评估词语在文档中重要程度的统计方法‌,广泛应用于搜索引擎、文本分类和关键词提取等场景 。‌‌

IDF的完整计算分为4个关键步骤,最终生成TF‑IDF加权词项‑文档矩阵

步骤 1:计算 DF(文档频率)

文档频率D F DFDF代表:整个文档库中,包含该词的文档占全部文档的比例

计算公式:
D F ( w o r d ) = 包含该词的文档数量 文档库总文档数 DF(word) = \frac{\text{包含该词的文档数量}}{\text{文档库总文档数}}DF(word)=文档库总文档数包含该词的文档数量
D F DFDF值越大,代表这个词汇在语料中越普遍,区分文档差异的价值越低。

以本次案例为例,文档总数N = 3 N=3N=3

  • 词「川味」出现在3篇文档:D F = 3 3 = 1.0 DF=\dfrac{3}{3}=1.0DF=33=1.0
  • 词「水煮牛肉」出现在2篇文档:D F = 2 3 ≈ 0.67 DF=\dfrac{2}{3}\approx0.67DF=320.67
  • 词「牛里脊」仅出现在1篇文档:D F = 1 3 ≈ 0.33 DF=\dfrac{1}{3}\approx0.33DF=310.33

步骤 2:权重反转

DF的数值逻辑和我们想要的打分逻辑是相反的:

  • DF大(到处都出现)→ 我们希望权重低
  • DF小(很少出现)→ 我们希望权重高

DF取倒数,就是把分数逻辑翻转过来:
中间权重 = 1 D F ( w o r d ) = 总文档数量 包含该词的文档数量 \text{中间权重} = \frac{1}{DF(word)}=\frac{\text{总文档数量}}{\text{包含该词的文档数量}}中间权重=DF(word)1=包含该词的文档数量总文档数量

代入案例演算:

  1. 川味:D F = 1.0 DF=1.0DF=1.0,中间权重1 1.0 = 1 \displaystyle \frac{1}{1.0}=11.01=1
  2. 水煮牛肉:D F ≈ 0.67 DF≈0.67DF0.67,中间权重1 0.67 ≈ 1.5 \displaystyle \frac{1}{0.67}≈1.50.6711.5
  3. 牛里脊:D F ≈ 0.33 DF≈0.33DF0.33,中间权重1 0.33 ≈ 3 \displaystyle \frac{1}{0.33}≈30.3313
词项DF中间权重1 D F \boldsymbol{\dfrac{1}{DF}}DF1变化效果
川味1.01高频通用词,分值压低
水煮牛肉0.671.5中等稀有度,中等分值
牛里脊0.333稀有专属词,获得更高分值

直接取倒数会造成数值差距过大:稀有词权重爆炸放大。如果文档库很大(例如10000篇文档),只出现1次的词,中间权重直接等于10000,分值会严重失衡,打分结果失真。

因此需要下一步:对数平滑压缩数值范围。

步骤 3:对数平滑得到标准 IDF

引入对数函数压缩权重区间,得到工程上标准的逆文档频率IDF
I D F ( w o r d ) = log ⁡ ( 总文档数 包含该词的文档数 ) IDF(word)=\log\left(\frac{\text{总文档数}}{\text{包含该词的文档数}}\right)IDF(word)=log(包含该词的文档数总文档数)

案例计算:

  1. 川味:log ⁡ ( 1 ) = 0 \log(1)=0log(1)=0
  2. 水煮牛肉:log ⁡ ( 1.5 ) ≈ 0.41 \log(1.5)≈0.41log(1.5)0.41
  3. 牛里脊:log ⁡ ( 3 ) ≈ 1.10 \log(3)≈1.10log(3)1.10

对数不会改变大小关系:稀有词依然分数更高,只是把巨大的差值压缩到一个合理的小数区间。全局高频词最终权重归零,不再干扰检索打分。

词项命中文档数DFN 命中数 \boldsymbol{\dfrac{N}{命中数}}命中数N(倒数反转)IDF(对数平滑)
川味31.01 110 00
水煮牛肉20.671.5 1.51.50.41 0.410.41
牛里脊10.333 331.10 1.101.10

步骤 4:TF‑IDF 加权计算

T F ‑ I D F TF‑IDFTFIDF的计算公式:
T F ‑ I D F ( w o r d , d o c ) = T F ( w o r d , d o c ) × I D F ( w o r d ) \boldsymbol{TF‑IDF(word,doc) = TF(word,doc) \times IDF(word)}TFIDF(word,doc)=TF(word,doc)×IDF(word)

其中:

  • T F ( w o r d , d o c ) TF(word,doc)TF(word,doc)词在单篇文档内部的出现次数,代表词汇对这一篇文档的局部重要性
  • I D F ( w o r d ) IDF(word)IDF(word)词汇在整个文档库的全局权重,代表词汇本身的珍贵程度
  • T F ‑ I D F TF‑IDFTFIDF:二者相乘,得到该词汇在这篇文档里的最终加权得分

假设TF原始词项‑文档矩阵

词项Doc1(TF)Doc2(TF)Doc3(TF)
川味312
水煮牛肉310
牛里脊200

逐单元格手动演算:

  1. Doc1- 水煮牛肉:T F = 3 , I D F = 0.41 ⟹ 3 × 0.41 = 1.23 TF=3,\ IDF=0.41 \implies 3 \times 0.41 = \boldsymbol{1.23}TF=3,IDF=0.413×0.41=1.23
  2. Doc1- 牛里脊:T F = 2 , I D F = 1.10 ⟹ 2 × 1.10 = 2.20 TF=2,\ IDF=1.10 \implies 2 \times 1.10 = \boldsymbol{2.20}TF=2,IDF=1.102×1.10=2.20
  3. Doc1- 川味:T F = 3 , I D F = 0 ⟹ 3 × 0 = 0 TF=3,\ IDF=0 \implies 3 \times 0 = \boldsymbol{0}TF=3,IDF=03×0=0(通用词权重清零)

最终生成TF‑IDF 加权词项‑文档矩阵

词项 \ 文档Doc1Doc2Doc3
川味000
水煮牛肉1.230.410
牛里脊2.2000

当用户输入查询语句时,我们对查询文本执行完全相同的分词、TF‑IDF计算,生成查询向量;再将查询向量和矩阵中每一列的文档向量计算相似度,按照相似度从高到低完成文档排序,最终返回检索结果。

5. TF-IDF 致命短板

TF-IDF是传统稀疏检索的经典基线方案,依靠词频与全局词权重实现文档相关性打分,解决了基础命中打分、简易词频打分的核心缺陷。

但在实际检索场景中,原始TF-IDF算法存在两处致命短板,无法适配真实业务的检索需求:

  • 词频无上限,分数容易虚高:关键词无限堆砌即可拉高得分,低质量刷词文档排名靠前,排序失真。
  • 未做文档长度归一化:篇幅更长的文档天然拥有更多命中机会,打分对短而精炼的优质文档不公平。

正因TF‑IDF存在上述缺陷,工业界进一步演化出BM25算法,作为生产环境稀疏检索的标准实现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询