jieba分詞流程及部分原始碼解讀(一)

2021-08-21 14:15:45 字數 3392 閱讀 8927

首先我們來看一下jieba分詞的流程圖:

結巴中文分詞簡介

1)支援三種分詞模式:

精確模式:將句子最精確的分開,適合文字分析

全模式:句子中所有可以成詞的詞語都掃瞄出來,速度快,不能解決歧義

搜尋引擎模式:在精確的基礎上,對長詞再次切分,提高召回

2)支援繁體分詞

3)支援自定義詞典

4)基於trie樹結構實現高效的詞圖掃瞄,生成句子漢字所有可能成詞情況所構成的有向無環圖(dag)

5)  採用了動態規劃查詢最大概率路徑,找出基於詞頻的最大切分組合 6)對於詞庫中不存在的詞,也就是未登入詞,採用了基於漢字成詞能力的hmm模型,使用了viterbi演算法

接下來我們從原始碼分析一下:

__cut_dag 函式呼叫了 get_dag(sentence),這是用來生成每一塊(sentence)的有向無環圖dag。要生成dag就必須有語料庫的輔助了,所以在 同樣在 資料夾 jieba 下,可以找到乙個檔案:dict.txt。語料庫的有3列,第一列是詞,第二列是詞頻,第三列是詞性。在程式中初始化語料庫的動作在函式  initialize(dictionary) 中,它通過乙個包裝器 require_initialized 在 get_dag 函式被呼叫的時候才執行。**如下:

def require_initialized(fn):

@wraps(fn) #wraps的作用是保留被包裝函式的一些屬性,比如__doc__

global initialized

if initialized:

return fn(*args, **kwargs)

else:

initialize(dictionary)

return fn(*args, **kwargs)

語料庫trie樹載入完畢後,接下來我們來介紹如何進行dag分詞

以「正在學習大資料中的結巴分詞」為例,作為待分詞的輸入文字。

jieba.__init__.py中實現了jieba分詞介面函式cut(self, sentence, cut_all=false, hmm=true)。

jieba分詞介面主入口函式,會首先將輸入文字解碼為unicode編碼,然後根據入參,選擇不同的切分方式,本文主要以精確模式進行講解,因此cut_all和hmm這兩個入參均為預設值;

分詞中get_dag函式實現如下

# -*- coding: utf-8 -*-

import marshal

def get_dag(sentence):

n = len(sentence)

i,j=0,0

p = trie

dag = {}

while i=n:

i+=1

j=ip=trie

else:

p = trie

i+=1

j=ifor i in xrange(len(sentence)):

if i not in dag:

dag[i] =[i]

return dag

#動態規劃,計算最大概率的切分組合

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([ (概率對數,詞語末字位置) for x in dag[idx] ])

# 以idx:(概率對數最大值,詞語末字位置)鍵值對形式儲存在route中

# route[x+1][0] 表示 詞路徑[x+1,n-1]的最大概率對數,

# [x+1][0]即表示取句子x+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])

if __name__=='__main__':

sentence=u'正在學習大資料中的結巴分詞'

trie,freq,total,min_freq = marshal.load(open(u'd:\jieba.cache','rb'))#使用快取載入重要變數

rs=get_dag(sentence)#獲取dag

route={}

calc(sentence,rs,0,route)#根據得分進行初步分詞

print route

我們已經有了詞庫(dict.txt)的字首字典和待分詞句子sentence的dag,基於詞頻的最大切分 要在所有的路徑中找出一條概率得分最大的路徑,該怎麼做呢? 

jieba中的思路就是使用動態規劃方法,從後往前遍歷,選擇乙個頻度得分最大的乙個切分組合。 

具體實現見**,已給詳細注釋。

#動態規劃,計算最大概率的切分組合

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([ (概率對數,詞語末字位置) for x in dag[idx] ])

# 以idx:(概率對數最大值,詞語末字位置)鍵值對形式儲存在route中

# route[x+1][0] 表示 詞路徑[x+1,n-1]的最大概率對數,

# [x+1][0]即表示取句子x+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])

那麼登陸詞部分解釋完畢,下來就是未登陸詞,利用viterbi演算法來解決未登入詞的處理方法,後續更

jieba分詞原始碼分析

jieba是乙個開源的中文分詞庫。posseg 自定義詞典 init.py jieba分詞的入口 compat.py dict.txt 總的詞庫,記錄乙個詞 詞頻和詞性 test 測試demo encoding utf 8 import jieba seg list jieba.cut 我來到北京清...

原始碼閱讀之jieba分詞

功能 計算每一步的最優路徑 輸入 sentence 我是來到北京清華大學 dag route defcalc self,sentence,dag,route n len sentence route n 0,0 logtotal log self.total for idx in xrange n ...

jieba分詞原始碼解讀四

在上一節中我們考察了結巴分詞對於未登入詞的分詞方法,它使用了hmm模型和用來解碼hmm的維特比演算法。較之基於語料庫打分的初步分詞結果,例句 喬治馬丁寫冰與火之歌拖了好久 分詞情況變成了這樣 喬治 馬丁 寫冰 與 火之歌 拖 了 好久 比原來有改進,但改進幅度可以忽略不計。下一步我們就要除錯 了,目...