BM25(Best Matching 25)是一种广泛用于信息检索的排名函数,特别适合全文检索(Full Text Search) 场景。它是对传统 TF-IDF(Term Frequency-Inverse Document Frequency)模型的改进,通过引入词频饱和(Term Frequency Saturation)和文档长度归一化(Document Length Normalization)来优化文档与查询的相关性评分。BM25 在 Milvus 2.5.10 中被用来支持全文检索,将文本转换为稀疏向量(SPARSE_FLOAT_VECTOR
)并计算相关性得分。
以下基于 Milvus 文档(https://milvus.io/docs/zh/full-text-search.md)、学术资源和其他公开信息,详细介绍 BM25 的定义、工作原理、数学公式、在 Milvus 中的应用、优缺点以及与分析器的关系。
1. BM25 定义
BM25 是一种概率检索模型,旨在根据查询词(Query Terms)与文档(Document)的相关性对文档进行排名。它由 Stephen E. Robertson 和 Karen Spärck Jones 在 1990 年代开发,广泛应用于搜索引擎(如 Elasticsearch、Solr)和向量数据库(如 Milvus)。BM25 的核心思想是:
- 词频(Term Frequency, TF):查询词在文档中出现的频率越高,相关性越高,但高频词的贡献会逐渐饱和。
- 逆文档频率(Inverse Document Frequency, IDF):查询词在整个文档集合中的稀有程度越高,权重越大。
- 文档长度归一化:较短的文档通常比长文档更相关,因为词频在短文档中更集中。
BM25 通过调整参数(如 k1
和 b
)平衡这些因素,提供灵活且高效的排名。
2. BM25 工作原理
BM25 的工作流程包括以下步骤:
- 文本预处理:
- 使用分析器(Analyzer)对查询和文档文本进行分词(Tokenization)和过滤(Filtering,如小写、停用词移除)。
- 示例:查询“vector database”分词为
["vector", "database"]
。
- 令牌匹配:
- 比较查询的令牌与文档中的令牌,计算每个匹配令牌的贡献。
- 相关性评分:
- 为每个文档计算 BM25 分数,基于 TF、IDF 和文档长度。
- 结果排序:
- 按 BM25 分数从高到低排序,返回 Top-K 结果。
在 Milvus 中,BM25 通过 SPARSE_FLOAT_VECTOR
实现,分析器将文本转换为稀疏向量,BM25 算法计算向量间的相关性得分。
3. BM25 数学公式
BM25 的核心公式如下,用于计算查询 Q Q Q 和文档 D D D 的相关性得分:
score ( D , Q ) = ∑ i = 1 n IDF ( q i ) ⋅ TF ( q i , D ) ⋅ ( k 1 + 1 ) TF ( q i , D ) + k 1 ⋅ ( 1 − b + b ⋅ ∣ D ∣ avgdl ) \text{score}(D, Q) = \sum_{i=1}^{n} \text{IDF}(q_i) \cdot \frac{\text{TF}(q_i, D) \cdot (k_1 + 1)}{\text{TF}(q_i, D) + k_1 \cdot \left(1 - b + b \cdot \frac{|D|}{\text{avgdl}}\right)} score(D,Q)=i=1∑nIDF(qi)⋅TF(qi,D)+k1⋅(1−b+b⋅avgdl∣D∣)TF(qi,D)⋅(k1+1)
公式分解
- q i q_i qi:查询中的第 i i i 个词(令牌)。
- TF ( q i , D ) \text{TF}(q_i, D) TF(qi,D):词 q i q_i qi 在文档 D D D 中的词频(Term Frequency)。
-
IDF
(
q
i
)
\text{IDF}(q_i)
IDF(qi):词
q
i
q_i
qi 的逆文档频率,衡量词的稀有性:
IDF ( q i ) = log ( N − n ( q i ) + 0.5 n ( q i ) + 0.5 + 1 ) \text{IDF}(q_i) = \log \left( \frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1 \right) IDF(qi)=log(n(qi)+0.5N−n(qi)+0.5+1)- N N N:文档集合中的总文档数。
- n ( q i ) n(q_i) n(qi):包含词 q i q_i qi 的文档数。
- ∣ D ∣ |D| ∣D∣:文档 D D D 的长度(通常以词数计)。
- avgdl \text{avgdl} avgdl:集合中所有文档的平均长度。
- k 1 k_1 k1:控制词频饱和的参数(通常 1.2-2.0,调整 TF 的影响)。
- b b b:控制文档长度归一化的参数(通常 0.75,调整长文档的惩罚)。
参数解释
- k 1 k_1 k1:调节词频的增益,值越大,高频词的贡献越大。
- b b b:调节文档长度的影响, b = 0 b=0 b=0 忽略长度, b = 1 b=1 b=1 完全归一化。
- IDF:稀有词(如“Milvus”)比常见词(如“the”)有更高权重。
- TF 饱和:避免高频词(如出现 100 次 vs. 10 次)过度主导评分。
- 长度归一化:短文档(如摘要)比长文档(如书籍)更容易获得高分。
4. BM25 在 Milvus 中的应用
在 Milvus 2.5.10 中,BM25 是全文检索(Full Text Search)的核心算法,通过稀疏向量(SPARSE_FLOAT_VECTOR
)实现。以下是其在 Milvus 中的具体应用:
4.1 配置 BM25
- 字段要求:
VARCHAR
字段:存储原始文本,设置enable_analyzer=True
。SPARSE_FLOAT_VECTOR
字段:存储 BM25 生成的稀疏向量。
- BM25 函数:
- 使用
Function
(FunctionType.BM25
)将文本转换为稀疏向量。 - 示例:
bm25_function = Function( name="content_bm25", input_field_names=["content"], output_field_names=["sparse_vector"], function_type=FunctionType.BM25 ) schema.add_function(bm25_function)
- 使用
- 索引:
- 使用
AUTOINDEX
索引类型,指定metric_type="BM25"
。 - 示例:
index_params.add_index(field_name="sparse_vector", index_type="AUTOINDEX", metric_type="BM25")
- 使用
- 分析器:
- 默认使用
standard
分析器,可通过analyzer_params
指定english
、chinese
等。 - 示例:
analyzer_params={"type": "english"}
。
- 默认使用
4.2 全文检索流程
- 插入数据:
- 用户插入
VARCHAR
字段的文本。 - BM25 函数自动将文本转换为
SPARSE_FLOAT_VECTOR
。
- 用户插入
- 查询:
- 用户输入自然语言查询(如 “vector database”)。
- Milvus 使用相同的分析器分词,生成查询的稀疏向量。
- 搜索:
- 使用
client.search
在sparse_vector
字段上执行 BM25 搜索。 - 结果按相关性得分排序。
- 使用
4.3 混合搜索
- BM25(稀疏向量)常与密集向量(
FLOAT_VECTOR
)结合,用于混合搜索(Hybrid Search)。 - 示例:结合 BM25(关键词匹配)和 HNSW(语义搜索),使用
RRFRanker
融合结果。
5. BM25 示例代码
以下是基于你的修正代码(混合搜索示例)的简化版本,专注于 BM25 全文检索,展示其在 Milvus 中的使用。
from pymilvus import MilvusClient, DataType, FunctionType, Function
from pymilvus.client.constants import ConsistencyLevel
# 连接 Milvus
client = MilvusClient(uri="http://localhost:19530")
try:
# 创建数据库
client.create_database(db_name="test_db")
client.use_database(db_name="test_db")
# 创建 Schema
schema = MilvusClient.create_schema(auto_id=True)
schema.add_field(
field_name="pk",
datatype=DataType.VARCHAR,
is_primary=True,
auto_id=True,
max_length=100
)
schema.add_field(
field_name="text",
datatype=DataType.VARCHAR,
max_length=65535,
enable_analyzer=True,
analyzer_params={"type": "english"}
)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
# 定义 BM25 函数
bm25_function = Function(
name="text_bm25_sparse",
input_field_names=["text"],
output_field_names=["sparse_vector"],
function_type=FunctionType.BM25
)
schema.add_function(bm25_function)
# 创建集合
client.create_collection(
collection_name="bm25_collection",
schema=schema,
consistency_level=ConsistencyLevel.Session
)
# 创建索引
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="sparse_vector",
index_type="AUTOINDEX",
metric_type="BM25"
)
client.create_index(collection_name="bm25_collection", index_params=index_params)
# 插入数据
documents = [
{"text": "Artificial intelligence was founded as an academic discipline in 1956."},
{"text": "Alan Turing was the first person to conduct substantial research in AI."},
{"text": "Born in Maida Vale, London, Turing was a mathematician and computer scientist."}
]
client.insert(collection_name="bm25_collection", data=documents)
# 加载集合
client.load_collection(collection_name="bm25_collection")
# BM25 全文检索
results = client.search(
collection_name="bm25_collection",
data=["Artificial Intelligence"],
anns_field="sparse_vector",
limit=2,
output_fields=["text"],
consistency_level=ConsistencyLevel.Strong
)
print("BM25 search results:")
for result in results[0]:
print(f"Document: {result['entity']['text']}, Score: {result['distance']}")
except Exception as e:
print(f"Error: {e}")
finally:
# 清理
client.drop_collection(collection_name="bm25_collection")
client.drop_database(db_name="test_db")
输出示例
BM25 search results:
Document: Artificial intelligence was founded as an academic discipline in 1956., Score: 0.823
Document: Alan Turing was the first person to conduct substantial research in AI., Score: 0.654
代码说明
- Schema:定义
text
(VARCHAR
,enable_analyzer=True
)和sparse_vector
(SPARSE_FLOAT_VECTOR
)。 - BM25 函数:将
text
转换为sparse_vector
。 - 索引:使用
AUTOINDEX
和metric_type="BM25"
。 - 搜索:直接输入查询文本,Milvus 自动分词并计算 BM25 分数。
6. BM25 的优缺点
优点
- 高效性:稀疏向量表示(
SPARSE_FLOAT_VECTOR
)节省存储,适合大规模文本检索。 - 相关性强:通过 TF 饱和和 IDF 权重,BM25 比 TF-IDF 更准确。
- 灵活性:参数 k 1 k_1 k1 和 b b b 可调,适配不同场景。
- 语言支持:结合 Milvus 分析器,支持多语言(如
english
、chinese
)。 - 易集成:在 Milvus 中与密集向量搜索无缝结合,支持混合搜索。
缺点
- 依赖分词:BM25 性能依赖分析器的分词质量(如中文分词可能需优化)。
- 语义局限:BM25 基于词项匹配,无法捕捉深层语义(需结合密集向量)。
- 参数调优: k 1 k_1 k1 和 b b b 的选择需要实验,影响排名效果。
- 索引开销:稀疏向量和倒排索引增加存储和构建时间。
7. BM25 与分析器的关系
在 Milvus 中,BM25 依赖分析器(Analyzer) 处理文本:
- 分词:分析器(如
english
)将文本拆分为令牌,BM25 基于这些令牌计算 TF 和 IDF。 - 过滤:分析器通过小写(
lowercase
)、词干提取(stemmer
)、停用词移除(stop_words
)优化令牌。 - 配置:
- 默认
standard
分析器:简单分词,适合通用场景。 english
分析器:支持词干提取和停用词,适合英语文本。chinese
分析器:基于jieba
分词,适合中文文本。
- 默认
- 示例:
- 文本:“Milvus is a vector database”
english
分析器:["milvus", "vector", "databas"]
(小写、词干提取、移除“is”、“a”)。- BM25:计算每个令牌的 TF 和 IDF,生成稀疏向量。
8. 适用场景
- RAG(Retrieval-Augmented Generation):检索相关文档作为大模型上下文(如 Milvus 的 RAG 应用)。
- 关键词搜索:在技术文档、电商产品或新闻中查找包含查询词的记录。
- 混合搜索:结合 BM25(关键词)和密集向量(语义),提升搜索准确性。
- 多语言检索:使用特定语言分析器(如
chinese
)处理非英语文本。
9. 注意事项
- 版本兼容性:BM25 和
SPARSE_FLOAT_VECTOR
在 Milvus 2.5.x 中支持,确保使用 2.5.10 和pymilvus
2.5.6。 - 分析器选择:根据语言选择合适的分析器,中文文本需
chinese
分析器。 - 参数调优:Milvus 默认
k1=1.5
和b=0.75
,可通过analyzer_params
调整(需验证支持)。 - 性能:BM25 分词和索引构建会增加插入延迟,建议分批插入。
- 错误处理:确保
FunctionType.BM25
和AUTOINDEX
正确配置,避免稀疏向量为空。
10. 总结
BM25 是一种高效的全文检索排名算法,基于 TF、IDF 和文档长度归一化,优化了查询与文档的相关性评分。在 Milvus 2.5.10 中,BM25 通过 SPARSE_FLOAT_VECTOR
和 FunctionType.BM25
实现,与分析器配合处理文本分词和过滤,支持全文检索和混合搜索。BM25 适用于关键词搜索、RAG 和多语言场景,结合密集向量可实现语义增强的检索。