当前位置:   article > 正文

基于HMM的中文分词

基于hmm的中文分词

一、前言

  本文主要是基于隐马尔科夫模型对中文词进行分词。

二、HMM的理解

  HMM是一个统计模型,主要有HMM由初始状态概率分布π、状态转移概率分布A以及观测概率分布B确定,为了方便表达,把A, B, π 用 λ 表示,即:

            λ = (A, B, π)

  状态集合S:{B,M,E,S},N=4

  π:初始状态概率分布,如{B:-0.26268660809250016, E:-3.14e+100, M:-3.14e+100, S:-1.4652633398537678}

  A:状态转移概率分布,N*N的矩阵如:{'S': {'S': 747969.0, 'E': 0.0, 'M': 0.0, 'B': 563988.0}, 'E': {'S': 737404.0, 'E': 0.0, 'M': 0.0, 'B': 651128.0}

  B:观测概率分布或者称为发射概率分布,隐藏状态到观测状态的混淆矩阵,M*N,如{'S': {'否': 10.0, '昔': 25.0, '直': 238.0, '六': 1004.0, '殖': 17.0, '仗': 36.0, '挪': 15.0, '朗': 151.0}}

O:输出序列

  需理解什么是隐藏状态和可见状态,隐藏状态从一个隐藏状态到下一个隐藏状态;可见状态是一个隐藏状态到可见状态的输出。

A、HMM有两个假设

  1、假设任意时刻t的状态只依赖于前一个时刻的状态,与其他时刻的状态和观测序列无关

  2、假设任意时刻的观测只依赖与该市可的马尔科夫的状态,与其他观测,状态无关。

B、HMM常见的解决三个问题

  1、已知λ和观测序列O = {o1,o2, ..., oT},对观测序列的概率进行计算,即计算模型 λ 下观测序列O出现的概率P(O | λ),是一种概率计算问题,常用前向-后向算法(关心概率和的问题);

  2 、根据观测序列,参数λ求解,是一种学习问题,非监督学习算法,常用EM算法;

  3、已知λ观测序列O = {o1,o2, ..., oT},对隐藏状态的最大序列进行预测,即计算给定观测序列条件概率P(I|O,  λ )最大的状态序列I,是一种预测/解码问题,监督学习算法,常用维特比算法,如中文分词(关心最大概率问题);

C、HMM的概率计算问题算法的推导

  问题:已知HMM的参数 λ,和观测序列O = {o1, o2, ...,oT},求P(O|λ)

  思路:列举所有可能状态长度为T的序列集合I={i1,i2,…,iT},求解各个状态序列与观测状态的联合概率P(O,I|λ),最后求解所有可能的状态序列求和∑P(O,I|λ)得到P(O|λ);

  算法推导:

  P(O,I|λ) = P(O|I,λ)*P(I|λ)

  P(O|I,λ) 即对固定状态下,观测状态为O的概率:

对于P(I|λ),

P(I|λ) = p(i1,i2,i3…iT|λ)

        = p(i1|λ)*p(i2|i1,λ)*p(i3…iT|λ)

        = P(i1 |λ)*P(i2 | i1, λ)*P(i3 | i2, λ)...P(iT | iT-1, λ)

  因此,

  最终得到

  但这个的时间复杂度是相当大的,因此提出了前向-后向算法(动态规划中的一个方法)进行改进,这儿不做详述。

  对于第三个问题,也就是本文所要进行的分词算法,只是将目标问题变成了从求和变成了求最大化的问题,因此利用了动态规划中的维特比算法,不做详述。

三、构建结构

四、分词的技术路线

五、实验

5.1 根据语料进行训练

  1. class HmmTrain:
  2. def __init__(self):
  3. self.line_index = -1
  4. self.char_set = set()
  5. def int(self):# 初始化字典
  6. trans_dic = {}# 存储状态转移矩阵
  7. emit_dict = {} # 发射概率(状态->词语的条件概率)
  8. Count_dict = {} # 存储所有状态序列,用于归一化分母
  9. start_dict = {} # 存储状态的初始概率
  10. state_list = ['B','M','E','S']
  11. for state in state_list:
  12. trans_dic[state] = {}
  13. for state1 in state_list:
  14. trans_dic[state][state1] = 0.0
  15. for state in state_list:
  16. start_dict[state] = 0.0
  17. emit_dict[state] = {}
  18. Count_dict[state] = 0
  19. return trans_dic, emit_dict, start_dict, Count_dict
  20. '''保存模型'''
  21. def save_model(self,word_dict,model_path):
  22. f = open(model_path, 'w')
  23. f.write(str(word_dict))
  24. f.close()
  25. '''词语状态转换'''
  26. def get_word_status(self,word): # 根据词语,输出词语对应的SBME状态
  27. '''
  28. S:单字词
  29. B:词的开头
  30. M:词的中间
  31. E:词的末尾
  32. '''
  33. word_status = []
  34. if len(word) == 1:
  35. word_status.append('S')
  36. elif len(word) == 2:
  37. word_status = ['B', 'E']
  38. else:
  39. M_num = len(word) - 2
  40. M_list = ['M'] * M_num
  41. word_status.append('B')
  42. word_status.extend(M_list)
  43. word_status.append('E')
  44. return word_status
  45. '''基于人工标注语料库,训练发射概率,初始窗台,转移概率'''
  46. def train(self, train_filepath, trans_path, emit_path, start_path):
  47. trans_dict, emit_dict, start_dict, Count_dict = self.int()
  48. for line in open(train_filepath):
  49. self.line_index +=1
  50. line = line.strip()
  51. if not line:
  52. continue
  53. char_list = []
  54. for i in range(len(line)):
  55. if line[i] == " ":
  56. continue
  57. char_list.append(line[i])
  58. self.char_set = set(char_list) # 训练语料库中所有字的集和
  59. word_list = line.split(" ")
  60. line_status = [] # 统计状态序列
  61. for word in word_list:
  62. line_status.extend(self.get_word_status(word))
  63. if len(char_list) == len(line_status):
  64. for i in range(len(line_status)):
  65. if i == 0: #如果只有一个词,则直接算作是初始概率
  66. start_dict[line_status[0]] += 1 # start_dict 记录句子第一个字的状态,用于计算初始状态概率
  67. Count_dict[line_status[0]] += 1 # 记录每一个状态的出现次数
  68. else: # 统计上一个状态到下一个状态,两个状态之间的转移概率
  69. trans_dict[line_status[i-1]][line_status[i]] += 1
  70. Count_dict[line_status[i]] += 1
  71. # 统计发射概率
  72. if char_list[i] not in emit_dict[line_status[i]]:
  73. emit_dict[line_status[i]][char_list[i]] = 0.0
  74. else:
  75. emit_dict[line_status[i]][char_list[i]] += 1 # 用于计算发射概率
  76. else:
  77. continue
  78. # 进行归一化
  79. for key in start_dict: # 状态的初始概率
  80. start_dict[key] = start_dict[key] *1.0 / self.line_index
  81. for key in trans_dict: # 状态转移概率
  82. for key1 in trans_dict[key]:
  83. trans_dict[key][key1] = trans_dict[key][key1] / Count_dict[key]
  84. for key in emit_dict: # 发射概率(状态->词语的条件概率)
  85. for word in emit_dict[key]:
  86. emit_dict[key][word] = emit_dict[key][word] /Count_dict[key]
  87. self.save_model(trans_dict,trans_path)
  88. self.save_model(emit_dict,emit_path)
  89. self.save_model(start_dict,start_path)
  90. return trans_dict,emit_dict,start_dict
  91. 5.2 对训练的语料进行预测
  92. class HmmCut:
  93. def __init__(self):
  94. trans_path = './model/prob_trans.model'
  95. emit_path = './model/prob_emit.model'
  96. start_path = './model/prob_start.model'
  97. self.prob_trans = self.load_model(trans_path)
  98. self.prob_emit = self.load_model(emit_path)
  99. self.prob_start = self.load_model(start_path)
  100. '''加载模型'''
  101. def load_model(self, model_path):
  102. f = open(model_path, 'r')
  103. a = f.read()
  104. word_dict = eval(a)
  105. f.close()
  106. return word_dict
  107. '''verterbi算法求解'''
  108. def viterbi(self, obs, states, start_p, trans_p, emit_p): # 维特比算法(一种递归算法)
  109. V = [{}]
  110. path = {}
  111. for y in states:
  112. V[0][y] = start_p[y] * emit_p[y].get(obs[0], 0) # 在位置0,以y状态为末尾的状态序列的最大概率
  113. path[y] = [y]
  114. for t in range(1, len(obs)):
  115. V.append({})
  116. newpath = {}
  117. for y in states:
  118. state_path = ([(V[t - 1][y0] * trans_p[y0].get(y, 0) * emit_p[y].get(obs[t], 0), y0) for y0 in states if V[t - 1][y0] > 0])
  119. if state_path == []:
  120. (prob, state) = (0.0, 'S')
  121. else:
  122. (prob, state) = max(state_path)
  123. V[t][y] = prob
  124. newpath[y] = path[state] + [y]
  125. path = newpath # 记录状态序列
  126. (prob, state) = max([(V[len(obs) - 1][y], y) for y in states]) # 在最后一个位置,以y状态为末尾的状态序列的最大概率
  127. return (prob, path[state]) # 返回概率和状态序列
  128. # 分词主控函数
  129. def cut(self, sent):
  130. prob, pos_list = self.viterbi(sent, ('B', 'M', 'E', 'S'), self.prob_start, self.prob_trans, self.prob_emit)
  131. seglist = list()
  132. word = list()
  133. for index in range(len(pos_list)):
  134. if pos_list[index] == 'S':
  135. word.append(sent[index])
  136. seglist.append(word)
  137. word = []
  138. elif pos_list[index] in ['B', 'M']:
  139. word.append(sent[index])
  140. elif pos_list[index] == 'E':
  141. word.append(sent[index])
  142. seglist.append(word)
  143. word = []
  144. seglist = [''.join(tmp) for tmp in seglist]
  145. return seglist

六、总结

本文主要是对hmm应用于中文分词进行知识归纳和理解,其思想可以用来对以后实际中遇到的问题进行一个思维模式的借鉴,其主要为对自己所学知识进行整理,用于记录自己所学的感悟。

七、参考资料

1、http://www.cnblogs.com/sddai/p/8475424.html

2、https://blog.csdn.net/continueoo/article/details/77893587

3、https://www.cnblogs.com/skyme/p/4651331.html

4、https://github.com/liuhuanyong/WordSegment/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/935890
推荐阅读
相关标签
  

闽ICP备14008679号