文本相似度-bm25算法原理及實(shí)現(xiàn)

原理

BM25算法,通常用來作搜索相關(guān)性平分桌肴。一句話概況其主要思想:對Query進(jìn)行語素解析皇筛,生成語素qi;然后坠七,對于每個(gè)搜索結(jié)果D水醋,計(jì)算每個(gè)語素qi與D的相關(guān)性得分,最后彪置,將qi相對于D的相關(guān)性得分進(jìn)行加權(quán)求和拄踪,從而得到Query與D的相關(guān)性得分。
BM25算法的一般性公式如下:


其中拳魁,Q表示Query惶桐,qi表示Q解析之后的一個(gè)語素(對中文而言,我們可以把對Query的分詞作為語素分析潘懊,每個(gè)詞看成語素qi姚糊。);d表示一個(gè)搜索結(jié)果文檔授舟;Wi表示語素qi的權(quán)重救恨;R(qi,d)表示語素qi與文檔d的相關(guān)性得分释树。
下面我們來看如何定義Wi肠槽。判斷一個(gè)詞與一個(gè)文檔的相關(guān)性的權(quán)重,方法有多種奢啥,較常用的是IDF秸仙。這里以IDF為例,公式如下:



其中扫尺,N為索引中的全部文檔數(shù)筋栋,n(qi)為包含了qi的文檔數(shù)。
根據(jù)IDF的定義可以看出正驻,對于給定的文檔集合弊攘,包含了qi的文檔數(shù)越多抢腐,qi的權(quán)重則越低。也就是說襟交,當(dāng)很多文檔都包含了qi時(shí)迈倍,qi的區(qū)分度就不高,因此使用qi來判斷相關(guān)性時(shí)的重要度就較低捣域。
我們再來看語素qi與文檔d的相關(guān)性得分R(qi啼染,d)。首先來看BM25中相關(guān)性得分的一般形式:


其中焕梅,k1迹鹅,k2,b為調(diào)節(jié)因子贞言,通常根據(jù)經(jīng)驗(yàn)設(shè)置斜棚,一般k1=2,b=0.75该窗;fi為qi在d中的出現(xiàn)頻率弟蚀,qfi為qi在Query中的出現(xiàn)頻率。dl為文檔d的長度酗失,avgdl為所有文檔的平均長度义钉。由于絕大部分情況下,qi在Query中只會(huì)出現(xiàn)一次规肴,即qfi=1捶闸,因此公式可以簡化為:


從K的定義中可以看到,參數(shù)b的作用是調(diào)整文檔長度對相關(guān)性影響的大小奏纪。b越大鉴嗤,文檔長度的對相關(guān)性得分的影響越大,反之越小序调。而文檔的相對長度越長醉锅,K值將越大,則相關(guān)性得分會(huì)越小发绢。這可以理解為硬耍,當(dāng)文檔較長時(shí),包含qi的機(jī)會(huì)越大边酒,因此经柴,同等fi的情況下,長文檔與qi的相關(guān)性應(yīng)該比短文檔與qi的相關(guān)性弱墩朦。
綜上坯认,BM25算法的相關(guān)性得分公式可總結(jié)為:


從BM25的公式可以看到,通過使用不同的語素分析方法、語素權(quán)重判定方法牛哺,以及語素與文檔的相關(guān)性判定方法陋气,我們可以衍生出不同的搜索相關(guān)性得分計(jì)算方法,這就為我們設(shè)計(jì)算法提供了較大的靈活性引润。

代碼實(shí)現(xiàn)

import math
import jieba
from utils import utils

# 測試文本
text = '''
自然語言處理是計(jì)算機(jī)科學(xué)領(lǐng)域與人工智能領(lǐng)域中的一個(gè)重要方向巩趁。
它研究能實(shí)現(xiàn)人與計(jì)算機(jī)之間用自然語言進(jìn)行有效通信的各種理論和方法。
自然語言處理是一門融語言學(xué)淳附、計(jì)算機(jī)科學(xué)议慰、數(shù)學(xué)于一體的科學(xué)。
因此奴曙,這一領(lǐng)域的研究將涉及自然語言别凹,即人們?nèi)粘J褂玫恼Z言,
所以它與語言學(xué)的研究有著密切的聯(lián)系缆毁,但又有重要的區(qū)別番川。
自然語言處理并不是一般地研究自然語言到涂,
而在于研制能有效地實(shí)現(xiàn)自然語言通信的計(jì)算機(jī)系統(tǒng)脊框,
特別是其中的軟件系統(tǒng)。因而它是計(jì)算機(jī)科學(xué)的一部分践啄。
'''

class BM25(object):

    def __init__(self, docs):
        self.D = len(docs)
        self.avgdl = sum([len(doc)+0.0 for doc in docs]) / self.D
        self.docs = docs
        self.f = []  # 列表的每一個(gè)元素是一個(gè)dict浇雹,dict存儲(chǔ)著一個(gè)文檔中每個(gè)詞的出現(xiàn)次數(shù)
        self.df = {} # 存儲(chǔ)每個(gè)詞及出現(xiàn)了該詞的文檔數(shù)量
        self.idf = {} # 存儲(chǔ)每個(gè)詞的idf值
        self.k1 = 1.5
        self.b = 0.75
        self.init()

    def init(self):
        for doc in self.docs:
            tmp = {}
            for word in doc:
                tmp[word] = tmp.get(word, 0) + 1  # 存儲(chǔ)每個(gè)文檔中每個(gè)詞的出現(xiàn)次數(shù)
            self.f.append(tmp)
            for k in tmp.keys():
                self.df[k] = self.df.get(k, 0) + 1
        for k, v in self.df.items():
            self.idf[k] = math.log(self.D-v+0.5)-math.log(v+0.5)

    def sim(self, doc, index):
        score = 0
        for word in doc:
            if word not in self.f[index]:
                continue
            d = len(self.docs[index])
            score += (self.idf[word]*self.f[index][word]*(self.k1+1)
                      / (self.f[index][word]+self.k1*(1-self.b+self.b*d
                                                      / self.avgdl)))
        return score

    def simall(self, doc):
        scores = []
        for index in range(self.D):
            score = self.sim(doc, index)
            scores.append(score)
        return scores

if __name__ == '__main__':
    sents = utils.get_sentences(text)
    doc = []
    for sent in sents:
        words = list(jieba.cut(sent))
        words = utils.filter_stop(words)
        doc.append(words)
    print(doc)
    s = BM25(doc)
    print(s.f)
    print(s.idf)
    print(s.simall(['自然語言', '計(jì)算機(jī)科學(xué)', '領(lǐng)域', '人工智能', '領(lǐng)域']))

分段再分詞結(jié)果

[['自然語言', '計(jì)算機(jī)科學(xué)', '領(lǐng)域', '人工智能', '領(lǐng)域', '中', '一個(gè)', '方向'], 
['研究', '人', '計(jì)算機(jī)', '之間', '自然語言', '通信', '理論', '方法'], 
['自然語言', '一門', '融', '語言學(xué)', '計(jì)算機(jī)科學(xué)', '數(shù)學(xué)', '一體', '科學(xué)'], 
[],
['這一', '領(lǐng)域', '研究', '涉及', '自然語言'], 
['日常', '語言'], 
['語言學(xué)', '研究'], 
['區(qū)別'], 
['自然語言', '研究', '自然語言'], 
['在于', '研制', '自然語言', '通信', '計(jì)算機(jī)系統(tǒng)'], 
['特別', '軟件系統(tǒng)'], 
['計(jì)算機(jī)科學(xué)', '一部分']]

s.f
列表的每一個(gè)元素是一個(gè)dict,dict存儲(chǔ)著一個(gè)文檔中每個(gè)詞的出現(xiàn)次數(shù)

[{'中': 1, '計(jì)算機(jī)科學(xué)': 1, '領(lǐng)域': 2, '一個(gè)': 1, '人工智能': 1, '方向': 1, '自然語言': 1}, 
{'之間': 1, '方法': 1, '理論': 1, '通信': 1, '計(jì)算機(jī)': 1, '人': 1, '研究': 1, '自然語言': 1}, 
{'融': 1, '一門': 1, '一體': 1, '數(shù)學(xué)': 1, '科學(xué)': 1, '計(jì)算機(jī)科學(xué)': 1, '語言學(xué)': 1, '自然語言': 1}, 
{}, 
{'領(lǐng)域': 1, '這一': 1, '涉及': 1, '研究': 1, '自然語言': 1}, 
{'日常': 1, '語言': 1}, 
{'語言學(xué)': 1, '研究': 1}, 
{'區(qū)別': 1}, 
{'研究': 1, '自然語言': 2}, 
{'通信': 1, '計(jì)算機(jī)系統(tǒng)': 1, '研制': 1, '在于': 1, '自然語言': 1}, 
{'軟件系統(tǒng)': 1, '特別': 1}, 
{'一部分': 1, '計(jì)算機(jī)科學(xué)': 1}]

s.df
存儲(chǔ)每個(gè)詞及出現(xiàn)了該詞的文檔數(shù)量

{'在于': 1, '人工智能': 1, '語言': 1, '領(lǐng)域': 2, '融': 1, '日常': 1, '人': 1, '這一': 1, '軟件系統(tǒng)': 1, '特別': 1, '數(shù)學(xué)': 1, '通信': 2, '區(qū)別': 1, '之間': 1, '計(jì)算機(jī)科學(xué)': 3, '科學(xué)': 1, '一體': 1, '方向': 1, '中': 1, '理論': 1, '計(jì)算機(jī)': 1, '涉及': 1, '研制': 1, '一門': 1, '研究': 4, '語言學(xué)': 2, '計(jì)算機(jī)系統(tǒng)': 1, '自然語言': 6, '一部分': 1, '一個(gè)': 1, '方法': 1}

s.idf
存儲(chǔ)每個(gè)詞的idf值

{'在于': 2.0368819272610397, '一部分': 2.0368819272610397, '一個(gè)': 2.0368819272610397, '語言': 2.0368819272610397, '領(lǐng)域': 1.4350845252893225, '融': 2.0368819272610397, '日常': 2.0368819272610397, '人': 2.0368819272610397, '這一': 2.0368819272610397, '軟件系統(tǒng)': 2.0368819272610397, '特別': 2.0368819272610397, '數(shù)學(xué)': 2.0368819272610397, '通信': 1.4350845252893225, '區(qū)別': 2.0368819272610397, '之間': 2.0368819272610397, '一門': 2.0368819272610397, '科學(xué)': 2.0368819272610397, '一體': 2.0368819272610397, '方向': 2.0368819272610397, '中': 2.0368819272610397, '理論': 2.0368819272610397, '計(jì)算機(jī)': 2.0368819272610397, '涉及': 2.0368819272610397, '研制': 2.0368819272610397, '計(jì)算機(jī)科學(xué)': 0.9985288301111273, '研究': 0.6359887667199966, '語言學(xué)': 1.4350845252893225, '計(jì)算機(jī)系統(tǒng)': 2.0368819272610397, '自然語言': 0.0, '人工智能': 2.0368819272610397, '方法': 2.0368819272610397}

s.simall(['自然語言', '計(jì)算機(jī)科學(xué)', '領(lǐng)域', '人工智能', '領(lǐng)域'])
['自然語言', '計(jì)算機(jī)科學(xué)', '領(lǐng)域', '人工智能', '領(lǐng)域']與每一句的相似度

[5.0769919814311475, 0.0, 0.6705449078118518, 0, 2.5244316697250033, 0, 0, 0, 0.0, 0.0, 0, 1.2723636062357853]

詳細(xì)代碼

https://github.com/jllan/jannlp/blob/master/similarity/bm25.py

參考

http://www.cnblogs.com/hdflzh/p/4034602.html
http://www.aiuxian.com/article/p-2690039.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屿讽,一起剝皮案震驚了整個(gè)濱河市昭灵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伐谈,老刑警劉巖烂完,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诵棵,居然都是意外死亡抠蚣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門履澳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘶窄,“玉大人,你說我怎么就攤上這事距贷”澹” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵忠蝗,是天一觀的道長现横。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么戒祠? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任晦攒,我火速辦了婚禮,結(jié)果婚禮上得哆,老公的妹妹穿的比我還像新娘脯颜。我一直安慰自己,他們只是感情好贩据,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布栋操。 她就那樣靜靜地躺著,像睡著了一般饱亮。 火紅的嫁衣襯著肌膚如雪矾芙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天近上,我揣著相機(jī)與錄音剔宪,去河邊找鬼。 笑死壹无,一個(gè)胖子當(dāng)著我的面吹牛葱绒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斗锭,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼地淀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了岖是?” 一聲冷哼從身側(cè)響起帮毁,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎豺撑,沒想到半個(gè)月后烈疚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聪轿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年爷肝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屹电。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阶剑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出危号,到底是詐尸還是另有隱情牧愁,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布外莲,位于F島的核電站猪半,受9級特大地震影響兔朦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜磨确,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一沽甥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乏奥,春花似錦摆舟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骗炉,卻和暖如春照宝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背句葵。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工厕鹃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乍丈。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓剂碴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親诗赌。 傳聞我的和親對象是個(gè)殘疾皇子汗茄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容