文章目录
- [1. 算法介绍](#1. 算法介绍)
- [2. 整体流程](#2. 整体流程)
-
- [2.1 📦 阶段一:离线预处理](#2.1 📦 阶段一:离线预处理)
- [2.2 🔍 阶段二:在线实时检索](#2.2 🔍 阶段二:在线实时检索)
- [3. 核心概念](#3. 核心概念)
-
- [3.1 系统词汇表](#3.1 系统词汇表)
- [3.2 稀疏向量](#3.2 稀疏向量)
- [3.3 文档-词项矩阵](#3.3 文档-词项矩阵)
- [4. 打分算法迭代演进](#4. 打分算法迭代演进)
-
- [4.1 版本一:简单命中计数打分](#4.1 版本一:简单命中计数打分)
- [4.2 版本二:TF 原始词频打分](#4.2 版本二:TF 原始词频打分)
- [4.3 版本三:TF‑IDF(词频‑逆文档频率)](#4.3 版本三:TF‑IDF(词频‑逆文档频率))
-
- [步骤 1:计算 DF(文档频率)](#步骤 1:计算 DF(文档频率))
- [步骤 2:权重反转](#步骤 2:权重反转)
- [步骤 3:对数平滑得到标准 IDF](#步骤 3:对数平滑得到标准 IDF)
- [步骤 4:TF‑IDF 加权计算](#步骤 4:TF‑IDF 加权计算)
- [5. TF-IDF 致命短板](#5. TF-IDF 致命短板)
1. 算法介绍
关键词检索 (Keyword Search)属于信息检索技术的基础方法之一,是一种通过输入关键字获取文档信息的查询方式。
这种技术几十年来一直驱动着数据库和搜索引擎的检索。它的简单性和高效性,使其成为现代
RAG系统检索的核心组成部分。
基本思路 :统计文档与查询重叠词数量,重叠越多原始分数越高,但不理解语义,只看字面词共现。
示例,当前有三个做菜相关的文档:
Doc1:做川味水煮牛肉,首选牛里脊,搭配豆芽莴笋打底。Doc2:这家川味菜馆的水煮牛肉支持外卖,可以在家点单食用。Doc3:这家川味老店生意火爆,很多食客会在家点麻辣火锅。
用户输入提问:
text
怎么自制川味水煮牛肉?
输入会进行关键词拆分为:怎么、自制、川味、水煮牛肉,根据关键词三个文档都会被召回:
| 文档 | 文档真实语义 | 命中关键词 | 字面命中数量 |
|---|---|---|---|
| Doc1 | 讲解自制水煮牛肉做法,匹配用户意图 | 川味、水煮牛肉 | 2 |
| Doc2 | 介绍餐馆外卖服务,并非做菜教程 | 川味、水煮牛肉 | 2 |
| Doc3 | 介绍自制麻辣火锅,与水煮牛肉无关 | 川味 | 1 |
其中
Doc1、Doc2字面命中数量一致,但语义相关性天差地别,简单的命中计数无法区分真实相关性。
2. 整体流程
TF‑IDF 的完整链路分为 知识库离线构建流程 和 用户在线检索流程 两大阶段:
- 离线阶段(预处理) :对全部文档分词 → 构建全局词表 → 统计
TF/DF→ 计算IDF,生成TF‑IDF稀疏向量库 - 在线阶段(实时查询) :用户输入
Query→ 分词 → 使用离线保存的IDF表计算Query的TF‑IDF稀疏向量 → 积分排序 → 返回Top‑K文档
2.1 📦 阶段一:离线预处理

一次性执行,知识库更新时重新跑:
- 原始文档输入:收集知识库全部非结构化文本。
- 文本清洗 :去除标点、特殊符号、过滤无意义停用词(
的、是、怎么)。 - 分词:将连续句子切割成独立词语列表。
- 构建全局词汇表:合并所有文档词语,去重,给每个词分配唯一固定下标,保证所有向量维度对齐。
- 计算 TF(词频 Term Frequency):针对每一篇文档,统计每个词汇在本文档内出现多少次。
- 计算 DF(文档频率 Document Frequency) :统计整个知识库中,包含该词语的文档一共有多少篇。
- 计算 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 N N = 知识库文档总数; D F DF DF 越大(词语越通用), I D F IDF IDF 分值越低。 - 生成 TF‑IDF 加权向量
T F ‑ I D F = T F × I D F TF‑IDF = TF × IDF TF‑IDF=TF×IDF
每个词语最终权重 = 局部词频 × 全局稀有度权重 - 持久化存储 :保存所有文档
TF‑IDF稀疏向量,以及全局IDF字典,供线上查询复用。
2.2 🔍 阶段二:在线实时检索

每次用户提问执行:
- 用户输入自然语言
Query。 - 对
Query使用和文档完全一致的清洗、分词规则。 - 统计
Query内每个词汇的局部词频TF。 - 读取离线预计算的全局
IDF字典,得到查询词语的加权分值。 - 通过倒排索引快速筛选出包含目标关键词的候选文档 ;对候选文档累加词语的
TF‑IDF权重作为最终相关性得分。 - 按相关性分数降序排序,截取前 K 个文档作为检索结果返回。
3. 核心概念
3.1 系统词汇表
系统词汇表:整个知识库提前固定好的、不变化的全局词语清单,它给每一个词语分配唯一固定下标,用来统一所有文档向量的维度。
把全部文档里所有不重复的词收集、去重,整理成一份全局词列表,这份列表就是系统词汇表。
上面的 Doc1、Doc2、Doc3 三个做菜文档,收集去重后的系统词汇表(固定顺序,全局唯一):
| 下标 | 词语 |
|---|---|
| 0 | 川味 |
| 1 | 水煮牛肉 |
| 2 | 牛里脊 |
| 3 | 豆芽 |
| 4 | 莴笋 |
| 5 | 菜馆 |
| 6 | 外卖 |
| 7 | 麻辣火锅 |
3.2 稀疏向量
为了让知识库能够被检索,每个文档都会生成一个稀疏向量。
这些词频会被存储在一个向量中,这个向量为系统词汇表中的每个词都分配一个固定位置,向量中的每个数字,表示该词语在文本里出现的次数。
上面那套词汇表规则中,每个文档词频对应的向量为:
Doc1:1, 1, 1, 1, 1, 0, 0, 0Doc2:1, 1, 0, 0, 0, 1, 1, 0Doc3: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(词频)形式:单元格填入该词在文档中的实际出现次数。数值越大,说明这个词在当前文档里反复出现,主题关联性更强。
上面的 Doc1、Doc2、Doc3 词项‑文档矩阵 (One‑Hot 形式):
| 词项\文档 | Doc1 | Doc2 | Doc3 |
|---|---|---|---|
| 川味 | 1 | 1 | 1 |
| 水煮牛肉 | 1 | 1 | 0 |
| 牛里脊 | 1 | 0 | 0 |
| 豆芽 | 1 | 0 | 0 |
| 莴笋 | 1 | 0 | 0 |
| 菜馆 | 0 | 1 | 0 |
| 外卖 | 0 | 1 | 0 |
| 麻辣火锅 | 0 | 0 | 1 |
有了这个矩阵之后,我们就把非结构化的自然语言文本,转换成了计算机可以直接运算的数值化结构化数据。
可以很方便地从一个词查找包含该词的所有文档,一般也叫倒排索引 。因为通常我们是从文档出发,思考它包含哪些词(正向索引),这里是给定一个词语,列出所有包含这个词的文档。
| 词语 | 对应文档列表 |
|---|---|
| 川味 | 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词频 |
|---|---|---|---|
| 自制 | 2 | 0 | 0 |
| 川味 | 3 | 1 | 2 |
| 水煮牛肉 | 3 | 1 | 0 |
| TF总分 | 8 | 2 | 2 |
打分计算过程:
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 Jones在1972年提出,后来与TF结合形成了现在广泛使用的TF-IDF算法 。是一种用于评估词语在文档中重要程度的统计方法,广泛应用于搜索引擎、文本分类和关键词提取等场景 。
IDF 的完整计算分为 4 个关键步骤,最终生成 TF‑IDF 加权词项‑文档矩阵。
步骤 1:计算 DF(文档频率)
文档频率 D F DF DF 代表:整个文档库中,包含该词的文档占全部文档的比例。
计算公式:
D F ( w o r d ) = 包含该词的文档数量 文档库总文档数 DF(word) = \frac{\text{包含该词的文档数量}}{\text{文档库总文档数}} DF(word)=文档库总文档数包含该词的文档数量
D F DF DF 值越大,代表这个词汇在语料中越普遍,区分文档差异的价值越低。
以本次案例为例,文档总数 N = 3 N=3 N=3:
- 词「川味」出现在
3篇文档: D F = 3 3 = 1.0 DF=\dfrac{3}{3}=1.0 DF=33=1.0 - 词「水煮牛肉」出现在
2篇文档: D F = 2 3 ≈ 0.67 DF=\dfrac{2}{3}\approx0.67 DF=32≈0.67 - 词「牛里脊」仅出现在
1篇文档: D F = 1 3 ≈ 0.33 DF=\dfrac{1}{3}\approx0.33 DF=31≈0.33
步骤 2:权重反转
DF 的数值逻辑和我们想要的打分逻辑是相反的:
DF大(到处都出现)→ 我们希望权重低DF小(很少出现)→ 我们希望权重高
对 DF 取倒数,就是把分数逻辑翻转过来:
中间权重 = 1 D F ( w o r d ) = 总文档数量 包含该词的文档数量 \text{中间权重} = \frac{1}{DF(word)}=\frac{\text{总文档数量}}{\text{包含该词的文档数量}} 中间权重=DF(word)1=包含该词的文档数量总文档数量
代入案例演算:
- 川味: D F = 1.0 DF=1.0 DF=1.0,中间权重 1 1.0 = 1 \displaystyle \frac{1}{1.0}=1 1.01=1
- 水煮牛肉: D F ≈ 0.67 DF≈0.67 DF≈0.67,中间权重 1 0.67 ≈ 1.5 \displaystyle \frac{1}{0.67}≈1.5 0.671≈1.5
- 牛里脊: D F ≈ 0.33 DF≈0.33 DF≈0.33,中间权重 1 0.33 ≈ 3 \displaystyle \frac{1}{0.33}≈3 0.331≈3
| 词项 | DF | 中间权重 1 D F \boldsymbol{\dfrac{1}{DF}} DF1 | 变化效果 |
|---|---|---|---|
| 川味 | 1.0 | 1 | 高频通用词,分值压低 |
| 水煮牛肉 | 0.67 | 1.5 | 中等稀有度,中等分值 |
| 牛里脊 | 0.33 | 3 | 稀有专属词,获得更高分值 |
直接取倒数会造成数值差距过大:稀有词权重爆炸放大。如果文档库很大(例如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(包含该词的文档数总文档数)
案例计算:
- 川味: log ( 1 ) = 0 \log(1)=0 log(1)=0
- 水煮牛肉: log ( 1.5 ) ≈ 0.41 \log(1.5)≈0.41 log(1.5)≈0.41
- 牛里脊: log ( 3 ) ≈ 1.10 \log(3)≈1.10 log(3)≈1.10
对数不会改变大小关系:稀有词依然分数更高,只是把巨大的差值压缩到一个合理的小数区间。全局高频词最终权重归零,不再干扰检索打分。
| 词项 | 命中文档数 | DF | N 命中数 \boldsymbol{\dfrac{N}{命中数}} 命中数N(倒数反转) | IDF(对数平滑) |
|---|---|---|---|---|
| 川味 | 3 | 1.0 | 1 1 1 | 0 0 0 |
| 水煮牛肉 | 2 | 0.67 | 1.5 1.5 1.5 | 0.41 0.41 0.41 |
| 牛里脊 | 1 | 0.33 | 3 3 3 | 1.10 1.10 1.10 |
步骤 4:TF‑IDF 加权计算
T F ‑ I D F TF‑IDF TF‑IDF 的计算公式:
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)} TF‑IDF(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‑IDF TF‑IDF:二者相乘,得到该词汇在这篇文档里的最终加权得分
假设 TF 原始词项‑文档矩阵:
| 词项 | Doc1(TF) | Doc2(TF) | Doc3(TF) |
|---|---|---|---|
| 川味 | 3 | 1 | 2 |
| 水煮牛肉 | 3 | 1 | 0 |
| 牛里脊 | 2 | 0 | 0 |
逐单元格手动演算:
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.41⟹3×0.41=1.23Doc1- 牛里脊: 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.10⟹2×1.10=2.20Doc1- 川味: T F = 3 , I D F = 0 ⟹ 3 × 0 = 0 TF=3,\ IDF=0 \implies 3 \times 0 = \boldsymbol{0} TF=3, IDF=0⟹3×0=0(通用词权重清零)
最终生成TF‑IDF 加权词项‑文档矩阵:
| 词项 \ 文档 | Doc1 | Doc2 | Doc3 |
|---|---|---|---|
| 川味 | 0 | 0 | 0 |
| 水煮牛肉 | 1.23 | 0.41 | 0 |
| 牛里脊 | 2.20 | 0 | 0 |
当用户输入查询语句时,我们对查询文本执行完全相同的分词、TF‑IDF计算,生成查询向量;再将查询向量和矩阵中每一列的文档向量计算相似度,按照相似度从高到低完成文档排序,最终返回检索结果。
5. TF-IDF 致命短板
TF-IDF 是传统稀疏检索的经典基线方案,依靠词频与全局词权重实现文档相关性打分,解决了基础命中打分、简易词频打分的核心缺陷。
但在实际检索场景中,原始 TF-IDF 算法存在两处致命短板,无法适配真实业务的检索需求:
- 词频无上限,分数容易虚高:关键词无限堆砌即可拉高得分,低质量刷词文档排名靠前,排序失真。
- 未做文档长度归一化:篇幅更长的文档天然拥有更多命中机会,打分对短而精炼的优质文档不公平。
正因
TF‑IDF存在上述缺陷,工业界进一步演化出BM25算法,作为生产环境稀疏检索的标准实现。