官术网_书友最值得收藏!

2.2.2 隱馬爾可夫模型

隱馬爾可夫模型(Hidden Markov Models,HMM)是序列數據處理和統計學習的一種重要概率模型,具有建模簡單、數據計算量小、運行速度快、識別率高等特點,近幾年來已經被成功應用到許多工程任務中。

1.隱馬爾可夫概念

隱馬爾可夫模型(HMM)由兩個序列組成,一個是不可觀察的(隱藏的)狀態變化序列,一個是由該隱藏的狀態所產生的可觀察的符號序列。隱馬爾可夫模型由其初始分布、轉移概率矩陣和發射概率矩陣完全確定。一個建好的隱馬爾可夫模型的運作過程可以這樣描述:由初始分布隨機地產生一個狀態,然后按轉移概率矩陣隨機地從一個狀態轉移到另一個狀態,在每一個狀態按發射概率矩陣隨機地生成一個字符,結果是產生一個狀態序列和一個相應的字符序列。

一般情況下,不僅模型能夠產生的狀態序列不是唯一的,每一個狀態序列能夠產生的字符序列也不是唯一的。模型以一定的概率產生特定的狀態序列,該狀態序列又以一定的概率產生特定的字符序列。在實際問題中,通常字符序列是可觀測的,狀態序列是不可觀測的,是“隱藏的”。

HMM可以表示為λ=(A,S,π),描述如下:

(a)隱狀態集SS={S1,S2,S3,…,SN},N表示狀態數。

(b)觀察符號集VV ={V1,V2,V3,…,VM},M表示符號數。

(c)狀態轉換概率矩陣AA={ai}j

式中

式中,Q=(q1,q2,…,qL)表示狀態序列,其中qiS,1≤iLL表示序列的長度。t為當前時刻。aij為從狀態si轉換到s j的概率。

(d)符號釋放概率矩陣EE={e jk},其中

式中,O=(o1,o2,…,oL)表示符號序列,其中o jV,1≤ jLe jk表示在狀態S j下產生符號Vk的概率。

(e)初始狀態分布ππ={πi},其中

表示模型起始于狀態Si(即時間為0時的狀態)的概率。

基于HMM的模式識別方法的基本思想:對于一個可用HMM描述的模式識別問題,其可能出現的所有不同的隱馬爾可夫模型λ1,λ2,λ3,…,λn構成一個集合Λ,其中任意一個元素λi為特定的隱馬爾可夫模型,其中包括此特定隱馬爾可夫模型的轉移矩陣、初分布及觀測概率。觀測概率的定義如下:

給定一組樣本觀測值(o1,o2,…,on),選擇λ?Λ,使得

λ?是觀測(o1,o2,…,on)的最可能模式。

2.隱馬爾可夫模型基本算法

HMM需解決的基本問題:

·概率問題。給定一個HMM(λ=(A,S,π))和一個觀測符號序列O,求由該模型得到序列O的概率P(O|λ)。

·解碼問題:給定HMM(λ=(A,S,π))和一個觀測序列O,解碼問題就是尋找最可能的隱狀態序列,即如何選擇產生該觀察序列O的最佳狀態序列Q*,也就是如何最好地解釋符號序列O

·學習問題。給定一組觀測序列樣本集,如何確定其對應的HMM(λ=(A,S,π)),使該模型能夠最好地描述觀測樣本集,也就是該模型的參數能夠使P(O|λ)最大。

(1)前向后向算法

對于可能性問題,引入前向變量和后向變量進行動態規劃求解。

1)前向算法

前向算法的簡單圖解如圖2-8所示。

圖2-8 前向算法示意圖

前向變量αj(t)表示在t時刻狀態為s j,且序列的前t個符號為o1,o2,…,ot的概率,即

根據前向變量的定義,可以得到:

前向變量的迭代公式:

前向算法的流程:

(a)初始化:

ai(0)=πiei(o 1),1≤iN

(b)遞歸:

          t=1,2,3,…,L
              j=1,2,3,…,N
                  a j(t)=ej(ot)∑k(ak(t-1)akj)
              next
          next

(c)終止:

P (o|λ)=∑kαk(L)

(d)算法結束。

2)后向算法

后向算法的簡單圖解如圖2-9所示。

圖2-9 后向算法示意圖

后向變量βj(t)表示在t時刻狀態為s j,且序列從t+1到L個符號為ot+1,ot+2,…,ot+L的概率,即

根據后向變量的定義,可以得到:

后向變量的迭代公式:

后向算法的流程如下:

(a)初始化:

βi (L)=1 1≤iN

(b)遞歸:

          t=L-1,…,1
              j= 1,2,…,N
                βj(t)=∑kαjk ek(ot+ 1)βk(t+1)
              next
        next

(c)終止:

Po λ( | )=∑k k k(1) k(1)

(d)算法結束。前向算法和后向算法的時間復雜度為o(N 2L),空間復雜度為o(NL)。

(2)解碼問題

根據給定的HMM(λ=(A,E,π))和符號序列O=(o1,o2,…,oL),確定一條具有最大概率的狀態序列,即最優路徑Q*

應用Viterbi算法求解最優路徑。定義變量δj(t),它表示由狀態序列q1,q 2,…,qtqt =s j產生出符號序列o1,o2,…,ot的最大概率,即

δj (t)可以通過迭代計算得到:

從初始狀態開始迭代計算δj(t),一直到序列末端,然后再回溯得到最優路徑。

Viterbi算法流程如下:

(a)初始化:

δi(1)=πiei(o 1) 1≤iN

(b)遞歸:

(c)終止:

(d)回溯:

qt *=φt+1(qt+1),t=L-1,L-2,…,1

(e)算法結束。

Viterbi算法提供了有效的計算方法來分析HMM中的觀察序列,以此得到最可能的隱狀態序列,并利用遞歸來減小計算負擔。

(3)訓練問題當只有符號序列o1,o2,…,on是給定的時,才要通過訓練學習估計模型的各項參數,使觀測的符號序列能夠“最好”地被描述,即λ能夠使P(o1,o2,…,on|λ)最大。

如果符號序列是相互獨立的,則它們的聯合分布概率即為它們單個概率的乘積:

如果路徑已知,則要統計各個特定的初始狀態,轉移概率以及產生概率。記某種初始狀態、轉移和出現的次數為πiAijEi(Vk),則初始狀態概率、轉移概率和產生概率的極大似然概率估計值為

如果路徑未知,常采用Baum-Welch算法來實現對HMM的參數估計。

根據前向變量和后向變量的定義,可以得到:

通過對所有位置以及所有序列的求和,可以得到:

再根據式(2-116)~式(2-118)即可以得到模型的各項參數估計。

Baum-Welch算法流程如下:

定義變量γt(i)=P(qt =si|O,λ),ξt(i,j)=P(qt =si,qt+1=s j|O,λ)

=時刻t=1在狀態Si的期望值=γ1(i)。

此時有

,1≤iN,1≤ jN

可以通過證明得出結論:P(o|λ)迭代收斂到局部最佳。

3.隱馬爾可夫模型的C語言實現

下面介紹隱馬爾可夫模型的C語言實現代碼。

動態數組申請的代碼如代碼2-2所示。

代碼2-2動態數組申請代碼

        float *vector(long nl,long nh)
        {
            float *v;
            v=(float *)calloc((unsigned) (nh-nl+1),sizeof(float));
            if (!v) nrerror("allocation failure in vector()");
            return v-nl;
        }
        int *ivector(long nl,long nh)
        {
            int *v;
            v=(int *)calloc((unsigned) (nh-nl+1),sizeof(int));
            if (!v) nrerror("allocation failure in ivector()");
            return v-nl;
        }
        unsigned char *cvector(long nl,long nh)
        {
            unsigned char *v;
            v=(unsigned char *)calloc((unsigned) (nh-nl+1),sizeof(unsigned char));
            if (!v) nrerror("allocation failure in cvector()");
            return v-nl;
        }
        unsigned long *lvector(long nl,long nh)
        {
            unsigned long *v;
            v=(unsigned long *)calloc((unsigned) (nh-nl+1),sizeof(unsigned long));
            if (!v) nrerror("allocation failure in lvector()");
            return v-nl;
        }
        double *dvector(long nl,long nh)
        {
            double *v;
            v=(double *)calloc((unsigned) (nh-nl+1),sizeof(double));
            if (!v) nrerror("allocation failure in dvector()");
            return v-nl;
        }
        float **matrix(long nrl,long nrh,long ncl,long nch)
        {
            int i;
            float **m;
            m=(float **) calloc((unsigned) (nrh-nrl+1),sizeof(float*));
            if (!m) nrerror("allocation failure 1 in matrix()");
            m -= nrl;
            for(i=nrl;i<=nrh;i++) {
                m[i]=(float *) calloc((unsigned) (nch-ncl+1),sizeof(float));
                if (!m[i]) nrerror("allocation failure 2 in matrix()");
                m[i] -= ncl;
            }
            return m;
        }
        double **dmatrix(long nrl,long nrh,long ncl,long nch)
        {
            int i;
            double **m;
            m=(double **) calloc((unsigned) (nrh-nrl+1),sizeof(double*));
            if (!m) nrerror("allocation failure 1 in dmatrix()");
            m -= nrl;
            for(i=nrl;i<=nrh;i++) {
                m[i]=(double *) calloc((unsigned) (nch-ncl+1),sizeof(double));
                if (!m[i]) nrerror("allocation failure 2 in dmatrix()");
                m[i] -= ncl;
            }
            return m;
        }
        int **imatrix(long nrl,long nrh,long ncl,long nch)
        {
            int i,**m;
            m=(int **)calloc((unsigned) (nrh-nrl+1),sizeof(int*));
            if (!m) nrerror("allocation failure 1 in imatrix()");
            m -= nrl;
            for(i=nrl;i<=nrh;i++) {
                m[i]=(int *)calloc((unsigned) (nch-ncl+1),sizeof(int));
                if (!m[i]) nrerror("allocation failure 2 in imatrix()");
                m[i] -= ncl;
            }
            return m;
        }
        unsigned char **cmatrix(long nrl,long nrh,long ncl,long nch)
        {
            int i;
            unsigned char **m;
            m=(unsigned char **) calloc((unsigned) (nrh-nrl+1),sizeof(unsigned char*));
            if (!m) nrerror("allocation failure 1 in dmatrix()");
            m -= nrl;
            for(i=nrl;i<=nrh;i++) {
                m[i]=(unsigned char *) calloc((unsigned) (nch-ncl+1),sizeof(unsigned char));
                if (!m[i]) nrerror("allocation failure 2 in dmatrix()");
                m[i] -= ncl;
            }
            return m;
        }
        //求一個二維矩陣的子矩陣
        float **submatrix(float **a,long oldrl,long oldrh,long oldcl,long oldch,long
            newrl,long newcl)
        {
            int i,j;
            float **m;
            m=(float **) calloc((unsigned) (oldrh-oldrl+1),sizeof(float*));
            if (!m) nrerror("allocation failure in submatrix()");
            m -= newrl;
            for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+oldcl-newcl;
            return m;
        }
        //將一個實數一維數組轉化為二維數組
        float **convert_matrix(float *a,long nrl,long nrh,long ncl,long nch)
        {
            int i,j,nrow,ncol;
            float **m;
            nrow=nrh-nrl+1;
            ncol=nch-ncl+1;
            m = (float **) calloc((unsigned) (nrow),sizeof(float*));
            if (!m) nrerror("allocation failure in convert_matrix()");
            m -= nrl;
            for(i=0,j=nrl;i<=nrow-1;i++,j++) m[j]=a+ncol*i-ncl;
            return m;
        }

前向算法的代碼如代碼2-3所示。

代碼2-3前向算法代碼

        //前向算法估計參數
        void Forward(HMM *phmm,int T,int *O,double **alpha,double *pprob)
        {
            int i,j;
            int t;
            double sum;
            //初始化
            for(i=1;i<=phmm->N;i++)
                alpha[1][i]=phmm->pi[i]*phmm->B[i][O[1]];
            //遞歸
            for(t=1;t<T;t++){
                for(j=1;j<=phmm->N;j++){
                    sum=0.0;
                for(i=1;j<=phmm->N;i++)
                    sum+=alpha[t][i]*(phmm->A[i][j]);
                alpha[t+1][j]=sum*(phmm->B[j][O[t+1]]);
                }
            }
            //終止
            *pprob=0.0;
            for(i=1;i<=phmm->N;i++)
                *pprob+=alpha[T][i];
        }
        void ForwardWithScale(HMM*phmm,int T,int *O,double **alpha,double *scale,double
            *pprob)
        // pprob是對數概率
        {
            int i,j;
            int t;
            double sum;
            //初始化
            scale[1]=0.0;
            for(i=1;i<=phmm->N;i++){
                alpha[1][i]=phmm->pi[i]*(phmm->B[i][O[1]+1]);
                scale[1]+=alpha[1][i];
            }
            for(i=1;i<=phmm->N;i++)
                alpha[1][i]/=scale[1];
            //遞歸
            for(t=1;t<T;t++){
                scale[t+1]=0.0;
                for(j=1;j<=phmm->N;j++){
                    sum=0.0;
                    for(i=1;i<=phmm->N;i++)
                          sum+=alpha[t][i]*(phmm->A[i][j]);
                    alpha[t+1][j]=sum*(phmm->B[j][O[t+1]]);
                    scale[t+1]+=alpha[t+1][j];
                }
                for(j=1;j<=phmm->N;j++)
                    alpha[t+1][j]/=scale[t+1];
            }
            //終止
            *pprob=0.0;
            for(t=1;t<=T;t++)
                *pprob+=log(scale[t]);
        }

后向算法的代碼如代碼2-4所示。

代碼2-4后向算法代碼

        //后向算法估計參數
        void Backward(HMM *phmm, int T, int *O, double **beta, double *pprob)
        {
            int i, j;
            int t;
            double sum;
            //初始化
            for (i = 1; i <= phmm->N; i++)
                beta[T][i] = 1.0;
            //遞歸
            for (t = T -1; t >= 1; t--)
            {
                for (i = 1; i <= phmm->N; i++)
                {
                    sum = 0.0;
                    for (j = 1; j <= phmm->N; j++)
                          sum += phmm->A[i][j] * (phmm->B[j][O[t+1]])*beta[t+1][j];
                    beta[t][i] = sum;
                }
            }
            //終止
            *pprob = 0.0;
            for (i = 1; i <= phmm->N; i++)
                *pprob += beta[1][i];
        }
        void BackwardWithScale(HMM *phmm, int T, int *O, double **beta, double *scale,double *pprob)
        {
            int i, j;
            int t;
            double sum;
            //初始化
            for (i = 1; i <= phmm->N; i++)
                beta[T][i] = 1.0/scale[T];
            //遞歸
            for (t = T -1; t >= 1; t--)
            {
                for (i = 1; i <= phmm->N; i++)
                {
                    sum = 0.0;
                    for (j = 1; j <= phmm->N; j++)
                        sum += phmm->A[i][j] * (phmm->B[j][O[t+1]])*beta[t+1][j];
                    beta[t][i] = sum/scale[t];
                }
            }
        }

Viterbi算法的代碼如代碼2-5所示。

代碼2-5 Viterbi算法代碼

        #define VITHUGE  100000000000.0
        void Viterbi(HMM *phmm, int T, int *O, double **delta, int **psi, int *q, double *pprob)
        {
            int i, j;
            int t;
            int maxvalind;
            double  maxval, val;
            //初始化
            for (i = 1; i <= phmm->N; i++)
            {
                delta[1][i] = phmm->pi[i] * (phmm->B[i][O[1]]);
                psi[1][i] = 0;
            }
            //遞歸
            for (t = 2; t <= T; t++)
            {
                for (j = 1; j <= phmm->N; j++)
                {
                    maxval = 0.0;
                    maxvalind = 1;
                    for (i = 1; i <= phmm->N; i++)
                    {
                          val = delta[t-1][i]*(phmm->A[i][j]);
                          if (val > maxval) {
                              maxval = val;
                              maxvalind = i;
                        }
                    }
                    delta[t][j] = maxval*(phmm->B[j][O[t]]);
                    psi[t][j] = maxvalind;
                }
            }
            //終止
            *pprob = 0.0;
            q[T] = 1;
            for (i = 1; i <= phmm->N; i++)
            {
                if (delta[T][i] > *pprob)
                {
                    *pprob = delta[T][i];
                    q[T] = i;
                }
            }
            for (t = T -1; t >= 1; t--)
                q[t] = psi[t+1][q[t+1]];
      }
      void ViterbiLog(HMM *phmm, int T, int *O, double **delta, int **psi, int *q, double
            *pprob)
      {
            int i, j;
            int t;
            int maxvalind;
            double  maxval, val;
            double  **biot;
            //預處理
            for (i = 1; i <= phmm->N; i++)
                phmm->pi[i] = log(phmm->pi[i]);
            for (i = 1; i <= phmm->N; i++)
                for (j = 1; j <= phmm->N; j++)
                {
                    phmm->A[i][j] = log(phmm->A[i][j]);
                }
            biot = dmatrix(1, phmm->N, 1, T);
            for (i = 1; i <= phmm->N; i++)
                for (t = 1; t <= T; t++)
                {
                    biot[i][t] = log(phmm->B[i][O[t]]);
                }
            //初始化
            for (i = 1; i <= phmm->N; i++)
            {
                delta[1][i] = phmm->pi[i] + biot[i][1];
                psi[1][i] = 0;
            }
            //遞歸
            for (t = 2; t <= T; t++)
            {
                for (j = 1; j <= phmm->N; j++)
                {
                    maxval = -VITHUGE;
                    maxvalind = 1;
                    for (i = 1; i <= phmm->N; i++)
                    {
                        val = delta[t-1][i] + (phmm->A[i][j]);
                        if (val > maxval)
                        {
                              maxval = val;
                              maxvalind = i;
                        }
                    }
                    delta[t][j] = maxval + biot[j][t];
                    psi[t][j] = maxvalind;
                }
            }
            //終止
            *pprob = -VITHUGE;
            q[T] = 1;
            for (i = 1; i <= phmm->N; i++)
            {
                if (delta[T][i] > *pprob)
                {
                    *pprob = delta[T][i];
                    q[T] = i;
                }
            }
            //回朔
            for (t = T -1; t >= 1; t--)
                q[t] = psi[t+1][q[t+1]];
        }

Baum-Welch算法的代碼如代碼2-6所示。

代碼2-6 Baum-Welch算法代碼

        #define DELTA 0.001
        void BaumWelch(HMM *phmm, int T, int *O, double **alpha, double **beta, double**gamma, int *pniter, double *plogprobinit, double *plogprobfinal)
        {
                int i, j, k;
                int t, l = 0;
                double  logprobf, logprobb;
                double  numeratorA, denominatorA;
                double  numeratorB, denominatorB;
                double ***xi, *scale;
                double delta, deltaprev, logprobprev;
                deltaprev = 10e-70;
                xi = AllocXi(T, phmm->N);
                scale = dvector(1, T);
                ForwardWithScale(phmm, T, O, alpha, scale, &logprobf);
                *plogprobinit = logprobf;
                BackwardWithScale(phmm, T, O, beta, scale, &logprobb);
                ComputeGamma(phmm, T, alpha, beta, gamma);
                ComputeXi(phmm, T, O, alpha, beta, xi);
                logprobprev = logprobf;
                do{
         //重新估計t=1時,狀態為i的頻率
         for (i = 1; i <= phmm->N; i++)
phmm->pi[i] = .001 + .999*gamma[1][i];
         //重新估計轉移矩陣和觀察矩陣
         for (i = 1; i <= phmm->N; i++)
         {
denominatorA = 0.0;
for (t = 1; t <= T -1; t++)
denominatorA += gamma[t][i];
for (j = 1; j <= phmm->N; j++)
{
numeratorA = 0.0;
for (t = 1; t <= T -1; t++)
numeratorA += xi[t][i][j];
phmm->A[i][j] = .001 + .999*numeratorA/denominatorA;
}
denominatorB = denominatorA + gamma[T][i];
for (k = 1; k <= phmm->M; k++)
{
numeratorB = 0.0;
for (t = 1; t <= T; t++)
{
if (O[t] == k)
numeratorB += gamma[t][i];
                        }
                        phmm->B[i][k] = .001 +.999*numeratorB/denominatorB;
                    }
                }
                ForwardWithScale(phmm, T, O, alpha, scale, &logprobf);
                BackwardWithScale(phmm, T, O, beta, scale, &logprobb);
                ComputeGamma(phmm, T, alpha, beta, gamma);
                ComputeXi(phmm, T, O, alpha, beta, xi);
                //計算兩次直接的概率差
                delta = logprobf - logprobprev;
                logprobprev = logprobf;
                l++;
            }
            while (delta > DELTA);
            *pniter = l;
            *plogprobfinal = logprobf; /* log P(O|estimated model) */
            FreeXi(xi, T, phmm->N);
            free_dvector(scale, 1, T);
      }
      void ComputeGamma(HMM *phmm, int T, double **alpha, double **beta, double **gamma)
      {
            int i, j;
            int t;
            double  denominator;
            for (t = 1; t <= T; t++)
            {
                denominator = 0.0;
                for (j = 1; j <= phmm->N; j++)
                {
                    gamma[t][j] = alpha[t][j]*beta[t][j];
                    denominator += gamma[t][j];
                }
                for (i = 1; i <= phmm->N; i++)
                    gamma[t][i] = gamma[t][i]/denominator;
            }
      }
      void ComputeXi(HMM* phmm, int T, int *O, double **alpha, double **beta, double ***xi)
      {
            int i, j;
            int t;
            double sum;
            for (t = 1; t <= T -1; t++) {
                sum = 0.0;
                for (i = 1; i <= phmm->N; i++)
                    for (j = 1; j <= phmm->N; j++)
                    {
                        xi[t][i][j] = alpha[t][i]*beta[t+1][j]
                              *(phmm->A[i][j])
                              *(phmm->B[j][O[t+1]]);
                        sum += xi[t][i][j];
                    }
                for (i = 1; i <= phmm->N; i++)
                    for (j = 1; j <= phmm->N; j++)
                        xi[t][i][j]  /= sum;
            }
        }
        double *** AllocXi(int T, int N)
        {
            int t;
            double ***xi;
            xi = (double ***) malloc(T*sizeof(double **));
            xi --;
            for (t = 1; t <= T; t++)
                xi[t] = dmatrix(1, N, 1, N);
            return xi;
        }
        void FreeXi(double *** xi, int T, int N)
        {
            int t;
            for (t = 1; t <= T; t++)
                free_dmatrix(xi[t], 1, N, 1, N);
            xi ++;
            free(xi);
        }
主站蜘蛛池模板: 石泉县| 喀喇| 台州市| 正宁县| 海南省| 连州市| 渭南市| 镇康县| 信阳市| 庐江县| 晋州市| 栖霞市| 郑州市| 且末县| 平南县| 扶余县| 楚雄市| 洪泽县| 永胜县| 巴马| 开化县| 监利县| 太原市| 千阳县| 余姚市| 光泽县| 隆昌县| 壶关县| 信阳市| 富顺县| 拜城县| 阜新市| 信丰县| 随州市| 惠东县| 贵阳市| 资溪县| 临沭县| 育儿| 阿拉善盟| 论坛|