首先我們來看一下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的維特比演算法。較之基於語料庫打分的初步分詞結果,例句 喬治馬丁寫冰與火之歌拖了好久 分詞情況變成了這樣 喬治 馬丁 寫冰 與 火之歌 拖 了 好久 比原來有改進,但改進幅度可以忽略不計。下一步我們就要除錯 了,目...