HMM前向算法——基於比例因子(java實現)

package hmm.model;

import hmm.bean.HMMHelper;
import util.TCMMath;

/**
 * 【改進後的前向算法】
 * 【帶比例因子修正的前向算法 :計算觀察序列的概率 】
 * 【注意】 改進後,就沒必要使用後向算法來求觀測序列概率了,直接利用中間比例因子scale就可以求得,在改進的前向算法中以寫logProb()函數
 * 
 * 前向算法:
 * 目的:
 * 1、先計算前向變量矩陣
 * 2、再用前向變量矩陣 來 計算一個觀測序列的概率
 */
public class ForwardWithScale extends HMM{
    public int[] O;//觀測序列observe//如yellow red blue yellow green 這些在enum Color {red,yellow,blue,green }的索引位置

    public double[][] alpha; //前向變量矩陣
    
    public double[] scale;//用於修正的比例因子——從帶比例因子修正後的前向算法計算
    
    /**
     * flag 表示 A和B是否是自然對數化(lnX)  true: A和B自然對數化後傳進來  false: A和B未自然對數化
     */
    public ForwardWithScale(double[][] A, double[][] B, double[] PI, int[] O, boolean flag){
        super(A, B, PI, flag);
        this.O=O;
    }
    
    public ForwardWithScale(HMM hmm, int[] O){
        super(hmm);
        this.O=O;
    }
    
    /**
     * 【計算前向變量矩陣】
     * 在時間 t 的條件下,hmm輸出觀察序列O(1)O(2)...O(t)且該時間t下的隱藏狀態爲s_i(第i個隱藏狀態,共N種隱藏狀態)的概率
     * alpha[ t ][ i ] = alpha_t( i ) = log(P(O(1)O(2)...O(t), q_t=s_i | λ))
     */
    public void CalculateForeMatrix(){
        int T = O.length;
        alpha = new double[ T ][ N ];//每一時刻(每行)上 可能出現的多個狀態的發生的前向變量概率
        scale = new double[ T ];//【比例因子】
        scale[ 0 ] =Double.NEGATIVE_INFINITY;
        //1、初始化,計算初始時刻(直覺上的第1時刻)所有狀態的局部概率
        for (int i = 0; i < N ; i++){
            alpha[ 0 ][ i ] = logPI[ i ] + logB[ i ][ O[ 0 ] ];
            /*******************增加部分*****************/
            scale[ 0 ] =  TCMMath.logplus( scale[ 0 ] , alpha[ 0 ][ i ]);
        }
        /*******************增加部分*****************/
        for(int i=0; i< N; i++){//利用比例因子歸一化
            alpha[ 0 ][ i ] -= scale [ 0 ];
        }
        
        //2、歸納,遞歸計算每個時間點的局部概率
        for (int t = 1; t < T; t++){//從(直覺上的第2時刻)即t=1(下標從0開始)觀測值算起——第時間t下開始循環
            scale[ t ] = Double.NEGATIVE_INFINITY;
            for (int j = 0; j < N; j++) {//
                double sum = Double.NEGATIVE_INFINITY; // = log(0)
                for (int i = 0; i < N; i++){//到第 i 種隱狀態下的累計概率
                    //sum+=alpha[ t-1 ][ i ] * A[ i ] [ j ] 
                    sum = TCMMath.logplus( sum, alpha[t - 1][ i ] + logA[ i ][ j ]);
                }
                //alpha[ t ][ j ] = 【t-1時刻 所有 隱藏狀態 i 】到達 【t時刻 隱藏狀態 j】並【t時刻顯示出O( t )】的前向變量概率
                //alpha[ t ] [ j ] = ∑ ( alpha[ t-1 ][ i ] * A[ i ] [ j ] ) *B[ j ] [ O(t) ]  求和符號表示 1<=i <=N
                alpha[ t ][ j ] = logB[ j ][ O[ t ] ] + sum;//在 【t 時刻】 下 輸出觀察序列 O1O2……Ot(已知觀測序列的局部) 且位於第 j 種隱藏狀態發生的概率
                /*******************增加部分*****************/
                scale[ t ] =  TCMMath.logplus( scale[ t ] , alpha[ t ][ j ]);//比例因子
            }
            /*******************增加部分*****************/
            for (int j = 0; j < N; j++) {//利用比例因子歸一化
                alpha[ t ][ j ] -= scale [ t ];
            }
        }
    }
    
    /**
     * 【計算觀測序列的概率】——返回的是自然對數
     */
    public double logProb() {
        //3、終止,求概率就直接使用比例因子求得
        int T = O.length;
        double sum = 0; // = log(1)
        for(int t=0; t< T; t++){
            sum +=  scale[ t ];
        }
        return sum;
    }
    
    /**
     * 【計算觀測序列的概率】——前提是先計算前向變量矩陣
     * P( O | μ ) = ∑alpha_T( i ) (求和上界N,求和下界i=1)
     * @return 返回的結果是概率的自然對數
     * 計算 t=T 時刻下輸出觀察序列 O0……OT(已經觀測序列的局部)且位於第 T 狀態下發生的概率
    public double logProb() {
        //3.終止,觀察序列的概率等於最終時刻( T )所有局部概率之和
        double sum = Double.NEGATIVE_INFINITY; // = log(0)
        int T = O.length;
        for (int i = 0; i < N; i++){
            sum = TCMMath.logplus(sum, alpha[ T-1 ][ i ]);//下標從0開始
        }
        return sum;
    }
    */
    
    /**
     * 打印前向變量矩陣
     */
    public void print() {
        for (int j = 0; j < N; j++) {
            for (int i = 0; i < alpha.length; i++){
                System.out.print(HMMHelper.fmtlog(alpha[ i ][ j ]));
            }
            System.out.println();
        }
    }

Reference

宗成慶.統計自然語言處理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章