隐马尔可夫模型 HMM 的python实现

https://www.cnblogs.com/d-roger/articles/5719979.html

 摘要:

本文主要介绍隐马尔可夫模型HMM的python实现,参考的文献主要是:
[1]. Lawrence R. Rabiner, ‘A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition,’ Proceedings of the IEEE, Vol. 77, No. 2, pp. 257-286, February, 1989
[2]. Dawei Shen, 'Some Mathematics for HMM' October 13th, 2008
[3]. 统计学习方法, 李航
[4]. 数学之美, 吴军

文献一是非常经典的Hidden Markov Models的入门文章,思路清晰,讲解透彻,且有一定深度;文献二和三也有很多内容是参考文献一来的。

文献二主要的优势是一些计算式推导出了一些简便的结果,使代码实现起来更简单,从而在另一个角度提供了更清晰的概念。

《统计学习方法》中的第十章专门讲了隐马尔可夫模型,更是用拉格朗日乘子法写出拉格朗日函数,进而求偏导数的方法推导出Baum-Welch算法的模型参数估计公式,有趣的是,文献一是用概率理论的方式直接写出参数估计公式的,两者的结果当然是完全一样。另外这本书还提供了一个“盒子和球模型”的实例来说明隐马尔可夫模型的相关概念,非常便于快速理解隐马尔可夫模型到底是怎么一回事。读过这本书的网友应该知道李航老师这本书内容丰富但只有区区两百多页,每篇都是干货满满,简明扼要。当然,有些地方实在“太扼要”了。

《数学之美》这本书第26章比较清晰地说明了维特比Viterbi算法的原理,如果不太理解这个算法的,可以去阅读这本书。另外,这本书不仅适合初学者或着外行人阅读并理解相关机器学习的算法,思路;同时,它对有一定基础的MLer也非常有助益。

本文不会介绍太多概念性的东西,会直接推荐读者参阅哪份文献的哪一部分。而且,也会直接引用参考文献的公式(或等式)来阐述内容。所以想随本文一起学习的读者需要准备相关的文献(前两份文献可以直接搜索,两者都是可以下载的pdf文档)。

Outline 大纲:

  1. Notation
  2. 维特比算法的python实现
  3. 前向算法 ,后向算法,gamma(γ),xi(ξ),Baum-Welch算法及其python实现
  4. 带scale的Baum-Welch算法,多项观测序列带scale的Baum-Welch算法,
  5. 拓展

1. Notation
S={s1,s2,…,sN,} 表示所有可能状态的集合(《统计学习方法》使用Q来表示),
N 表示可能的状态数,
V={v1,v2,…,vM,} 表示可能的观测的集合,
M 表示可能的观测数,
I 表示长度为T的状态序列,O是对应的观测序列,
A 表示状态转移概率矩阵,
B 表示观测概率矩阵,
Pi 表示初始状态概率向量,
Lambda =(A, B, Pi)表示隐马尔可夫模型的参数模型。

如果没有接触过 或者不熟悉隐马尔可夫模型(HMM)的读者 可以参阅《统计学习方法》, 它提供了一个“盒子和球模型”的实例来说明隐马尔可夫模型的相关概念,非常便于快速理解隐马尔可夫模型到底是怎么一回事。

2. 维特比算法的python实现
通常来说隐马尔可夫模型(以下简称HMM)有3个基本问题:概率计算问题,学习问题,预测问题(也称解码问题)。第一个问题对应前向算法和后向算法;二,三一般使用Baum-Welch算法,维特比Viterbi算法来解决。
大部分时候前向算法和后向算法都是为Baum-Welch算法服务的,而维特比Viterbi算法是单独存在的,所以我们先讲维特比(以下简称Viterbi)算法。

Viterbi算法实际是用了动态规划原理简化了求解的复杂度。之所以说简化,是相对穷举法(列出所有可能的状态序列I,然后取概率最大的一个序列)而言的。
如果不理解Viterbi算法可以参阅《数学之美》第26章。

算法实现就很简单了,直接参照《统计学习方法》第185页,算法10.5的步骤。
首先定义一个类:

 1 import numpy as np2 3 class HMM:4     def __init__(self, Ann, Bnm, Pi, O):5         self.A = np.array(Ann, np.float)6         self.B = np.array(Bnm, np.float)7         self.Pi = np.array(Pi, np.float)8         self.O = np.array(O, np.float)9         self.N = self.A.shape[0]
10         self.M = self.B.shape[1]

Viterbi算法作为类HMM的函数,相关代码如下

 1     def viterbi(self):2         # given O,lambda .finding I3 4         T = len(self.O)5         I = np.zeros(T, np.float)6 7         delta = np.zeros((T, self.N), np.float)  8         psi = np.zeros((T, self.N), np.float)9
10         for i in range(self.N):
11             delta[0, i] = self.Pi[i] * self.B[i, self.O[0]]
12             psi[0, i] = 0
13
14         for t in range(1, T):
15             for i in range(self.N):
16                 delta[t, i] = self.B[i,self.O[t]] * np.array( [delta[t-1,j] * self.A[j,i]
17                     for j in range(self.N)] ).max()
18                 psi[t,i] = np.array( [delta[t-1,j] * self.A[j,i]
19                     for j in range(self.N)] ).argmax()
20
21         P_T = delta[T-1, :].max()
22         I[T-1] = delta[T-1, :].argmax()
23
24         for t in range(T-2, -1, -1):
25             I[t] = psi[t+1, I[t+1]]
26
27         return I

delta,psi 分别是 δ,ψ

其中np, 是import numpy as np, numpy这个包很好用,它的argmax()方法在这里非常实用。

3. 前向算法 ,后向算法,gamma-γ,xi-ξ,Baum_Welch算法及其python实现
HMM的公式推导包含很多概率值,如果你不能比较好地理解概率相关知识的话,相应的公式推导过程会比较难以理解,可以阅读Bishop写的《Pattern Recognition And Machine Learning》这本书,当然,在机器学习方面这本书一直都是经典。

前向(forward)概率矩阵alpha-α(公式书写时它根a很像,注意区分),
后向(backward)概率矩阵beta-β
算法定义和步骤参阅《统计学习方法》第175页或者文献二,
相关代码如下:

    def forward(self):T = len(self.O)alpha = np.zeros((T, self.N), np.float)for i in range(self.N):        alpha[0,i] = self.Pi[i] * self.B[i, self.O[0]]for t in range(T-1):for i in range(self.N):summation = 0   # for every i 'summation' should reset to '0'for j in range(self.N):summation += alpha[t,j] * self.A[j,i]alpha[t+1, i] = summation * self.B[i, self.O[t+1]]summation = 0.0for i in range(self.N):summation += alpha[T-1, i]Polambda = summationreturn Polambda,alpha

    def backward(self):T = len(self.O)beta = np.zeros((T, self.N), np.float)for i in range(self.N):beta[T-1, i] = 1.0for t in range(T-2,-1,-1):for i in range(self.N):summation = 0.0     # for every i 'summation' should reset to '0'for j in range(self.N):summation += self.A[i,j] * self.B[j, self.O[t+1]] * beta[t+1,j]beta[t,i] = summationPolambda = 0.0for i in range(self.N):Polambda += self.Pi[i] * self.B[i, self.O[0]] * beta[0, i]return Polambda, beta

Polambda表示P(O| λ)

接下来计算gamma-γ和 xi-ξ。 根据《统计学习方法》的公式可以得到如下代码:

    def compute_gamma(self,alpha,beta):T = len(self.O)gamma = np.zeros((T, self.N), np.float)       # the probability of Ot=qfor t in range(T):for i in range(self.N):gamma[t, i] = alpha[t,i] * beta[t,i] / sum(alpha[t,j] * beta[t,j] for j in range(self.N) )return gamma

    def compute_xi(self,alpha,beta):T = len(self.O)xi = np.zeros((T-1, self.N, self.N), np.float)  # note that: not Tfor t in range(T-1):   # note: not Tfor i in range(self.N):for j in range(self.N):numerator = alpha[t,i] * self.A[i,j] * self.B[j,self.O[t+1]] * beta[t+1,j]# the multiply term below should not be replaced by 'nummerator',# since the 'i,j' in 'numerator' are fixed.# In addition, should not use 'i,j' below, to avoid error and confusion.denominator = sum( sum(     alpha[t,i1] * self.A[i1,j1] * self.B[j1,self.O[t+1]] * beta[t+1,j1] for j1 in range(self.N) )   # the second sumfor i1 in range(self.N) )    # the first sumxi[t,i,j] = numerator / denominatorreturn xi

注意计算时要传入参数alpha,beta

然后来实现Baum_Welch算法,根据《统计学习方法》或者文献二,

首先初始化参数,怎么初始化是很重要。因为Baum_Welch算法(亦是EM算法的一种特殊体现)并不能保证得到全局最优值,很容易就掉到局部最优然后出不来了。
当delta_lambda大于某一值时一直运行下去。
关于x的设置,如果过小,程序容易进入死循环,因为每一次的收敛过程lambda会有比较大的变化,那么当它接近局部/全局最优时,就会在左右徘徊一直是delta_lambda > x。

    def Baum_Welch(self):# given O list finding lambda model(can derive T form O list)# also given N, M, T = len(self.O)V = [k for k in range(self.M)]# initialization - lambda self.A = np.array(([[0,1,0,0],[0.4,0,0.6,0],[0,0.4,0,0.6],[0,0,0.5,0.5]]), np.float)self.B = np.array(([[0.5,0.5],[0.3,0.7],[0.6,0.4],[0.8,0.2]]), np.float)# mean value may not be a good choiceself.Pi = np.array(([1.0 / self.N] * self.N), np.float)  # must be 1.0 , if 1/3 will be 0# self.A = np.array([[1.0 / self.N] * self.N] * self.N) # must array back, then can use[i,j]# self.B = np.array([[1.0 / self.M] * self.M] * self.N)x = 1delta_lambda = x + 1times = 0# iteration - lambdawhile delta_lambda > x:  # xPolambda1, alpha = self.forward()           # get alphaPolambda2, beta = self.backward()            # get betagamma = self.compute_gamma(alpha,beta)     # use alpha, betaxi = self.compute_xi(alpha,beta)lambda_n = [self.A,self.B,self.Pi]for i in range(self.N):for j in range(self.N):numerator = sum(xi[t,i,j] for t in range(T-1))denominator = sum(gamma[t,i] for t in range(T-1))self.A[i, j] = numerator / denominatorfor j in range(self.N):for k in range(self.M):numerator = sum(gamma[t,j] for t in range(T) if self.O[t] == V[k] )  # TBDdenominator = sum(gamma[t,j] for t in range(T))self.B[i, k] = numerator / denominatorfor i in range(self.N):self.Pi[i] = gamma[0,i]# if sum directly, there will be positive and negative offsetdelta_A = map(abs, lambda_n[0] - self.A)  # delta_A is still a matrixdelta_B = map(abs, lambda_n[1] - self.B)delta_Pi = map(abs, lambda_n[2] - self.Pi)delta_lambda = sum([ sum(sum(delta_A)), sum(sum(delta_B)), sum(delta_Pi) ])times += 1print timesreturn self.A, self.B, self.Pi

4.带scale的Baum-Welch算法,多项观测序列带scale的Baum-Welch算法
理论上来说上面已经完整地用代码实现了HMM, 然而事实总是没有那么简单,后续还有不少问题需要解决,不过这篇文章只提两点,一个是用scale解决计算过程中容易发送的浮点数下溢问题,另一个是同时输入多个观测序列的改进版Baum-Welch算法。

参考文献二: scaling problem
根据文献二的公式我们加入scale,重写forward(),backward(),Baum-Welch() 三个方法。

    def forward_with_scale(self):T = len(self.O)alpha_raw = np.zeros((T, self.N), np.float)alpha = np.zeros((T, self.N), np.float)c = [i for i in range(T)]  # scaling factor; 0 or sequence doesn't matterfor i in range(self.N):        alpha_raw[0,i] = self.Pi[i] * self.B[i, self.O[0]]c[0] = 1.0 / sum(alpha_raw[0,i] for i in range(self.N))for i in range(self.N):alpha[0, i] = c[0] * alpha_raw[0,i]for t in range(T-1):for i in range(self.N):summation = 0.0for j in range(self.N):summation += alpha[t,j] * self.A[j, i]alpha_raw[t+1, i] = summation * self.B[i, self.O[t+1]]c[t+1] = 1.0 / sum(alpha_raw[t+1,i1] for i1 in range(self.N))for i in range(self.N):alpha[t+1, i] = c[t+1] * alpha_raw[t+1, i]return alpha, cdef backward_with_scale(self,c):T = len(self.O)beta_raw = np.zeros((T, self.N), np.float)beta = np.zeros((T, self.N), np.float)for i in range(self.N):beta_raw[T-1, i] = 1.0beta[T-1, i] = c[T-1] * beta_raw[T-1, i]for t in range(T-2,-1,-1):for i in range(self.N):summation = 0.0for j in range(self.N):summation += self.A[i,j] * self.B[j, self.O[t+1]] * beta[t+1,j]beta[t,i] = c[t] * summation   # summation = beta_raw[t,i]return beta

    def Baum_Welch_with_scale(self):T = len(self.O)V = [k for k in range(self.M)]# initialization - lambda   ,  should be float(need .0)self.A = np.array([[0.2,0.2,0.3,0.3],[0.2,0.1,0.6,0.1],[0.3,0.4,0.1,0.2],[0.3,0.2,0.2,0.3]])self.B = np.array([[0.5,0.5],[0.3,0.7],[0.6,0.4],[0.8,0.2]])x = 5delta_lambda = x + 1times = 0# iteration - lambdawhile delta_lambda > x:  # xalpha,c = self.forward_with_scale()beta = self.backward_with_scale(c)    lambda_n = [self.A,self.B,self.Pi]for i in range(self.N):for j in range(self.N):numerator_A = sum(alpha[t,i] * self.A[i,j] * self.B[j, self.O[t+1]]* beta[t+1,j] for t in range(T-1))denominator_A = sum(alpha[t,i] * beta[t,i] / c[t] for t in range(T-1))self.A[i, j] = numerator_A / denominator_Afor j in range(self.N):for k in range(self.M):numerator_B = sum(alpha[t,j] * beta[t,j] / c[t]for t in range(T) if self.O[t] == V[k] )  # TBDdenominator_B = sum(alpha[t,j] * beta[t,j] / c[t] for t in range(T))self.B[j, k] = numerator_B / denominator_B# Pi have no business with cdenominator_Pi = sum(alpha[0,j] * beta[0,j] for j in range(self.N))for i in range(self.N):      self.Pi[i] = alpha[0,i] * beta[0,i] / denominator_Pi #self.Pi[i] = gamma[0,i]   # if sum directly, there will be positive and negative offsetdelta_A = map(abs, lambda_n[0] - self.A)  # delta_A is still a matrixdelta_B = map(abs, lambda_n[1] - self.B)delta_Pi = map(abs, lambda_n[2] - self.Pi)delta_lambda = sum([ sum(sum(delta_A)), sum(sum(delta_B)), sum(delta_Pi) ])times += 1print timesreturn self.A, self.B, self.Pi

第二个问题,根据文献二,我们直接实现带scale的修改版Baum-Welch算法。为了方便,我们将这个函数单独出来,写在HMM类的外面:

# for multiple sequences of observations symbols(with scaling alpha & beta)
# out of class HMM, independent function
def modified_Baum_Welch_with_scale(O_set):# initialization - lambda  A = np.array([[0.2,0.2,0.3,0.3],[0.2,0.1,0.6,0.1],[0.3,0.4,0.1,0.2],[0.3,0.2,0.2,0.3]])B = np.array([[0.2,0.2,0.3,0.3],[0.2,0.1,0.6,0.1],[0.3,0.4,0.1,0.2],[0.3,0.2,0.2,0.3]])# B = np.array([[0.5,0.5],[0.3,0.7],[0.6,0.4],[0.8,0.2]])Pi = [0.25,0.25,0.25,0.25]                               # computing alpha_set, beta_set, c_setO_length = len(O_set)whatever = [j for j in range(O_length)]alpha_set, beta_set = whatever, whateverc_set = [j for j in range(O_length)]  # can't use whatever, the c_set will be 3d-array ???N = A.shape[0]M = B.shape[1]T = [j for j in range(O_length)]   # can't use whatever, the beta_set will be 1d-array ???for i in range(O_length):T[i] = len(O_set[i])V = [k for k in range(M)]  x = 1delta_lambda = x + 1times = 0    while delta_lambda > x:   # iteration - lambda        lambda_n = [A, B]for i in range(O_length):alpha_set[i], c_set[i] = HMM(A, B, Pi, O_set[i]).forward_with_scale()beta_set[i] = HMM(A, B, Pi, O_set[i]).backward_with_scale(c_set[i])for i in range(N): for j in range(N):numerator_A = 0.0denominator_A = 0.0for l in range(O_length):raw_numerator_A = sum( alpha_set[l][t,i] * A[i,j] * B[j, O_set[l][t+1]] * beta_set[l][t+1,j] for t in range(T[l]-1) )numerator_A += raw_numerator_Araw_denominator_A = sum( alpha_set[l][t,i] * beta_set[l][t,i] / c_set[l][t]for t in range(T[l]-1) )                    denominator_A += raw_denominator_AA[i, j] = numerator_A / denominator_Afor j in range(N):for k in range(M):numerator_B = 0.0denominator_B = 0.0for l in range(O_length):raw_numerator_B = sum( alpha_set[l][t,j] * beta_set[l][t,j] / c_set[l][t] for t in range(T[l]) if O_set[l][t] == V[k] )numerator_B += raw_numerator_Braw_denominator_B = sum( alpha_set[l][t,j] * beta_set[l][t,j] / c_set[l][t] for t in range(T[l]) )denominator_B += raw_denominator_B    B[j, k] = numerator_B / denominator_B# Pi should not need to computing in this case, # in other cases, will get some corresponding Pi# if sum directly, there will be positive and negative offsetdelta_A = map(abs, lambda_n[0] - A)  # delta_A is still a matrixdelta_B = map(abs, lambda_n[1] - A)delta_lambda = sum([ sum(sum(delta_A)), sum(sum(delta_B)) ])times += 1print timesreturn A, B

这里我们不重新估算pi,在实际应用中pi根据情况而定,有时并不需要。

正如作者所说:By using scaling, we happily find that all Pl’s terms are cancelled out! The resulting format looks much cleaner! 

这真的是一个happily finding。

这样就实现了一个基本的HMM, 虽然比较基础,但是得益于这个模型本身的强大,区区这200多行代码已经有很强大的功能,读者可以试一试,操作得当应该可以用来实现一些简单的预测。

5.扩展

引用文献一的原话:

V. Implementation issues for HMMs

The discussion in the previous two sections has primarily dealt with the theory of HMMs and several variations on the form of the model. In this section we deal with several practical implementation issues including scaling, multiple observation sequences, initial parameter estimates, missing data, and choice of model size and type. For some of these implementation issues we can prescribe exact analytical solutions; for other issues we can only provide some set-of-the-pants experience gained from working with HMMs over the last several years.

读者可以研读文献一后面的内容,以便进一步学习,也可以搜索相关的新文献来深入学习,毕竟这篇文章是1989年出的。

通俗易懂理解——viterbi算法

https://zhuanlan.zhihu.com/p/40208596

动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法,通常情况下应用于最优化问题,这类问题一般有很多个可行的解,每个解有一个值,而我们希望从中找到最优的答案。

在计算机科学领域,应用动态规划的思想解决的最基本的一个问题就是:寻找有向无环图(篱笆网络)当中两个点之间的最短路径(实际应用于地图导航、语音识别、分词、机器翻译等等)。
下面举一个比较简单的例子做说明:求S到E的最短路径。如下图(各点之间距离不相同):

我们知道,要找到S到E之间最短路径,最容易想到的方法就是穷举法。也就是把所有可能的路径都例举出来。从S走向A层共有4种走法,从A层走向B层又有4种走法,从B层走向C层又有4种走法,然后C层走向E点只有一种选择。所以最终我们穷举出了4X4X4=64种可能。显然,这种方法必定可行。但在实际的应用当中,对于数量极其庞大的结点数和边数的图,其计算复杂度也将会变得非常大,而计算效率也会随之降低。

因此,这里选择使用一种基于动态规划的方式来寻找最佳路径。

所谓动态规划。其核心就是“动态”的概念,把大的问题细分为多个小的问题,基于每一步的结果再去寻找下一步的策略,通过每一步走过之后的局部最优去寻找全局最优。这样解释比较抽象,下面直接用回刚刚的例子说明。如下图:

首先,我们假设S到E之间存在一条最短路径(红色),且这条路径经过C2点,那么我们便一定能够确定从S到C2的64条(4X4X4=64)子路径当中,该子路径一定最短。(证明:反证法。如果S到C2之间存在一条更短的子路径,那么便可以用它来代替原先的路径,而原先的路径显然就不是最短了,这与原假设自相矛盾)。

同理,我们也可以得出从S到B2点为两点间最短子路径的结论。这时候,真相便慢慢浮出水面:既然如此,我们计算从S点出发到点C2的最短路径,是不是只要考虑从S出发到B层所有节点的最短路径就可以了?答案是肯定的!因为,从S到E的“全局最短”路径必定经过在这些“局部最短”子路径。没错!这就是上面提及到的通过局部最优的最优去寻找全局最优,问题的规模被不断缩小!
接下来,要揭晓答案了!继续看下图:

回顾之前的分析:我们计算从S起到C2点的最短路径时候只需要考虑从S出发到B层所有节点的最短路径,B层也如是。对B2来说,一共有4条路线可以到达,分别是A1→B2,A2→B2,A3→B2,A4→B2。我们需要做的就是把A2→B2这条最短路线保留,而其他3条删除掉(因为根据以上的分析,它们不可能构成全程的最短路线)。OK,来到这里,我们会发现一个小“漏洞”,这段S→A2→B2→C2→E的路线只是我一厢情愿的假设,最短路径不一定是经过以上这些点。所以,我们要把每层的每个节点都考虑进来。
以下是具体的做法:
step1:从点S出发。对于第一层的3个节点,算出它们的距离d(S,A1),d(S,A2),d(S,A3),d(S,A4),因为只有一步,所以这些距离都是S到它们各自的最短距离。
step2:对于B层的所有节点(B1,B2,B3,B4),要计算出S到它们的最短距离。我们知道,对于特定的节点B2,从S到它的路径可以经过A层的任何一个节点(A1,A2,A3,A4)。对应的路径长就是d(S,B2)=d(S,Ai)+d(Ai,B2)(其中i=1,2,3,4)。由于A层有4个节点(即i有4个取值),我们要一一计算,然后找到最小值。这样,对于B层的每个节点,都需要进行4次运算,而B层有4个节点,所以共有4X4=16次运算。
step3:这一步是该算法的核心。我们从step2计算得出的结果只保留4个最短路径值(每个节点保留一个)。那么,若从B层走向C层来说,该步骤的基数已经不再是4X4,而是变成了4!也就是说,从B层到C层的最短路径只需要基于B层得出的4个结果来计算。这种方法一直持续到最后一个状态,每一步计算的复杂度为相邻两层的计算复杂度为4X4乘积的正比!再通俗点说,连接这两两相邻层的计算符合变成了“+”号,取代了原先的“X”号。用这种方法,只需进行4X4X2=32次计算!
其实上述的算法就是著名的维特比算法,事实上非常简单!

重点:

我在看第3步的时候还是有些懵逼,我个人一开始看他从终点开始倒推着说,并没有解决我心中的疑惑,因为我一直想着点与点之间的权重不同,总觉得他忽略了什么,直到step3还是没能彻底理解,后面自己再重新思考下才真的明白其中含义(可能自己脑子太不好使)。

在上图中,s到B层的距离中以b1为例,可以知道s-a1-b1,s-a2-b1,s-a3-b1,s-a4-b1四条路线的大小,因而可以知道有一条路径比如s-a1-b1是s到B1的最短路径。同样的道理,可以知道4条路径分别为s到b1,b2,b3,b4的最短路径。也就是可以忽略了A层的存在,又直接考虑s到B层再到C层的最短路径,继续找到4条路径分别为s层到c1,c2,c3,c4的最短路径,依此循环,直至到达终点。

若假设整个网格的宽度为D,网格长度为N,那么若使用穷举法整个最短路径的算法复杂度为O(DN),而使用这种算法的计算复杂度为O(ND2)。试想一下,若D与N都非常大,使用维特比算法的效率将会提高几个数量级!
代码实现(C语言版):

同样是实现从S到E的最短路径。不过这次把刚刚的情况简化了一下,原理是相同的。#include<stdlib.h>
#include<stdio.h>
#define x 9999
#define max 9999
int data[10][10];
int dist[10];//记录最短路径为多少
int path[10];//记录最短路径
int kmin(int,int);
void fpath(int a[][10]);
int froute(int a[][10]);
void main()
{int i,m;int a[10][10]={{x,4,2,3,x,x,x,x,x,x}, {x,x,x,x,10,9,x,x,x,x},{x,x,x,x,6,7,10,x,x,x},{x,x,x,x,x,3,8,x,x,x},{x,x,x,x,x,x,x,4,8,x},{x,x,x,x,x,x,x,9,6,x},{x,x,x,x,x,x,x,5,4,x},{x,x,x,x,x,x,x,x,x,8},{x,x,x,x,x,x,x,x,x,4},{x,x,x,x,x,x,x,x,x,x}};/*for (i=0;i<10;i++){for(j=0;j<10;j++)printf("%d ",a[i][j]);printf("\n"); }*/fpath(a);printf("最短路径大小为: %d\n",dist[9]);m=froute(a);for(i=m-1;i>=0;i--)printf("最短路径经过: %d\n",path[i]);}void fpath(int a[][10]){int i,j,k;42 dist[0]=0;for(i=1;i<10;i++){k=max;for(j=0;j<i;j++){if(a[j][i]!=x)if((dist[j]+a[j][i])<k)k=dist[j]+a[j][i];  } dist[i]=k;}}int froute(int a[][10]){int j,b,k=1,i=9;path[0]=10;while(i>0){for(j=i-1;j>=0;j--){ if(a[j][i]!=x){b=dist[i]-a[j][i];if(b==dist[j]){path[k++]=j+1;i=j; break;}}} }return k;
}

隐马尔可夫链模型的训练与预测相关推荐

  1. 基于mlr3工具包的机器学习(1)——数据、模型、训练、预测

    专注系列化.高质量的R语言教程 (查看推文索引) mlr3是一个关于机器学习的工具包,关于它的详细介绍可参见: 网页版:https://mlr3book.mlr-org.com/intro.html ...

  2. SQL脚本实现算法模型的训练,预测

    前言 搜索团队正好需要计算一些词汇的相似性,这个用Word2Vec是很方便的.于是我立马安排算法团队帮个忙弄下.但回头想想,因为这么点事,打断了算法手头的工作,这简直不能忍. 由于我司内部已经在使用基 ...

  3. mnn模型从训练-转换-预测

    之前写过一个文章转换mnn模型 但是没有从头开始,而是直接使用的一个模型,本文想直接从头到尾直接做一下 训练模型的代码如下: 注意:此代码必须tf2.3及其以上才能运行,以下的代码均在tf2.3运行的 ...

  4. 化学实验室自动化 - 1. 深度学习视觉检测(实例分割) - Mask-RCNN模型训练和预测

    在上一篇文章中,我们完成了化学实验室常见物体的COCO格式的实例分割数据集制作.上一篇文章的数据集中总共只有65张图像,而且被分成了训练集.验证集和测试集,经Mask-RCNN模型训练测试,发现模型的 ...

  5. 机器学习入门实践——线性回归模型(波士顿房价预测)

    机器学习入门实践--线性回归模型(波士顿房价预测) 一.背景介绍 给定一个大小为 n n n的数据集 { y i , x i 1 , . . . , x i d } i = 1 n {\{y_{i}, ...

  6. 解决隐马模型中预测问题的算法是?

    感想 隐马尔可夫模型涉及的算法很多,周志华的<机器学习>,李航的<统计学系方法>都有讲过,可能当时理解的不深,导致现在都忘干净了,现在是时候弥补一下了. problem 解决隐 ...

  7. R语言决策树、bagging、随机森林模型在训练集以及测试集的预测结果(accuray、F1、偏差Deviance)对比分析、计算训练集和测试集的预测结果的差值来分析模型的过拟合(overfit)情况

    R语言决策树.bagging.随机森林模型在训练集以及测试集的预测结果(accuray.F1.偏差Deviance)对比分析.计算训练集和测试集的预测结果的差值来分析模型的过拟合(overfit)情况 ...

  8. ML之xgboost:基于xgboost(5f-CrVa)算法对HiggsBoson数据集(Kaggle竞赛)训练实现二分类预测(基于训练好的模型进行新数据预测)

    ML之xgboost:基于xgboost(5f-CrVa)算法对HiggsBoson数据集(Kaggle竞赛)训练实现二分类预测(基于训练好的模型进行新数据预测) 目录 输出结果 设计思路 核心代码 ...

  9. DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练、预测

    DL之DNN:自定义2层神经网络TwoLayerNet模型(封装为层级结构)利用MNIST数据集进行训练.预测 导读           计算图在神经网络算法中的作用.计算图的节点是由局部计算构成的. ...

  10. DL之DNN:自定义2层神经网络TwoLayerNet模型(计算梯度两种方法)利用MNIST数据集进行训练、预测

    DL之DNN:自定义2层神经网络TwoLayerNet模型(计算梯度两种方法)利用MNIST数据集进行训练.预测 导读 利用python的numpy计算库,进行自定义搭建2层神经网络TwoLayerN ...

最新文章

  1. android不调用系统发送短信,android之两种方式调用短信发送接口
  2. 在vs2005中使用Jmail发送邮件问题
  3. 针对阿片类药物使用障碍的药物重定位
  4. Unity热更新技术整理
  5. 垃圾回收算法|GC标记-清除算法
  6. EasyExcel实现文件读取、导出、上传、下载操作
  7. 陷阱:C++模块之间的”直接依赖“和”间接依赖“与Makefile的撰写
  8. 什么样的人贷款更容易?
  9. Java多线程系列(十一):ReentrantReadWriteLock的实现原理与锁获取详解
  10. Qt文档阅读笔记-构造WebSocket服务端
  11. 饿了么2020外卖备注图鉴:12个关键词覆盖我们的生活
  12. 《Ray Tracing in One Weekend》——Chapter 6: Antialiasing
  13. HTML网页设计:十一、表单
  14. ICMAX解析运行内存发展新趋势 LPDDR4X将会给手机带来哪些改变?
  15. 路由追踪测试软件,路由追踪命令是什么 使用路由追踪的技巧
  16. C# 文件复制和移动操作(单个文件)
  17. html文件导入到u盘中,技术分享 - 编程实现U盘插入自动复制U盘内容到本地
  18. 为VMware vSphere创建CentOS 7 Terraform模板
  19. 计算机组成原理实验 实验一 存储器实验
  20. 浅谈快件清关与邮关的区别

热门文章

  1. 萧条下的养殖业成就兽药电子商务新时代
  2. FPGA实现对数log2和10*log10
  3. 支付宝资金预授权(冻结、解冻、转支付、异步通知回调、撤销、授权操作查询)
  4. 区块链技术入门学习指引
  5. python3之http.server模块
  6. [精品书单]3D打印机课程设计
  7. 对比线程,一个VCPU是什么
  8. js对联广告,顶部浮动广告,固定位置广告插件
  9. mysql 安装版本选择_选择要安装的MySQL版本
  10. java 读取rtf字节_JAVA读取RTF文档