HMM算法-解碼問題


這篇文章記錄一下解決HMM三大問題的第二個問題的學習過程。回憶一下,第二個問題是什么來着?給定HMM模型\(lambda\)和觀測序列O,求產生這個觀測序列概率最大的狀態序列是什么?把這個問題叫做解碼問題,也是挺貼切的~

求解這個問題,有一個經典的算法,叫做Viterbi算法。Viterbi是個了不起的人物,數學之美第26就是講Viterbi和他的Viterbi算法。

Viterbi算法針對籬笆網絡有向圖(Lattice)的最短路徑問題提出,是一個特殊但是應用最廣泛的動態規划算法。凡是使用HMM的問題都可以用它來解碼,包括數字通信、語音識別、機器翻譯、拼音轉漢字、分詞等。

Viterbi算法類似求解觀測序列概率時使用的forward方法,它也定義了一個變量:

\(\delta=\displaystyle\max_{q_1...q_{t-1}}P(q_1q_2...q_t=s_i,O_1O_2..O_t|\lambda)\)。

這是t時刻狀態\(q_t=s_i\)時的最優狀態序列和前t個觀察序列的聯合概率。\(\delta_i(t)\)和\(\delta_j(t+1)\)的關系是:

\(\delta_j(t+1)=[\displaystyle\max_{1 \leq i \leq N}\delta_i(t)a_{ij}]b_j(O_{t+1})\)

這個和forward方法非常的類似,forward方法是從t轉移到t+1時的所有N個可能的概率加和,而vebiter是這N個狀態的求最大。

整個解碼過程可以概括為:

 

雖然現在看來維特比算法並不是很復雜,但是當時提出來可是一件非常了不起的事!有些真理就是一旦發現就是如此簡單,但是發現它,可能要窮盡幾代人的努力。

下面用viterbi解決一個小的問題,問題是這樣的:假設手里有三個不同的骰子。第一個骰子是我們平常見的骰子(稱這個骰子為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。 然后我們擲骰子,得到一個數字,1,2,3,4,5,6,7,8中的一個。不停的重復上 述過程,我們會得到一串數字,每個數字都是1,2,3,4,5,6,7,8中的一個。例 如我們可能得到這么一串數字(擲骰子10次):1 6 3 5 2 7 3 5 2 4 。現在求每次拋出去的是哪個骰子?

python 代碼如下

def verbiter(S,K,A,B,pi,Obv):
    '''
    求解出現Obv概率最大的狀態序列
    :param S: array,狀態符號集合
    :param K: array,觀測符號集合
    :param A: matrix,轉移矩陣
    :param B: matrix,發射矩陣
    :param pi: 初始矩陣
    :param Obv: 觀測序列
    :return: 狀態序列
    '''
    N = len(S)
    M = len(K)
    Delta = []
    #初始化
    start_node = K.index(Obv[0])
    fst_row = [{"prob":pi[i]*B[i][start_node],"pre_node":-1} for i in range(0,N)]
    Delta.append(fst_row)


    for t in range(1,M):
        row = []
        for j in range(0,N):
            ob = K.index(Obv[t])
            ob_prop = B[j][ob]
            trans = [Delta[t-1][i]["prob"]*A[i][j] for i in range(0,N)]
            maxindex, maxvalue = max(enumerate(trans), key=lambda x: x[1])
            row.append({"prob":maxvalue*ob_prop,"pre_node":maxindex})
        Delta.append(row)

    #T時刻最大概率狀態
    max_s,max_prob = max(enumerate(Delta[M-1]),key=lambda x:x[1]["prob"])

    #最優路徑
    cur_node = max_s
    path = [max_s]
    for t in range(M-1,1,-1):
        pre_node = Delta[t][cur_node]["pre_node"]
        path.append(pre_node)
        cur_node = pre_node

    path.reverse()
    print ','.join(S[i] for i in path)
    print max_prob["prob"]



if __name__ == "__main__":
    S = ["D6","D4","D8"]
    K = [1,2,3,4,5,6,7,8]
    A = [[1/3.0,1/3.0,1/3.0],
         [1/3.0,1/3.0,1/3.0],
         [1/3.0,1/3.0,1/3.0]]
    B = [[1/6.0,1/6.0,1/6.0,1/6.0,1/6.0,1/6.0,0,0],
         [1/4.0,1/4.0,1/4.0,1/4.0,0,0,0,0],
         [1/8.0,1/8.0,1/8.0,1/8.0,1/8.0,1/8.0,1/8.0,1/8.0]]
    pi = [1/3.0,1/3.0,1/3.0]
    Obv = [1,6,3,5,2,7,3,5,2,4]
    verbiter(S,K,A,B,pi,Obv)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM