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)
#结果:['研究', '生命', '起源']