Python中的jieba源碼分析

本篇內(nèi)容主要講解“Python中的jieba源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Python中的jieba源碼分析”吧!

成都創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比宿州網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式宿州網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋宿州地區(qū)。費(fèi)用合理售后完善,十載實(shí)體公司更值得信賴。

前言

jieba分詞是Python 里面幾個(gè)比較流行的中文分詞工具之一。為了理解分詞工具的工作原理,以及實(shí)現(xiàn)細(xì)節(jié)對(duì)jieba進(jìn)行了詳細(xì)的閱讀。

讀代碼之前,我有幾個(gè)問題是這樣的:

分詞工具的實(shí)現(xiàn)都有哪幾個(gè)步驟?
結(jié)巴分詞的文檔說是使用了HMM模型,但是HMM 模型是如何運(yùn)用在分詞工具中的?,以及模型是如何產(chǎn)生的?
幾乎所有的分詞工具都支持用戶添加詞庫,但是用戶詞庫到底在分詞過程中扮演什么角色?
簡(jiǎn)介

jieba 分詞支持三種分詞模式,官方文檔給出了如下的Example

import jieba
 
seg_list = jieba.cut("我來到北京清華大學(xué)", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式
 seg_list = jieba.cut("我來到北京清華大學(xué)", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精確模式
 seg_list = jieba.cut("他來到了網(wǎng)易杭研大廈")  # 默認(rèn)是精確模式print(", ".join(seg_list))
 
seg_list = jieba.cut_for_search("小明碩士畢業(yè)于中國科學(xué)院計(jì)算所,后在日本京都大學(xué)深造")  # 搜索引擎模式print(", ".join(seg_list))

考慮到文章篇幅的限制,我會(huì)詳細(xì)解讀默認(rèn)模式也就是jieba.cut方法的所有實(shí)現(xiàn)。 閱讀過程中會(huì)涉及一些算法原理,本文不做詳細(xì)解釋。

首先使用概率無向圖,獲得最大概率路徑.概率無向圖的構(gòu)建完全依賴于字典,最大概率路徑求解也是依賴字典中的詞頻。 最后使用HMM模型來解決未登錄詞(Out Of Vocabulary) ,所以在整個(gè)過程如果沒有模型也是可以的,只要你有一個(gè)很好的詞典。最大概率路徑的求解還有很多方法,記得HanLP的求解就有實(shí)現(xiàn)最短路徑。

粗分

首先會(huì)使用正則將文本切分,正則什么樣?就跟現(xiàn)則的是默認(rèn)模式還是全模式。正則如下:

re_han_default = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._]+)", re.U)
re_han_cut_all = re.compile("([\u4E00-\u9FD5]+)", re.U)

到底有什么區(qū)別: 我寫了個(gè)測(cè)試:

test_str = u'我在重慶abc,他也在重慶? 1234你在重慶嗎'print (re_han_default.split(test_str))print (re_han_cut_all.split(test_str))

輸出:

['', '我在重慶abc', ',', '他也在重慶', '? ', '1234你在重慶嗎', '']
['', '我在重慶', 'abc,', '他也在重慶', '? 1234', '你在重慶嗎', '']

上面輸出的list 里面每一個(gè)被成為block。

細(xì)分

對(duì)粗分產(chǎn)生的blok ‘a(chǎn)bc’這樣的不能被re.han匹配的會(huì)直接作為結(jié)果反回。對(duì)于和中文連在一起的會(huì)進(jìn)入下一個(gè)階段細(xì)分。

DAG構(gòu)建

細(xì)分的第一步是構(gòu)建 DAG 即有向無環(huán)圖。構(gòu)建的核心代碼如下:

def get_DAG(self, sentence):        self.check_initialized() # 初始化,加載詞典
        DAG = {}
        N = len(sentence)        for k in xrange(N):
            tmplist = []
            i = k
            frag = sentence[k]            while i < N and frag in self.FREQ:
                if self.FREQ[frag]:
                    tmplist.append(i)
                i += 1
                frag = sentence[k:i + 1]            if not tmplist:
                tmplist.append(k)
            DAG[k] = tmplist        return DAG

怎么個(gè)意思呢: 舉個(gè)例子 我來到北京清華大學(xué) 產(chǎn)生的DAG 結(jié)果如下:

{0: [0], 1: [1, 2], 2: [2], 3: [3, 4], 4: [4], 5: [5, 6, 8], 6: [6, 7], 7: [7, 8], 8: [8]}

使用dict 來存儲(chǔ)圖數(shù)據(jù)結(jié)構(gòu)。字典中的key 是沒個(gè)字對(duì)應(yīng)句子的index,后面的value 是一個(gè)list就是可達(dá)的路徑。比如{1:[1,2]}意思就是“來”和“來到”這兩個(gè)詞在詞典中存在。其他的類推。

圖的產(chǎn)生依賴于self.FREQ這個(gè)變量,這是存儲(chǔ)字典的,其結(jié)構(gòu)是詞做key ,詞出現(xiàn)次數(shù)做value 的dict. 所以詞典的好壞對(duì)分詞結(jié)果會(huì)有很大的影響。如果根本不存在的路徑給連上了,應(yīng)該連上的沒有連上。后面的HMM模型也是沒辦法解決的后者,當(dāng)然最大概率路徑會(huì)解決部分第一個(gè)問題,但是都是有限的。所以詞典是相當(dāng)關(guān)鍵的。

最大概率路徑求解

有了上面的DAG 下面求是求解最大概率路徑。這個(gè)問題有很多中方法,jieba 使用的是動(dòng)態(tài)規(guī)劃。先不解釋動(dòng)態(tài)規(guī)劃是什么,直接看代碼,

def calc(self, sentence, DAG, route):
        N = len(sentence)
        route[N] = (0, 0)
        logtotal = log(self.total)        for idx in xrange(N - 1, -1, -1):
            route[idx] = max((log(self.FREQ.get(sentence[idx:x + 1]) or 1) -
                              logtotal + route[x + 1][0], x) for x in DAG[idx])

真?zhèn)€過程就上面幾行。關(guān)鍵就在max 那一句。這個(gè)問題不在這里展開。但是有個(gè)小的技巧說下:在對(duì)很小的數(shù)據(jù)進(jìn)行操作的時(shí)候,Python 也是可能向下溢出的,什么意思看下面的例子:

b = 0.0000001print b**100

結(jié)果會(huì)打印0.0 所有有個(gè)方法就是取log 。這個(gè)方法在很多地方都是有用的。 上面還用到了連個(gè)tuple比較這一技巧。

求解的結(jié)果如果分詞時(shí)候參數(shù)設(shè)置的不適用HMM模型,到這里就結(jié)束了。求解結(jié)果部分如下:key 同樣是對(duì)應(yīng)的index.第二個(gè)就代表的是來到這個(gè)詞。

{0: (-32.587853155857076, 0), 1: (-27.379629658355885, 2),}
未登錄詞

上面的最大概率在一定程度上解決了歧義問題,但是在分詞里面還有另外一個(gè)問題未登錄詞問題也叫OOV(Out Of Vocabulary). jieba 使用HMM來識(shí)別未登錄詞。 比如: “我來到譽(yù)存科技” 這句話,產(chǎn)生的最大概率路徑是

{0: (-42.29693140266269, 0), 1: (-37.0887079051615, 2), 2: (-33.93639839927486, 2), 3: (-28.257272562332492, 3), 4: (-17.872975353951055, 4), 5: (-8.250710549196151, 6), 6: (-10.881580216048834, 6), 7: (0, 0)}

看到3,和4 都是獨(dú)立的詞,如果不使用HMM 這個(gè)詞就會(huì)被分成兩個(gè)字。其邏輯就是把多個(gè)連續(xù)的單字組合成新的blok 使用HMM來切分。HMM到底怎么切分呢?

HMM

HMM 隱馬模型的定義自己可以去查,就算查完你也不一定能說清楚到底在分詞的時(shí)候怎么使用的,但是不查絕對(duì)不知道。 在分詞之前語料會(huì)被標(biāo)注,標(biāo)注的方式有很多中。其中比較多的是BMES對(duì)應(yīng)的是B(begin)詞的開頭,M(Middle)詞的中間,E(End)詞的結(jié)束,S(Single)單個(gè)的詞 HMM有幾個(gè)概念,和分詞這個(gè)具體問題的對(duì)應(yīng)關(guān)系如下:

狀態(tài)序列(state sequence):BMES 這些狀態(tài)
觀測(cè)序列(observation sequence):就是看到的需要分詞的句子,所有的字組成一個(gè)序列。
現(xiàn)在的問題就是一直觀測(cè)序列求狀態(tài)序列。但是第一部我們需要建立HMM模型。 HMM 有三個(gè)基本組成: 初始概率狀態(tài)概率分布A 狀態(tài)轉(zhuǎn)移矩陣pi 觀測(cè)概率分布B

如果有了上面三個(gè)元素一個(gè)HMM模型就是定好了。當(dāng)然還有HMM模型有很多假設(shè),此處省略。 jieba 是如何得到這三個(gè)變量的了。這就是HMM的學(xué)習(xí)問題 了。在標(biāo)注好的語料之上。可以使用極大似然估計(jì)來估計(jì)這三個(gè)參數(shù)。這里也看到,語料是關(guān)鍵因素,語料的質(zhì)量決定這三個(gè)參數(shù)。其實(shí)估計(jì)的過程不管其中的原理就是一些統(tǒng)計(jì)計(jì)算。jieba 把這三個(gè)元素分別存貯在三個(gè)py文件中:

prob_start.py: 初始狀態(tài)概率 prob_trans.py: 狀態(tài)轉(zhuǎn)移 prob_emit.py: 觀測(cè)概率分布

看看 prob_start:

P={'B': -0.26268660809250016, 'E': -3.14e+100, 'M': -3.14e+100, 'S': -1.4652633398537678}

-3.14e+100表示的是無窮小。意思就是第一個(gè)字不可能是E,或者M(jìn).只可能是B,S具體是多少,使用語料中的頻率做估計(jì)。

到此,相信大家對(duì)“Python中的jieba源碼分析”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

分享名稱:Python中的jieba源碼分析
地址分享:http://bm7419.com/article44/jjspee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、網(wǎng)站設(shè)計(jì)服務(wù)器托管、網(wǎng)站維護(hù)建站公司、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)