維特比算法
維特比算法(英語:Viterbi algorithm)是一種動態規劃算法。它用於尋找最有可能產生觀測事件序列的維特比路徑——隱含狀態序列,特別是在馬爾可夫信息源上下文和隱馬爾可夫模型中。
術語「維特比路徑」和「維特比算法」也被用於尋找觀察結果最有可能解釋相關的動態規劃算法。例如在統計句法分析中動態規劃算法可以被用於發現最可能的上下文無關的派生(解析)的字符串,有時被稱為「維特比分析」。
維特比算法由安德魯·維特比於1967年提出,用於在數字通信鏈路中解卷積以消除噪音。[1] 此算法被廣泛應用於CDMA和GSM數字蜂窩網絡、撥號調製解調器、衛星、深空通信和802.11無線網絡中解卷積碼。現今也被常常用於語音識別、關鍵字識別、計算語言學和生物信息學中。例如在語音(語音識別)中,聲音信號做為觀察到的事件序列,而文本字符串,被看作是隱含的產生聲音信號的原因,因此可對聲音信號應用維特比算法尋找最有可能的文本字符串。
算法
假設給定隱式馬爾可夫模型(HMM)狀態空間 ,共有k個狀態,初始狀態 的概率為 ,從狀態 到狀態 的轉移概率為 。 令觀察到的輸出為 。 產生觀察結果的最有可能的狀態序列 由遞推關係給出:[2]
此處 是前 個最終狀態為 的觀測結果最有可能對應的狀態序列的概率。 通過保存向後指針記住在第二個等式中用到的狀態 可以獲得維特比路徑。聲明一個函數 ,它返回若 時計算 用到的 值 或若 時的 . 這樣:
這裡我們使用了arg max的標準定義 算法複雜度為
例子
想象一個鄉村診所。村民有着非常理想化的特性,要麼健康要麼發燒。他們只有問診所的醫生的才能知道是否發燒。 聰明的醫生通過詢問病人的感覺診斷他們是否發燒。村民只回答他們感覺正常、頭暈或冷。
假設一個病人每天來到診所並告訴醫生他的感覺。醫生相信病人的健康狀況如同一個離散馬爾可夫鏈。病人的狀態有兩種「健康」和「發燒」,但醫生不能直接觀察到,這意味着狀態對他是「隱含」的。每天病人會告訴醫生自己有以下幾種由他的健康狀態決定的感覺的一種:正常、冷或頭暈。這些是觀察結果。 整個系統為一個隱馬爾可夫模型(HMM)。
醫生知道村民的總體健康狀況,還知道發燒和沒發燒的病人通常會抱怨什麼症狀。 換句話說,醫生知道隱馬爾可夫模型的參數。 這可以用Python語言表示如下:
states = ('Healthy', 'Fever')
observations = ('normal', 'cold', 'dizzy')
start_probability = {'Healthy': 0.6, 'Fever': 0.4}
transition_probability = {
'Healthy' : {'Healthy': 0.7, 'Fever': 0.3},
'Fever' : {'Healthy': 0.4, 'Fever': 0.6},
}
emission_probability = {
'Healthy' : {'normal': 0.5, 'cold': 0.4, 'dizzy': 0.1},
'Fever' : {'normal': 0.1, 'cold': 0.3, 'dizzy': 0.6},
}
在這段代碼中, 起始概率start_probability
表示病人第一次到訪時醫生認為其所處的HMM狀態,他唯一知道的是病人傾向於是健康的。這裡用到的特定概率分布不是均衡的,如轉移概率大約是{'Healthy': 0.57, 'Fever': 0.43}
。 轉移概率transition_probability
表示潛在的馬爾可夫鏈中健康狀態的變化。在這個例子中,當天健康的病人僅有30%的機會第二天會發燒。放射概率emission_probability
表示每天病人感覺的可能性。假如他是健康的,50%會感覺正常。如果他發燒了,有60%的可能感覺到頭暈。
病人連續三天看醫生,醫生發現第一天他感覺正常,第二天感覺冷,第三天感覺頭暈。 於是醫生產生了一個問題:怎樣的健康狀態序列最能夠解釋這些觀察結果。維特比算法解答了這個問題。
def viterbi(obs, states, start_p, trans_p, emit_p):
V = [{}]
path = {}
# Initialize
for st in states:
V[0][st] = start_p[st] * emit_p[st][obs[0]]
path[st] = [st]
# Run Viterbi when t > 0
for t in range(1,len(obs)):
V.append({})
newpath = {}
for curr_st in states:
paths_to_curr_st = []
for prev_st in states:
paths_to_curr_st.append((V[t-1][prev_st] * trans_p[prev_st][curr_st] * emit_p[curr_st][obs[t]], prev_st))
curr_prob, prev_state = max(paths_to_curr_st)
V[t][curr_st] = curr_prob
newpath[curr_st] = path[prev_state] + [curr_st]
# No need to keep the old paths
path = newpath
for line in dptable(V):
print(line)
prob, end_state = max([(V[-1][st], st) for st in states])
return prob, path[end_state]
def dptable(V):
# Print a table of steps from dictionary
yield ' ' * 4 + ' '.join(states)
for t in range(len(V)):
yield '{} '.format(t) + ' '.join(['{:.4f}'.format(V[t][state]) for state in V[0]])
函數viterbi
具有以下參數: obs
為觀察結果序列, 例如 ['normal', 'cold', 'dizzy']
; states
為一組隱含狀態; start_p
為起始狀態概率; trans_p
為轉移概率; 而 emit_p
為放射概率。 為了簡化代碼,我們假設觀察序列 obs
非空且 trans_p[i][j]
和 emit_p[i][j]
對所有狀態 i,j 有定義。
在運行的例子中正向/維特比算法使用如下:
def example():
return viterbi(observations,
states,
start_probability,
transition_probability,
emission_probability)
print(example())
維特比算法揭示了觀察結果 ['normal', 'cold', 'dizzy']
最有可能由狀態序列 ['Healthy', 'Healthy', 'Fever']
產生。 換句話說,對於觀察到的活動, 病人第一天感到正常,第二天感到冷時都是健康的,而第三天發燒了。
維特比算法的計算過程可以直觀地由格圖表示。 維特比路徑本質上是穿過格式結構的最長路徑。 診所例子的格式結構如下, 黑色加粗的是維特比路徑:
在實現維特比算法時需注意許多編程語言使用浮點數計算,當 p 很小時可能會導致結果算術下溢。 避免這一問題的常用技巧是在整個計算過程中使用對數概率,在對數系統中也使用了同樣的技巧。 當算法結束時,可以通過適當的冪運算獲得精確結果。
偽代碼
首先是一些問題必要的設置。 設觀察值空間為 、 狀態空間為 、 觀察值序列為 , 為 轉移矩陣,其中 為從狀態 轉移到 的轉移概率、 為 放射矩陣(emission matrix),其中 為在狀態 觀察到 的概率、 大小為 的初始概率數組 , 為 的概率。 我們稱路徑 為生成觀察值 的狀態序列。
在這個動態規劃問題中, 我們構造了兩個大小為 的二維表 。 的每個元素, ,保存生成 時最有可能的路徑 , 的概率。 的每個元素, ,保存最有可能路徑 , 的 。
我們按 增序填充兩個表 。
- ,
- 輸入
- 觀察空間 ,
- 狀態 ,
- 觀察序列 若在 時間觀察值為 ,則 ,
- 大小為 的轉移矩陣 , 為從狀態 到 的轉移概率,
- 大小為 的放射矩陣 , 為狀態 觀察到 的概率,
- 大小為 的初始概率數組 , 為 的概率,
- 輸出
- 最有可能的隱含狀態序列
function VITERBI( O, S, π, Y, A, B ) : X for each state si do T1[i,1] ← πi·Biy1 T2[i,1] ← 0 end for for i ← 2,3,...,T do for each state sj do end for end for xT ← szT for i ← T,T-1,...,2 do zi-1 ← T2[zi,i] xi-1 ← szi-1 end for return X end function
使用動態規劃的算法
注釋
- ^ 29 Apr 2005, G. David Forney Jr: The Viterbi Algorithm: A Personal History. [2012-11-23]. (原始內容存檔於2016-06-02).
- ^ Xing E, slide 11
參考資料
- Viterbi AJ. Error bounds for convolutional codes and an asymptotically optimum decoding algorithm. IEEE Transactions on Information Theory. April 1967, 13 (2): 260–269 [2012-11-23]. doi:10.1109/TIT.1967.1054010. (原始內容存檔於2020-07-29). (note: the Viterbi decoding algorithm is described in section IV.) Subscription required.