之前介绍过TF-IDF计算短文本相似度,见:传统方法TF-IDF解决短文本相似度问题,想着就把这一系列都介绍完吧,也算是自己的归纳总结,今天就介绍一下如何使用BM25算法计算短文本相似度。

上一篇短文本相似度算法研究文章中,我们举过这样一个场景,在问答系统任务(问答机器人)中,我们往往会人为地配置一些常用并且描述清晰的问题及其对应的回答,我们将这些配置好的问题称之为“标准问”。当用户进行提问时,常常将用户的问题与所有配置好的标准问进行相似度计算,找出与用户问题最相似的标准问,并返回其答案给用户,这样就完成了一次问答操作。

我们就以该场景,来介绍BM25的公式算法以及code代码。

其实,BM25算法是TF-IDF算法的优化,我们回顾一下,如何计算一个用户问题与一个标准问题的TF-IDF相似度?
Score_TF_IDF(Q,d)=∑inIDFi∗TF(qi,d)Score\_TF\_IDF(Q,d)=\sum_i^nIDF_i*TF(q_i,d)Score_TF_IDF(Q,d)=i∑n​IDFi​∗TF(qi​,d)一个用户问题与一个标准问题的TF-IDF相似度,是将用户问题中每一词与标准问题计算得到的TF-IDF值求和。

BM25算法也是如此,计算公式如下:
Score(Q,d)=∑inWiR(qi,d)Score(Q,d)=\sum_i^nW_iR(q_i,d)Score(Q,d)=i∑n​Wi​R(qi​,d)其中, QQQ 为用户问题, ddd 为“标准问”库中的一个标准问题, nnn 为用户问题中词的个数,qiq_iqi​ 为用户问题中第iii 个词,WiW_iWi​ 为该词的权重,R(qi,d)R(q_i,d)R(qi​,d) 为该词与标准问题的相关性分数。

**WiW_iWi​相当于TF-IDF算法中的IDF,R(qi,d)R(q_i,d)R(qi​,d) 相当于是TF-IDF算法中的TF;**只不过BM25对这两个指标进行了优化,具体如下:Wi=log⁡(N−dfi+0.5dfi+0.5)W_i=\log(\frac{N-df_i+0.5}{df_i+0.5})Wi​=log(dfi​+0.5N−dfi​+0.5​)其中,NNN表示“标准问”库中标准问题的总个数, dfidf_idfi​表示包含词汇qiq_iqi​ 的标准问题的个数。R(qi,d)=fi(k1+1)fi+K∗qfi(k2+1)qfi+k2R(q_i,d)=\frac{f_i(k_1+1)}{f_i+K}*\frac{qf_i(k_2+1)}{qf_i+k_2}R(qi​,d)=fi​+Kfi​(k1​+1)​∗qfi​+k2​qfi​(k2​+1)​K=k1∗(1−b+b∗dlavg_dl)K=k_1*(1-b+b*\frac{dl}{avg\_dl})K=k1​∗(1−b+b∗avg_dldl​)其中, k1,k2k_1,k_2k1​,k2​和bbb是调协因子,一般分别设为2,1,0.75; fif_ifi​ 表示词汇qiq_iqi​ 在标准问题中出现的次数;qfiqf_iqfi​ 表示词汇 qiq_iqi​在用户问题中出现的次数; dldldl 为标准问题的长度;avg_dlavg\_dlavg_dl 为“标准问”库中所有标准问题的平均长度。

根据上述例子,我们进行coding,BM25类定义具体如下

import numpy as np
from collections import Counterclass BM25_Model(object):def __init__(self, documents_list, k1=2, k2=1, b=0.5):self.documents_list = documents_listself.documents_number = len(documents_list)self.avg_documents_len = sum([len(document) for document in documents_list]) / self.documents_numberself.f = []self.idf = {}self.k1 = k1self.k2 = k2self.b = bself.init()def init(self):df = {}for document in self.documents_list:temp = {}for word in document:temp[word] = temp.get(word, 0) + 1self.f.append(temp)for key in temp.keys():df[key] = df.get(key, 0) + 1for key, value in df.items():self.idf[key] = np.log((self.documents_number - value + 0.5) / (value + 0.5))def get_score(self, index, query):score = 0.0document_len = len(self.f[index])qf = Counter(query)for q in query:if q not in self.f[index]:continuescore += self.idf[q] * (self.f[index][q] * (self.k1 + 1) / (self.f[index][q] + self.k1 * (1 - self.b + self.b * document_len / self.avg_documents_len))) * (qf[q] * (self.k2 + 1) / (qf[q] + self.k2))return scoredef get_documents_score(self, query):score_list = []for i in range(self.documents_number):score_list.append(self.get_score(i, query))return score_list

其中,documents_list 表示需要输入的文本列表,内部每个文本需要事先分好词;documents_number表示文本总个数;avg_documents_len 表示所有文本的平均长度;f 用于存储每个文本中每个词的出现的次数;idf用于存储每个词汇的权重值;init函数是类初始化函数,用于求解文本集合中的f和idf变量;get_score函数是获取一个文本与文本列表中一个文本的bm25相似度值;get_documents_score函数是获取一个文本与文本列表中所有文本的bm25相似度值。

定义好的BM25类如何去使用呢?具体如下:

首先,给出文本集合,也就是我们上文场景中提到的“标准问”库;

document_list = ["行政机关强行解除行政协议造成损失,如何索取赔偿?","借钱给朋友到期不还得什么时候可以起诉?怎么起诉?","我在微信上被骗了,请问被骗多少钱才可以立案?","公民对于选举委员会对选民的资格申诉的处理决定不服,能不能去法院起诉吗?","有人走私两万元,怎么处置他?","法律上餐具、饮具集中消毒服务单位的责任是不是对消毒餐具、饮具进行检验?"]

然后,我们对其进行分词操作;

import jieba
document_list = [list(jieba.cut(doc)) for doc in document_list]

得到结果如下:

[['行政', '机关', '强行', '解除', '行政', '协议', '造成', '损失', ',', '如何', '索取', '赔偿', '?'],
['借钱', '给', '朋友', '到期', '不', '还', '得', '什么', '时候', '可以', '起诉', '?', '怎么', '起诉', '?'],
['我', '在', '微信', '上', '被', '骗', '了', ',', '请问', '被', '骗', '多少', '钱', '才', '可以', '立案', '?'],
['公民', '对于', '选举', '委员会', '对', '选民', '的', '资格', '申诉', '的', '处理', '决定', '不服', ',', '能', '不能', '去', '法院', '起诉', '吗', '?'],
['有人', '走私', '两万元', ',', '怎么', '处置', '他', '?'],
['法律', '上', '餐具', '、', '饮具', '集中', '消毒', '服务', '单位', '的', '责任', '是不是', '对', '消毒', '餐具', '、', '饮具', '进行', '检验', '?']]

接下来,我们实例化BM25类,生成一个对象;

bm25_model = BM25_Model(document_list)

我们默认 k1,k2k_1,k_2k1​,k2​和bbb使用默认值。

通过参数调用,观察示例化的对象中documents_list ,documents_number,avg_documents_len ,f 和idf变量具体存储了什么;

print(bm25_model.documents_list)
print(bm25_model.documents_number)
print(bm25_model.avg_documents_len)
print(bm25_model.f)
print(bm25_model.idf)

结果如下:

documents_list:
[['行政', '机关', '强行', '解除', '行政', '协议', '造成', '损失', ',', '如何', '索取', '赔偿', '?'], ['借钱', '给', '朋友', '到期', '不', '还', '得', '什么', '时候', '可以', '起诉', '?', '怎么', '起诉', '?'], ['我', '在', '微信', '上', '被', '骗', '了', ',', '请问', '被', '骗', '多少', '钱', '才', '可以', '立案', '?'], ['公民', '对于', '选举', '委员会', '对', '选民', '的', '资格', '申诉', '的', '处理', '决定', '不服', ',', '能', '不能', '去', '法院', '起诉', '吗', '?'], ['有人', '走私', '两万元', ',', '怎么', '处置', '他', '?'], ['法律', '上', '餐具', '、', '饮具', '集中', '消毒', '服务', '单位', '的', '责任', '是不是', '对', '消毒', '餐具', '、', '饮具', '进行', '检验', '?']]
15.666666666666666
documents_number:
6
avg_documents_len:
15.666666666666666
tf:
[{'行政': 0.15384615384615385, '机关': 0.07692307692307693, '强行': 0.07692307692307693, '解除': 0.07692307692307693, '协议': 0.07692307692307693, '造成': 0.07692307692307693, '损失': 0.07692307692307693, ',': 0.07692307692307693, '如何': 0.07692307692307693, '索取': 0.07692307692307693, '赔偿': 0.07692307692307693, '?': 0.07692307692307693},
{'借钱': 0.06666666666666667, '给': 0.06666666666666667, '朋友': 0.06666666666666667, '到期': 0.06666666666666667, '不': 0.06666666666666667, '还': 0.06666666666666667, '得': 0.06666666666666667, '什么': 0.06666666666666667, '时候': 0.06666666666666667, '可以': 0.06666666666666667, '起诉': 0.13333333333333333, '?': 0.13333333333333333, '怎么': 0.06666666666666667},
{'我': 0.058823529411764705, '在': 0.058823529411764705, '微信': 0.058823529411764705, '上': 0.058823529411764705, '被': 0.11764705882352941, '骗': 0.11764705882352941, '了': 0.058823529411764705, ',': 0.058823529411764705, '请问': 0.058823529411764705, '多少': 0.058823529411764705, '钱': 0.058823529411764705, '才': 0.058823529411764705, '可以': 0.058823529411764705, '立案': 0.058823529411764705, '?': 0.058823529411764705},
{'公民': 0.047619047619047616, '对于': 0.047619047619047616, '选举': 0.047619047619047616, '委员会': 0.047619047619047616, '对': 0.047619047619047616, '选民': 0.047619047619047616, '的': 0.09523809523809523, '资格': 0.047619047619047616, '申诉': 0.047619047619047616, '处理': 0.047619047619047616, '决定': 0.047619047619047616, '不服': 0.047619047619047616, ',': 0.047619047619047616, '能': 0.047619047619047616, '不能': 0.047619047619047616, '去': 0.047619047619047616, '法院': 0.047619047619047616, '起诉': 0.047619047619047616, '吗': 0.047619047619047616, '?': 0.047619047619047616},
{'有人': 0.125, '走私': 0.125, '两万元': 0.125, ',': 0.125, '怎么': 0.125, '处置': 0.125, '他': 0.125, '?': 0.125},
{'法律': 0.05, '上': 0.05, '餐具': 0.1, '、': 0.1, '饮具': 0.1, '集中': 0.05, '消毒': 0.1, '服务': 0.05, '单位': 0.05, '的': 0.05, '责任': 0.05, '是不是': 0.05, '对': 0.05, '进行': 0.05, '检验': 0.05, '?': 0.05}]
idf:
{'行政': 1.2992829841302609, '机关': 1.2992829841302609, '强行': 1.2992829841302609, '解除': 1.2992829841302609, '协议': 1.2992829841302609, '造成': 1.2992829841302609, '损失': 1.2992829841302609, ',': -0.587786664902119, '如何': 1.2992829841302609, '索取': 1.2992829841302609, '赔偿': 1.2992829841302609, '?': -2.5649493574615367, '借钱': 1.2992829841302609, '给': 1.2992829841302609, '朋友': 1.2992829841302609, '到期': 1.2992829841302609, '不': 1.2992829841302609, '还': 1.2992829841302609, '得': 1.2992829841302609, '什么': 1.2992829841302609, '时候': 1.2992829841302609, '可以': 0.5877866649021191, '起诉': 0.5877866649021191, '怎么': 0.5877866649021191, '我': 1.2992829841302609, '在': 1.2992829841302609, '微信': 1.2992829841302609, '上': 0.5877866649021191, '被': 1.2992829841302609, '骗': 1.2992829841302609, '了': 1.2992829841302609, '请问': 1.2992829841302609, '多少': 1.2992829841302609, '钱': 1.2992829841302609, '才': 1.2992829841302609, '立案': 1.2992829841302609, '公民': 1.2992829841302609, '对于': 1.2992829841302609, '选举': 1.2992829841302609, '委员会': 1.2992829841302609, '对': 0.5877866649021191, '选民': 1.2992829841302609, '的': 0.5877866649021191, '资格': 1.2992829841302609, '申诉': 1.2992829841302609, '处理': 1.2992829841302609, '决定': 1.2992829841302609, '不服': 1.2992829841302609, '能': 1.2992829841302609, '不能': 1.2992829841302609, '去': 1.2992829841302609, '法院': 1.2992829841302609, '吗': 1.2992829841302609, '有人': 1.2992829841302609, '走私': 1.2992829841302609, '两万元': 1.2992829841302609, '处置': 1.2992829841302609, '他': 1.2992829841302609, '法律': 1.2992829841302609, '餐具': 1.2992829841302609, '、': 1.2992829841302609, '饮具': 1.2992829841302609, '集中': 1.2992829841302609, '消毒': 1.2992829841302609, '服务': 1.2992829841302609, '单位': 1.2992829841302609, '责任': 1.2992829841302609, '是不是': 1.2992829841302609, '进行': 1.2992829841302609, '检验': 1.2992829841302609}

最后,我们给出一个用户问题,通过bm25算法,计算出“标准问”库中所有标准问的相似度值;

query = "走私了两万元,在法律上应该怎么量刑?"
query = list(jieba.cut(query))
scores = bm25_model.get_documents_score(query)

结果如下:

[-3.41951, -3.39528, 0.03410, -2.88660, 0.04016, -0.67311]

通过结果我们可以发现,第五个标准问“有人走私两万元,怎么处置他?”与用户问题“走私了两万元,在法律上应该怎么量刑?”最为相似,符合我们的预期。

解释一下:为什么会出现负值,并且正值都很小?

是因为,我们举例的标准问库较小,有一些词汇或标点,例如:“?”,出现在很多标准问句中,导致“?”的idf值小于0,在做累加时,导致相似度一些为负值,并且正值都很小。因此,我们无论是使用TF-IDF,还是BM25时,都最好去掉停用词。

以上,就是本人对BM25算法的总结,有错误地方还希望指出,欢迎大家交流。

【短文本相似度】传统方法BM25解决短文本相似度问题相关推荐

  1. 【TF-IDF】传统方法TF-IDF解决短文本相似度问题

    机器学习算法与自然语言处理出品 @公众号原创专栏作者 刘聪NLP 学校 | 中国药科大学 药学信息学硕士 知乎专栏 | 自然语言处理相关论文 前几天写了一篇短文本相似度算法研究的文章,不过里面介绍的方 ...

  2. simhash mysql_海量数据相似度计算之simhash短文本查找

    在前一篇文章 <海量数据相似度计算之simhash和海明距离> 介绍了simhash的原理,大家应该感觉到了算法的魅力.但是随着业务的增长 simhash的数据也会暴增,如果一天100w, ...

  3. 短文本相似度在线计算_短文本相似度在线比较_短文本相似度在线查询_自然语言处理_百度AI开放平台

    短文本相似度计算服务能够提供不同短文本之间相似度的计算,输出的相似度是一个介于0到1之间的实数值,越大则相似度越高.相似度数值建议在一组数据中进行整体比对选用,输出数值越大,则代表语义相似程度相对越高 ...

  4. 闲聊机器人实例四:python实现小姜机器人,BERT检索模型,新增一个余弦相似度Cosine层, 解决BERT句向量编码传输耗时问题。部署tf-serving

    BERT检索模型,新增一个余弦相似度Cosine层, 解决BERT句向量编码传输耗时问题.部署tf-serving 业务需求 BERT向量召回问答对, FAQ标准问答对数据量不大 不能把BERT编码部 ...

  5. 太吾绘卷加载卡54_太吾绘卷支持度卡40%解决技巧详解_可可网

    太吾绘卷支持率怎么提升?学习武功等对支持率是有要求,有蛐蛐的可以选择挑战同门,下面是小编给大家带来的支持度卡40%解决方法介绍. 太吾绘卷支持率怎么提升 有蛐蛐的可以选择挑战同门,不过要小心对面输了可 ...

  6. java 中文相似度6_确定中文字符串的相似度解决方案

    分析 最简单的问题求解 字符串由一组不同含义的单词组成,它不同于数值型变量,可以用一个特定的数值来确定它的大小或位置,所以用何种方式来描述两个字符串之间的距离,成为了一个值得探讨的问题. 通常情况下, ...

  7. R语言计算杰卡德相似系数(Jaccard Similarity)实战:自定义函数计算Jaccard相似度、对字符串向量计算Jaccard相似度、将Jaccard相似度转化为Jaccard距离

    R语言计算杰卡德相似系数(Jaccard Similarity)实战:自定义函数计算Jaccard相似度.对字符串向量计算Jaccard相似度.将Jaccard相似度转化为Jaccard距离 目录 R ...

  8. 影响计算机质量度的指标,[计算机]专业版的质量度权重分析.ppt

    (一)什么是质量度? 质量度是搜索推广中的评价关键词质量的综合性指标,在账户中以三星等级的方式呈现. 质量度越高,意味着推广的质量越优秀,同等条件下赢得潜在客户(网民)的关注不认可的能力越强.,质量度 ...

  9. 向量点积衡量相似度_点积相似度、余弦相似度、欧几里得相似度

    看starspace的时候发现它实现了dot和cos两种similarity的度量方式,这里总结一下: 余弦相似度衡量两个向量在方向上的相似性,而不care两个向量的实际长度,A和B的长度即使是一个超 ...

最新文章

  1. 洛谷——P1044 栈
  2. 【实操手册】一篇文章教你快速开始使用视频点播
  3. LATEX 用法个人记录
  4. SQL Server系统数据库– msdb数据库
  5. 【转】LDA-linear discriminant analysis
  6. 链表的基本操作Basic Operation of LinkList
  7. JavaScript 中常用的弹窗
  8. 多应用集中落地,四川区块链产业爆发增长
  9. Activity焦点
  10. 走进小作坊(八)----公益之痒
  11. STM32内部ADC基准电压计算方法
  12. python用于爬虫的包是_Python 爬虫之抓包的理解
  13. win7设置电脑保护眼睛颜色(终极方法,提供两种颜色选择)
  14. 20个案例掌握PL/SQL 基础
  15. 计算机专业英语博士就业情况,北京外国语大学博士就业前景怎么样
  16. kafka中文教程(一)
  17. 《通过UE4/UE5的ProceduralMesh组件创建网格》
  18. SCI论文从入门到精通——IEEE论文那些事儿
  19. Linux ❉ HTTPS服务器详解
  20. NVT平台model屏幕配置

热门文章

  1. 《JAVA与模式》之装修者模式
  2. Redis集群方案应该怎么做?都有哪些方案?
  3. FBString分析与使用
  4. Redis再入门 codis 对比 Memcached
  5. Java基础拾遗:线程相关问题
  6. rcp异步多参数实例
  7. 自适应网站设计对百度友好的关键:添加applicable-device标签(转)
  8. 如何初始化一个vue项目
  9. c语言综合知识,软件设计师教程综合知识集锦之C语言编程风格
  10. c语言函数编写格式,在c语言中如何实现函数模板?