一、 文檔打分的運作機制:TF-IDF
1、Lucene
和es
的打分機制是一個公式,將查詢作為輸入,使用不同的手段來確定每一篇文檔的得分,將每一個因素最后通過公式綜合起來,返回該文檔的最終得分,這個綜合考量的過程,就是希望相關的文檔被優先返回的考量過程,在Lucene
和es
中這種相關性稱為得分
2、 在開始計算得分之前,es
使用了被搜索詞條的頻率和它有多常見來影響得分,從兩個方面理解
①一個詞條在某篇文檔中出現的次數越多,該文檔就越相關,稱之為詞頻(term frequency),TF
②一個詞條如果在不同的文檔中出現的次數越多,它就越不相關,稱之為逆文檔頻率(inverse document frequency),IDF
3、詞頻:TF,考慮一篇文檔得分的首要方式,是查看一個詞條在文檔中出現的次數,比如某篇文章圍繞es
的打分展開的,那么文章中肯定會多次出現相關字眼,當查詢時,我們認為該篇文檔更符合,所以,這篇文檔的得分會更高
4、逆文檔頻率:IDF
①相對於詞頻,逆文檔頻率稍顯復雜,如果一個詞條在索引中的不同文檔中出現的次數越多,那么它就越不重要
②示例,假如es
索引中,有如下3篇文檔
The rules-which require employees to work from 9 am to 9 pm
In the weeks that followed the creation of 996.ICU in March
The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser.
③詞條ICU
的文檔頻率是2
,因為它出現在2篇文檔中,文檔的逆源自得分乘以1/DF
,DF
是該詞條的文檔頻率,這就意味着,由於ICU
詞條擁有更高的文檔頻率,所以,它的權重會降低
④詞條the
的文檔頻率是3
,它在3篇文檔中都出現了,注意,盡管the
在后兩篇文檔出都出現兩次,但是它的詞頻是還是3
,因為,逆文檔詞頻只檢查詞條是否出現在某篇文檔中,而不檢查它在這篇文檔中出現了多少次,那是詞頻該干的事兒**
⑤逆文檔詞頻是一個重要的因素,用來平衡詞條的詞頻,比如我們搜索the 996.ICU
,單詞the
幾乎出現在所有的文檔中(中文中比如的
),如果這個鬼東西要不被均衡一下,那么the
的頻率將完全淹沒996.ICU
,所以,逆文檔詞頻就有效的均衡了the
這個常見詞的相關性影響,以達到實際的相關性得分將會對查詢的詞條有一個更准確地描述
5、當詞頻和逆文檔詞頻計算完成。就可以使用TF-IDF
公式來計算文檔的得分了
二、其他的打分方法
1、TF-IDF
結合向量空間模型的實用評分模式,是es
和Lucene
最為主流的評分機制,但這並不是唯一的,除了TF-IDF
這種實用模型之外,還有其他模型
2、Okapi BM25
①k1和b是數值的設置,用於調整得分是如何計算的
②k1控制對於得分而言詞頻(TF)的重要性
③b是介於0 ~ 1
之間的數值,它控制了文檔篇幅對於得分的影響程度
④默認情況下,k1
設置為1.2
,而b
則被設置為0.75
⑤discount_overlaps
的設置用於告訴es
,在某個字段中,多少個分詞出現在同一位置,是否應該影響長度的標准化,默認值是true
3、隨機性分歧(Divergence from randomness),即DFR相似度
4、LM Dirichlet相似度
5、LM Jelinek Mercer相似度
三、配置打分模型
1、簡要配置BM25打分模型
# `BM25`是一種基於概率的打分框架
PUT w2
{
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"similarity": "BM25"
}
}
}
}
}
PUT w2/doc/1
{
"title":"The rules-which require employees to work from 9 am to 9 pm"
}
PUT w2/doc/2
{
"title":"In the weeks that followed the creation of 996.ICU in March"
}
PUT w2/doc/3
{
"title":"The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser."
}
GET w2/doc/_search
{
"query": {
"match": {
"title": "the 996"
}
}
}
2、BM25高級配置
PUT w3
{
"settings": {
"index": {
"analysis": {
"analyzer":"ik_smart"
}
},
"similarity": {
"my_custom_similarity": {
"type": "BM25",
"k1": 1.2,
"b": 0.75,
"discount_overlaps": false
}
}
},
"mappings": {
"doc": {
"properties": {
"title": {
"type": "text",
"similarity":"my_custom_similarity"
}
}
}
}
}
PUT w3/doc/1
{
"title":"The rules-which require employees to work from 9 am to 9 pm"
}
PUT w3/doc/2
{
"title":"In the weeks that followed the creation of 996.ICU in March"
}
PUT w3/doc/3
{
"title":"The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser."
}
GET w3/doc/_search
{
"query": {
"match": {
"title": "the 996"
}
}
}
3、配置全局打分模型:如果要使用某種特定的打分模型,並且希望應用到全局,那么就在elasticsearch.yml
配置文件中加入
index.similarity.default.type: BM25
四、boosting
1、簡介
①boosting
是一個用來修改文檔相關性的程序,boosting
有兩種使用場景
②索引的時候,比如在定義mappings的時候
③查詢一篇文檔的時候
④以上兩種方式都可以提升一個篇文檔的得分,需要注意的是,在索引期間修改的文檔boosting是存儲在索引中的,要想修改boosting必須重新索引該篇文檔
2、索引期間的boosting
PUT w4
{
"mappings": {
"doc": {
"properties": {
"name": {
"boost": 2.0,
"type": "text"
},
"age": {
"type": "long"
}
}
}
}
}
①一勞永逸是沒錯,但一般不這么推薦
②原因之一是因為一旦映射建立完成,那么所有name
字段都會自動擁有一個boost
值,要想修改這個值,那就必須重新索引文檔
③另一個原因是,boost
值是以降低精度的數值存儲在Lucene
內部的索引結構中,只有一個字節用於存儲浮點型數值(存不下就損失精度了),所以,計算文檔的最終得分時可能會損失精度
④最后,boost
是應用與詞條的,因此,再被boost
的字段中如果匹配上了多個詞條,就意味着計算多次的boost
,這將會進一步增加字段的權重,可能會影響最終的文檔得分
3、查詢期間的boosting
①在es
中,幾乎所有的查詢類型都支持boost
,正如那些match、multi_match
等等
②示例,在查詢期間,使用match查詢進行boosting
PUT w5
{
"mappings":{
"doc":{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
PUT w5/doc/1
{
"title":"Lucene is cool",
"content": "Lucene is cool"
}
PUT w5/doc/2
{
"title":"Elasticsearch builds on top of lucene",
"content":"Elasticsearch builds on top of lucene"
}
PUT w5/doc/3
{
"title":"Elasticsearch rocks",
"content":"Elasticsearch rocks"
}
GET w5/doc/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title":{
"query": "elasticserach rocks",
"boost": 2.5
}
}
},
{
"match": {
"content": "elasticserach rocks"
}
}
]
}
}
}
# 就對於最終得分而言,`content`字段,加了`boost`的`title`查詢更有影響力,也只有在`bool`查詢中,`boost`更有意義
4、跨越多個字段的查詢
①boost
也可以用於multi_match
查詢
GET w5/doc/_search
{
"query": {
"multi_match": {
"query": "elasticserach rocks",
"fields": ["title", "content"],
"boost": 2.5
}
}
}
②除此之外,還可以使用特殊的語法,只為特定的字段指定一個boost
,通過在字段名稱后添加一個^
符號和boost
的值,告訴es只需對那個字段進行boost
GET w5/doc/_search
{
"query": {
"multi_match": {
"query": "elasticserach rocks",
"fields": ["title^3", "content"]
}
}
}
# `title`字段被`boost`了3倍
# 在使用`boost`的時候,無論是字段或者詞條,都是按照相對值來`boost`的,而不是乘以乘數
# 如果對於所有的待搜索詞條`boost`了同樣的值,那么就好像沒有`boost`一樣
# 因為Lucene會標准化`boost`的值,如果`boost`一個字段`4`倍,不是意味着該字段的得分就是乘以`4`的結果
# 所以,如果你的得分不是按照嚴格的乘法結果,也不要擔心