BM25算法介绍
BM25(Best Matching)算法是当前信息检索领域主流的文本匹配算法,主要内容是计算query到文档集合的相似度得分。BM25可以视作Tf-IDF算法的优化。
TF-IDF算法
t f − i d f s c o r e = t f × i d f = 某文档中目标词出现的数量 某文档总词数 × l o g 文档总数 包含目标词的文档数量 tf-idf_{score}=tf×idf=\frac{{某文档中目标词出现的数量}}{{某文档总词数}}×log\frac{{文档总数}}{{包含目标词的文档数量}} tf−idfscore=tf×idf=某文档总词数某文档中目标词出现的数量×log包含目标词的文档数量文档总数
BM25算法
BM25算法主要有下面三个部分组成:
-
query中每个单词的重要性(可以理解为idf部分)
-
query中每个单词与文档之间的相关性(对tf部分的优化,并考虑了文档的长度)
-
query中每个单词与query本身的相关性(该部分只有在当query很长时才会使用)
TF-IDF和BM25比较
-
BM25在tf-idf的基础上增加了几个可调节的参数,使其在应用中更具灵活性和实用性。
-
BM25对于词频、逆文档频率以及字段长度的归一化具有更合理的定义。
-
在词频的重要性方面,BM25有一个上限,即随着词频增长,词的重要性增长程度会被限制。
BM25的公式
s c o r e ( Q , d ) = ∑ i = 1 n w i R ( q i , d ) {\rm{score}}(Q,d) = \sum\limits_{i = 1}^n {{w_i}R({q_i},d)} score(Q,d)=i=1∑nwiR(qi,d)
其中 Q Q Q表示一条query, q i q_i qi 表示query中的第 i i i 个词, w i w_i wi表示自身的重要性, d d d 表示待匹配的文档。
自身重要性
w i w_i wi 的计算方式同idf类似:
w i = i d f q i = l o g N − d f i + 0.5 d f i + 0.5 w_i=idf_{q_i}=log\frac {{N-df_i+0.5}}{{df_i+0.5}} wi=idfqi=logdfi+0.5N−dfi+0.5
其中 N N N表示待匹配的全部文档数, d f i df_i dfi为包含了 q i q_i qi的文档总数。对于某个 q i q_i qi,包含 q i q_i qi的文档数越多,说明该 q i q_i qi越不重要。 w i w_i wi 一定程度上可以用来刻画 q i q_i qi与文档之间的相关性。
单词与文档之间的相关性
单词与文档之间相关性的刻画依赖一个重要发现:词频和相关性之间的关系是非线性的。即每个词和文档的相关性分数不会超过某个阈值,当词出现的次数达到一个阈值之后,其影响就不再线性增长,而这个阈值和文档本身相关。因此在刻画单词与文档的相关性时,BM25时这么设计的:
S ( q i , d ) = ( k 1 + 1 ) t f q i d K + t f q i d S(q_i,d)=\frac {{(k_1+1)tf_{q_id}}}{K+tf_{q_id}} S(qi,d)=K+tfqid(k1+1)tfqid
K = k 1 ( 1 − b + b × L d L a v e ) K=k_1(1-b+b×\frac{L_d}{L_{ave}}) K=k1(1−b+b×LaveLd)
其中 t f q i d tf_{q_id} tfqid表示单词 q i q_i qi在文档d中的词频, L d L_d Ld表示文档d的长度, L a v e L_{ave} Lave表示所有文档的平均长度,变量 k 1 k_1 k1表示为正的参数,用来标准化文章词频的范围。b是另一个参数且0<b<1,b表示使用文档长度来表示信息量的程度。当b=1,是完全使用文档长度来衡量词的权重,当b为0时,表示不使用文档长度来衡量词的权重。
单词与query之间的相关性
只有当query很长时,才需要刻画单词与query之间的相关性。公式为:
S ( q i , Q ) = ( k 3 + 1 ) × t f q i q k 3 × t f q i q S(q_i,Q)=\frac {(k_3+1)×tf_{q_iq}}{k_3×tf_{q_iq}} S(qi,Q)=k3×tfqiq(k3+1)×tfqiq
其中 q i q_i qi表示query中的单词, t f q i q tf_{q_iq} tfqiq表示单词 q i q_i qi在query中出现的频数。 k 3 k_3 k3是一个可调节的正参数,用来矫正query中的词频范围.
整体公式
s c o r e ( Q , d ) = ∑ i = 1 n ( l o g N − d f i + 0.5 d f i + 0.5 × ( k 1 + 1 ) t f q i d K + t f q i d × ( k 3 + 1 ) × t f q i q k 3 × t f q i q ) {\rm{score}}(Q,d) = \sum\limits_{i = 1}^n ({{log\frac {{N-df_i+0.5}}{{df_i+0.5}}}×\frac {{(k_1+1)tf_{q_id}}}{K+tf_{q_id}}×\frac {(k_3+1)×tf_{q_iq}}{k_3×tf_{q_iq}}}) score(Q,d)=i=1∑n(logdfi+0.5N−dfi+0.5×K+tfqid(k1+1)tfqid×k3×tfqiq(k3+1)×tfqiq)
参数经验值
根据实验, k 1 k_1 k1和 k 3 k_3 k3一般取值1.2~2。b取值0.75。
实例程序 使用gensim下的bm25模块
from gensim.summarization import bm25
import jieba
def test_gensim_bm25():
# 给定多个文档
corpus = ["5万元资金,该做什么行业",
"美增加汽车关税,为何汽车价格不降反升",
"汽车销售人员的服务水准非常烂,该怎么解决",
"未来房价会跌到什么程度",
"十万元能上路的汽车,买什么比较好"]
# 对每个文档切词(示例作用 不进行去停用词)
corpus_cut = [jieba.lcut(line) for line in corpus]
# 生成模型
bm25Model = bm25.BM25(corpus_cut)
test_query = "你想买汽车吗" # query
test_query_cut = jieba.lcut(test_query)
scores = bm25Model.get_scores(test_query_cut) # 计算相似度得分(与corpus_cut顺序对应)
print("scores", scores)
# 输出
for i, j in zip(scores, corpus):
print('分值:{},原句:{}'.format(i, j))
print('\n')
if __name__ == '__main__':
test_gensim_bm25()