最近思考了一下未来,结合老师的意见,还是决定挑一个方向开始研究了,虽然个人更喜欢鼓捣。深思熟虑后,结合自己的兴趣点,选择了NLP方向,感觉比纯粹的人工智能、大数据之类的方向有趣多了,个人还是不适合纯粹理论研究 :)。发现图书馆一本语言处理方面的书也没有后,在京东找了一本书--《NLP汉语自然语言处理原理与实践》,到今天看了大约150页,发现还是很模糊,决定找点代码来看。


  从最简单的分词开始,发现分词的库已经很多了,选择了比较轻巧的jieba来研究。看了一下GitHub的基本介绍,突然感觉:我次奥,这也不过如此嘛,来来来写一个。jieba对于词典外的词用HMM模型进行解决,用Viterbi算法实现。网上对于HMM的解释很多,我个人也不太能够通过数学公司解释,其实模型比较简单,先了解了马尔可夫模型后就能比较容易地理解隐马尔可夫模型。


  这里记录一下对于HMM模型中,解决求隐藏状态链问题的Viterbi算法的学习。

在知乎问题:https://www.zhihu.com/question/20136144 中,我看了高票答案地回答,基本理解了思想,但是这个回答稍微有一点不全面,并没有强调回溯的思想,这里让我对算法产生了一点误解,后面有提到。

  大约花了半天时间阅读网上各种资料后,我选了一个Python代码决定仿照写一次 (https://blog.csdn.net/youfefi/article/details/74276546)代码用了numpy数组,由于我对numpy的函数不是太熟悉,决定先用list写一遍。令我惊讶的是,由于我脑袋太笨加上当时对维特比算法不是很清晰,我没有看懂作者的代码(略显尴尬),没办法,决定先按照自己的思路写出来。写的过程中发现算法有点出奇简单,居然简单3层循环就出来了,大概40分钟写完了。不出意外,写完运行发现和网上代码结果不一致。

  打断点开始调试,发现自己求出的概率矩阵是正确的,但最后路径不正确,意识到可能自己的理解有问题。再次上网查询资料。参考 https://www.2cto.com/kf/201609/544539.html 的文章,发现自己的理解是有问题的:

我的理解是依次迭代每个时刻,找到概率最大的状态即为该时刻状态,这样理解错误在于求的隐藏状态是一个链,根据马尔可夫假设,下一刻时刻的状态是依赖前一个状态的,我的理解就将状态之间割裂了,无法行成链。

正确的算法是每次迭代过程中,记录每种状态概率最大时其前驱状态,这样到最后一个时刻,选择概率最大的状态,再进行回溯。

代码如下:直接使用List、迭代过程中没有对概率进行判断,还有优化空间。

 1 # state 存放隐藏序列,sunny 0 rainy 1
2 # obser 存放观测序列 0 1 2 对应 walk shop clean
3 # start_p 是初始概率,0元素对应sunny的初始概率 1元素对应rainy的概率
4 # transition_p 转移概率矩阵 2*2 行为初始状态 列为新状态
5 # emission_p 发射概率矩阵 2*3 行为隐藏状态 列为可观测状态
6
7 # 迭代过程,每次只需要记录第t个时间点 每个节点的最大概率即可,后续计算时直接使用前序节点的最大概率即可
8 def compute(obser, state, start_p, transition_p, emission_p):
9 # max_p 记录每个时间点每个状态的最大概率,i行j列,(i,j)记录第i个时间点 j隐藏状态的最大概率
10 max_p = [[0 for col in range(len(state))] for row in range(len(obser))]
11 # path 记录max_p 对应概率处的路径 i 行 j列 (i,j)记录第i个时间点 j隐藏状态最大概率的情况下 其前驱状态
12 path = [[0 for col in range(len(state))] for row in range(len(obser))]
13 # 初始状态(1状态)
14 for i in range(len(state)):
15 # max_p[0][i]表示初始状态第i个隐藏状态的最大概率
16 # 概率 = start_p[i] * emission_p [state[i]][obser[0]]
17 max_p[0][i] = start_p[i] * emission_p[state[i]][obser[0]]
18 path[0][i] = i
19 # 后续循环状态(2-t状态)
20 # 此时max_p 中已记录第一个状态的两个隐藏状态概率
21 for i in range(1, len(obser)): # 循环t-1次,初始已计算
22 max_item = [0 for i in range(len(state))]
23 for j in range(len(state)): # 循环隐藏状态数,计算当前状态每个隐藏状态的概率
24 item = [0 for i in state]
25 for k in range(len(state)): # 再次循环隐藏状态数,计算选定隐藏状态的前驱状态为各种状态的概率
26 p = max_p[i - 1][k] * emission_p[state[j]][obser[i]] * transition_p[state[k]][state[j]]
27 # k即代表前驱状态 k或state[k]均为前驱状态
28 item[state[k]] = p
29 # 设置概率记录为最大情况
30 max_item[state[j]] = max(item)
31 # 记录最大情况路径(下面语句的作用:当前时刻下第j个状态概率最大时,记录其前驱节点)
32 # item.index(max(item))寻找item的最大值索引,因item记录各种前驱情况的概率
33 path[i][state[j]] = item.index(max(item))
34 # 将单个状态的结果加入总列表max_p
35 max_p[i] = max_item
36 #newpath记录最后路径
37 newpath = []
38 #判断最后一个时刻哪个状态的概率最大
39 p=max_p[len(obser)-1].index(max(max_p[len(obser)-1]))
40 newpath.append(p)
41 #从最后一个状态开始倒着寻找前驱节点
42 for i in range(len(obser) - 1, 0, -1):
43 newpath.append(path[i][p])
44 p = path[i][p]
45 newpath.reverse()
46 return newpath
47
48
49 if __name__ == '__main__':
50 # 隐状态
51 hidden_state = ['rainy', 'sunny']
52 # 观测序列
53 obsevition = ['walk', 'shop', 'clean']
54 state_s = [0, 1]
55 obser = [0, 1, 2]
56 # 初始状态,测试集中,0.6概率观测序列以sunny开始
57 start_probability = [0.6, 0.4]
58 # 转移概率,0.7:sunny下一天sunny的概率
59 transititon_probability = [[0.7, 0.3], [0.4, 0.6]]
60 # 发射概率,0.4:sunny在0.4概率下为shop
61 emission_probability = [[0.1, 0.4, 0.5], [0.6, 0.3, 0.1]]
62 result = compute(obser, state_s, start_probability, transititon_probability, emission_probability)
63 for k in range(len(result)):
64 print(hidden_state[int(result[k])])

Viterbi 算法 Python实现 [NLP学习一]的更多相关文章

  1. ZH奶酪:隐马尔可夫模型学习小记——forward算法+viterbi算法+forward-backward算法(Baum-welch算法)

    网上关于HMM的学习资料.博客有很多,基本都是左边摘抄一点,右边摘抄一点,这里一个图,那里一个图,公式中有的变量说不清道不明,学起来很费劲. 经过浏览几篇博文(其实有的地方写的也比较乱),在7张4开的 ...

  2. 【数据结构与算法Python版学习笔记】引言

    学习来源 北京大学-数据结构与算法Python版 目标 了解计算机科学.程序设计和问题解决的基本概念 计算机科学是对问题本身.问题的解决.以及问题求解过程中得出的解决方案的研究.面对一 个特定问题,计 ...

  3. 隐马尔可夫模型(HMM)及Viterbi算法

    HMM简介   对于算法爱好者来说,隐马尔可夫模型的大名那是如雷贯耳.那么,这个模型到底长什么样?具体的原理又是什么呢?有什么具体的应用场景呢?本文将会解答这些疑惑.   本文将通过具体形象的例子来引 ...

  4. Viterbi算法和隐马尔可夫模型(HMM)算法

    隐马尔可夫模型(HMM)及Viterbi算法 https://www.cnblogs.com/jclian91/p/9954878.html HMM简介   对于算法爱好者来说,隐马尔可夫模型的大名那 ...

  5. 隐马尔可夫模型(HMM)及Viterbi算法

    HMM简介 对于算法爱好者来说,隐马尔可夫模型的大名那是如雷贯耳.那么,这个模型到底长什么样?具体的原理又是什么呢?有什么具体的应用场景呢?本文将会解答这些疑惑. 本文将通过具体形象的例子来引入该模型 ...

  6. 分词(Tokenization) - NLP学习(1)

    自从开始使用Python做深度学习的相关项目时,大部分时候或者说基本都是在研究图像处理与分析方面,但是找工作反而碰到了很多关于自然语言处理(natural language processing: N ...

  7. 自然语言处理NLP学习笔记一:概念与模型初探

    前言 先来看一些demo,来一些直观的了解. 自然语言处理: 可以做中文分词,词性分析,文本摘要等,为后面的知识图谱做准备. http://xiaosi.trs.cn/demo/rs/demo 知识图 ...

  8. 从Theano到Lasagne:基于Python的深度学习的框架和库

    从Theano到Lasagne:基于Python的深度学习的框架和库 摘要:最近,深度神经网络以“Deep Dreams”形式在网站中如雨后春笋般出现,或是像谷歌研究原创论文中描述的那样:Incept ...

  9. Comprehensive learning path – Data Science in Python深入学习路径-使用python数据中学习

    http://blog.csdn.net/pipisorry/article/details/44245575 关于怎么学习python,并将python用于数据科学.数据分析.机器学习中的一篇非常好 ...

随机推荐

  1. 盘点 HashMap 的实现原理及面试题

    1.请你谈谈 HashMap 的工作原理如果被问到 HashMap 相关的问题,它的工作原理都会被作为面试的开场白,这个时候先装作若有所思的样子冷静一下.首先 HashMap 是基于 hashing ...

  2. 9、二进制安装K8s之增加node

    二进制安装K8s之增加node 1.复制文件,要部署几台就直接复制即可 #二进制文件 scp /data/k8s/bin/{kubelet,kube-proxy} root@192.168.100.1 ...

  3. 【转】ps命令详解与使用

    ps 概述 Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态 ...

  4. java 将字符串拆分成块装数组

    split 将字符串拆分 regex=???,根据???以其为界进行拆分. public String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串. 该方法的作用 ...

  5. Map 综述(一):彻头彻尾理解 HashMap

    转载自:https://blog.csdn.net/justloveyou_/article/details/62893086 摘要: HashMap是Map族中最为常用的一种,也是 Java Col ...

  6. jQuery中的表单过滤选择器(四、七)::input、:text、:password、:radio、:checkbox、:file等

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...

  7. 【CSS复合选择器、元素显示模式、背景】前端小抄(3) - Pink老师自学笔记

    [CSS复合选择器.元素显示模式.背景]前端小抄(3) 本学习笔记是个人对 Pink 老师课程的总结归纳,转载请注明出处! 一.CSS的复合选择器 1.1 什么是复合选择器 在 CSS 中,可以根据选 ...

  8. 🏆【Alibaba微服务技术系列】「Dubbo3.0技术专题」回顾Dubbo2.x的技术原理和功能实现及源码分析(温故而知新)

    RPC服务 什么叫RPC? RPC[Remote Procedure Call]是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范.它允许程序调用另一个地址空间(通常是共享网络的另 ...

  9. 剑指 Offer 38. 字符串的排列

    剑指 Offer 38. 字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" ...

  10. shell逐行读取文件内容

    shell 中逐行读取文件内容 1.语法简介 #!/bin/bash <<EOF shell 中逐行读取文件内容的语法如下所示. 这里虽然很简单,但是再配合上其他的工具,如sed,awk, ...