溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python中的jieba源碼分析

發(fā)布時(shí)間:2021-09-01 14:23:03 來(lái)源:億速云 閱讀:171 作者:chen 欄目:編程語(yǔ)言

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

前言

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

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

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

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

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

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

首先使用概率無(wú)向圖,獲得最大概率路徑.概率無(wú)向圖的構(gòu)建完全依賴于字典,最大概率路徑求解也是依賴字典中的詞頻。 最后使用HMM模型來(lái)解決未登錄詞(Out Of Vocabulary) ,所以在整個(gè)過(guò)程如果沒(méi)有模型也是可以的,只要你有一個(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ū)別: 我寫(xiě)了個(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 即有向無(wú)環(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è)例子 我來(lái)到北京清華大學(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 來(lái)存儲(chǔ)圖數(shù)據(jù)結(jié)構(gòu)。字典中的key 是沒(méi)個(gè)字對(duì)應(yīng)句子的index,后面的value 是一個(gè)list就是可達(dá)的路徑。比如{1:[1,2]}意思就是“來(lái)”和“來(lái)到”這兩個(gè)詞在詞典中存在。其他的類推。

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

最大概率路徑求解

有了上面的DAG 下面求是求解最大概率路徑。這個(gè)問(wèn)題有很多中方法,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ò)程就上面幾行。關(guān)鍵就在max 那一句。這個(gè)問(wèn)題不在這里展開(kāi)。但是有個(gè)小的技巧說(shuō)下:在對(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è)就代表的是來(lái)到這個(gè)詞。

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

上面的最大概率在一定程度上解決了歧義問(wèn)題,但是在分詞里面還有另外一個(gè)問(wèn)題未登錄詞問(wèn)題也叫OOV(Out Of Vocabulary). jieba 使用HMM來(lái)識(shí)別未登錄詞。 比如: “我來(lái)到譽(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來(lái)切分。HMM到底怎么切分呢?

HMM

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

狀態(tài)序列(state sequence):BMES 這些狀態(tài)
觀測(cè)序列(observation sequence):就是看到的需要分詞的句子,所有的字組成一個(gè)序列。
現(xiàn)在的問(wè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í)問(wèn)題 了。在標(biāo)注好的語(yǔ)料之上??梢允褂脴O大似然估計(jì)來(lái)估計(jì)這三個(gè)參數(shù)。這里也看到,語(yǔ)料是關(guān)鍵因素,語(yǔ)料的質(zhì)量決定這三個(gè)參數(shù)。其實(shí)估計(jì)的過(guò)程不管其中的原理就是一些統(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表示的是無(wú)窮小。意思就是第一個(gè)字不可能是E,或者M(jìn).只可能是B,S具體是多少,使用語(yǔ)料中的頻率做估計(jì)。

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

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI