def __cut(sentence):
global emit_P
prob, pos_list = viterbi(sentence, 'BMES', start_P, trans_P, emit_P)
begin, nexti = 0, 0
# print pos_list, sentence
for i, char in enumerate(sentence):
pos = pos_list[i]
if pos == 'B':
begin = i
elif pos == 'E':
yield sentence[begin:i + 1]
nexti = i + 1
elif pos == 'S':
yield char
nexti = i + 1
if nexti < len(sentence):
yield sentence[nexti:]
主要处理内容就一行prob, pos_list = viterbi(sentence, 'BMES', start_P, trans_P, emit_P),后面内容为结果输出。所以cut函数的作用是调用viterbi函数然后输出。
理解viterbi函数需要对viterbi算法进行理解,参考《HMM模型之viterbi算法》。
2、viterbi函数
代码如下:
def viterbi(obs, states, start_p, trans_p, emit_p):
V = [{}] # tabular
path = {}
for y in states: # init
V[0][y] = start_p[y] + emit_p[y].get(obs[0], MIN_FLOAT)
path[y] = [y]
for t in xrange(1, len(obs)):
V.append({})
newpath = {}
for y in states:
em_p = emit_p[y].get(obs[t], MIN_FLOAT)
(prob, state) = max(
[(V[t - 1][y0] + trans_p[y0].get(y, MIN_FLOAT) + em_p, y0) for y0 in PrevStatus[y]])
V[t][y] = prob
newpath[y] = path[state] + [y]
path = newpath
(prob, state) = max((V[len(obs) - 1][y], y) for y in 'ES')
return (prob, path[state])
变量V是一个list,list每一个元素是一个字典,list的长度为obs的长度。字典的keys为隐藏状态集,values为局部概率。
变量path是局部最佳路径,keys为隐藏状态集,values为list即为局部最佳路径。
第一个循环即为求t=1时刻的局部概率和局部路径,相关概念请参考《HMM模型之viterbi算法》。get()函数为取值,第二个参数MIN_FLOAT为全部变量,值为:-3.14e100,表示一个极小的概率,函数表示如果在能够取到第一个参数的值,则返回相应的值,否则返回MIN_FLOAT。
第二个循环求t>1时刻,局部概率和局部路径。
(prob, state) = max(
[(V[t - 1][y0] + trans_p[y0].get(y, MIN_FLOAT) + em_p, y0) for y0 in PrevStatus[y]])
PrevStatus[y]表示隐藏状态y的前一个时刻可能的隐藏状态集。这个计算含义为通过t-1时刻的局部概率计算t时刻隐藏状态为y的局部概率和反向指针。其中返回值是一个元组,prob是局部概率,state是反向指针。
newpath[y] = path[state] + [y]
这是得到t时刻的局部最佳路径,即反向指针指向的局部最佳路径+新的状态y。
(prob, state) = max((V[len(obs) - 1][y], y) for y in 'ES')
计算到这里已经得到末尾元素的各个隐藏状态的局部概率和局部最佳路径,因为最后一个元素的隐藏状态只可能是E或者S,所以只需要比较这两个状态的局部概率即可。较大者即为全局的最佳概率和最佳路径。