小白給小白詳解維特比算法(一)
初見HMM求解狀態序列用到的維特比算法時,其實內心真的是崩潰的:數不盡的假設和公式,讓人頭昏腦漲的同時也擊潰了自信心。但是仔細研究一下會發現其實問題蠻簡單的,本文就致力於嘗試用更通俗的方式解釋一下維特比算法和它是如何運用在HMM求解狀態序列中的,因爲我也是剛看個差不多……所以如果有不對的地方請各位直接噴別留情!
(以下部分內容參考了吳軍《數學之美》26.1,感謝吳軍博士通俗易懂的講解)
籬笆網絡(Lattice)的最短路徑問題
這個問題長什麼樣子?
嘗試着回答一下這個問題(就算沒辦法回答也請先別關閉這個窗口!):
已知下圖的籬笆網絡,每個節點之間的數字表示相鄰節點之間的距離,舉個例子來說,如果我走 ,這個距離是 。那麼如果讓你從A走到E,最短路徑是哪一條呢?
圖1
這個問題難在哪裏?
好啦不用嘗試啦!顯然大家都知道,通過窮舉的方法是很容易得到最短路徑,可是問題就在於如果窮舉的話,需要的加法次數不用算你也知道實在是太多啦(每條路徑需要計算 次加法,一共 條路徑共 次計算)!像這種沒幾層的籬笆網絡也就罷了,如果每層13個節點,一共12層(然而這個規模對於標註問題來說也依然根本不算什麼),可想而知那個線有多亂,如果僅僅窮舉的話,這個計算量(大致是每條 次計算,一共 條路徑共大約 次計算)怕是超級計算機也吃不消。
爲了不直接給公式讓大家關掉窗口,我們嘗試着一點一點來解決這個問題
簡化成這個模樣,你總能回答了吧?
如下圖,如果我想讓你找到 的最短路徑,就很簡單了吧?
圖2
顯然圖2上只有三條路徑,我們分別計算之後得到最短路徑應該是 這一條,路程是17。
這個時候請把這個問題和上一個問題做一個對比,同時從相反的方向考慮一下上一個問題:如果我最終想要到達E這個節點,其實無論如何都是要經過D這一層的,那麼要是我知道從A到D的每一個節點的最短路徑長度(在剛纔的圖上,我們事實上假設了他們分別是15、14和12),再加上從D的各節點到E的路程就得到了最終路徑的長度。
當然我們還需要再多想一點:如果想要真的按照A到E的最短路徑來走的話,我們其實不會選擇 和 這兩個節點,因爲哪怕(以) (爲例)路徑更短(就算是10),但是加上 的距離之後就變得更長了(這時候也是18)。相應的我們也就明白這樣一個道理:雖然我們最後沒有選擇 和 這兩個節點,但我們是否真的就不需要得到A到他們的最短路徑了呢?答案當然是否定的:從剛纔的例子我們很容易理解,站在D層的角度來看的話,最終的長度是由“歷史”(A到每一個D的長度)和“未來”(每一個D到E的長度)同時決定的,只有同時掌握了“歷史”和“未來”,世界才能在我們手中(旁白:你說什麼呢)!
當然剛纔我們從A到D層的路徑是假設出來的,事實上如果我們真的要解決最開始那個問題,我們需要求解這個問題:
圖3
我們就可以知道,最終的最短路徑到底走的是哪個D。
下一步我們該幹什麼?
我們已經明白了,爲了確定從哪個D到E纔是最短的,我們就必須確定A到每一個D的最短路徑。誒?這個問題是不是從哪裏見過?
其實這就是所謂的“動態規劃”的核心了:子問題幾乎是完全一樣的,我們只要一個一個解決了子問題(的子問題),最終的問題就迎刃而解了。
爲了確定這個問題,我們就需要一個D一個D地去考慮(旁白:???),比如在考慮 的時候不考慮 等等。把問題簡化成這個樣子:
圖4
這個圖是不是和圖1
非常相似?
然後根據圖3
的思想,我們把它簡化成這個樣子:
圖5
這個圖是不是和圖3
非常相似?
問題相應就變成了從A到每一個C的最短路徑是多少?
解決了這個問題的話,我們就可以知道,最終如果走了比如 ,前面路過的到底是哪個C
再進一步遞推:爲了確定到某一個C的最短路徑,我們需要確定的就是這個問題:
圖6
不不不!聰明的你(O__O”…)一定發現了,這根本算不得什麼問題,因爲其實它是這樣的:
圖7
因爲我們已經推到最後一步了!從A到每一個B的路徑事實上都是已知的,我們只需要把他們加在一起去比較大小就可以了!
別忘了我們的目標是什麼:我們事實上是要確認如果最終路過了 ,前面路過的是哪一個B。
從動態規劃的角度考慮,假設我們最終的路徑能路過 而我們已經走到了 ,那麼可以肯定地說,我們來到 的路徑一定是所有來到 路徑裏面最短的那一條(在這個圖上看應該是 )。因爲如果我們沒有走最短的那一條(比如我們錯走成了 ),那麼我們只要用更短的那一條( )去替換也一樣可以走到 。
別倒立了,我們再從頭想一下這個問題!
我們剛纔是從最終的E節點倒推考慮的這個問題,這一次我們從A真正的走一遍。
我們是怎麼走過來的
我們在每一層 都僅僅需要考慮這樣一個問題:
爲了到下一個層的某一個確定的節點 ,我們到底應該從哪一個 出發呢?
一旦確定了從哪一個 出發,其他的 就不在我考慮範圍內了。
就像我們剛纔說的,只要我能找到去 應該從 走,我就不需要考慮其他的到 的路徑了。這樣就大大的減少了路徑總數。
來,我們從A開始走
這個沒啥說的:6,7,5我們順利走到了B層
我們確定到每一個C的節點,應該路過哪一個B:
- :6+5=11, 7+4=11, 5+4=9,最終選擇 ,拋棄其他到 的路徑,長度9
- :12 ,10 ,11, 最終選擇 ,拋棄其他到 的路徑,長度10
- :15,14,11,最終選擇 ,拋棄其他到 的路徑,長度11
我們順利走到了C層,同時得到了到每一個C的最短路程
我們確定到每一個D的節點,應該路過哪一個C
- D_1:9+7=16,10+5=15,11+5=16,最終選擇 ,拋棄其他到 的路徑,長度15
- D_2:17,14,18,最終選擇 ,拋棄其他到 的路徑,長度14
- D_3:12,13,17,最終選擇 ,拋棄其他到 的路徑,長度12
我們順利走到了D層,同時得到了到每一個D的最短路程
我們確定每一個D到最終節點E的路程
顯然最後應該選擇 ,完整路徑爲
路程爲17。拋棄其他所有路徑。
這次我們需要算的次數大約是多少呢?
簡單來說,從A到B有3條路徑,每一條我們需要算到每一個C的最短距離,所以是 ,只要我們確定了每一個到C的最短距離剩下的事情就可以從C開始考慮了:每一個C需要確定到每一個D的最短距離,最終再加上D到E的距離就搞定了( )。
如果換成是12層,每層最多13節點的話,每推一步的計算量最大規模在 (爲了確定從哪一個B來到C,需要對每一個B到每一個C進行一次計算,上述計算每步是 次),而因爲子問題都是一樣的,所以增長是與網絡長度成正比的:推進12次也僅僅乘以12而已。當然計算的方式不可能像這樣簡單乘一乘就搞定,但是可以看得出要比窮舉要簡單得多了。
We are almost there!
這幾乎就是維特比算法了。至於維特比算法更公式化的描述和在隱含馬爾科夫過程中的應用,我們下一文再說!