TF-IDF VS BM25
在ES5.0版本之前,ES一直用的是TF-IDF来进行相关度算分;在5.0后的版本,ES换成了BM25版本。本文将从算法设计的角度,先介绍两个算法,再结合ES来尝试分析一下各自的优缺点。
算法介绍
TF-IDF和BM25都是用作ES中排序依据的核心部分,它们是组成Lucene中“field weight”的部分,“field weight”用来评测的是search term的匹配程度的。
TF-IDF
TF-IDF的计算公式如下:
score(q, d) = coord(q, d) * queryNorm(q) * \sum_{t\ in\ q} tf(t\ in\ d) * idf(t)^2*boost(t) * norm(t, d)
算法中用到的所有指标如下:
- TF, Term Frequency,:term 在当前文档中出现的次数
$TF(t\ in\ d) = \sqrt[2]{frequency} $
;也就是说,指定的term出现次数越多的文档,TF值越高. - IDF, Inverse Document Frequency,
$IDF(t) = 1 + \log(\tfrac{numDocs}{docFreq + 1}) $
, 其中docFreq
表示term出现的文档数目,numDocs
表示总的文档数。越少见的term的IDF分数越高 $coord(q, d)$
是权重因子,用来表示在某一个文档中包含的query中的term数:包含query中term更多的文档,会得到更高的coord分数$queryNorm(q)$
是用作query中的归一化因子。在同一次搜索中,该因子并不会影响文档的排序,但是该因子使得不同的搜索甚至不同索引的搜索得到的分数变得相互之间可比较。$queryNorm(q) = \tfrac{1}{sumOfSquaredWeight^{\tfrac{1}{2}}}$
boost
term的放大因子,可以根据自己需求,提高query中某些term的权重。norm(t,d)
由两部分组成:Field boost 和 length Norm,呈现的搜索效果是其他情况均相同的情况下,文档越短,相关度越高
TF-IDF算法在文档数量级不大,文档的变化不够丰富的情况,表现还是不错的;但是当加入更多变的文档,比如说评论,短文之后,表现不如BM系列算法。
BM25
score(q, d) = \sum_{t\ in\ d} IDF(t)\tfrac{f(q_i,d)*(k1+1)}{f(q_i,d) + k1*(1-b+b*\tfrac{fieldLen}{avgFieldLen})}
- IDF,BM25中的IDF与TF-IDF中的定义相同,但是计算方式变了。
$IDF = \ln(1+\tfrac{docCount - f(q) + 0.5}{f(q)+0.5})$
docCount是所有的文档数(如果有分片,则为当前分片上的文档数),f(q) 是term出现的文档数。 $\tfrac{fieldLen}{avgFieldLen}$
BM25中加入了平均长度- b 是长度的权重,默认值为0.75
- f(q,d) 相当于TF-IDF中的TF
- k1 是用来决定TF的饱和度,默认值是1.2。
Dig into BM25
首先拆开来分析BM25中的每一个因子:
- IDF(t):
- 公式为:
$IDF = \ln(1+\tfrac{docCount - f(q) + 0.5}{f(q)+0.5})$
- “The IDF component of our formula measures how often a term occurs in all of the documents and “penalizes” terms that are common.”
- 举个例子,假如某个分片上共有4个文档,term1在其中2个文档中出现,term2在其中4个文档中出现,它俩的IDF分别为:
$IDF(term1) = \ln(1+\tfrac{(4-2+0.5)}{4+0.5}) = \ln(1 + \tfrac{2.5}{4.5}) = 0.69314$
$IDF(term2) = \ln(1+\tfrac{(4-4+0.5)}{4+0.5}) = \ln(1 + \tfrac{0.5}{4.5}) = 0.10536$
- 可以看出,越少见的term,IDF应该更低
- 公式为:
$relatedLen = \tfrac{fieldLen}{avgFieldLen}$
BM25中加入了平均文档长度。相对于平均文章长度越长的文章,relatedLen值越大,最终得到的算分也就会越小;长度越短的文章,得到的算分会越高- 值得注意的一点是,在elasticsearch中,文章长度并不是字数,而是用term数来表示的
- 也就是说,上述结论应该表述为:term数越多的文档(至少有一个term不匹配query中的terms),获得的分数会越低
- analyzer matters
- 我们注意到公式中,在相对长度前有一个放大因子
b
,b
越大,相对长度的作用就会越明显,如果设成0,文章长度就不再影响最终的算分情况。在ES中,b的默认值为0.75。 k1
和$f(q_i, D)$
在公式的分子分母都出现。$f(q_i, D)$
类似于TF-IDF中的TF,表示query中的第i个term在文档D中出现的次数- term在一篇文档中出现的次数越多,因子的就会越高
k1
被用来决定term频率的饱和度。用来限制单个term可以影响query在指定文档中相关度的最大值。- 在TF-IDF中,term发生的次数越多,TF的分数会越高 ;但是在BM25中,加入了K1之后,就不会无休止的增加。如图所示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZ9Onvdf-1577018099945)(https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/blt69235a8d75917f47/5c57eb4322d96be10bcca3a4/term_frequency_saturation.png)]
- 当
tf() <= k1
时,分数增加的特别快;而当tf() > k1
之后,分数增加的幅度会越来越小。 - **“terms occurring extra times add extra score.” **
- 也就是说,某一个term < k1 次出现时候,对于相关度的影响比较大;随着出现次数增加,对于相关度的影响越来越;当超过k1次的时候,影响基本不会有太大的变化。
- 在ES中,k1的默认值为1.2
如何选择合适的b和k1
首先需要明确,没有最好的b,k1,只有最适合的参数。两个变量的范围分别是:
- b应该在[0,1]之间,实验表明理想的范围应该是[0.3, 0.9]
- 当考虑调整b的值的时候,需要考虑的是在当前业务场景中,多长的文档才算是长文档
- 对于主题比较明确单一的文档,比如说,学术论文,通篇可能都在论述一个主题,这种场景下文档长度不应该过度的影响主题term的相关性,应该设置的b的值较低一些;
- 对于主题不明确,可能包含多个主题的文档,如一篇微博、散文、随笔、评论等,这样的场景中,文档的长度就会显得比较重要了,需要设置的b的值略高一些。
- k1应该在[0,3]之间,当然可以更高,但是实验表明,理想的范围应该是[0.5,2.0]
- 当考虑调整k1时候,需要考虑的是term什么情况下显得饱和
- 对于一些长文档,特别是小说,应该设置k1的值偏高一些。直白一些来说,某个词出现多少次,才会把这个词归结为文档的主题的候选词。
- 相反的,对于较短的文档,应该设置k1的值低一些。
- 举一个不太恰当的例子:调整k1和b就像是兑一杯糖水。
- 首先应该确定的是,当前场景中,需要多少毫升的糖水。水有多少,类似于文章有多长,多长的文章才算是长文档,进而确定b的值
- 接着应该确定的是,需要假如多少糖,才会认为这是一杯糖水。k1之于b,就像糖之于水,多少浓度的水才是合适的。
实际应用中的相关性算分
在实际应用中,当无法快速的搜索到想要的结果时,往往并不需要考虑到BM25算法中b和k1,往往更多考虑到的会是:
- phrase matches
- 同义词
- 换一个分词器
- 等等其他方式
但是当上述方式没法产生预期效果的时候,应该想起角落里边还躺着的b和k1。除此之外,在建立index mapping的时候,如果业务场景固定且预料之中变动不大的情况下(如:知网论文检索,豆瓣评价检索)等,可以适当的调整b和k1的值,以使得召回结果更加准确。
参考文献
[1]The BM25 Algorithm and its Variables
[3]Considerations for Picking b and k1 in Elasticsearch
[4]相关度评分背后的理论
来源:CSDN
作者:U5years
链接:https://blog.csdn.net/U5years/article/details/103656697