1. 程式人生 > >HMM-維特比演算法理解與實現(python)

HMM-維特比演算法理解與實現(python)

[HMM-前向後向演算法理解與實現(python)](https://www.cnblogs.com/gongyanzh/p/12880387.html) [HMM-維特比演算法理解與實現(python)]() --- ### 解碼問題 - 給定觀測序列 $O=O_1O_2...O_T$,模型 $\lambda (A,B,\pi)$,找到最可能的狀態序列 $I^∗=\{i^∗_1,i^∗_2,...i^∗_T\}$ #### 近似演算法 - 在每個時刻 $t$ 選擇最可能的狀態,得到對應的狀態序列 根據[HMM-前向後向演算法](https://www.cnblogs.com/gongyanzh/p/12880387.html)計算時刻 $t$ 處於狀態 $i^*_t$ 的概率: $$ i^∗_t=argmax[\gamma_t(i)],t=1,2,...T\\ \gamma_t(i) = \frac{\alpha_{i}(t) \beta_{i}(t)}{\sum_{i=1}^{N} \alpha_{i}(t) \beta_{i}(t)} $$ 但是無法保證得到的解是全域性最優解 #### 維特比演算法 維特比演算法的基礎可以概括為下面三點(來源於吳軍:數學之美): 1. 如果概率最大的路徑經過籬笆網路的某點,則從起始點到該點的子路徑也一定是從開始到該點路徑中概率最大的。 2. 假定第 `t` 時刻有 `k` 個狀態,從開始到 `t` 時刻的 `k` 個狀態有 `k` 條最短路徑,而最終的最短路徑必然經過其中的一條。 3. 根據上述性質,在計算第 `t+1` 時刻的最短路徑時,只需要考慮從開始到當前的`k`個狀態值的最短路徑和當前狀態值到第 `t+1` 時刻的最短路徑即可。如求`t=3`時的最短路徑,等於求`t=2`時,從起點到當前時刻的所有狀態結點的最短路徑加上`t=2`到`t=3`的各節點的最短路徑。 ![image-20200512214719644](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200512214719644.png) **通俗理解維特比演算法**,對上面三點加深理解 假如你從S和E之間找一條最短的路徑,最簡單的方法就是列出所有可能的路徑 ($O(T^N)$),選出最小的,顯然時間複雜度太高。怎麼辦?(摘自[3]) 使用維特比演算法 ![image-20200512223610958](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200512223610958.png) S到A列的路徑有三種可能:`S-A1,S-A2,S-A3`,如下圖 ![image-20200513202915071](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513202915071.png) `S-A1,S-A2,S-A3` 中必定有一個屬於全域性最短路徑。繼續往右,到了B列 對B1: ![image-20200513202742395](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513202742395.png) 會產生3條路徑: ``` S-A1-B1,S-A2-B1,S-A3-B1 ``` 假設`S-A3-B1`是最短的一條,刪掉其他兩條。得到 ![image-20200513203551041](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513203551041.png) 對B2: ![image-20200513203743119](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513203743119.png) 會產生3條路徑: ``` S-A1-B2,S-A2-B2,S-A3-B2 ``` 假設`S-A1-B2`是最短的一條,刪掉其他兩條。得到 ![image-20200513203847969](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513203847969.png) 對B3: ![image-20200513204015153](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513204015153.png) 會產生3條路徑: ``` S-A1-B3,S-A2-B3,S-A3-B3 ``` 假設`S-A2-B3`是最短的一條,刪掉其他兩條。得到 ![image-20200513204233084](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513204233084.png) 現在我們看看對B列的每個節點有哪些,回顧維特比演算法第二點 > 假定第 `t` 時刻有 `k` 個狀態,從開始到 `t` 時刻的 `k` 個狀態有 `k` 條最短路徑,而最終的最短路徑必然經過其中的一條 B列有三個節點,所以會有三條最短路徑,最終的最短路徑一定會經過其中一條。如下圖 ![image-20200513204552391](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513204552391.png) 同理,對C列,會得到三條最短路徑,如下圖 ![image-20200513205546888](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513205546888.png) 到目前為止,仍然無法確定哪條屬於全域性最短。最後,我們繼續看E節點 ![image-20200513205723395](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513205723395.png) 最終發現最短路徑為`S-A1-B2-C3-E` **數學描述** 在上述過程中,對每一列(每個時刻)會得到對應狀態數的最短路徑。在數學上如何表達?記錄路徑的**最大概率值** $ \delta_t(i)$ 和對應路徑**經過的節點** $\psi_t(i)$。 定義在時刻 $t$ 狀態為 $i$ 的所有單條路徑中概率最大值為 $$ \delta_{t}(i)=\max _{i_{1}, i_{2}, \ldots, i_{t-1}} P\left(i_{t}=i, i_{t-1}, \ldots, i_{1}, o_{t}, \ldots, o_{1} | \lambda\right), i=1,2, \ldots, N $$ 遞推公式 $$ \begin{aligned} \delta_{t+1}(i) &=\max _{i_{1}, i_{2}, \ldots, i_{t}} P\left(i_{t+1}=i, i_{t}, \ldots, i_{1}, o_{t+1}, \ldots, o_{1} | \lambda\right) \\ &=\max _{1 \leq j \leq N}\left[\delta_{t}(j) a_{j i}\right] b_{i}\left(o_{t+1}\right), i=1,2, \ldots, N ; t=1,2, \ldots, T-1 \end{aligned} $$ 定義在時刻 $t$ 狀態為 $i$ 的所有單條路徑中,概率最大路徑的第 $t - 1$ 個節點為 $$ \psi_{t}(i)=\arg \max _{1 \leq j \leq N}\left[\delta_{t-1}(j) a_{j i}\right], i=1,2, \ldots, N $$ 維特比演算法步驟: ​ step1:初始化 $$ \begin{aligned}&\delta_{1}(i)=\pi_{i} b_{i}\left(o_{1}\right), i=1,2, \ldots, N\\&\psi_{1}(i)=0, i=1,2, \ldots, N\\\end{aligned} $$ ​ step2:遞推,對 $t=2,3,...,T$ $$ \delta_{t}(i)=\max _{1 \leq j \leq N}\left[\delta_{t-1}(j) a_{j i}\right] b_{i}\left(o_{t}\right), i=1,2, \ldots, N \\\psi_{t}(i)=\arg \max _{1 \leq j \leq N}\left[\delta_{t-1}(j) a_{j i}\right], i=1,2, \ldots, N \\ $$ ​ step3:計算時刻 $T$ 最大的 $ \delta _T(i)$,即為最可能隱藏狀態序列出現的概率。計算時刻$T$最大的$\psi_T(i)$,即為時刻$T$最可能的隱藏狀態。 $$ P^{*}=\max _{1 \leq i \leq N} \delta_{T}(i) \quad i_{T}^{*}=\arg \max _{1 \leq i \leq N} \delta_{T}(i) $$ ​ step4:最優路徑回溯,對$t=T-1,...,1$ $$ i_{t}^{*}=\psi_{t+1}\left(i_{t+1}^{*}\right)\\I^*=(i_{1}^{*},i_{2}^{*},...,i_{T}^{*}) $$ **程式碼實現** 假設從三個 袋子 `{1,2,3}`中 取出 4 個球 `O={red,white,red,white}`,模型引數$\lambda = (A,B,\pi)$ 如下,計算狀態序列,即取出的球來自哪個袋子 ``` #狀態 1 2 3 A = [[0.5,0.2,0.3], [0.3,0.5,0.2], [0.2,0.3,0.5]] pi = [0.2,0.4,0.4] # red white B = [[0.5,0.5], [0.4,0.6], [0.7,0.3]] ``` ```python def hmm_viterbi(A,B,pi,O): T = len(O) N = len(A[0]) delta = [[0]*N for _ in range(T)] psi = [[0]*N for _ in range(T)] #step1: init for i in range(N): delta[0][i] = pi[i]*B[i][O[0]] psi[0][i] = 0 #step2: iter for t in range(1,T): for i in range(N): temp,maxindex = 0,0 for j in range(N): res = delta[t-1][j]*A[j][i] if res>temp: temp = res maxindex = j delta[t][i] = temp*B[i][O[t]]#delta psi[t][i] = maxindex #step3: end p = max(delta[-1]) for i in range(N): if delta[-1][i] == p: i_T = i #step4:backtrack path = [0]*T i_t = i_T for t in reversed(range(T-1)): i_t = psi[t+1][i_t] path[t] = i_t path[-1] = i_T return delta,psi,path A = [[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]] B = [[0.5,0.5],[0.4,0.6],[0.7,0.3]] pi = [0.2,0.4,0.4] O = [0,1,0,1] hmm_viterbi(A,B,pi,O) ``` 結果 ![image-20200513231008945](https://gitee.com/gongyanzh/blogpic/raw/master/pictures/image-20200513231008945.png) references: [1]https://www.cnblogs.com/kaituorensheng/archive/2012/12/04/2802140.html [2] https://blog.csdn.net/hudashi/java/article/details/87875259 [3] https://www.zhihu.com/question/