【筆記】隱馬爾可夫

隱馬爾可夫模型

核心結構

  1. 狀態序列(隱型,不可見) I

  2. 觀測序列 O

  3. 初始概率分佈 pi

  4. 狀態轉移概率分佈 A

  5. 觀測概率分佈 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, ]
                ...
            }
    

要解決的問題

  1. 概率計算問題

    • 描述:已知(pi, A, B),求給定O出現的概率。
    • 解決方案:
      1. 前向算法
        • 前向概率:對於第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)
      2. 後向算法
        • 後向概率:對於第t層的狀態i,出現(O[t + 1], O[t + 2], ..., O[N])的概率,考慮對第t + 1層的所有狀態的影響。
        • 後向算法同前向算法相同,區別在於前者是向後看,後者是向前看。不再贅述。
  2. 學習問題

    • 描述: 已知O,求參數(pi, A, B),使得在這組參數下出現O的概率最高。
    • 解決方案
      • 監督學習:傳統概率計算
      • 非監督學習:Baum-Welch算法(EM算法)
  3. 預測問題

    • 描述:已知(pi, A, B, O),求最有可能對應的狀態序列(隱狀態)。
    • 解決方案:
      1. 近似算法
        窮舉所有可能的序列,對每一種計算概率,選出最大的。
      2. 維特比算法
        是一種動態規劃的思想,下一層最大值依賴上一層的最大值,且不會影響上一層(下一層由上一層遞推得出),因此“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())
      
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章