1 詞典加載
from pyhanlp import *
def load_dictionary():
"""
加載Hanlp中的Mini詞庫
:return:一個set形式的詞庫
"""
IOUtil = JClass('com.hankcs.hanlp.corpus.io.IOUtil')
path = HanLP.Config.CoreDictionaryPath.replace('.txt','.mini.txt')
dic = IOUtil.loadDictionary([path])
return set(dic.keySet())
這裏使用的是hanlp提供的詞庫,後面的切分算法也可以使用自己的詞典,hanlp相關的配置安裝可以參考
2 完全切分算法
完全切分指的是,找出一段文本中的所有單詞。這並不是標準意義上的分詞,有些人將這個過程誤稱爲分詞,其實不準確。
不考慮效率的話,樸素的完全切分算法其實很簡單。只要遍歷文本中連續序列,查詢該序列是否在字典中即可。定義詞典爲dic,文本爲txt,當前處理位置爲i,代碼如下:
def fully_segment(txt,dic):
res = []
for i in range(len(txt)):
for j in range(i+1,len(txt)+1):
tmp = txt[i:j]
if(tmp in dic):
res.append(tmp)
return res
調用樣例:
a = load_dictionary()
aa = '就讀北京大學'
fully_segment(aa,a)
#結果:['就', '就讀', '讀', '北', '北京', '北京大學', '京', '大', '大學', '學']
3 最長匹配算法
上面的輸出並不是中文分詞,我們更需要那種有意義的詞語序列,而不是所有出現在詞典中的單詞所構成的鏈表。比如,我們希望“北京大學”成爲一整個詞,而不是“北京”+“大學”之類的碎片。爲了達到這個目的,需要完善一下我們的規則,考慮到越長的單詞表達的意義越豐富,於是我們定義單詞越長優先級越高。具體說來,就是在某個下標爲起點遞增查詞的過程中,優先輸出更長的單詞,這種規則被稱爲最長匹配算法。該下標的掃描順序如果從前往後,稱爲正向最長匹配,反之則稱逆向最長匹配。
3.1 正向最長匹配
def forward_segment(txt,dic):
res = []
i = 0
while(i<len(txt)):
maxl = 0
r = ''
for j in range(i+1,len(txt)+1):
tmp = txt[i:j]
if(tmp in dic):
r = tmp
maxl = j
i = maxl
res.append(r)
return res
調用樣例和結果展示:
forward_segment(aa,a)
#結果:['就讀', '北京大學']
3.2 逆向最長匹配
def backward_segment(txt,dic):
res = []
i = len(txt)
while(i > 0):
minl = 0
j = i - 1
r = ''
while(j >= 0):
tmp = txt[j:i]
if(tmp in dic):
r = tmp
minl = j
j-=1
i = minl
res.append(r)
res.reverse()
return res
調用樣例和結果展示:
backward_segment(aa,a)
#結果:['就讀', '北京大學']
3.3 雙向最長匹配
提出這種方法的原因是爲了綜合上面兩種最長匹配算法的優勢。這是一種融合兩種匹配方法的複雜規則:
- 同時執行正向和逆向最長匹配,若兩者的詞數不同,則返回詞數更少的那一個。
- 否則,返回兩者中單字更少的那一個。當單字數也相同時,優先返回逆向最長匹配的結果。
def bidrectional_segment(txt,dic):
f = forward_segment(txt,dic)
b = backward_segment(txt,dic)
lf = len(f)
lb = len(b)
return b if(lf>=lb) else f
調用和結果樣例:
bb = '研究生命起源'
bidrectional_segment(bb,a)
#結果:['研究', '生命', '起源']