隐马尔科夫模型,Hidden Marcov Model,是可用于标注问题的统计学习模型,描述由隐藏的马尔科夫链随机生成观测序列的过程,属于生成模型,是一种比较重要的机器学习方法,在语音识别等领域有重要的应用。
本文不打算使用书面的一大堆公式来说明,本人对公式无感,能用例子说明的根本不想碰公式,不知道是不是霍金说过,多加一条公式就会损失一大片读者。PS:不管有没有说过了,是这个意思,whatever
首先说明一下基本概念,概念来自李航《统计学习方法》第十章【1】
然后根据书中的例子,来具体说明一下HMM的三要素功能
书中的例子举得很明白,所以现在应该也了解了隐马尔科夫模型的三个要素了
下面通过博客园博主Skyme的博文《一文搞懂HMM》【2】的例子对HMM做一个代码上的解释,需要说明的是,我无意侵犯其知识产权,只是觉得该文举得例子比较好,所以就想拿过来用,毕竟自己画图举例什么的太过麻烦,博文原地址我也给出来了,算是论文里面的引用吧。
好的言归正传,我们来看一下HMM需要解决的基本的三个问题,仍然是使用【1】书的内容:
结合具体的问题,来看一下这三个问题究竟是什么。
假设我手里有三个不同的骰子。第一个骰子是我们平常见的骰子(称这个骰子为D6),6个面,每个面(1,2,3,4,5,6)出现的概率是1/6。第二个骰子是个四面体(称这个骰子为D4),每个面(1,2,3,4)出现的概率是1/4。第三个骰子有八个面(称这个骰子为D8),每个面(1,2,3,4,5,6,7,8)出现的概率是1/8。
我们假设开始掷骰子的时候的初始概率是一样的也就是1/3,则初始状态是 PI={1/3,1/3,1/3} 分别对应于D6,D4,D8, 为了方便阐述,我们假设每个状态的转移概率都是1/3,则转移矩阵(也就是上文提到的A矩阵)是
然后观测结果集合是{1,2, 3, 4,5, 6, 7, 8}
每个状态的观测概率矩阵,也就是上文提到的B矩阵是
到此,PI,A,B 这HMM三要素都到齐了,至于观测序列,则根据具体的问题具体说明
现在模型已经给出了,让我们看一下HMM的三个问题对于这个问题的具体实现
[1],概率计算问题:我们已知上面的HMM模型参数,也知道现在的观测序列,现在我们想知道从原始模型到这个观测序列的概率是多少。
现在我们假设现在的观测序列是 1-6-3
我现在想知道从之前的模型参数值(PI,A,B)掷骰子到1-6-3的概率有多大
解决办法:
如果使用最原始的首先枚举出每个可能的状态集然后分别求概率,然后把这些概率想加,这种办法的效率太低,因此需要使用一种更加好的算法来计算
这里使用的是前向算法,计算理论推导在【1】书中有详细的过程,这里使用【2】举得例子
需要说明的是原文的计算结果似乎是有错误,所以这里讲原文的求和数据省去了,但是方法还是一样计算,这下应该是很明了的的计算方法,就像一条计算链条,多长的结果都能计算出来,下面是代码实现过程:
package com.luchi.hmm; import com.luchi.hmm.problem1.status; /* * @description * 隐马尔科夫模型主要解决的是三个问题 * 1,预测问题,也就是解码问题,已知模型lambda=(A,B,PI)和观测序列,其给定观测序列条件概率P(I|O)最大的状态序列I * 2,概率计算问题:给定模型lambda=(A,B,PI)和观测序列O,计算在该模型lambda下观测序列O出现的概率P(O|lambda) * 3,学习问题,已知观测序列O,估计模型lambda=(A,B,PI)的参数 * * 这里解决的是第二个问题,也就是计算概率问题,这里使用了前向算法 * @author:luchi */ public class problem2 { //定义状态转移矩阵 double [][]trans=new double[][]{ {0.33,0.33,0.33}, {0.33,0.33,0.33}, {0.33,0.33,0.33}, }; //状态名称 enum status{ D6, D4, D8 }; //总状态数 int status_length=3; //掷骰子事件分别是1,2,3,4,5,6 //每个状态下掷骰子事件发生概率 double[][] emission_probability = new double[][]{ {0.167, 0.167, 0.167,0.167,0.167,0.167,0,0}, {0.250,0.250,0.250,0.250,0,0,0,0}, {0.125,0.125,0.125,0.125,0.125,0.125,0.125,0.125} }; int activity_length=emission_probability[0].length; //观察序列为 int []observation=new int[]{1,6,3}; //初始序列 double[]start=new double[]{0.33,0.33,0.33}; //计算出现的概率 public void calcuProb(){ double[][]midStore=new double[observation.length][status_length]; double totalProb=0.0; //初始化第一个概率 for(int i=0;i<status_length;i++){ double temp=start[i]*emission_probability[i][observation[0]-1]; midStore[0][i]=temp; totalProb+=temp; } if(observation.length>=1){ for(int i=1;i<observation.length;i++){ totalProb=0.0; int desPos=observation[i]-1; for(int j=0;j<status_length;j++){ double temp=0.0; for(int t=0;t<status_length;t++){ temp+=midStore[i-1][t]*trans[t][j]*emission_probability[j][desPos]; } midStore[i][j]=temp; totalProb+=temp; } System.out.println(totalProb); } } } public static void main(String[]args){ problem2 p=new problem2(); p.calcuProb(); } }
算出来的结果是:0.003082643085456001
[2],学习问题:知道骰子有几种(隐含状态数量),不知道每种骰子是什么(转换概率),观测到很多次掷骰子的结果(可见状态链),我想反推出每种骰子是什么(转换概率)。
也就是不知道上面所描述的HMM模型参数(PI,A,B),现在需根据观测结果O 来反推HMM的参数,这是一个学习过程,也是一个比较重要的问题,一般使用E-M算法进行推导,因为可以把HMM的状态模型看作是未知参数,因此就有了E步和M步,因为本人对EM算法的理解还处于高斯混合模型GMM阶段,并不算很熟悉,所以不打算详细介绍,有兴趣的话可以看【1】的第181-184的推导
[3].预测问题:知道HMM的参数(PI,A,B)也已知观测掷骰子的结果O(o1,o2,o3,o4...),现在想知道o1,o2,o3...是由哪些骰子(D6,D4,D8)掷出来的
这里使用的是维特比算法,维特比算法实际上是用动态规划求解隐马尔科夫预测问题,也就是使用动态规划求解概率最大路径,这个时候一条路径对应的一个状态序列。下面通过一个例题说明:
由此可见,维特比算是其实是一个类似回溯算法一样的计算过程,每个阶段都储存上一个阶段到这个阶段概率最大的状态,然后到达最终态的时候回溯找到状态序列
还是以文章的掷骰子为题为例,HMM的模型(PI,A,B)都不变,现在观察到的点数(观测序列)是(1 6 3 5 2 7 3 5 2 4),然后我们根据维特比算法来求解最大概率的掷到这个点数序列的状态集
(也就是是哪个筛子掷的的集合),根据上图例10.3的维特比算法的说明可以将这个问题用以下代码实现
package com.luchi.hmm; /* * @description * 隐马尔科夫模型主要解决的是三个问题 * 1,预测问题,也就是解码问题,已知模型lambda=(A,B,PI)和观测序列,其给定观测序列条件概率P(I|O)最大的状态序列I * 2,概率计算问题:给定模型lambda=(A,B,PI)和观测序列O,计算在该模型lambda下观测序列O出现的概率P(O|lambda) * 3,学习问题,已知观测序列O,估计模型lambda=(A,B,PI)的参数 * * 这个首先解决的是第一个问题,也就是预测问题,主要有两种算法,一个是近似算法,一个是维特比算法,这里使用了viterbi算法 @author:luchi * */ public class problem1 { //定义状态转移矩阵 double [][]trans=new double[][]{ {0.33,0.33,0.33}, {0.33,0.33,0.33}, {0.33,0.33,0.33}, }; //状态名称 enum status{ D6, D4, D8 }; //总状态数 int status_length=3; //掷骰子事件分别是1,2,3,4,5,6 //每个状态下掷骰子事件发生概率 double[][] emission_probability = new double[][]{ {0.167, 0.167, 0.167,0.167,0.167,0.167,0,0}, {0.250,0.250,0.250,0.250,0,0,0,0}, {0.125,0.125,0.125,0.125,0.125,0.125,0.125,0.125} }; int activity_length=emission_probability[0].length; //观察序列为 int []observation=new int[]{1,6,3,5,2,7,3,5,2,4}; //初始序列 double[]start=new double[]{0.33,0.33,0.33}; //维特比算法求解 public void viterbi(){ //初始化存储中间矩阵 double[][]midStore=new double[observation.length][status_length]; int[][]traceBack=new int[observation.length][status_length]; //初始化第一个数据 for(int i=0,desPos=observation[0]-1;i<status_length;i++){ midStore[0][i]=start[0]*emission_probability[i][desPos]; } //从第二个观测序列开始计算 for(int i=1;i<observation.length;i++){ int desPos=observation[i]-1; //注意数组是从0开始的,而不是1 for(int j=0;j<status_length;j++){ double maxProb=-1; int traceStatus=0; //比较前面的序列 for(int t=0;t<status_length;t++){ double tempProb=midStore[i-1][t]*trans[t][j]*emission_probability[j][desPos]; if(tempProb>maxProb){ maxProb=tempProb; traceStatus=t; } } midStore[i][j]=maxProb; traceBack[i][j]=traceStatus; } } //比较最后一个阶段的最大概率 int max_end_status=0; double max_end_prob=-1; for(int end_status=0;end_status<status_length;end_status++){ double prob=midStore[observation.length-1][end_status]; if(prob>max_end_prob){ max_end_prob=prob; max_end_status=end_status; } } //回溯输出最大概率状态模型 int []path=new int[observation.length]; path[observation.length-1]=max_end_status; int curStatus=max_end_status; for(int i=observation.length-1;i>=1;i--){ int preStatus=traceBack[i][curStatus]; path[i-1]=preStatus; curStatus=preStatus; } for(int i=0;i<path.length;i++){ System.out.println(status.values()[path[i]]); } } public static void main(String[]args){ problem1 p=new problem1(); p.viterbi(); } }
预测的结果是:D4 D6 D4 D6 D4 D8 D4 D6 D4 D4
恩,这就是我对Hidden Morcov Model的浅见,不足之处还望指正
参考文献:
【1】李航 《统计学习方法》
【2】skyme 《一文搞懂HMM》 http://www.cnblogs.com/skyme/p/4651331.html#commentform
相关推荐
详细的讲解了HMM模型,以及前向算法,维特比算法,前向后向算法。 2.HMM模型的java代码实现,实现了前向算法,后向算法和维特比算法。代码注释清楚,便于阅读。 3.提供了两篇关于解递归式的论文,提供了求解递归算法...
在实际应用中,HMM模型需要通过学习算法来估计状态转移概率矩阵A、观测概率矩阵B和初始状态向量π。常用的学习方法有Baum-Welch算法(也称前向-后向算法),它是一种特殊的期望最大化(Expectation-Maximization, EM...
2. **解码问题**:给定一个HMM模型和一个观测序列,找出最有可能产生该观测序列的隐状态序列。 3. **学习问题**:给定一个HMM模型和一组观测序列,调整模型参数以最大化观测序列出现的概率。 针对这三个问题,HMM中...
在提供的压缩包文件"HMM"中,可能包含Matlab代码示例,用于演示如何建立HMM模型、训练模型参数、进行解码以及评估。这些代码可以帮助理解HMM的工作原理,并在具体项目中进行应用。 通过深入理解和熟练运用HMM,你...
隐马尔科夫模型(Hidden Markov Model,简称HMM)是一种统计建模方法,尤其在自然语言处理、语音识别、生物信息学等领域有广泛应用。HMM的核心思想是假设观察序列是由不可见的状态序列生成的,而这些状态遵循...
隐马尔科夫模型(Hidden Markov Model, HMM)是一种统计模型,它用来描述一个含有隐含未知参数的马尔科夫过程。HMM在时间序列数据的分析中非常有用,尤其是当观测数据是由无法直接观测到的隐状态序列决定时。在语音...
隐马尔科夫模型(HMM)是一种统计建模技术,用于处理时序数据,尤其在自然语言处理、语音识别和生物信息学等领域有广泛应用。HMM 的核心思想是假设系统内部存在一些不可直接观测的状态,这些状态按照马尔科夫过程...
隐马尔科夫模型用Python写的,程序中实现了前向算法、后向算法、维特比算法、前向后向算法,前面的算法比较简单,后面的前向后向算法是用于训练模型的,稍微复杂一点。从测试的结果来看,模型训练有可能收敛于局部...
HMM模型通常与两种算法密切相关:维特比算法(Viterbi Algorithm)和前向-后向算法(Forward-Backward Algorithm)。 维特比算法主要用于找出给定观测序列下最可能的隐藏状态序列,即找到一条概率最大的路径。在...
隐马尔科夫模型(Hidden Markov Model,简称HMM)是概率统计领域中的一个重要模型,尤其在自然语言处理、语音识别和机器视觉等领域有着广泛的应用。它是一种能够描述序列数据生成过程的统计模型,其核心思想是假设...
本文献探讨了一种基于隐马尔科夫模型(Hidden Markov Model, HMM)的动作识别技术,特别是针对手势识别的研究。该研究旨在解决自动手势识别中面临的挑战,即处理不可预测和模糊的非手势手部动作。通过引入一种阈值...
【隐马尔科夫模型(HMM)的应用】在中文分词中,HMM模型可以用来估计词语出现的概率,通过Viterbi算法或N-最短路径方法寻找最可能的分词结果。HMM的层叠形模型(CHMM)进一步增强了分词的准确性,通过多层结构处理复杂...
隐马尔科夫模型(Hidden Markov Model,HMM)是一种统计模型,它基于观测序列通过学习未知参数来推断最有可能的序列状态。在自然语言处理领域,HMM 被广泛应用于语音识别、手写识别、生物信息学以及文本分析等多个...
隐马尔科夫模型(HMM)是一种统计参数语音合成方法,它可以生成自然和有适当韵律的合成语音。HMM 代表了一个音素序列,也代表了各种用相同的方式去进行单元选择的语言规格的背景。从 HMM 中生成的声学参数根据语言...
隐马尔科夫模型(Hidden Markov Model,简称HMM)是概率统计领域中的一个重要模型,广泛应用于自然语言处理、语音识别、生物信息学等多个领域。在这个HMM的MATLAB源代码压缩包中,包含了实现HMM核心算法的代码,如前...
数据挖掘算法总结–隐马尔科夫模型(HMM)与Vertibi算法 隐马尔科夫模型(HMM)是一种常用的数据挖掘算法,用于解决模式识别、自然语言处理、语音识别等问题。HMM 由三部分组成:观察状态、隐状态和概率矩阵。观察...