jieba原始碼解析(一) 分詞之前

2022-08-14 05:15:18 字數 2952 閱讀 4057

總的來說,jieba分詞主要是基於統計詞典,構造乙個字首詞典;然後利用字首詞典對輸入句子進行切分,得到所有的切分可能,根據切分位置,構造乙個有向無環圖;通過動態規劃演算法,計算得到最大概率路徑,也就得到了最終的切分形式。

jieba採用了延遲載入機制,在import後,不會立刻載入詞典檔案,在利用jieba.cut或jieba.lcut分詞的時候才載入本地詞典。如果有必要可以採用下面方式進行手動初始化:

import jieba

jieba.initialize() # 預設主詞典dict.txt

print(jieba.lcut("這是一句測試文字!"))

主詞典檔案dict.txt根據98年人民**語料和一些**的分詞結果統計所得,形如:

at&t 3 nz

b超 3 n

以「詞 詞頻 詞性」作為一條記錄。

jieba本身是乙個類庫,其初始化可以指定主詞典檔案,通過初始化時指定不同字典檔案可以達到同時對不同領域分詞的目的。假如現在有a和b兩個不同領域的文字和詞典檔案,通過下面方式可以同時做不同的分詞:

import jieba

# 不同詞典初始化

ajieba = jieba.tokenizer('adict.txt')

bjieba = jieba.tokenizer('bdict.txt')

# 分詞

awords = ajieba.lcut('a文字')

bwords = bjieba.lcut('b文字')

初始化時會讀取dict.txt,生成字首詞典。在__init__.py指令碼中,可以看出通過下面方式讀取並生成詞典資料:

def gen_pfdict(f_name):

lfreq = {}

ltotal = 0

f = open(f_name, 'rb')

for lineno, line in enumerate(f, 1):

try:

line = line.strip().decode('utf-8') # 解析dict.txt

word, freq = line.split(' ')[:2] # 獲取詞和詞頻

freq = int(freq)

lfreq[word] = freq

ltotal += freq

for ch in range(len(word)): # 獲取字首

wfrag = word[:ch + 1]

if wfrag not in lfreq: # 如果某字首詞不在字首詞典中,則將對應詞頻設定為0

lfreq[wfrag] = 0

except valueerror:

raise valueerror('invalid dictionary entry in %s at line %s: %s' % (f_name, lineno, line))

f.close()

return lfreq, ltotal

lfreq和ltotal分別表示字首詞典和總詞頻,二者通過marshal模組將資料持久化到本地。假如以上面dict.txt示例中的兩個詞,構成lfreq,其結果如下:

keyvaluea0

at0at&0

at&t3b

0b超3根據jieba專案修改日誌,可以看到從2023年的0.34版本,jieba就的詞典結構就不再使用trie!但可以學習一下trie樹的構造方式:

def gen_trie(f_name):

'''trie樹'''

lfreq = {}

trie = {}

ltotal = 0.0

with open(f_name, 'rb') as f:

lineno = 0

for line in f.read().rstrip().decode('utf-8').split('\n'):

lineno += 1

try:

word,freq,_ = line.split(' ')

freq = float(freq)

lfreq[word] = freq

ltotal+=freq

p = trie

for c in word:

if c not in p:

p[c] ={}

p = p[c]

p['']='' # ending flag

except valueerror:

raise valueerror

return trie, lfreq,ltotal

為什麼jieba沒有使用trie樹作為本地詞典儲存的資料結構?

參考jieba中的issue--不用trie,減少記憶體加快速度;優化**細節 #187,本處直接引用該issue的comment,如下:

對於get_dag()函式來說,用trie資料結構,特別是在python環境,記憶體使用量過大。經實驗,可構造乙個字首集合解決問題。

該集合儲存詞語及其字首,如set(['數', '資料', '資料結', '資料結構'])。在句子中按字正向查詢詞語,在字首列表中就繼續查詢,直到不在字首列表中或超出句子範圍。大約比原詞庫增加40%詞條。

該版本通過各項測試,與原版本分詞結果相同。

測試:一本5.7m的**,用預設字典,64位ubuntu,python 2.7.6。

trie:第一次載入2.8秒,快取載入1.1秒;記憶體277.4mb,平均速率724kb/s;

字首字典:第一次載入2.1秒,快取載入0.4秒;記憶體99.0mb,平均速率781kb/s;

此方法解決純python中trie空間效率低下的問題。

同時改善了一些**的細節,遵循pep8的格式,優化了幾個邏輯判斷。

原始碼閱讀之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分詞流程及部分原始碼解讀(一)

首先我們來看一下jieba分詞的流程圖 結巴中文分詞簡介 1 支援三種分詞模式 精確模式 將句子最精確的分開,適合文字分析 全模式 句子中所有可以成詞的詞語都掃瞄出來,速度快,不能解決歧義 搜尋引擎模式 在精確的基礎上,對長詞再次切分,提高召回 2 支援繁體分詞 3 支援自定義詞典 4 基於trie...

一周一原始碼之Vector原始碼解析

vector本質上也是個可擴充套件的陣列,可以把它想象成執行緒安全的arraylist。執行緒安全的關鍵是synchronized關鍵字。jdk1.8中,vector類的原始碼有1400余行,在這裡不會對諸多的方法一一詳細介紹,只針對核心 常用的 分為幾個部分來解析,以 arraylist的實現與特...