隐马尔可夫模型(HMM)是将分词作为字在字串中的序列标注任务来实现的。其基本思路是:每个字在构造一个特定的词语时都占据着一个确定的构词位置,现规定每个字最多只有四个构词位置:即B(词首)、M(词中)、E(词尾)和S(单独成词),那么下面句子(1)的分词结果就可以直接表示成如(2)所示的逐字标注形式:

(1)中文/分词/是/文本处理/不可或缺/的/一步!

(2)中/B文/E分/B词/E是/S文/B本/M处/M理/E不/B可/M或/M缺/E的/S一/B步/E!/S

用数学抽象表示如下:用代表输入的句子,n为句子长度,表示字,代表输出的标签,那么理想的输出即为:

在分词任务上,o即为B、M、E、S这四种标记,为诸如“中”“文”等句子中的每个字,包括标点等非中文字符。

需要注意的是,是关于2n个变量的条件概率,且n不固定。因此,几乎无法对进行精确计算。这里引入观测独立性假设,即每个字的输出仅仅与当前字有关,于是就能得到下式:

事实上,的计算要容易得多。通过观测独立性假设,目标问题得到极大简化。然而该方法完全没有考虑上下文,且会出现不合理的情况。比如按照之前设定的B、M、E和S标记,正常来说B后面只能是M或者E,然而基于观测独立性假设,我们很可能得到诸如BBB、BEM等的输出,显然是不合理的。

HMM就是用来解决该问题的一种方法。在上面的公式中,我们一直期望求解的是,通过贝叶斯公式能够得到:

为给定的输入,因此计算为常数,可以忽略,因此最大化等价于最大化.

针对作马尔可夫假设,得到:

同时,对有:

这里作齐次马尔可夫假设,每个输出仅仅与上一个输出有关,那么:

于是:

在HMM中,将称为观测概率,称为转移概率。通过设置某些,可以排除类似BBB、EM等不合理的组合。

求解的常用方法是Veterbi算法。它是一种动态规划方法,核心思想是:如果最终的最优路径经过某个,那么从初始节点到点的路径必然也是一个最优路径。

根据这个思想,可以通过递推的方法,在考虑每个时只需要求出所有经过各的候选点的最优路径,然后再与当前的结合比较。这样每步只需要算不超过次,就可以逐步找出最优路径。Viterbi算法的效率是,l是候选数目最多的节点的候选数目,它正比于n,这是非常高效率的。

参考:https://book.douban.com/subject/30247776/

下面通过python来实现HMM,将其封装成一个类,类名即HMM:

class HMM(object):def __init__(self):# 状态值集合self.state_list = ['B', 'M', 'E', 'S']# 状态转移概率self.A_dic = {}# 观测概率self.B_dic = {}# 状态的初始概率self.Pi_dic = {}# 统计状态(B,M,E,S)出现次数,求P(o)self.Count_dic = {}# 计算转移概率、观测概率以及初始概率def train(self, path):# 初始化参数def init_parameters():for state in self.state_list:self.A_dic[state] = {s: 0.0 for s in self.state_list}  # 键为状态,值为字典(键为状态,值为转移概率值)self.B_dic[state] = {}  # 键为状态,值为字典(键为字或标点,值为观测概率值)self.Pi_dic[state] = 0.0self.Count_dic[state] = 0def makeLabel(text):out_text = []if len(text) == 1:out_text.append('S')else:out_text += ['B'] + ['M'] * (len(text) - 2) + ['E']return out_textinit_parameters()line_num = 0with open(path, encoding='utf-8') as f:for line in f:line_num += 1line = line.strip()  # 每个句子去掉首尾空格if not line:continueword_list = [i for i in line if i != ' ']  # 字的集合line_list = line.split()  # 词的集合line_state = []  # 句子的状态序列for w in line_list:line_state.extend(makeLabel(w))  # 给句子中的每个词打标签assert len(word_list) == len(line_state)for k, v in enumerate(line_state):self.Count_dic[v] += 1  # 统计每个状态的频数(B,M,E,S)if k == 0:self.Pi_dic[v] += 1  # 每个句子的第一个字的状态(B,S),用于计算初始状态概率else:self.A_dic[line_state[k - 1]][v] += 1  # 计算转移概率self.B_dic[line_state[k]][word_list[k]] = \self.B_dic[line_state[k]].get(word_list[k], 0) + 1.0  # 计算观测概率self.Pi_dic = {k: v * 1.0 / line_num for k, v in self.Pi_dic.items()}self.A_dic = {k: {k1: v1 / self.Count_dic[k] for k1, v1 in v.items()} for k, v in self.A_dic.items()}self.B_dic = {k: {k1: (v1 + 1) / self.Count_dic[k] for k1, v1 in v.items()} for k, v inself.B_dic.items()}  # 加1平滑return selfdef viterbi(self, text, states, start_p, trans_p, emit_p):"""text:要切分的句子states:状态值集合(B,M,E,S)start_p:初始概率trans_p:转移概率emit_p:观测概率"""V = [{}]  # 局部概率,每个时刻的概率为{'B':,'M':,'E':,'S':}path = {}  # 最优路径# 初始化,计算初始时刻所有状态的局部概率,最优路径为各状态本身for y in states:V[0][y] = start_p[y] * emit_p[y].get(text[0], 0)path[y] = [y]# 初始化为{'B': ['B'], 'M': ['M'], 'E': ['E'], 'S': ['S']}# 递推,递归计算除初始时刻外每个时间点的局部概率和最优路径for t in range(1, len(text)):V.append({})  # 每个时刻都有一个局部概率,用字典{}表示newpath = {}# 检验训练的观测概率矩阵中是否有该字neverSeen = text[t] not in emit_p['S'].keys() and \text[t] not in emit_p['M'].keys() and \text[t] not in emit_p['E'].keys() and \text[t] not in emit_p['B'].keys()for y in states:emitP = emit_p[y].get(text[t], 0) if not neverSeen else 1.0  # 如果是未知字,则观测概率同设为1(prob, state) = max([(V[t - 1][y0] * trans_p[y0].get(y, 0) * emitP, y0) for y0 in states if V[t - 1][y0] >= 0])# 这里乘观测概率是为了方便计算局部概率,max即记录为局部概率,乘不乘观测概率对于max选择哪个t-1到t的最大概率路径没有影响V[t][y] = prob  # 迭代更新t时刻的局部概率Vnewpath[y] = path[state] + [y]  # 实时更新t时刻y状态的最优路径,传统的维特比算法只记录每个时刻的每个状态的反向指针path = newpath  # 更新t时刻所有状态的最优路径# 终止,观测序列的概率等于T时刻的局部概率(prob, state) = max([(V[len(text) - 1][y], y) for y in ['E', 'S']])  # 只需要考虑句尾是E或者S的情况return prob, path[state]def cut(self, text):prob, pos_list = self.viterbi(text, self.state_list, self.Pi_dic, self.A_dic, self.B_dic)  # 返回最优概率和最优路径begin = 0result = []  # 分词结果for i, char in enumerate(text):  # 以字为单位遍历句子pos = pos_list[i]if pos == 'B':begin = ielif pos == 'E':result.append(text[begin: i + 1])elif pos == 'S':result.append(char)return result

测试一下分词效果:

hmm = HMM()
hmm.train('./msr_training.utf8')text = '这是一个非常棒的方案!'
res = hmm.cut(text)
print(text)
print(res)

这是一个非常棒的方案!
['这是', '一个', '非常', '棒', '的', '方案', '!']

微软亚洲研究院的测试数据集上评测效果,主要评测参数包括准确率(Precision)、召回率(Recall)、F1值,计算方法为:

将文本的分词结果用数值对来表示。每一对数字对应一个词,表示词的首字和末字在文本中的位置。

例如有一个字符串文本:

万人大会堂今晚座无虚席

字符串中每个字符的索引分别为:

0  1  2  3  4  5  6  7  8  9 10

标准分词结果如下:

万|人|大会堂|今晚|座无虚席

分词结果用一个个数值对来表示(根据每个词在字符串中的索引顺序):

(0,0) (1,1) (2,4) (5,6) (7,10)

这样通过比较 标准分词结果 和 测试分词结果 的数值对的重合情况,就能计算出测试分词结果的正确分词数。从而计算出准确率和召回率。

参考:https://blog.csdn.net/wcy708708/article/details/82951899

精确率和召回率的计算公式:

精确率P=切分结果中正确分词数/切分结果中所有分词数 × 100%

召回率R=切分结果中正确分词数/标准答案中所有分词数 × 100%

同时与jieba分词工具的分词效果相比较:

def text2tuple(path, cut=True, J=False):import jiebawith open(path) as f:dic = {}i = 0for line in f:line = line.strip()if cut:res = line.split()else:if J:res = jieba.cut(line)else:res = hmm.cut(line)dic[i] = []num = 0for s in res:dic[i].append((num, num + len(s) - 1))num += len(s)i += 1return dicdef test(test, gold, J=False):dic_test = text2tuple(test, cut=False, J=J)dic_gold = text2tuple(gold, J=J)linelen = len(dic_test)assert len(dic_test) == len(dic_gold)num_test = 0num_gold = 0num_right = 0for i in range(linelen):seq_test = dic_test[i]seq_gold = dic_gold[i]num_test += len(seq_test)num_gold += len(seq_gold)for t in seq_test:if t in seq_gold:num_right += 1P = num_right / num_testR = num_right / num_goldF1 = P * R / (P + R)return P, R, F1P, R, F1 = test('./msr_test.utf8', './msr_test_gold.utf8')
print("HMM的精确率:", round(P, 3))
print("HMM的召回率:", round(R, 3))
print("HMM的F1值:", round(F1, 3))P, R, F1 = test('./msr_test.utf8', './msr_test_gold.utf8', J=True)
print("jieba的精确率:", round(P, 3))
print("jieba的召回率:", round(R, 3))
print("jieba的F1值:", round(F1, 3))

结果如下:

HMM的精确率: 0.762
HMM的召回率: 0.789
HMM的F1值: 0.388

jieba的精确率: 0.815
jieba的召回率: 0.811
jieba的F1值: 0.407

效果比jieba分词工具相差不多。

基于HMM和维特比算法的中文分词相关推荐

  1. 基于Hmm模型和Viterbi算法的中文分词和词性标注

    使用 python 实现基于Hmm模型和Viterbi算法的中文分词及词性标注:使用 最大概率算法 进行优化.最终效果:人民日报语料:分词(F1:96.189%):词性标注(F1:97.934%) 完 ...

  2. 自然语言处理之维特比算法实现中文分词

    维特比算法实现中文分词实例 维特比(viterbi)算法介绍 算法思路 分词实例 维特比(viterbi)算法介绍 维特比算法是一种动态规划算法用于寻找最有可能产生观测事件序列的-维特比路径-隐含状态 ...

  3. 维特比算法 python_维特比算法 实现中文分词 python实现

    最近我在学习自然语言处理,相信大家都知道NLP的第一步就是学分词,但分词≠自然语言处理.现如今分词工具及如何使用网上一大堆.我想和大家分享的是结巴分词核心内容,一起探究分词的本质. (1).基于前缀词 ...

  4. 中文分词算法python_Python FMM算法的中文分词器实现方法源码

    这是一篇基于Python代码使用FMM算法达到中文分词效果实现方法的文章.中文语句分词因为编码的关系在Python语言中并不是很好处理,关于中文乱码与编码的问题解决方法,可以参考玩蛇网的Python中 ...

  5. 视频教程-隐马尔科夫算法:中文分词神器-深度学习

    隐马尔科夫算法:中文分词神器 在中国知网从事自然语言处理和知识图谱的开发,并负责带领团队完成项目,对深度学习和机器学习算法有深入研究. 吕强 ¥49.00 立即订阅 扫码下载「CSDN程序员学院APP ...

  6. 基于MMSeg算法的中文分词类库

    最近在实现基于lucene.net的搜索方案,涉及中文分词,找了很多,最终选择了MMSeg4j,但MMSeg4j只有Java版,在博客园上找到了*王员外*(http://www.cnblogs.com ...

  7. Viterbi算法实现中文分词和词性标注

    Viterbi算法 目标 过程 词典分词 统计分词 词性标注 附录 附录二 附录三 源码地址 目标 实现基于词典的分词方法和统计分词方法 对分词结果进行词性标注 对分词及词性标注结果进行评价,包括4个 ...

  8. 基于 Ansj 的 elasticsearch 2.3.1 中文分词插件

    前言 这是一个elasticsearch的中文分词插件,基于Ansj中文分词.发起者Onni大神. 2.3.1插件安装 进入Elasticsearch目录运行如下命令 进入es目录执行如下命令 ./b ...

  9. python语言常用的中文分词第三方库是_基于boost使用Python调用NLPIR(ICTCLAS2013)中文分词组件...

    最近需要用到中文分词,本来想省事,用python的第三方库结巴分词,但看了下API,计算文本关键词的方法没有没有返回关键字对应的权值,翻了下文档应该是不还不支持,只好继续使用中科院的那套ICTCLAS ...

  10. iOS中文近似度的算法及中文分词(结巴分词)的集成

    引言 技术无关, 可跳过. 最近在写一个独立项目, 基于斗鱼直播平台的开放接口, 对斗鱼的弹幕进行实时的分析, 最近抽空记录一下其中一些我个人觉得值得分享的技术. 在写这个项目的时候我一直在思考, 弹 ...

最新文章

  1. Windows 编程之 对话框总结
  2. 2014Esri国际用户大会ArcGIS Online
  3. 68.connect-flash 用法详解 req,flash()
  4. 设计模式C++实现(15)——观察者模式
  5. ajax id sort,ajax返回的json内容进行排序使用sort()方法实现
  6. vc下c语言网络编程,用VC编写C/S消息传送程序
  7. python数据统计代码_Python 数据的累加与统计的示例代码
  8. 信用卡消费退款,商家让客户付手续费,合理吗?
  9. 面试官 | 如何提高服务器的并发能力?
  10. python excel 单元格格式_python设置单元格数值格式
  11. 解除工作压力的四大疗法
  12. [7]对话框控件的变量绑定
  13. 关于以太坊的nonce值
  14. 计算机wifi共享怎么设置,电脑怎么共享wifi热点 电脑设置wifi热点教程
  15. eact源码解析7.Fiber架构
  16. 3D Touch 之死
  17. 微信大全 微信支付 微信登录 小程序 PC 公众号
  18. on duplicate key update不生效_万粉盛典amp;六周年庆|双十一提前嗨!惠玩惠购不做尾款人!...
  19. Android开发拨打座机分机号码
  20. java之Map集合总结

热门文章

  1. Qt Creator 启动失败 可能的解决办法
  2. webvector将html转为svg或者png图片的工具
  3. a的n次方的快速算法及大数相乘
  4. 摩托罗拉发布RhoElements HTML5框架
  5. Struts2 初探
  6. 【OpenCV的cvSplit函数】
  7. 李宏毅机器学习homework0
  8. 【ENVI预处理】辐射校正、影像配准、图像融合、图像镶嵌 、图像裁剪、图像增强
  9. c语言模拟题第五套,2013年计算机二级C语言考试全真模拟试题第五套
  10. java线程知识点拾遗(CAS)