维特比算法实现中文分词实例

  • 维特比(viterbi)算法介绍
  • 算法思路
  • 分词实例

维特比(viterbi)算法介绍

维特比算法是一种动态规划算法用于寻找最有可能产生观测事件序列的-维特比路径-隐含状态序列,可以解决任何一个图中的最短路径问题,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 术语“维特比路径”和“维特比算法”也被用于寻找观察结果最有可能解释相关的动态规划算法。例如在统计句法分析中动态规划算法可以被用于发现最可能的上下文无关的派生(解析)的字符串,有时被称为“维特比分析”
        在隐马尔科夫链中,任意时刻t下状态的值有多个,以拼音转汉字为例,输入拼音为“yike”可能有的值为一棵,一刻或者是一颗等待,用符号xij表示状态xi的第j个可能值,将状态序列按值展开,就得到了一个篱笆网(特殊的图——篱笆网络的有向图(Lattice ))了,这也就是维特比算法求解最优路径的图结构。

隐马尔科夫的预测问题就是要求图中的一条路径,使得该路径对应的概率值最大。 对应上图来讲,假设每个时刻x可能取的值为3,如果直接求的话,有3^N的组合数,底数3为篱笆网络宽度,指数N为篱笆网络的长度,计算量非常大。维特比利用动态规划的思想来求解概率最大路径(可理解为求图最短路径),使得复杂度正比于序列长度,复杂度为O(N⋅D⋅D), N为长度,D为宽度,从而很好地解决了问题的求解。

算法思路

输入:模型P(A∣B)=P(B∣A)P(A)P(B)P(A \mid B) = \frac{ P(B \mid A) P(A) }{ P(B) }P(A∣B)=P(B)P(B∣A)P(A)​和观测O=(o1,o2,⋯,oT)O=\left(o_{1}, o_{2}, \cdots, o_{T}\right)O=(o1​,o2​,⋯,oT​)

输出:最优路径I∗=(i1∗,i2∗,⋯,iT∗)I^{*}=\left(i_{1}^{*}, i_{2}^{*}, \cdots, i_{T}^{*}\right)I∗=(i1∗​,i2∗​,⋯,iT∗​)
(1)初始化δ1(i)=πibi(o1),i=1,2,⋯,N\delta_{1}(i)=\pi_{i} b_{i}\left(o_{1}\right), \quad i=1,2, \cdots, Nδ1​(i)=πi​bi​(o1​),i=1,2,⋯,N
ψ1(i)=0,i=1,2,⋯,N\psi_{1}(i)=0, \quad i=1,2, \cdots, Nψ1​(i)=0,i=1,2,⋯,N

(2)递推。对t=2,3,…,T
δt(i)=max⁡1⩽j<N[δt−1(j)aji]bi(ot),i=1,2,⋯,N\delta_{t}(i)=\max _{1 \leqslant j<N}\left[\delta_{t-1}(j) a_{j i}\right] b_{i}\left(o_{t}\right), \quad i=1,2, \cdots, Nδt​(i)=1⩽j<Nmax​[δt−1​(j)aji​]bi​(ot​),i=1,2,⋯,N
ψt(i)=arg⁡max⁡1≤j≤N[δt−1(j)ajt],i=1,2,⋯,N\psi_{t}(i)=\arg \max _{1 \leq j \leq N}\left[\delta_{t-1}(j) a_{j t}\right], \quad i=1,2, \cdots, Nψt​(i)=arg1≤j≤Nmax​[δt−1​(j)ajt​],i=1,2,⋯,N

(3)终止P∗=max⁡1≤i⩽NδT(i)P^{*}=\max _{1 \leq i \leqslant N} \delta_{T}(i)P∗=1≤i⩽Nmax​δT​(i)
iT∗=arg⁡max⁡1⩽i⩽N[δT(i)]i_{T}^{*}=\arg \max _{1 \leqslant i \leqslant N}\left[\delta_{T}(i)\right]iT∗​=arg1⩽i⩽Nmax​[δT​(i)]

分词实例

class HMM(object):# 初始化一些全局信息def __init__(self):import os# 主要是用于存取算法中间结果,不用每次都训练模型self.model_file = './hmm_model.pkl'# 状态值集合self.state_list = ['B', 'M', 'E', 'S']# 参数加载,用于判断是否需要重新加载model_fileself.load_para = False# 用于加载已计算的中间结果,当需要重新训练时,需初始化清空结果def try_load_model(self, trained):if trained:import picklewith open(self.model_file, 'rb') as f:self.A_dic = pickle.load(f)self.B_dic = pickle.load(f)self.Pi_dic = pickle.load(f)self.load_para = Trueelse:# 状态转移概率(状态->状态的条件概率)self.A_dic = {}# 发射概率(状态->词语的条件概率)self.B_dic = {}# 状态的初始概率self.Pi_dic = {}self.load_para = False# 计算得到HMM所需要的转移概率、发射概率以及初始概率def train(self, path):# 重置几个概率矩阵self.try_load_model(False)# 统计状态出现次数,求p(o)Count_dic = {}# 初始化参数函数def init_parameters():for state in self.state_list:        self.A_dic[state] = {s: 0.0 for s in self.state_list}self.Pi_dic[state] = 0.0self.B_dic[state] = {}Count_dic[state] = 0# 为每个读进来的字打标签,也即是状态值def 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()#print('init_parameters:',self.A_dic)line_num = -1# 观察者集合,主要是字以及标点等words = set()with open(path, encoding='utf8') as f:for line in f:line_num += 1#if line_num==2:  # 测试用#breakline = line.strip() # 用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列#print('here:',line)if not line:continueword_list = [i for i in line if i != ' ']#print('word_list:',word_list)words |= set(word_list)  # 更新字的集合,这里用的是集合的并操作linelist = line.split()  # 按空格分割字串#print('there:',linelist)line_state = []for w in linelist:line_state.extend(makeLabel(w))#print('line_state:',line_state)assert len(word_list) == len(line_state)for k, v in enumerate(line_state): # 这里的k:元素下标  v:元素值#print('k,v:',k,v)Count_dic[v] += 1if k == 0:self.Pi_dic[v] += 1  # 每个句子的第一个字的状态,用于计算初始状态概率else:# 计算转移pinlv:[line_state[k - 1]][v]用的非常好self.A_dic[line_state[k - 1]][v] += 1                       #print('B_dic[line_state[k]]:',self.B_dic[line_state[k]].get(word_list[k], 0))# 计算发射频率,dict.get(key, default=None)self.B_dic[line_state[k]][word_list[k]] = \self.B_dic[line_state[k]].get(word_list[k], 0) + 1.0  #print('self.B_dic:',self.B_dic)                #print('A_dic:\n',self.A_dic)#print('self.Pi_dic:',self.Pi_dic)# 计算初始概率:用每行开头字的状态值除以所有行self.Pi_dic = {k: v * 1.0 / line_num for k, v in self.Pi_dic.items()}#print('self.Pi_dic:',self.Pi_dic)#print('Count_dic:',Count_dic)# 状态转移概率:4x4的矩阵的每一个转移状态值除以该状态出现的总数(count(M/B)/count(B))self.A_dic = {k: {k1: v1 / Count_dic[k] for k1, v1 in v.items()}for k, v in self.A_dic.items()}#print('self.A_dic~:',self.A_dic)#加1平滑(发射概率计算同状态转移计算方式大致一样)self.B_dic = {k: {k1: (v1 + 1) / Count_dic[k] for k1, v1 in v.items()}for k, v in self.B_dic.items()}#序列化import picklewith open(self.model_file, 'wb') as f:pickle.dump(self.A_dic, f)pickle.dump(self.B_dic, f)pickle.dump(self.Pi_dic, f)# 返回对象本身return self#代码实现维特比算法def viterbi(self, text, states, start_p, trans_p, emit_p):      #print('start_p:',start_p,'\n','trans_p:', trans_p,'\n','emit_p:',emit_p)V = [{}]path = {}for y in states: #print('TT:',emit_p[y].get(text[0], 0))V[0][y] = start_p[y] * emit_p[y].get(text[0], 0)path[y] = [y]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()#print(text[t],neverSeen)for y in states:#设置未知字单独成词emitP = emit_p[y].get(text[t], 0) if not neverSeen else 1.0 (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])#print('prob:',prob,'state:',state,'——>',y)              V[t][y] = prob#print('V[t]:~',V[t])newpath[y] = path[state] + [y]        #print('newpath:',newpath)path = newpathif emit_p['M'].get(text[-1], 0)> emit_p['S'].get(text[-1], 0):(prob, state) = max([(V[len(text) - 1][y], y) for y in ('E','M')])else:(prob, state) = max([(V[len(text) - 1][y], y) for y in states])return (prob, path[state])def cut(self, text):import osif not self.load_para:self.try_load_model(os.path.exists(self.model_file))prob, pos_list = self.viterbi(text, self.state_list, self.Pi_dic, self.A_dic, self.B_dic) begin, next = 0, 0    for i, char in enumerate(text):pos = pos_list[i]                                                 if pos == 'B':begin = ielif pos == 'E':yield text[begin: i+1]next = i+1elif pos == 'S':yield charnext = i+1if next < len(text):yield text[next:]

通过调用上述方法对人民日报的分词语料进行训练,最终打印了一下原文以及分词后的结果

hmm = HMM()
#模型训练,数据使用的是人民日报的分词预料
hmm.train('./trainCorpus.txt_utf8')
#hmm.train('./test.txt_utf8')text = '这是一个非常棒的方案!'
res = hmm.cut(text)
print(text)
print(str(list(res)))

最终结果如下,这里使用的的是对人民日报的分词进行训练的。

最后说明一点本章节代码来自涂铭老师的《Python自然语言处理实战核心技术与算法》一书。

自然语言处理之维特比算法实现中文分词相关推荐

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

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

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

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

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

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

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

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

  5. 语音识别维特比解码_自然语言处理之维特比算法

    简介 鉴于维特比算法可解决多步骤中每步多选择模型的最优选择问题,本文简要介绍了维特比算法的基本理论,并从源代码角度对维特比算法进行剖析,并对源码中涉及的要点进行了解读,以便能快速应用该算法解决自然语言 ...

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

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

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

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

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

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

  9. 自然语言处理期末复习(2)中文分词

    1.汉语分词:通过计算机程序把组成汉语文本的字串自动转换 为词串的过程被称为自动切分 2.汉语切分的原因:(1)语音的合成(2)信息检索(3)词语计量分析 3.汉语分词基本方法:(1)基于词表的方法( ...

最新文章

  1. 用Jsp来实现文件下载功能的几种方式
  2. 加载NMGameX.dll时出错?
  3. TCP/IP / TCP 头
  4. 算法训练 字符串的展开
  5. 2016-12-17 新浪博客服务器挂掉了,所有博客页面都无法打开
  6. 程序员必知8大排序3大查找(一)
  7. 又一个“众所周知”的DAL层设计BUG
  8. TensorFlow-谷歌深度学习库 数据读取器
  9. Listary安装+破解
  10. Power Builder软件的下载安装
  11. Java 一个简单的接口使用案例
  12. php unlink 无法删除,php unlink()删除文件实例讲解
  13. 对标阿里P7Android最全面试题合集(GitHub、掘金高赞收集)
  14. 第7章第33节:五图排版:错落有致的波浪式排版 [PowerPoint精美幻灯片实战教程]
  15. 如何计算Java对象的大小
  16. 玫瑰花的python程序代码_python玫瑰花代码讲解,怎样用程序编写编写玫瑰花的代码,c程序或gava或者python...
  17. 复旦计算机学硕上岸,复旦大学工程与应用技术研究院电子信息2020年考研上岸前辈经验指导...
  18. 聊天机器人中的深度学习
  19. 2021年施工员-设备方向-通用基础(施工员)试题及解析及施工员-设备方向-通用基础(施工员)模拟试题
  20. php随机名人名言,php随机输出名人名言的函数

热门文章

  1. 【webLive】用Flv.js+OBS做直播
  2. ST-GCN源码分析
  3. 优秀Java开源项目
  4. Mapreduce 任务获取配置信息和counters 信息
  5. 西门子PLC S7-200SMART Modbus TCP通讯的步骤和要点
  6. 中年大叔学雷达--雷达回波信号的产生(LFM echo Matlab)
  7. 怎样减肚子效果最好?练这6个瑜伽动作就够了
  8. CSS栅格布局(Grid)
  9. 搬砖之路----MusicPlayer 一个基于Vlc(2.0+)开发的android音乐播放器--浅析在android开发过程中播放器选择之路!
  10. LaTeX常用符号及希腊字母