viterbi算法实例及python实现

article/2025/9/12 9:16:46

Python中hmmlearn给出了三种HMM模型:MultiomialHMM,GaussianHMM,GMMHMM。本文以MultiomialHMM为例,使用《从机器学习到深度学习》中第六章的活动/天气模型进行推算。

       假设有这样一个问题,远在另一个城市上大学的儿子每天通过邮件向你汇报他今天做的最多事情是什么,这些事情可能是这三项之一:打球、读书、访友。那么在这种场景下,如何推测儿子所在城市的天气情况。假设儿子活动有如下规律:在晴天更可能打球,在阴天更可能访友,在雨天更可能的读书,在散发概率矩阵中显示为概率值大小。

       在这个问题中,儿子的活动类型构成了模型的可见层,天气情况成了隐藏层。

       那么 1.状态的样本空间 states = ('晴', '阴','雨');

               2.起始个状态概率  start_probability = {'晴':0.5, '阴':0.3,'雨':0.2}
               3.状态转移概率     transition_probability = {
                                                                                    '晴': {'晴':0.7, '阴':0.2,'雨':0.1},
                                                                                     '阴': {'晴':0.3, '阴':0.5,'雨':0.2},
                                                                                     '雨': {'晴':0.3, '阴':0.4,'雨':0.3},
                                                                                   }
                4.状态->观测的发散概率  emission_probability = {
                                                                                     '晴': { '读书': 0.3,'打球': 0.4, '访友': 0.3},
                                                                                     '阴': { '读书': 0.3,'打球': 0.2, '访友': 0.5},
                                                                                     '雨': { '读书': 0.8,'打球': 0.1, '访友': 0.1},
                                                                                   }

                 5.观测的样本空间 observations = ( '读书', '打球','访友','读书')

则MultiomialHMM代码如下:

import numpy as np
from hmmlearn import hmmstates = ['晴', '阴','雨']
n_states = len(states)observations = ['读书', '打球','访友','读书']
n_observations = len(observations)start_probability = np.array([0.5, 0.3, 0.2])transition_probability = np.array([[0.7, 0.2, 0.1],[0.3, 0.5, 0.2],[0.3, 0.4, 0.3]
])emission_probability = np.array([[0.3, 0.4, 0.3],[0.3, 0.2, 0.5],[0.8, 0.1, 0.1]
])model = hmm.MultinomialHMM(n_components=n_states)
model.startprob_=start_probability
model.transmat_=transition_probability
model.emissionprob_=emission_probability
print(model.startprob_)
print(model.transmat_)
print(model.emissionprob_)
observe_chain=np.array([0,1,2,0]).reshape(-1,1)
print(model.score(observe_chain))
print(model.predict(observe_chain))

结果如下:

 第一行为初始概率,第二至四行为转移概率矩阵,第五至七行为emission概率,-4.31142为打分,[0000]为预测结果。接下来用维特比算法计算隐藏状态链,即HMM模型的解码问题。


维特比算法用来在给定HMM模型参数和一个可见状态链时,找出最可能的隐藏状态链。

维特比算法的过程可分为两步:

1.正方向推算:用动态规划的方法从第一个隐藏结点开始逐步计算每个可能状态;

2.反方向推理:计算到最后一个结点时,概率最大的那个状态就是最后一个隐藏结点的被推测状态。

       则计算由下图所示:

第四天结果没算,则前三天应该为雨、晴、晴,对应概率为0.16,0.042,0.0082。书本中给出最终结果为[晴 晴 晴 晴],与计算有出入,于是我找了一段代码进行验算。

python代码实现如下 (原代码https://blog.csdn.net/zhangduan8785/article/details/80443650)

import math
# 状态的样本空间
states = ('晴', '阴','雨')
# 观测的样本空间
observations = ( '读书', '打球','访友','读书')
# 起始个状态概率
start_probability = {'晴':0.5, '阴':0.3,'雨':0.2}
# 状态转移概率
transition_probability = {'晴': {'晴':0.7, '阴':0.2,'雨':0.1},'阴': {'晴':0.3, '阴':0.5,'雨':0.2},'雨': {'晴':0.3, '阴':0.4,'雨':0.3},
}
# 状态->观测的发散概率
emission_probability = {'晴': { '读书': 0.3,'打球': 0.4, '访友': 0.3},'阴': { '读书': 0.3,'打球': 0.2, '访友': 0.5},'雨': { '读书': 0.8,'打球': 0.1, '访友': 0.1},
}
# 计算以E为底的幂
def E(x):#return math.pow(math.e,x)return xdef display_result(observations,result_m):"""较为友好清晰的显示结果:param result_m::return:"""# 从结果中找出最佳路径#print(result_m)infered_states = []final = len(result_m) - 1(p, pre_state), final_state = max(zip(result_m[final].values(), result_m[final].keys()))infered_states.insert(0, final_state)infered_states.insert(0, pre_state)for t in range(final - 1, 0, -1):_, pre_state = result_m[t][pre_state]infered_states.insert(0, pre_state)print(format("Viterbi Result", "=^59s"))head = format("obs", " ^10s")head += format("Infered state", " ^18s")for s in states:head += format(s, " ^15s")print(head)print(format("", "-^59s"))for obs,result,infered_state in zip(observations,result_m,infered_states):item = format(obs," ^10s")item += format(infered_state," ^18s")for s in states:item += format(result[s][0]," >12.8f")if infered_state == s:item += "(*)"else:item +="   "print(item)print(format("", "=^59s"))def viterbi(obs, states, start_p, trans_p, emit_p):result_m = [{}] # 存放结果,每一个元素是一个字典,每一个字典的形式是 state:(p,pre_state)# 其中state,p分别是当前状态下的概率值,pre_state表示该值由上一次的那个状态计算得到for s in states:  # 对于每一个状态result_m[0][s] = (E(start_p[s]*emit_p[s][obs[0]]),None) # 把第一个观测节点对应的各状态值计算出来#print('11',result_m[0][s])for t in range(1,len(obs)):result_m.append({})  # 准备t时刻的结果存放字典,形式同上for s in states: # 对于每一个t时刻状态s,获取t-1时刻每个状态s0的p,结合由s0转化为s的转移概率和s状态至obs的发散概率# 计算t时刻s状态的最大概率,并记录该概率的来源状态s0# max()内部比较的是一个tuple:(p,s0),max比较tuple内的第一个元素值result_m[t][s] = max([(E(result_m[t-1][s0][0]*trans_p[s0][s]*emit_p[s][obs[t]]),s0) for s0 in states])#print(result_m[t][s])return result_m    # 所有结果(包括最佳路径)都在这里,但直观的最佳路径还需要依此结果单独生成,在显示的时候生成def example():"""一个可以交互的示例"""result_m = viterbi(observations,states,start_probability,transition_probability,emission_probability)display_result(observations,result_m)while True:user_obs = input("轮到你来输入观测,计算机来推断可能状态\n""使用 'N' 代表'打球', 'C' 代表'读书','D'代表'访友'\n""您输入:('q'将退出):")if len(user_obs) ==0 or 'q' in user_obs or 'Q' in user_obs:breakelse:obs = []for o in user_obs:if o.upper() == 'N':obs.append("打球")elif o.upper() == 'C':obs.append("读书")elif o.upper() == 'D':obs.append("访友")else:passresult_m = viterbi(obs,states,start_probability,transition_probability,emission_probability)display_result(obs,result_m)if __name__ == "__main__":example()

结果:

实验结果与书本结果一直,计算结果也与我计算一致,但是第一天结果判断有出入:0.16大于0.15,却把可能判定为了0.15,这位同学的代码中还给出了一段交互代码,我观测CCCCCDDDDDDDNNN,这时第一天预测概率相同,却把可能性判定为0.16。这个疑问进行保留。 接下来我又观测了CCNNNNNDDDDDDDD,即第一天为读书,结果如下:

由概率可知前两天--雨天概率大于晴天,但都把结果判定为晴天,第八天开始,晴天概率都大于阴天,却判定为阴天。

观测NNDDDDCCCCCCC,即第一天为打球:

前两天判定结果与计算概率相同,但第三天到第九天结果却与判定不同。

如果修改初始概率为start_probability = {'晴':0.1, '阴':0.3,'雨':0.6},则结果为:

第一二次观察结果与判定是一致的,但第三次中很显然第一天、第四天、第六天、第七天、第八天、第九天、第十天的判定与结果都不一致。


对于这个问题,是因viterbi算法是分两步,我们一直在用第一步前向计算,但是确定其判定结果是根据第二步反方向推理(计算到最后一个结点时,概率最大的那个状态就是最后一个隐藏结点的被推测状态)。所以,计算出某一天的天气分布概率,这并不能就说明概率高的就是当天的天气,要从最后一天向前推,也就是如下:


http://chatgpt.dhexx.cn/article/tMXImogu.shtml

相关文章

在HMM中实际应用Viterbi算法的例子

在HMM中实际应用Viterbi算法的例子 Viterbi概念动态规划使用HMM的Viterbi算法参考Viterbi概念 本质:动态规划算法 维特比算法是多步骤每步多选择模型的最优选择问题。 其在每一步的所有选择都保存了前续所有步骤到当前步骤当前选择的最小总代价(或者最大价值)以及当前代价…

HMM和viterbi算法初步实践-----中文分词

马尔科夫性质:当一个随机过程在给定现在状态及所有过去状态情况下,其未来状态的条件概率分布仅依赖于当前状态。换句话说,在给定现在状态时,它与过去状态(即该过程的历史路径)是条件独立的(也就是没有任何的…

HMM和Viterbi算法

一、隐马尔可夫模型(Hidden Markov Model) 1、简介 隐含马尔可夫模型并不是俄罗斯数学家马尔可夫发明的,而是美国数学家鲍姆提出的,隐含马尔可夫模型的训练方法(鲍姆-韦尔奇算法)也是以他名字命名的。隐含马…

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

使用 python 实现基于Hmm模型和Viterbi算法的中文分词及词性标注;使用 最大概率算法 进行优化。最终效果:人民日报语料:分词(F1:96.189%);词性标注(F1:97.934%) 完整代码和数据,参见本实验的 github地址:h…

【生信算法】利用HMM纠正测序错误(Viterbi算法的python实现)

利用HMM纠正测序错误(Viterbi算法的python实现) 问题背景 对两个纯系个体M和Z的二倍体后代进行约~0.05x的低覆盖度测序,以期获得后代个体的基因型,即后代中哪些片段分别来源于M和Z。已知: 后代中基因型为MM、MZ&…

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

Viterbi算法 目标过程词典分词统计分词词性标注 附录附录二附录三 源码地址 目标 实现基于词典的分词方法和统计分词方法对分词结果进行词性标注对分词及词性标注结果进行评价,包括4个指标:正确率、召回率、F1值和效率 过程 词典分词 基于词典的分词…

viterbi 算法与python实现

Viterbi算法 (部分内容转自知乎:《如何通俗地讲解 viterbi 算法?》) 1、问题描述 如下如所示,如何快速找到从 S 到 E 的最短路径? 一:遍历穷举法,可行,但速度太慢&am…

维特比算法(viterbi)原理以及简单实现

维特比算法 看一下维基百科的解释,维特比算法(Viterbi algorithm)是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 通俗易懂的解释知乎…

flask中jsonify遇到的坑

1.jsonify可以将字典转换成json对象传入前端 data {"movie": movie_list,"page": page,"dic_list": dic,"total_page": total_page}>>坑1 字典的值不能为range(x,x),上图dic就是像range(x,x),会报错 …

flask中的jsonify返回的是乱码

用flask返回json时遇到了返回字符串支持中文显示的问题,在web端显示的是utf-8的编码如图; 虽然不影响接口的读取,但是可读性太差,于是研究了一下怎么直接显示成中文。最后找到了解决方案如下,在配置中加入下面一行代码就OK了。 …

request jsonify

python的flask框架为用户提供了直接返回包含json格式数据响应的方法,即jsonify,在开发中会经常用到。如下一段简单的flask后端代码,服务端视图函数根据请求参数返回json格式的数据到客户端。 转载于:https://www.cnblogs.com/daqingzi/p/9018…

Flask使用json或jsonify返回响应的数据

前言 在做网站前后端数据交互的时候,我们经常需要使用Ajax等方法向后端发送请求来获取响应的数据,而我们经常需要的就是json格式的响应数据,它实际上就是一个字符串。要知道Flask如何返回json响应数据,首先就需要知道如何将字典di…

Flask 学习-8. jsonify返回中文没正常显示问题

前言 Flask 接口返回的json 格式数据有中文的时候,默认是以ASCII码 返回的,没正常显示中文。 jsonify 返回 json 数据 函数直接返回dict 数据 或返回jsonfy() 函数处理的数据,都是以json格式返回的 from flask import Flask, jsonify fro…

flask jsonify之序列化时的default函数、jsonify序列化自定义对象

目录 1.看源码 2、重写默认的default函数,实现自己的序列化机制 3、把对象转化成字典 3.1 __dict__的方式 3.2、定义keys和__getitem__的方式 4、最终的代码实现 5、关于default函数的其他知识 1.看源码 打开site-package,flask,json…

Flask | 解决jsonify返回中文乱码问题

在采用 return jsonify(data) 返回内容中含有中文时,前端接收数据出现中文乱码问题,乱码格式如下(仅中文为ASCII码): 故在此记录下该问题的解决方式,以作后期参考: 在定义Flask app时&#xff…

jsonify返回中文编码的问题

app.config[JSON_AS_ASCII]Flase 在它下面加上上面的代码就欧克了 没加之前或者为True: 加了之后:

flask学习二(jsonify)

示例一: 实现动态路由,代码如下 # coding:utf-8 from flask import Flask from flask import jsonify # 创建对象 app Flask(__name__)users_list {"1001":["123","张三",19],"1002":["234","…

flask中jsonify和json区别

一 JSON数据结构 要把json与字典区分开来 dumps(字典转换成Json) loads(Json转换成字典) Python 的字典是一种数据结构,JSON 是一种数据格式。 json 就是一个根据某种约定格式编写的纯字符串,不具备任何数据结构的特征。而 python 的字典…

关于flask入门教程-记录集转jsonify

Flask 框架里,可以用 jsonify 返回 json 数据,但是为什么不用 Python 自带的 json 模块返回 JSON 数据呢? 其实两者是差不多的,jsonify指明了Content-Type 是 application/json , 这样做是符合 HTTP 协议的规定的&…

Flask 的 jsonify 理解

文章目录 python 代码解决原因Content-Type的区别 python 代码 # -*- coding:utf-8 -*- from flask import Flask, jsonifyapp Flask(__name__)urls [{id: 1,title: python,description: https://www.python.org/},{id: 2,title: flask,description: https://flask.palletsp…