隱馬爾可夫模型
核心結構
-
狀態序列(隱型,不可見) I
-
觀測序列 O
-
初始概率分佈 pi
-
狀態轉移概率分佈 A
-
觀測概率分佈 B
假設有三個骰子,分別是四面、六面、八面。現在每次隨機選擇一枚骰子投擲,進行十次,得到的一串數字即爲觀測序列,對應的每次選擇的骰子爲狀態序列。
因爲是隨機選,所以三個骰子被選的概率相同,即初始概率分佈爲[1/3, 1/3, 1/3]
因爲每次選擇是獨立的,所以若第一次選取第一枚骰子,下一次選取三枚骰子的概率分別爲[1/3, 1/3, 1/3],假設行向量分別對應這三枚骰子,則
A = { [1/3, 1/3, 1/3], [1/3, 1/3, 1/3], [1/3, 1/3, 1/3], }
對於一枚骰子,投擲結果的概率對應觀測概率分佈,即
B = { [1/4, 1/4, 1/4, 1/4], [1/6, 1/6, 1/6, 1/6, 1/6, 1/6, ] ... }
要解決的問題
-
概率計算問題
- 描述:已知(pi, A, B),求給定O出現的概率。
- 解決方案:
- 前向算法
- 前向概率:對於第t層的狀態i,出現(O[1], O[2], ..., O[t])的概率,考慮t-1層的所有狀態的影響。
- 同維特比算法思想類似,屬於動態規劃。下一層的狀態由上一層的推出,區別在於求上一層的累加值而非最大值。“dpT[j]”代表觀測序列爲{o1, o2, ..., oT}且此時隱狀態爲I[j]的概率,所以dpT[j] * A[j][i]代表上式又追加到了隱狀態I[i]。進而,遍歷(j = 1, 2, .. , N)得到第上一層所有的隱狀態跳轉到下一層的隱狀態i的概率和。再進而,對上述求和式乘以Bi(o[T+1]),就得到該層(t + 1)的狀態i觀測到狀態O[t + 1]的概率。
此時時間複雜度是O(T * N),計算完了第一個隱狀態。一共有N種隱狀態,所以全部計算加和的時間複雜度是O(T * N ^ 2)
- 後向算法
- 後向概率:對於第t層的狀態i,出現(O[t + 1], O[t + 2], ..., O[N])的概率,考慮對第t + 1層的所有狀態的影響。
- 後向算法同前向算法相同,區別在於前者是向後看,後者是向前看。不再贅述。
- 前向算法
-
學習問題
- 描述: 已知O,求參數(pi, A, B),使得在這組參數下出現O的概率最高。
- 解決方案
- 監督學習:傳統概率計算
- 非監督學習:Baum-Welch算法(EM算法)
-
預測問題
- 描述:已知(pi, A, B, O),求最有可能對應的狀態序列(隱狀態)。
- 解決方案:
- 近似算法
窮舉所有可能的序列,對每一種計算概率,選出最大的。 - 維特比算法
是一種動態規劃的思想,下一層最大值依賴上一層的最大值,且不會影響上一層(下一層由上一層遞推得出),因此“dp[i]”代表在第i層選擇的節點,可使從首層到第i層的概率最大。
下面是借鑑的@hankcs的代碼實現。
""" 維特比算法思想 長度爲n的觀測序列,對應一個[len(state), n]維的矩陣,每一個列向量是一層。 假設當前處理到第i層,則要: 1. 借用第i-1層的結果,得到該層最大的概率。 2. 記錄路徑。記錄 使第i層第j個狀態概率最大的 第i-1層的狀態q。即原先的path[q]加上q """ states = ('Rainy', 'Sunny') observations = ('walk', 'shop', 'clean') start_probability = {'Rainy': 0.6, 'Sunny': 0.4} transition_probability = { 'Rainy': {'Rainy': 0.7, 'Sunny': 0.3}, 'Sunny': {'Rainy': 0.4, 'Sunny': 0.6}, } emission_probability = { 'Rainy': {'walk': 0.1, 'shop': 0.4, 'clean': 0.5}, 'Sunny': {'walk': 0.6, 'shop': 0.3, 'clean': 0.1}, } def viterbi(obs, states, start_p, trans_p, emit_p): """ :param obs:觀測序列 :param states:隱狀態 :param start_p:初始概率(隱狀態) :param trans_p:轉移概率(隱狀態) :param emit_p: 發射概率 (隱狀態表現爲顯狀態的概率) :return: """ # 路徑概率表 V[時間][隱狀態] = 概率 V = [{}] # 一箇中間變量,代表當前狀態是哪個隱狀態 path = {} # 初始化初始狀態 (t == 0) for y in states: V[0][y] = start_p[y] * emit_p[y][obs[0]] path[y] = [y] print(path) # 對 t > 0 跑一遍維特比算法 for t in range(1, len(obs)): V.append({}) newpath = {} for y in states: # max函數中第二個參數y0,存儲返回的隱狀態state # 概率 隱狀態 = 前狀態是y0的概率 * y0轉移到y的概率 * y表現爲當前狀態的概率 (prob, state) = max([(V[t - 1][y0] * trans_p[y0][y] * emit_p[y][obs[t]], y0) for y0 in states]) # 記錄最大概率 V[t][y] = prob # 記錄路徑 newpath[y] = path[state] + [y] # 更新路徑 path = newpath # 輸出到達第i層的隱狀態路徑 # print(newpath) (prob, state) = max([(V[len(obs) - 1][y], y) for y in states]) return (prob, path[state]) def example(): return viterbi(observations, states, start_probability, transition_probability, emission_probability) if __name__ == "__main__": print(example())
- 近似算法