相关性描述的是⼀个⽂档和查询语句匹配的程度。ES 会对每个匹配查询条件的结果进⾏算分_score。_score 的评分越高,相关度越高。
ES 5.0之前使用TF-IDF 相关性算法, 5.0之后使用了BM25算法
TF-IDF
公式
score(q,d) = queryNorm(q)
· coord(q,d)
· ∑ (t.getBoost()
.tf(t in d)
· idf(t)
· norm(t,d)
) (t in q)
- score(q,d) 文档d对查询q的相关性得分
- queryNorm(q) 查询的规范化因子
- coord(q,d) 协调因子
- ∑ 文档d的查询q中每个词t的权重之和
- tf(t in d) 文档d中t词的词频(出现次数)
- idf(t) t词的逆文档频率
- t.getBoost() 已应用于查询的boost
- norm(t,d) 是字段长度归一值,与检索时字段的Boost (如果存在)相结合。
BM25
整体而言 BM25 就是对 TF-IDF 算法的改进,对于 TF-IDF 算法,TF(t) 部分的值越大,整个公式返回的值就会越大。BM25 就针对这点进行来优化,随着TF(t) 的逐步加大,该算法的返回值会趋于一个数值。如下图所示:
公式
该公式前半部分是IDF, 后半部分是TF + NORM
- IDF(qi,D):查询项的逆文档频率
- N:索引中的文档总数
- n(qi) : 索引字段中包含查询项的文档数量
- f(qi,D): 查询项在文档D中出现的频次
- k1: 这个参数控制着词频结果在词频饱和度中的上升速度。默认值为1.2。值越小饱和度变化越快,值越大饱和度变化越慢
- b: 这个参数控制着字段长归一值所起的作用,0.0会禁用归一化,1.0会启用完全归一化。默认值为0.75
- |D|: 代表文档的长度,
- avgdl: 代表索引中平均字段长度
explain
PUT /blogs_index
{"settings": {"index": {"number_of_shards": 1,"number_of_replicas": 0}},"mappings": {"dynamic": false,"properties": {"title": {"type": "text","analyzer": "ik_smart"},"content": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"}}}
}POST /blogs_index/_bulk
{"index":{"_id":"1"}}
{"title":"es的相关度","content":"这是关于es的相关度的文章"}
{"index":{"_id":"2"}}
{"title":"相关度","content":"这是关于相关度的文章"}
{"index":{"_id":"3"}}
{"title":"es","content":"这是关于关于es和编程的必看文章"}
{"index":{"_id":"4"}}
{"title":"关注我,系统学习es","content":"这是关于es的文章,介绍了一点相关度的知识"}GET /blogs_index/_analyze
{"text": ["es的相关度"],"field": "title"
}{"tokens" : [{"token" : "es","start_offset" : 0,"end_offset" : 2,"type" : "ENGLISH","position" : 0},{"token" : "的","start_offset" : 2,"end_offset" : 3,"type" : "CN_CHAR","position" : 1},{"token" : "相关","start_offset" : 3,"end_offset" : 5,"type" : "CN_WORD","position" : 2},{"token" : "度","start_offset" : 5,"end_offset" : 6,"type" : "CN_CHAR","position" : 3}]
}GET /blogs_index/_search
{"query": {"match": {"title": "es的相关度"}},"explain": true
}
文档总分
查询结果中文档一得分2.5933092是有四个分词es,的,相关,度的分数相加所得
2.5933092 = 0.31387398 + 1.0594962 + 0.60996956 + 0.60996956
单个词得分
词es在分档1中的得分是0.31387398,如图描述是由boost * idf * tf 所得
0.31387398 = 2.2 * 0.35667494 * 0.40000004
单个词idf得分
词es在分档1中的idf得分0.35667494
0.35667494 = In(1 + (4 - 3 + 0.5) / (3 + 0.5)) = In (1 + 3/7) = In(10/7)
单个词tf得分
词es在分档1中的tf得分0.40000004, dl是文档1中title的分词数4, avgdl是整个索引title的平均分词数
0.40000004 = 1 / (1 + 1.2 *(1 - 0.75 + 0.75 * 4 / 3))= 1 / 2.5
更改BM25 参数 k1 和 b 的值
在介绍BM25算法时,我们知道 k1 参数【默认值1.2】控制着词频结果在词频饱和度中的上升速度。b 参数【默认值0.75】控制着字段长归一值所起的作用。
那么我们就可以通过手动定义这两个参数的值,从而去改变相关性算分。
只能在创建index的时候定义字段的similarity ,在后续,可以通过关闭索引,更新索引设置,开启索引这个过程进行更新 my_bm25 的 参数值。这样可以无须重建索引又能试验不同的相似度算法配置。
PUT /blogs_index
{"settings": {"similarity": {"my_bm25": {"type": "BM25","b": 0.8,"k1": 1.5}}},"mappings": {"dynamic": false,"properties": {"title": {"type": "text","analyzer": "ik_smart","similarity": "my_bm25"},"content": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"}}}
}
参考文献
[1]. https://www.cnblogs.com/geeks-reign/p/Okapi_BM25.html
[2]. Okapi BM25: a non-binary model
[3]. ES系列13:彻底掌握相关度:从TF-IDF、BM25到对相关度的控制