zoukankan      html  css  js  c++  java
  • 隐马尔科夫模型

    马尔科夫过程

    马尔科夫过程可以看做是一个自动机,以一定的概率在各个状态之间跳转。

    考虑一个系统,在每个时刻都可能处于N个状态中的一个,N个状态集合是 {S1,S2,S3,...SN}。我们现在用q1,q2,q3,…qn来表示系统在t=1,2,3,…n时刻下的状态。在t=1时,系统所在的状态q取决于一个初始概率分布PI,PI(SN)表示t=1时系统状态为SN的概率。

    马尔科夫模型有两个假设:

    1.      系统在时刻t的状态只与时刻t-1处的状态相关;(也称为无后效性)

    2.      状态转移概率与时间无关;(也称为齐次性或时齐性)

    第一条具体可以用如下公式表示:

    P(qt=Sj|qt-1=Si,qt-2=Sk,…)= P(qt=Sj|qt-1=Si)

    其中,t为大于1的任意数值,Sk为任意状态

    第二个假设则可以用如下公式表示:

    P(qt=Sj|qt-1=Si)= P(qk=Sj|qk-1=Si)

    其中,k为任意时刻。

    隐马尔科夫模型由初始状态向量S、状态转移概率矩阵A和观测概率矩阵B决定,S和A决定状态序列,B决定观测序列,因此,隐马尔科夫模型λ可以用三元组符号表示,即

        λ=(A,B,S)

    A,B,S称为隐马尔科夫模型的三要素。

    隐马尔科夫可以解决的三个问题

    (1)概率计算问题:给定模型λ=(A,B,S)和观测序列O=(o1,o2,...,or),计算在模型λ下观测序列O出现的概率p(O|λ)

    ①直接计算法        

          如果穷尽所有的状态组合,即S1S1...S1, S1S1...S2, S1S1...S3, ..., S3S3...S3。这样的话t1时刻有N个状态,t2时刻有N个状态...tT时刻有N个状态,这样的话一共有N*N*...*N= NT种组合,时间复杂度为O(NT),计算时,就会出现“指数爆炸”,当T很大时,简直无法计算这个值。为解决这一问题,Baum提出了前向算法。

    ②前向算法

          归纳过程

          首先引入前向变量αt(i):在时间t时刻,HMM输出序列为O1O2...OT,在第t时刻位于状态si的概率。

          当T=1时,输出序列为O1,此时计算概率为P(O1|μ):假设有三个状态(如下图)1、2、3,输出序列为O1,有三种可能一是状态1发出,二是从状态2发出,三是从状态3发出。另外从状态1发出观察值O1得概率为b1(O1),从状态2发出观察值O1得概率为b2(O1),从状态3发出观察值O1得概率为b3(O1)。因此可以算出

         P(O1|μ)= π1*b1(O1)+π2*b2(O1) +  π3*b3(O1)= α1(1) + α1(2) + α1(3)

                                        

          当T=2时,输出序列为O1O2,此时计算概率为P(O1O2|μ):假设有三个状态(如下图)1、2、3,输出序列为O1,有三种可能一是状态1发出,二是从状态2发出,三是从状态3发出。另外从状态1发出观察值O2得概率为b1(O2),从状态2发出观察值O2得概率为b2(O2),从状态3发出观察值O2得概率为b3(O2)。

          要是从状态1发出观察值O2,可能从第一时刻的1、2或3状态装换过来,要是从状态1转换过来,概率为α1(1)*a11*b1(O2),要是从状态2转换过来,概率为α1(2)*a21*b1(O2),要是从状态3转换过来,概率为α1(3)*a31*b1(O2),因此

         P(O1O2,q2=s1|μ)= α1(1)*a11*b1(O2)  + α1(2)*a21*b1(O2) + α1(3)*a31*b1(O2)=α2(1)

                                        

          同理:P(O1O2,q2=s1|μ)= α1(1)*a12*b2(O2)  + α1(2)*a22*b2(O2) + α1(3)*a32*b2(O2)=α2(2)

                   P(O1O2,q2=s1|μ)= α1(1)*a13*b1(O2)  + α1(2)*a23*b3(O2) + α1(3)*a33*b3(O2)=α2(3)

         所以:P(O1O2|μ)=P(O1O2,q2=s1|μ)+ P(O1O2,q2=s1|μ)+ P(O1O2,q2=s1|μ)

                                 =α2(1) + α2(2) + α2(3)

          以此类推。。。

          前向算法

           step1 初始化:α1(i) = πi*bi(O1), 1≤i≤N

           step2 归纳计算:

                             

           step3 终结:

                          P(O|μ)=

          时间复杂度

          计算某时刻的某个状态的前向变量需要看前一时刻的N个状态,此时时间复杂度为O(N),每个时刻有N个状态,此时时间复杂度为N*O(N)=O(N2),又有T个时刻,所以时间复杂度为T*O(N2)=O(N2T)。

          程序例证

          

            前向算法计算P(O|M):

            step1:α1(1) =π1*b1(red)=0.2*0.5=0.1          α1(2)=π2*b2(red)==0.4*0.4= 0.16          α1(3)=π3*b3(red)==0.4*0.7=0.21

            step2:α2(1)=α1(1)*a11*b1(white) + α1(2)*a21*b1(white) + α1(3)*a31*b1(white)

                         ...

            step3:P(O|M) = α3(1)+α3(2)+α3(3)程序代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
            float a[3][3] = {{0.5,0.2,0.3},{0.3,0.5,0.2},{0.2,0.3,0.5}};
            float b[3][2] = {{0.5,0.5},{0.4,0.6},{0.7,0.3}};
            float alpha[4][3];
            int i,j,k, count = 1;
            //output list
            int list[4] = {0,1,0,1};
            //step1:Initialization
            alpha[0][0] = 0.2 * 0.5;
            alpha[0][1] = 0.4 * 0.4;
            alpha[0][2] = 0.4 * 0.7;
            //step2:iteration
            for (i=1; i<=3; i++)
            {
                for(j=0; j<=2; j++)
                {
                    alpha[i][j] = 0;
                    for(k=0; k<=2; k++)
                    {
                       alpha[i][j] += alpha[i-1][k] * a[k][j] * b[j][list[count]];
                    }
                }
                count += 1;
            }
           for (i=0; i<=3; i++)
            {
                for(j=0; j<=2; j++)
                {
                    printf("a[%d][%d]=%f
    ",i+1,j+1,alpha[i][j]);
                }
            }
           //step3:end
           printf("Forward:%f
    ", alpha[3][0]+alpha[3][1]+alpha[3][2]);
           return 0;
    }

    ③后向算法

    对于HMM的评估问题,利用动态规划可以用前向算法,从前到后算出前向变量;也可以采用后向算法,从后到前算出后向变量。

    先介绍后向变量βt(i):给定模型μ=(A,B,π),并且在时间 时刻t 状态为s的前提下,输出序列为Ot+1Ot+2...OT的概率,即

                                        βt(i)=P(Ot+1Ot+2...OT|qt=si,μ)

    归纳过程

        假设仍然有3个状态

                     

        当t=T时,按照定义:时间t  状态q输出为OT+1......的概率,从T+1开始的输出是不存在的(因为T时刻是终止终止状态),即T之后是空,是个必然事件,因此βt(i)=1,1≤1≤N

        当t=T-1时,

                             

                     βT-1(i)=P(OT|qT-1=si,μ) = ai1*b1(OT)*βT(1)  +  ai2*b2(OT)*βT(2)  +  ai3*b3(OT)*βT(3)

          ......

        当t=1时,

           β1(1)=P(O2O3...OT|q2=s1,μ) = a11*b1(O2)*β2(1) + a12*b2(O2)*β2(2) + a13*b3(O2)*β2(3)

           β1(2)=P(O2O3...OT|q2=s1,μ) = a21*b1(O2)*β2(1) + a22*b2(O2)*β2(2) + a23*b3(O2)*β2(3)

           β1(3)=P(O2O3...OT|q2=s1,μ) = a31*b1(O2)*β2(1) + a32*b2(O2)*β2(2) + a33*b3(O2)*β2(3)

           P(O1O2...OT|μ) =   

                                 =  

                                 =  

    后向算法

        step1 初始化:βT(i)=1, 1≤1≤N

        step2 归纳计算:

                           1≤t≤T-1, 1≤i≤N

        step3 求终结和:

                       P(O|μ)=  

    时间复杂度

        计算某时刻在某个状态下的后向变量需要看后一时刻的N个状态,此时时间复杂度为O(N),每个时刻有N个状态,此时时间复杂度为N*O(N)=O(N2),又有T个时刻,所以时间复杂度为T*O(N2)=O(N2T)。

    程序例证

                 

    后向算法

        计算P(O|M):

        step1:β4(1) = 1          β4(2) = 1          β4(3) = 1

        step2:β3(1) = β4(1)*a11*b1(white) + β4(2)*a12*b2(white) + β4(3)*a13*b3(white)

                         ...

        step3:P(O|M) = π11(1)*b1(O1) + π21(2)*b2(O1) + π31(3)*b3(O1)

    程序代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
            float a[3][3] = {{0.5,0.2,0.3},{0.3,0.5,0.2},{0.2,0.3,0.5}};
            float b[3][2] = {{0.5,0.5},{0.4,0.6},{0.7,0.3}};
            float result[4][3];
            int list[4] = {0,1,0,1};
            result[3][0] = 1;
            result[3][1] = 1;
            result[3][2] = 1;
            int i,j,k, count = 3;
            for (i=2; i>=0; i--)
            {
                for(j=0; j<=2; j++)
                {
                    result[i][j] = 0;
                    for(k=0; k<=2; k++)
                    {
                       result[i][j] += result[i+1][k] * a[j][k] * b[k][list[count]];
                    }
                }
                count -= 1;
            }
           for (i=0; i<=3; i++)
            {
                for(j=0; j<=2; j++)
                {
                    printf("b[%d][%d] = %f
    ",i+1,j+1,result[i][j]);
    
                }
            }
            printf("backward:%f
    ", result[0][0]*0.2*0.5+result[0][1]*0.4*0.4+result[0][2]*0.4*0.7);
            return 0;
    }

    前向后向算法

    重新回顾:

        前向变量αt(i):在时刻t,在已知模型μ=(A,B,π)的条件下,状态处于si,输出序列为O102...Ot,前向变量为αt(i)

        后向变量βt(i):在时刻t,在已知模型μ=(A,B,π)和状态处于si的条件下,输出序列为Ot+1Ot+2...OT,后向变量为βt(i)

    公式推导:

        P(O,qt=si|μ) = P(O1O2...OT, qt=si|μ)

                             =P(O1O2...Ot, qt=si,Ot+1Ot+2...OT|μ)

                             =P(O1O2...Ot, qt=si|μ) * P(Ot+1Ot+2...OT|O1O2...Ot, qt=si,μ)

                             =P(O1O2...Ot, qt=si|μ) * P(Ot+1Ot+2...OT|qt=si,μ)

                             =αt(i) *  βt(i)

         P(O|μ)=

    案例分析:

       

          分析:

            P(q4=s3|O,M) =  P(q4=s3, O|M)/P(O|M)

                                = P(O,q4=s3|M)/P(O|M)

                                = α4(3) *  β4(3)

    程序代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main()
    {
            float a[3][3] = {{0.5,0.2,0.3},{0.3,0.5,0.2},{0.2,0.3,0.5}};
            float b[3][2] = {{0.5,0.5},{0.4,0.6},{0.7,0.3}};
            float result_b[8][3];
            float result_f[8][3];
            float result, result_t;
            int list[8] = {0,1,0,0,1,0,1,1};
            result_b[7][0] = 1;
            result_b[7][1] = 1;
            result_b[7][2] = 1;
            result_f[0][0] = 0.2 * 0.5;
            result_f[0][1] = 0.4 * 0.4;
            result_f[0][2] = 0.4 * 0.7;
            //Backward
            int i,j,k, count = 7;
            for (i=6; i>=0; i--)
            {
                for(j=0; j<=2; j++)
                {
                    result_b[i][j] = 0;
                    for(k=0; k<=2; k++)
                    {
                       result_b[i][j] += result_b[i+1][k] * a[j][k] * b[k][list[count]];
                    }
                }
                count -= 1;
            }
           for (i=0; i<=7; i++)
            {
                for(j=0; j<=2; j++)
                {
                    printf("b[%d][%d]= %f
    ",i+1,j+1, result_b[i][j]);
    
                }
            }
            printf("Backward:%f
    ", result_b[0][0]*0.2*0.5+result_b[0][1]*0.4*0.4+result_b[0][2]*0.4*0.7);
            //Forward
            count = 1;
            for (i=1; i<=7; i++)
            {
                for(j=0; j<=2; j++)
                {
                    result_f[i][j] = 0;
                    for(k=0; k<=2; k++)
                    {
                        result_f[i][j] += result_f[i-1][k] * a[k][j] * b[j][list[count]];
                    }
                }
                count += 1;
            }
            for (i=0; i<=7; i++)
            {
                for(j=0; j<=2; j++)
                {
                    printf("a[%d][%d]= %f
    ", i+1, j+1, result_f[i][j]);
                }
            }
            result = result_f[7][0] + result_f[7][1] + result_f[7][2];
            printf("Forward: %f
    ", result);
            
            result_t = 0;
            for (i=0; i<=2; i++)
            {
                result_t += result_f[3][i] * result_b[3][i];
            }
            printf("Result:%f
    ", result_f[3][2]*result_b[3][2]/result_t);
    
            return 0;
    }

    (2)学习模型:已知观测序列O=(o1,o2,...,or),估计模型λ=(A,B,S)的参数,使得在该模型下观测序列概率p(O|λ)最大,即用极大似然估计的方法估计参数

    隐马尔可夫模型的学习问题:给定一个输出序列O=O1O2...OT,如何调节模型μ=(A,B,π)的参数,使得P(O|M)最大。

          最大似然估计是一种解决方法,如果产生的状态序列为Q=q1q2...qT,根据最大似然估计,可以通过以下公式推算:

            πi‘ = δ(q1,si)

            aij' =  Q中从状态qi转移到qj的次数/Q中从状态qi转移到另一状态(包括qj)的次数

                 

            bj(k)' = Q中从状态qj发出符号Vk的次数/ Q中到达状态qj的次数

                     

          δ(x,y)为克罗奈克函数,当x=y时,δ(x,y)=1;否则,δ(x,y)=0

          但是注意,在实际中,状态Q=q1q2...qT是观察不到的(隐变量),因此上述的这种求法是有问题的。幸好希望最大化,可以用于含有隐变量的统计模型的参数最大似然估计。基本思想是初始时,随机的给模型参数赋值,但是要遵循模型对参数的限制,例如,从一个状态发出的所有状态转移概率之和为1,得到模型μ0。然后根据μ0中的具体值,带入下式,可以得到u1.依次往下迭代,直到收敛于最大似然估计值。这种迭代爬山算法可以局部使P(O|μ)最大。称为Baum-Welch算法或前向后向算法。

          给定HMM的参数μ和观察序列O=O1O2...OT,在时间t位于状态si,在时间t+1位于状态sj的概率为ξt(i,j)=P(qt=si,qt+1=sj|O,μ),公式推导如下:

                   ................(1)

           给定HMM μ 和序列O=O1O2...OT,在时间t位于状态si的概率为:.........(2)

           这样求μ的参数估计重新改写:

            πi‘ = r1(i) ...........(3)

            aij' =  Q中从状态qi转移到qj的次数/Q中从状态qi转移到另一状态(包括qj)的次数

                 = ..........(4)       

            bj(k)' = Q中从状态qj发出符号Vk的次数/ Q中到达状态qj的次数

                     = ..............(5)

    前向后项算法

         step1 初始化: 随机地给定参数 πi, aij, bj(k),使其满足条件:

                           

                           由此得到μ0,令i=0

          step2 EM计算:

                       E步骤:根据(1)(2)式计算期望ξt(i,j) 和 rt(i)

                       M步骤:根据期望ξt(i,j) 和 rt(i),带入(3)(4)(5)重新得到πi, aij, bj(k),得到μi+1

           step3 循环计算: i = i+1, 直到πi, aij, bj(k)收敛

    (3)预测问题:已知模型λ=(A,B,S)和观测序列O=(o1,o2,...,or),求给定观测序列条件概率p(O|λ)最大的状态序列。即给定观测序列,求最优可能的对应的状态序列。

               

          一种想法是求出每个状态的概率rt(i)最大(rt(i)=P(qt=si,O|μ)),记q't(i)=argQmax(rt(i)),但是这样做,忽略了状态之间的关系,很可能两个状态之间的概率为0,即aq't(i)q't+1(i)=0,这样求得的“最优”状态序列是不合法的。

          为防止状态之间转移概率为0(断续问题),换一种思路,不是求单个状态求得最大值,而是求得整个状态序列最大值,即求

                                       Q'= argQmaxP(Q|O,μ)

          此时用维特比算法,先定义下维特比变量δt(i):在时间t,HMM沿着一条路径到达状态si,并输出观察序列O=O1O2...Ot的最大概率:

           δt(i)=max P(q1q2...qt=si,O1O2...Ot|μ)

               

     

                                t                      t+1

          上图中,对于从t时刻三个到 t+1时刻的状态1,到底取状态1,2还是3,不是看单独状态1,2还是3的概率,而是看在状态1,2,3各自的维特比变量值乘以相应的状态转换概率,从中选出最大值,假设2时最大,那么记下t+1时刻状态1之前的路径是t时刻的状态2,以此类推。

          δt(i)的递归关系式: δt+1(i)=maxj δt(j)*aji*bi(Ot+1),为了记忆路径,定义路径变量ψt(i),记录该路径上的状态si的前一个状态。

    维特比算法

          step1 初始化:

                  δt(i) = πi*bi(O1), 1≤i≤N

                  ψt(i) = 0

          step2 归纳计算:

          δt(i)=max1≤j≤N δt-1(j)*aji*bi(Ot),2≤t≤T;1≤i≤N

                 记忆路径   ψt(i) = arg [max1≤j≤Nδt-1(j)*aji*bi(Ot)]

          step3 终结:

                QT' = arg max1≤i≤N T(i)]

                P'(QT') = max1≤i≤N T(i)]

       step4 路径回溯:

                 qt'=ψt+1(qt+1') , t=T-1,T-2...1

    时间复杂度

          计算某时刻的某个状态的前向变量需要比较前一时刻的N个状态,此时时间复杂度为O(N),每个时刻有N个状态,此时时间复杂度为N*O(N)=O(N2),又有T个时刻,所以时间复杂度为T*O(N2)=O(N2T)。

    程序例证

                 

          step1 初始化:δ1(1) = 0.2*0.5=0.1 ,δ1(2) = 0.4*0.4=0.16, δ1(3) = 0.4*0.7=0.21

          step2 归纳计算:δ2(1) =max[0.1*0.5,0.16*0.3,0.21*0.2]*0.6

           ...

          step3 终结:最佳路径是δ4(1)δ4(2)δ4(3)最大的一个对应的状态

          step4 回溯:从最后一个状态往回返

    程序代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
            float a[3][3] = {{0.5,0.2,0.3},{0.3,0.5,0.2},{0.2,0.3,0.5}};
            float b[3][2] = {{0.5,0.5},{0.4,0.6},{0.7,0.3}};
            float result[4][3];
            int list[4] = {0,1,0,1};
            int max[4][3];
            float tmp;
            //step1:Initialization
            result[0][0] = 0.2*0.5;
            result[0][1] = 0.4*0.4;
            result[0][2] = 0.4*0.7;
            
            int i,j,k, count = 1, max_node;
            float max_v;
            //step2:归纳运算
            for (i=1; i<=3; i++)
            {
                for(j=0; j<=2; j++)
                {
                    tmp = result[i-1][0] * a[0][j] * b[j][list[count]];
                    max[i][j] = 0;
                    for(k=1; k<=2; k++)
                    {
                        if(result[i-1][k] * a[k][j] * b[j][list[count]] > tmp)
                        {
                            tmp = result[i-1][k] * a[k][j]* b[j][list[count]];
                            max[i][j] = k;
                        }
                       result[i][j] = tmp;
                    }
                    max_v = result[3][0];
                    max_node = 0;
                    for (k=1; k<=2; k++)
                    {
                        if(result[3][k] > max_v)
                        {
                            max_v = result[3][k];
                            max_node = k;
                        }
                    }
                }
                count += 1;
            }
            //step3:终结
           for (i=0; i<=3; i++)
            {
                for(j=0; j<=2; j++)
                {
                    printf("%d %d     %f
    ",i+1,j+1,result[i][j]);
    
                }
            }
            printf("Pmax=%f
    ", max_v);
            printf("step4:%d   
    ", max_node+1);
            //step4:回溯
            for(k=3; k>=1; k--)
            {
                printf("step%d:%d  
    ",k, max[k][max_node]+1);
                max_node = max[k][max_node];
            }
            return 0;
        }

     转自http://www.cnblogs.com/kaituorensheng/archive/2012/12/01/2797230.html

  • 相关阅读:
    LintCode: Convert Sorted Array to Binary Search Tree With Minimal Height
    LNMP企业应用部署全过程(基于DEDE后台)
    提高Web服务器并发响应的经历
    提高Web服务器并发响应的经历
    提高Web服务器并发响应的经历
    提高Web服务器并发响应的经历
    华为设备RIP实施和理论详解
    华为设备RIP实施和理论详解
    MySQL 官方 Docker 镜像的使用
    Docker之docker设置系统的环境变量
  • 原文地址:https://www.cnblogs.com/nolonely/p/6849205.html
Copyright © 2011-2022 走看看