一、相关性算分基础
Elasticsearch 使用相关性评分(relevance scoring)来确定文档与查询的匹配程度。评分越高,文档越相关。在 Elasticsearch 的发展历程中,主要使用过两种评分算法:
| 算法 | Elasticsearch 版本 | 特点 |
|---|---|---|
| TF-IDF | 5.x 之前 | 经典的词频-逆文档频率算法 |
| BM25 | 5.x 及以后 | 更现代的改进算法,成为默认 |
二、TF-IDF 算法详解
1. TF-IDF 组成要素
TF-IDF = TF (词频) × IDF (逆文档频率) × 字段长度归一化
TF (Term Frequency) :
- 词项在当前文档中出现的频率
- 计算公式:
tf(t in d) = √frequency
IDF (Inverse Document Frequency) :
- 词项在所有文档中的稀有程度
- 计算公式:
idf(t) = 1 + log(numDocs / (docFreq + 1))
字段长度归一化(Field-length norm) :
- 字段越短,权重越高
- 计算公式:
norm(d) = 1 / √numTerms
2. TF-IDF 实际示例
假设有以下3个文档:
文档1: "苹果 苹果 香蕉"
文档2: "苹果 橙子"
文档3: "苹果 香蕉 葡萄"
查询:"苹果 香蕉"
TF计算:
- 文档1中"苹果"的TF: √2 ≈ 1.414
- 文档1中"香蕉"的TF: √1 = 1
IDF计算:
- "苹果"的IDF: 1 + log(3/3) = 1
- "香蕉"的IDF: 1 + log(3/2) ≈ 1.405
字段长度归一化:
- 文档1有3个词项: 1/√3 ≈ 0.577
文档1最终评分:
score = (TF("苹果") * IDF("苹果") + TF("香蕉") * IDF("香蕉")) * norm
= (1.414*1 + 1*1.405) * 0.577 ≈ 1.627
三、BM25 算法详解
1. BM25 改进点
BM25 (Best Match 25) 是 TF-IDF 的改进版本,主要优化:
- 词频饱和度控制:避免高频词过度影响
- 字段长度归一化更灵活:通过参数调节
2. BM25 公式
score(D, Q) = ∑ IDF(qi) * (f(qi, D) * (k1 + 1)) / (f(qi, D) + k1 * (1 - b + b * |D| / avgdl))
参数说明:
k1: 控制词频饱和度 (默认1.2)b: 控制字段长度影响 (默认0.75)|D|: 当前文档字段长度avgdl: 平均字段长度
3. BM25 实际示例
使用相同文档集:
参数设置:
- k1 = 1.2
- b = 0.75
- avgdl = (3+2+3)/3 ≈ 2.667
文档1评分计算:
f("苹果",D1)=2, f("香蕉",D1)=1
|D1|=3
TF("苹果")部分: (2*(1.2+1))/(2+1.2*(1-0.75+0.75*3/2.667)) ≈ 2.2/2.81 ≈ 0.782
TF("香蕉")部分: (1*(1.2+1))/(1+1.2*(1-0.75+0.75*3/2.667)) ≈ 2.2/1.81 ≈ 1.215
IDF("苹果")=1, IDF("香蕉")≈1.405
score = 1*0.782 + 1.405*1.215 ≈ 0.782 + 1.707 ≈ 2.489
四、Elasticsearch 实际操作
1. 查看评分详情
GET /your_index/_search
{
"query": {
"match": {
"content": "苹果 香蕉"
}
},
"explain": true // 显示评分细节
}
2. 比较 TF-IDF 和 BM25
设置相似度算法:
PUT /your_index
{
"settings": {
"index": {
"similarity": {
"my_tfidf": { // 自定义TF-IDF
"type": "classic"
},
"my_bm25": { // 自定义BM25
"type": "BM25",
"k1": 1.2,
"b": 0.75
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"similarity": "my_bm25" // 指定字段使用BM25
}
}
}
}
3. 实际查询示例
BM25查询 (默认):
GET /fruits/_search
{
"query": {
"match": {
"name": "苹果 香蕉"
}
}
}
TF-IDF查询:
PUT /fruits_tfidf
{
"settings": {
"index": {
"similarity": {
"default": {
"type": "classic"
}
}
}
}
}
// 然后执行相同查询
五、算法对比分析
| 特性 | TF-IDF | BM25 |
|---|---|---|
| 词频处理 | 线性增长 | 有上限的饱和曲线 |
| 字段长度影响 | 严格按长度反比 | 可调节(b参数) |
| 默认使用版本 | ≤5.x | ≥5.x |
| 长文档处理 | 对长文档不利 | 对长文档更公平 |
| 高频词影响 | 可能过度影响 | 抑制高频词影响 |
六、业务场景建议
-
使用BM25的场景:
- 通用搜索场景
- 文档长度差异大的情况
- 需要抑制高频词影响的场景
-
使用TF-IDF的场景:
- 需要与旧系统保持一致性
- 特定领域已知TF-IDF效果更好
- 需要更简单直接的评分模型
七、进阶调优
1. BM25参数调优
PUT /your_index
{
"settings": {
"index": {
"similarity": {
"custom_bm25": {
"type": "BM25",
"k1": 1.5, // 增大k1增强词频影响
"b": 0.6 // 减小b降低长度惩罚
}
}
}
}
}
2. 字段级别配置
PUT /your_index/_mapping
{
"properties": {
"title": {
"type": "text",
"similarity": "custom_bm25",
"norms": false // 禁用长度归一化
}
}
}
3. 组合查询权重
GET /_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {
"query": "苹果",
"boost": 2.0 // 提高权重
}
}
},
{
"match": {
"content": "香蕉"
}
}
]
}
}
}
通过理解这些评分算法的工作原理,您可以更好地优化Elasticsearch搜索相关性,满足不同业务场景的需求。