ai
  • index
  • cursor
  • vector
  • crawl
  • crawl-front
  • DrissionPage
  • logging
  • mysql
  • pprint
  • sqlalchemy
  • contextmanager
  • dotenv
  • Flask
  • python
  • job
  • pdfplumber
  • python-docx
  • redbook
  • douyin
  • ffmpeg
  • json
  • numpy
  • opencv-python
  • pypinyin
  • re
  • requests
  • subprocess
  • time
  • uuid
  • watermark
  • milvus
  • pymilvus
  • search
  • Blueprint
  • flash
  • Jinja2
  • secure_filename
  • url_for
  • Werkzeug
  • chroma
  • HNSW
  • pillow
  • pandas
  • beautifulsoup4
  • langchain-community
  • langchain-core
  • langchain
  • langchain_unstructured
  • libreoffice
  • lxml
  • openpyxl
  • pymupdf
  • python-pptx
  • RAGFlow
  • tabulate
  • sentence_transformers
  • jsonl
  • collections
  • jieba
  • rag_optimize
  • rag
  • rank_bm25
  • Hugging_Face
  • modelscope
  • all-MiniLM-L6-v2
  • ollama
  • rag_measure
  • ragas
  • ASGI
  • FastAPI
  • FastChat
  • Jupyter
  • PyTorch
  • serper
  • uvicorn
  • markdownify
  • NormalizedLevenshtein
  • raq-action
  • CrossEncoder
  • Bi-Encoder
  • neo4j
  • neo4j4python
  • matplotlib
  • Plotly
  • Streamlit
  • py2neo
  • abc
  • read_csv
  • neo4jinstall
  • APOC
  • neo4jproject
  • uv
  • GDS
  • heapq
  • 2. BM25 的核心公式
  • 3. 公式拆解
    • (1)逆文档频率(IDF)
    • (2)词频(TF)与文档长度归一化
  • 4. BM25 的排序逻辑(Rank_BM25)
  • 5. 参数调优建议
  • 6. BM25 与 TF-IDF 的对比
  • 7. 实际应用示例
  • 8. Rank_BM25 的优缺点
  • 9. 工具实现

BM25(Best Matching 25)是一种广泛使用的信息检索排序算法,用于计算查询(query)与文档(document)的相关性得分。它是 TF-IDF 的改进版本,通过引入更科学的归一化和参数调节,显著提升了搜索质量。

  • 应用场景:搜索引擎、文档检索、问答系统等。
  • 特点:
    • 考虑词频(TF)和逆文档频率(IDF)的平衡。
    • 对文档长度进行归一化,避免长文档占据不公平优势。

2. BM25 的核心公式 #

BM25 的得分公式如下:

$$ \text{score}(D, Q) = \sum_{i=1}^{n} \text{IDF}(q_i) \cdot \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot (1 - b + b \cdot \frac{|D|}{\text{avgdl}})} $$

变量说明:

  • $ Q $:查询(由词项 $ q_1, q_2, ..., q_n $ 组成)。
  • $ D $:待评分的文档。
  • $ f(q_i, D) $:词项 $ q_i $ 在文档 $ D $ 中的词频(TF)。
  • $ |D| $:文档 $ D $ 的长度(总词数)。
  • $ \text{avgdl} $:所有文档的平均长度。
  • $ \text{IDF}(q_i) $:词项 $ q_i $ 的逆文档频率。
  • $ k_1 $ 和 $ b $:可调参数(通常 $ k_1 \in [1.2, 2.0] $, $ b \in [0.5, 0.8] $)。

3. 公式拆解 #

(1)逆文档频率(IDF) #

衡量词项的全局重要性,罕见词得分更高:

$$ \text{IDF}(q_i) = \log \left( \frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1 \right) $$

  • $ N $:文档集合中的总文档数。
  • $ n(q_i) $:包含词项 $ q_i $ 的文档数。

(2)词频(TF)与文档长度归一化 #

BM25 通过以下部分调节词频的影响:

$$ \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot (1 - b + b \cdot \frac{|D|}{\text{avgdl}})} $$

  • $ k_1 $:控制词频饱和度的参数。
    • 当 $ k_1 = 0 $,忽略词频;值越大,词频影响越强。
  • $ b $:控制文档长度归一化的强度。
    • $ b = 0 $:完全忽略文档长度。
    • $ b = 1 $:强力惩罚长文档。

4. BM25 的排序逻辑(Rank_BM25) #

  1. 对每个文档计算 BM25 得分:根据上述公式,得到查询 $ Q $ 与每个文档 $ D $ 的相关性得分。
  2. 按得分降序排序:得分越高,文档与查询的相关性越强,排名越靠前。

5. 参数调优建议 #

  • $ k_1 $(默认 1.5):
    • 增大 $ k_1 $:词频高的词项贡献更大(适合长文档或重复词重要的场景)。
    • 减小 $ k_1 $:降低词频影响(适合短查询或去重需求)。
  • $ b $(默认 0.75):
    • 增大 $ b $:更严厉惩罚长文档(避免长文档仅因长度占据高分)。
    • 减小 $ b $:弱化长度影响(适合标题类短文本检索)。

6. BM25 与 TF-IDF 的对比 #

特性 BM25 TF-IDF
词频处理 非线性饱和(避免高频词主导) 线性增长(可能被高频词主导)
文档长度归一化 动态调节(参数 $ b $ 控制) 无直接归一化
参数灵活性 可调 $ k_1 $ 和 $ b $ 无参数

7. 实际应用示例 #

假设有以下文档集和查询:

  • 文档1:"apple banana apple"(长度=3)
  • 文档2:"apple fruit"(长度=2)
  • 查询:"apple"
  • 平均文档长度 $ \text{avgdl} = 2.5 $,设 $ k_1 = 1.5 $, $ b = 0.75 $。

计算步骤:

  1. 计算 IDF:$ \text{IDF}("apple") = \log \frac{2 - 2 + 0.5}{2 + 0.5} + 1 \approx 0 $(因为“apple”出现在所有文档中,IDF 较低)。
  2. 文档1得分:
    • 词频 $ f("apple", D1) = 2 $
    • 长度归一化部分:$ 1 - b + b \cdot \frac{3}{2.5} = 1.15 $
    • TF 部分:$ \frac{2 \cdot 2.5}{2 + 1.5 \cdot 1.15} \approx 1.32 $
    • 总分:$ 0 \times 1.32 = 0 $(因 IDF=0)。
  3. 文档2得分类似(实际应用中需调整参数或处理 IDF 为零的情况)。

8. Rank_BM25 的优缺点 #

  • 优点:
    • 比 TF-IDF 更适应多样化文本长度。
    • 参数可调,适合不同场景。
  • 缺点:
    • 需要预计算文档长度和 IDF。
    • 对超短查询(如单字)可能效果不佳。

9. 工具实现 #

  • Elasticsearch:默认使用 BM25 作为排序算法。
  • Python 库:rank_bm25(可直接计算文档排名):
# 导入BM25Okapi类,用于构建BM25检索模型
from rank_bm25 import BM25Okapi

# 定义语料库,每个元素为一个文档字符串
corpus = ["apple banana apple", "apple fruit"]

# 对每个文档进行分词,得到分词后的语料库
tokenized_corpus = [doc.split() for doc in corpus]

# 基于分词后的语料库构建BM25模型
bm25 = BM25Okapi(tokenized_corpus)

# 定义查询,并进行分词
query = "apple".split()

# 计算查询与每个文档的BM25得分
scores = bm25.get_scores(query)  # 得到文档得分

# 输出每个文档的得分
print(scores)

访问验证

请输入访问令牌

Token不正确,请重新输入