今天我们一起来学习一下自然语言处理中的bm25算法,bm25算法是常见的用来计算query和文章相关度的相似度的。其实这个算法的原理很简单,就是将需要计算的query分词成w1,w2,…,wn,然后求出每一个词和文章的相关度,最后将这些相关度进行累加,最终就可以的得到文本相似度计算结果。
首先我们来看一下bm25算法的计算公式:
S c o r e ( Q , d ) = ∑ i n W i ⋅ R ( q i , d ) Score(Q,d) = \sum\limits_i^n {{W_i} \cdot R({q_i},d)} Score(Q,d)=i∑n​Wi​⋅R(qi​,d)

我们来看看这个公式,首先Wi表示第i个词的权重,这里我们一般会使用TF-IDF算法来计算词语的权重,我在之前的博文对TF-IDF的理解与数学推导中,对TF-IDF算法有详细地分析与介绍,大家可以阅读参考。这个公式第二项R(qi,d)表示我们查询query中的每一个词和文章d的相关度,这一项就涉及到复杂的运算,我们慢慢来看。一般来说Wi的计算我们一般用逆项文本频率IDF的计算公式:
I D F ( q i ) = log ⁡ N + 0.5 n ( q i ) + 0.5 IDF({q_i}) = \log \frac{{N + 0.5}}{{n({q_i}) + 0.5}} IDF(qi​)=logn(qi​)+0.5N+0.5​

在这个公式中,N表示文档的总数,n(qi)表示包含这个词的文章数,为了避免对数里面分母项等于0,我们给分子分母同时加上0.5,这个0.5被称作调教系数,所以当n(qi)越小的时候IDF值就越大,表示词的权重就越大。我们来举个栗子:“bm25”这个词只在很少一部分的文章中出现,n(qi)就会很小,那么“bm25”的IDF值就很大;“我们”,“是”,“的”这样的词,基本上在每一篇文章中都会出现,那么n(qi)就很接近N,所以IDF值就很接近于0,接着我们来看公式中的第二项R(qi,d),我们首先来看看第二项的计算公式:
R ( q i , d ) = f i ( k 1 + 1 ) f i + K ⋅ q f i ( k 2 + 1 ) q f i + k 2 R({q_i},d) = \frac{{{f_i}({k_1} + 1)}}{{{f_i} + K}} \cdot \frac{{q{f_i}({k_2} + 1)}}{{q{f_i} + {k_2}}} R(qi​,d)=fi​+Kfi​(k1​+1)​⋅qfi​+k2​qfi​(k2​+1)​
在这个公式中,一般来说,k1、k2和b都是调节因子,k1=1、k2=1、b = 0.75,qfi表示qi在查询query中出现的频率,fi表示qi在文档d中出现的频率,因为在一般的情况下,qi在查询query中只会出现一次,因此把qfi=1和k2=1代入上述公式中,后面一项就等于1,最终可以得到:
R ( q i , d ) = f i ( k 1 + 1 ) f i + K R({q_i},d) = \frac{{{f_i}({k_1} + 1)}}{{{f_i} + K}} R(qi​,d)=fi​+Kfi​(k1​+1)​

我们再来看看K,在这里其实K的值也是一个公式的缩写,我们把K展开来看:
K =  k 1 ⋅ ( 1 − b + b ⋅ d l a v g ( d l ) ) K{\text{ = }}{{\text{k}}_1} \cdot (1 - b + b \cdot \frac{{dl}}{{avg(dl)}}) K = k1​⋅(1−b+b⋅avg(dl)dl​)

在K的展开式中dl表示文档的长度,avg(dl)表示文档的平均长度,b是前面提到的调节因子,从公式中可以看出在文章长度比平均文章长度固定的情况下,调节因子b越大,文章长度占有的影响权重就越大,反之则越小。在调节因子b固定的时候,当文章的长度比文章的平均长度越大,则K越大,R(qi,d)就越小。我们把K的展开式带入到bm25计算公式中去:
S c o r e ( Q , d ) = ∑ i n W i ⋅ R ( q i , d ) = ∑ i n W i ⋅ f i ( k 1 + 1 ) f i + K = ∑ i n I D F ( q i ) ⋅ f i ( k i + 1 ) f i + k 1 ⋅ ( 1 − b + b d l a v g ( d l ) ) = ∑ i n ( log ⁡ N + 0.5 n ( q i ) + 0.5 ) ⋅ f i ( k i + 1 ) f i + k 1 ⋅ ( 1 − b + b d l a v g ( d l ) ) Score(Q,d) = \sum\limits_i^n {{W_i} \cdot R({q_i},d) = \sum\limits_i^n {{W_i} \cdot \frac{{{f_i}({k_1} + 1)}}{{{f_i} + K}}} } \\= \sum\limits_i^n {IDF({q_i}) \cdot \frac{{{f_i}({k_i} + 1)}}{{{f_i} + {k_1} \cdot (1 - b + b\frac{{dl}}{{avg(dl)}})}}} = \sum\limits_i^n {(\log \frac{{N + 0.5}}{{n({q_i}) + 0.5}})} \cdot \frac{{{f_i}({k_i} + 1)}}{{{f_i} + {k_1} \cdot (1 - b + b\frac{{dl}}{{avg(dl)}})}} Score(Q,d)=i∑n​Wi​⋅R(qi​,d)=i∑n​Wi​⋅fi​+Kfi​(k1​+1)​=i∑n​IDF(qi​)⋅fi​+k1​⋅(1−b+bavg(dl)dl​)fi​(ki​+1)​=i∑n​(logn(qi​)+0.5N+0.5​)⋅fi​+k1​⋅(1−b+bavg(dl)dl​)fi​(ki​+1)​

在我们了解了bm25算法的原理了之后,我们再一起学习一下用Python来实现它,这里我用的是jupyter notebook,首先我们来导入几个需要用到的Python库:

import math
import jieba #结巴分词
import os
import re
import codecs

接着我们来定义一下测试的文本:

# 测试文本
text = '''
自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。
它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。
自然语言处理是一门融语言学、计算机科学、数学于一体的科学。
因此,这一领域的研究将涉及自然语言,即人们日常使用的语言,
所以它与语言学的研究有着密切的联系,但又有重要的区别。
自然语言处理并不是一般地研究自然语言,
而在于研制能有效地实现自然语言通信的计算机系统,
特别是其中的软件系统。因而它是计算机科学的一部分。
'''

定义好了测试文本以后,我们可以先定义一些tf、idf等参数:

f = [] # 列表的每一个元素是一个dict,dict存储着一个文档中每个词的出现次数
tf = {} # 储存每个词以及该词出现的文本数量
idf = {} # 储存每个词的idf值
k1 = 1.5
b = 0.75
def inition(docs):D = len(docs)avgdl = sum([len(doc)+ 0.0 for doc in docs]) / Dfor doc in docs:tmp = {}for word in doc:tmp[word] = tmp.get(word, 0) + 1  # 存储每个文档中每个词的出现次数f.append(tmp)for k in tmp.keys():tf[k] = tf.get(k, 0) + 1for k, v in tf.items():idf[k] = math.log(D - v + 0.5) - math.log(v + 0.5)return D, avgdl

初始化好了数据之后,我们来看一下计算文本相似度得分的方法:

def sim(doc, index):score = 0.0for word in doc:if word not in f[index]:continued = len(document[index])score += (idf[word] * f[index][word] * (k1 + 1) / (f[index][word] + k1 * (1 - b + b * d / avgdl)))return scoredef simall(doc):scores = []for index in range(D):score = sim(doc, index)scores.append(score)return scores

这两个方法支持单独计算,也支持批量计算,这些方法都定义好了之后,我们需要定义一个去stopword的方法,还有一个就是获取文章中句子的方法。首先我们来看看stopword的词典,我把这个stopwords.txt文档放在了网盘上,stopwords.txt密码为:9i62。这个文档我已经用utf-8编码了,可以直接使用,接下来我们需要定义一个去stopword的方法(stopwords.txt放在桌面上):

stop = set()
fr = codecs.open('./Desktop/stopwords.txt', 'r', 'utf-8')
for word in fr:stop.add(word.strip())
fr.close()
re_zh = re.compile('([\u4E00-\u9FA5]+)')def filter_stop(words):return list(filter(lambda x: x not in stop, words))

除了去停词的方法之外,我们还需要定义一个获取句子的方法:

def get_sentences(doc):line_break = re.compile('[\r\n]')delimiter = re.compile('[,。?!;]')sentences = []for line in line_break.split(doc):line = line.strip()if not line:continuefor sent in delimiter.split(line):sent = sent.strip()if not sent:continuesentences.append(sent)return sentences

接下来我们就可以开始进行文本操作了,首先我们对测试文本进行分词和去停词操作:

sents = get_sentences(text)
doc = []
for sent in sents:words = list(jieba.cut(sent))words = filter_stop(words)doc.append(words)
print(doc)
document = doc

我们将测试文本进行分词和去停词操作之后可以得到如下结果:

所有预备工作做好了之后,我们初始化所有数据,并计算出文本长度和平均文本长度:

D, avgdl = inition(doc)

这个时候我们就可以查看所有的参数信息,比如说我们可以打印出出现每个词的文本数量:

print(tf)

可以得到结果:

也可以打印每个词,以及每个词出现的次数:

print(f)

可以得到结果:

还可以打印出idf值:

print(idf)

可以得到结果:

最后我们可以通过一个分词之后的query来计算这个query和测试文本的相关度:

print(simall(['自然语言', '计算机科学', '领域', '人工智能', '领域']))

可以得到bm25算法的计算结果:

得到的这个向量的元素就是query和每一个测试文本的bm25相关度计算结果。嗯,在撸完这一套bm25算法之后,希望各位朋友对bm25算法有了更深的理解,本人能力有限,如博文中有纰漏之处,也望各位朋友指教,如有转载,也请标明出处,蟹蟹!

文本相似度bm25算法的原理以及Python实现(jupyter notebook)相关推荐

  1. 文本相似度-bm25算法原理及实现

    原理 BM25算法,通常用来作搜索相关性平分.一句话概况其主要思想:对Query进行语素解析,生成语素qi:然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分 ...

  2. java寻优算法_模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径...

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  3. 文本相似度几种计算方法及代码python实现

    文本相似度的计算广泛的运用在信息检索,搜索引擎, 文档复制等处: 因此在各种不同的情况与任务中,有不同的文本相似度计算. 方法1 编辑距离 编辑距离又称Levenshtein距离,是指将一个字符串转为 ...

  4. “好串”求解算法优化原理与Python实现

    佩服国防科大刘万伟老师的数学功底与编码功底,感谢刘老师无私分享! =====正文======= 题目要求:称一个 0-1 串是"好串",如果它的任何子串不在其中连续出现三次以上.编 ...

  5. 【Python】Jupyter Notebook的十大隐藏技巧--如何大大加速算法的迭代

    作者:杰少  Jupyter Notebook技巧大汇总 简 介 目前非常多的数据科学工作还是基于Notebook和Pycharm一起进行的,很多时候我们会在Notebook上面验证一些想法,然后再模 ...

  6. 网页去重||SimHash(高效的文本相似度去重算法)——适合大批量文档的相似度计算

    网页去重 之前我们对下载的url地址进行了去重操作,避免同样的url下载多次.其实不光url需要去重,我们对下载的内容也需要去重. 在网上我们可以找到许多内容相似的文章.但是实际我们只需要其中一个即可 ...

  7. 常用的文本相似度比较算法

    杰卡德相似性度量 (1)杰卡德相似系数 两个集合A和B交集元素的个数在A.B并集中所占的比例,称为这两个集合的杰卡德系数,用符号 J(A,B) 表示.杰卡德相似系数是衡量两个集合相似度的一种指标(余弦 ...

  8. TF-IDF与BM25算法原理

    1. TF-IDF原理 TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的一份文件的重要程度.字词的重要性随着它在文件中出现的次数成正比,但同时会随着它在语料库中出现的频率成反比 ...

  9. 从0到1,了解NLP中的文本相似度

    本文由云+社区发表 作者:netkiddy 导语 AI在2018年应该是互联网界最火的名词,没有之一.时间来到了9102年,也是项目相关,涉及到了一些AI写作相关的功能,为客户生成一些素材文章.但是, ...

最新文章

  1. 机器学习理论入门:第二章 经典监督学习算法-决策树
  2. 工控服务器性能指标,PLC的7大性能指标
  3. memcache的使用入门C++代码
  4. java jpanel 叠加_java – 如何在JPanel上叠加,调整大小和居中组件?
  5. 【原创】大叔问题定位分享(33)beeline连接presto报错
  6. 翻译: 构建基于卡尔曼滤波器的 IMU 用速度数据改进 IMU 姿态估计
  7. 数学模型 Lotka-Volterra
  8. java专有技术名词_关于Java的专有名词
  9. [Usaco2010 Hol]cowpol 奶牛政坛
  10. 高德地图两个不同的的功能合并
  11. 确保软件开发生命周期(SDLC)的安全
  12. 主流新产品开发模式介绍:集成产品开发管理
  13. ubuntu20.04lts初体验
  14. HDU 1560 sequence
  15. 前端已死?我看未必,但「低代码」已剑指前端程序员
  16. greenDao3 0使用小结
  17. 原创:谈谈计算机图像识别技术之身份证号码识别
  18. 追梦五年——不胜人生一场醉
  19. 通信原理实验之MPSK和MQAM 信号的星座图【100010369】
  20. android studio秘钥库文件不存在,[原]Android Studio查询SHA1的方法

热门文章

  1. 最全的大数据采集方法分类
  2. 高斯牛顿法----MATLAB实现
  3. Eclipse插件安装最简单方式--以Eclipse中文语言包汉化为例(附汉化包)
  4. AD19导出Gerber文件-嘉立创打板
  5. mac下的免费UML建模工具
  6. layui怎么给下拉框赋值_layui给select下拉框赋值
  7. R语言 cowplot包快速拼图
  8. Python自学笔记(二)命令行参数使用
  9. Android usb学习笔记:Android AOA协议Android端 流程总结
  10. Error: EBUSY: resource busy or locked