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

article/2025/9/12 10:21:38

维特比算法

看一下维基百科的解释,维特比算法(Viterbi algorithm)是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。
通俗易懂的解释知乎有很多,如:如何通俗地讲解 viterbi 算法?,我我这里重点是如何用python代码实现这个算法。

算法原理

维特比算法就是求所有观测序列中的最优,如下图所示,我们要求从S到E的最优序列,中间有3个时刻,每个时刻都有对应的不同观察的概率,下图中每个时刻不同的观测标签有3个。
在这里插入图片描述
求所有路径中最优路径,最容易想到的就是暴力解法,直接把所有路径全部计算出来,然后找出最优的。这方法理论上是可行,但当序列很长时,时间复杂夫很高。而且进行了大量的重复计算,viterbi算法就是用动态规划的方法就减少这些重复计算。
viterbi算法是每次记录到当前时刻,每个观察标签的最优序列,如下图,假设在t时刻已经保存了从0到t时刻的最优路径,那么t+1时刻只需要计算从t到t+1的最优就可以了,图中红箭头表示从t时刻到t+1时刻,观测标签为1,2,3的最优。
在这里插入图片描述
每次只需要保存到当前位置最优路径,之后循环向后走。到结束时,从最后一个时刻的最优值回溯到开始位置,回溯完成后,这个从开始到结束的路径就是最优的。
在这里插入图片描述

代码实现

下面用python简单实现一下viterbi算法

import numpy as npdef viterbi_decode(nodes, trans):"""Viterbi算法求最优路径其中 nodes.shape=[seq_len, num_labels],trans.shape=[num_labels, num_labels]."""# 获得输入状态序列的长度,以及观察标签的个数seq_len, num_labels = len(nodes), len(trans)# 简单起见,先不考虑发射概率,直接用起始0时刻的分数scores = nodes[0].reshape((-1, 1))paths = []# 递推求解上一时刻t-1到当前时刻t的最优for t in range(1, seq_len):# scores 表示起始0到t-1时刻的每个标签的最优分数scores_repeat = np.repeat(scores, num_labels, axis=1)# observe当前时刻t的每个标签的观测分数observe = nodes[t].reshape((1, -1))observe_repeat = np.repeat(observe, num_labels, axis=0)# 从t-1时刻到t时刻最优分数的计算,这里需要考虑转移分数transM = scores_repeat + trans + observe_repeat# 寻找到t时刻的最优路径scores = np.max(M, axis=0).reshape((-1, 1))idxs = np.argmax(M, axis=0)# 路径保存paths.append(idxs.tolist())best_path = [0] * seq_lenbest_path[-1] = np.argmax(scores)# 最优路径回溯for i in range(seq_len-2, -1, -1):idx = best_path[i+1]best_path[i] = paths[i][idx]return best_path

代码中整队scores和observe的repeat复制操作,是为了方便矩阵运算,减少循环的操作。
如果将M = scores_repeat + trans + observe_repeat,展开用for循环写,在t时刻M[i][j] = scores[i] + trans[i][j] + observe[j],M[i][j]表示从t-1时刻为i-1状态,t时刻为j-1状态的分数。
在这里插入图片描述
下面就是展开用for训练一步一步求解的伪码。

# 每个时刻scores更新的伪码
for t in range(1, seq_len):tmp_scores = scoresfor j in range(nums_labels):for i in range(nums_labels):M[i][j] = scores[i] + trans[i][j] + observe[t][j]tmp_scores[j] = max(M[i][j]) (0 <= i < nums_labels)scores = tmp_scores 

可以利用矩阵计算的原理,合并一些步骤。

for t in range(1, seq_len):scores_repeat = np.repeat(scores, num_labels, axis=1)observe = nodes[t].reshape((1, -1))observe_repeat = np.repeat(observe, num_labels, axis=0)M = scores_repeat + trans + observe_repeatscores = np.max(M, axis=0).reshape((-1, 1))

这里还有对scores和observe进行复制的操作,numpy运算中还可以在简化。

for t in range(1, seq_len):observe = nodes[t].reshape((1, -1))M = scores + trans + observescores = np.max(M, axis=0).reshape((-1, 1))

numpy在相加时可以自动扩充维度,横向和纵向都可以。
在这里插入图片描述
经过简化的viterbi算法完整版。

def viterbi_decode_v2(nodes, trans):"""Viterbi算法求最优路径v2其中 nodes.shape=[seq_len, num_labels],trans.shape=[num_labels, num_labels]."""seq_len, num_labels = len(nodes), len(trans)scores = nodes[0].reshape((-1, 1))paths = []# # 递推求解上一时刻t-1到当前时刻t的最优for t in range(1, seq_len):observe = nodes[t].reshape((1, -1))M = scores + trans + observescores = np.max(M, axis=0).reshape((-1, 1))idxs = np.argmax(M, axis=0)paths.append(idxs.tolist())best_path = [0] * seq_lenbest_path[-1] = np.argmax(scores)# 最优路径回溯for i in range(seq_len-2, -1, -1):idx = best_path[i+1]best_path[i] = paths[i][idx]return best_path

还有一种写法,最后不用回溯,每次把最优路径的索引都保存起来,并添加一个正常的路径,最后直接按索引找出最优路径,这个代码很少,但是不太好理解。

def viterbi_decode_v3(nodes, trans):"""Viterbi算法求最优路径其中 nodes.shape=[seq_len, num_labels],trans.shape=[num_labels, num_labels]."""seq_len, num_labels = len(nodes), len(trans)labels = np.arange(num_labels).reshape((1, -1))scores = nodes[0].reshape((-1, 1))paths = labelsfor t in range(1, seq_len):observe = nodes[t].reshape((1, -1))M = scores + trans + observescores = np.max(M, axis=0).reshape((-1, 1))idxs = np.argmax(M, axis=0)paths = np.concatenate([paths[:, idxs], labels], 0)best_path = paths[:, scores.argmax()]return best_path

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

相关文章

flask中jsonify遇到的坑

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

flask中的jsonify返回的是乱码

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

request jsonify

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

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

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

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

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

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

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

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

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

jsonify返回中文编码的问题

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

flask学习二(jsonify)

示例一&#xff1a; 实现动态路由&#xff0c;代码如下 # 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 的字典是一种数据结构&#xff0c;JSON 是一种数据格式。 json 就是一个根据某种约定格式编写的纯字符串&#xff0c;不具备任何数据结构的特征。而 python 的字典…

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

Flask 框架里&#xff0c;可以用 jsonify 返回 json 数据&#xff0c;但是为什么不用 Python 自带的 json 模块返回 JSON 数据呢&#xff1f; 其实两者是差不多的&#xff0c;jsonify指明了Content-Type 是 application/json &#xff0c; 这样做是符合 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…

Flask中jsonify和json.dumps用法以及区别(简单案例)

环境&#xff1a;python3.6, Flask1.0.3 flask提供了jsonify函数供用户处理返回的序列化json数据&#xff0c; 而python自带的json库中也有dumps方法可以序列化json对象. 其二者的区别&#xff0c;写个简单的案例实测一下便见分晓。 from flask import Flask from flask im…

const常量函数详解

在类中&#xff0c;如果你不希望某些数据被修改&#xff0c;可以使用const关键字加以限定。const 可以用来修饰成员变量和成员函数。 const常量与指针 const int *p1与const int *p2的顺序不同&#xff0c;但是他们指向的值都不能改变&#xff0c;上述代码说明虽然指向的值不能…

C++const函数的运用:深度解析const函数的魅力

C 深度解析const函数的魅力 1. C const函数的基本概念&#xff08;Basic Concepts of const Functions in C&#xff09;1.1 const函数的定义与特性&#xff08;Definition and Characteristics of const Functions&#xff09;1.2 const函数的使用场景&#xff08;Usage Scena…

const 用法

const 用法 const 修饰变量&#xff0c;这个变量被称为常变量&#xff0c;不能被修改&#xff0c;但本质上还是一个变量 #通过指针改变num的值 int main() {int num 10;int* p &num;*p 20;printf("%d ", num);return 0; }#这里num被 const修饰本来不能被修改…

const成员函数

const成员函数 const修饰成员函数的时候&#xff0c;const需要放在成员函数的后面&#xff0c;不能放在一开始&#xff0c;如果放在一开始的话&#xff0c;那么const其实是在修饰成员函数的返回值&#xff0c;而不是在修饰成员函数了 const成员函数中不能修改成员变量 普通成员…

const函数

const是衡量一个程序员是否老道的一个标准&#xff0c;除了修饰变量之外&#xff0c;还可以修饰函数&#xff0c;主要有以下几种形式 const int& fun(int& a); //修饰返回值 int& fun(const int& a); //修饰形参 int& fun(int& a) const{} //const成员…

梳理c++ const 修饰函数

const是衡量一个程序员是否老道的一个标准&#xff0c;除了修饰变量之外&#xff0c;还可以修饰函数&#xff0c;主要有以下几种形式 const int& fun(int& a); //修饰返回值 int& fun(const int& a); //修饰形参 int& fun(int& a) const{} //const成员…

C++基础——const成员函数

目录 一.Const成员函数 1.定义&#xff1a; 2.格式&#xff1a; 3.代码示例&#xff1a; .h文件&#xff1a; definition.cpp文件 特性&#xff1a; 例&#xff1a; 那么const对象既可以调用非const型成员函数吗&#xff1f; 问题3.const成员函数内可以调用其它…