2021SC@SDUSC
2021SC@SDUSC
之前三篇博客中分析的前缀词典、有向无环图和寻找最大概率路径的方法其实都是在函数__cut_DAG(self, sentence)中调用的,首先构建前缀词典,其次构建有向无环图,然后计算最大概率路径,最后基于最大概率路径进行分词,如果遇到未登录词,则调用HMM模型(隐马尔克夫模型)进行切分。其实也就是再对词进行切分。
对于未登录词(注意:未登录词不是词典没有的词,而是单个字的词)组合成buf,对buf使用HMM分词。HMM模型主要是将分词问题视为一个序列标注(sequence labeling)问题,其中,句子为观测序列,分词结果为状态序列。首先通过语料训练出HMM相关的模型,然后利用Viterbi算法进行求解,最终得到最优的状态序列,然后再根据状态序列,输出分词结果。
具体代码分析如下:
def __cut_DAG(self, sentence):
DAG = self.get_DAG(sentence) # 构建有向无环图
route = {}
self.calc(sentence, DAG, route) # 动态规划计算最大概率路径
x = 0
buf = ''
#句子长度
N = len(sentence)
#遍历句子
while x < N:
y = route[x][1] + 1 #确定词语AB中的B坐标y
l_word = sentence[x:y] #提取词语AB
if y - x == 1: #如果词语的长度为1
buf += l_word #buf中放进词语
else:
if buf:
#如果buf不为空,长度为1,代表里面已经放入过词,使用迭代器yield返回当前值
if len(buf) == 1:
yield buf
buf = ''
else:
#buf使用HMM的分词,对这些未识别成功的词进行标注
if not self.FREQ.get(buf):
recognized = finalseg.cut(buf)
for t in recognized:
yield t
else:
for elem in buf:
yield elem
buf = ''
#使用迭代器yield返回当前词语
yield l_word
#下次开始寻词从y处开始,x到y便是分的词
x = y
if buf:
if len(buf) == 1:
yield buf
elif not self.FREQ.get(buf):
recognized = finalseg.cut(buf)
for t in recognized:
yield t
else:
for elem in buf:
yield elem
这个函数很多地方用到了yield,一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
所以这个函数就是对其计算最大概率路径时生成的可能词的情况再做正向判断并输出。