轉載自——隱馬爾可夫模型 - HMM的三個問題 - 預測問題 - Viterbi算法
原文中的有些過程不是很詳細,我在這裏進行了修改!並且添加了代碼實現部分
目錄
問題: 在觀測序列已知的情況下,狀態序列未知。想找到一個最有可能產生當前觀測序列的狀態序列。
可以用下面兩種辦法來求解這個問題:
1、近似算法
2、Viterbi算法
近似算法
直接在每個時刻t時候最優可能的狀態作爲最終的預測狀態,使用下列公式計算概率值:
遍歷時間t。
優點是計算簡單;而缺點就很明顯了,沒有考慮時序關係,不能保證預測的狀態序列整體上是最優可能的狀態序列,預測的狀態序列可能有實際不發生的部分。
Viterbi算法
它的核心思想和運籌學中的使用動態規劃的思路求解最短路徑問題是一樣的。
其中it表示最短路徑,Ot表示觀測符號,lamda表示模型參數,根據上式可以得出變量sigma的遞推公式:
下面看個例子來深入理解這個公式。
HMM案例-Viterbi
在給定參數π、A、B的時候,得到觀測序列爲“白黑白白黑”,求出最優的隱藏狀態序列。
回溯最優路徑:
最優狀態序列爲:23223
代碼實現
這裏把最原始的viterbi算法做一個簡單的實現——代碼來自github——viterbi
###########################################################################################################
# Viterbi Algorithm for HMM
# dp, time complexity O(mn^2), m is the length of sequence of observation, n is the number of hidden states
##########################################################################################################
# five elements for HMM
states = ('1號盒子', '2號盒子','3號盒子')
observations = ('白球', '黑球', '白球','白球','黑球')
start_probability = {'1號盒子': 0.2, '2號盒子': 0.5,'3號盒子':0.3}
transition_probability = {
'1號盒子': {'1號盒子': 0.5, '2號盒子': 0.4,'3號盒子':0.1},
'2號盒子': {'1號盒子': 0.2, '2號盒子': 0.2,'3號盒子':0.6},
'3號盒子': {'1號盒子': 0.2, '2號盒子': 0.5,'3號盒子':0.3}
}
emission_probability = {
'1號盒子': {'白球': 0.4, '黑球': 0.6},
'2號盒子': {'白球': 0.8, '黑球': 0.2},
'3號盒子': {'白球': 0.5, '黑球': 0.5}
}
def Viterbit(obs, states, s_pro, t_pro, e_pro):
path = {s: [] for s in states} # init path: path[s] represents the path ends with s
curr_pro = {}
for s in states:
curr_pro[s] = s_pro[s] * e_pro[s][obs[0]]
for i in range(1, len(obs)):
last_pro = curr_pro
curr_pro = {}
for curr_state in states:
max_pro, last_sta = max(
((last_pro[last_state] * t_pro[last_state][curr_state] * e_pro[curr_state][obs[i]], last_state)
for last_state in states))
curr_pro[curr_state] = max_pro
path[curr_state].append(last_sta)
# find the final largest probability
max_pro = -1
max_path = None
for s in states:
path[s].append(s)
if curr_pro[s] > max_pro:
max_path = path[s]
max_pro = curr_pro[s]
# print '%s: %s'%(curr_pro[s], path[s]) # different path and their probability
return max_path
if __name__ == '__main__':
obs = ['白球', '黑球', '白球','白球','黑球']
print(Viterbit(obs, states, start_probability, transition_probability, emission_probability))
代碼原理很好理解,主要是要把不同時刻的最優路徑的當前狀態和前一時刻的狀態都保存下來,然後再回溯全局最優路徑。代碼不熟悉的話可以多調試一下!