メインコンテンツへスキップ
見出し画像

書記が数学やるだけ#950 隠れマルコフモデル

    隠れマルコフモデルの理論について。


    問題

    画像


    画像


    説明

    隠れマルコフモデル(hidden Markov model; HMM)は、確率モデルのひとつであり、観測されない(隠れた)状態をもつマルコフ過程である。これは状態空間モデルの一種と言える。

    画像


    画像


    画像


    隠れマルコフモデル(HMM)の基盤アルゴリズムには以下の3つがある。

    1. 前向きアルゴリズム(Forward Algorithm)

    目的:モデルの評価(Evaluation)

    モデル(状態遷移の確率や、そこからデータが出る確率)がすでに分かっている前提で、「このモデルから、今目の前にある観測データが出力される確率はどれくらいか?」を計算する。

    2. ビタビアルゴリズム(Viterbi Algorithm)

    目的:状態の復号(Decoding)

    観測データだけが見えている状態で、「その裏で、内部状態はどういう順番で遷移していたのか?」を計算する。

    3. バウム・ウェルチアルゴリズム(Baum-Welch Algorithm)

    目的:モデルの学習(Learning)

    手元にある大量の観測データだけを頼りに、「このデータが一番きれいに説明できるような、最適な確率の組み合わせ(パラメータ)」を逆算して学習する。 数理的にはEMアルゴリズムの特殊なケースであり、「今のパラメータで状態を予測する(Eステップ)」と「その予測を元にパラメータを更新する(Mステップ)」を何度も繰り返す。

    画像


    画像


    解答

    本問のHMMを図示すると以下の通り,同時確率は全経路の確率をかけて求める。

    画像


    漸化式を求めるには,周辺化(同時確率の分解)・条件付き確率の積の法則・マルコフ性を用いる。結果として,「時刻 t までに状態 si  にいる確率 αt (i)」に「状態遷移確率 aij 」をかけて全状態分を足し合わせ、最後に「状態 sj  から yt+1  が出力される確率 bj (yt+1 )」を掛け算したものとなる。

    画像


    前向きアルゴリズムが「総和(∑)」を計算していたのに対し、ビタビアルゴリズムは「最も確率の高い単一の経路(max)」を追跡する。

    画像


    Baum-Welchアルゴリズムについて,分子は「時刻 t までに状態 si  にたどり着き(α)、そこから sj  に遷移して yt+1  を出力し(a,b)、さらにそこから最後まで観測を全うする(β)」という一本の局所的な流れの重みを表し,それを全経路の総和(分母)で割ることで、正規化された条件付き確率(割合)を求めている。

    画像


    HMMの実装はhmmlearnを用いて簡単にできる。

    import numpy as np
    from hmmlearn import hmm
    
    # 1. 離散型の隠れマルコフモデル (CategoricalHMM) のインスタンス化
    # 状態数 (n_components) = 2
    model = hmm.CategoricalHMM(n_components=2, random_state=42)
    
    # 2. 問題1のパラメータを設定
    # 初期状態確率 pi
    model.startprob_ = np.array([0.6, 0.4])
    
    # 状態遷移確率行列 A
    model.transmat_ = np.array([
        [0.7, 0.3],  # s1からの遷移
        [0.4, 0.6]   # s2からの遷移
    ])
    
    # 出力(観測)確率行列 B
    # 0: v1, 1: v2 とマッピング
    model.emissionprob_ = np.array([
        [0.2, 0.8],  # s1からの出力
        [0.9, 0.1]   # s2からの出力
    ])
    
    # 3. 観測系列 Y = (v1, v1, v2) に対するViterbi復号
    # hmmlearnの入力は (系列長, 特徴量数) の2次元配列である必要があります
    X_observed = np.array([[0], [0], [1]])  # v1, v1, v2
    
    # decodeメソッドがViterbiアルゴリズムを実行
    logprob, state_sequence = model.decode(X_observed)
    
    print("--- Viterbiアルゴリズムの結果 ---")
    print(f"対数尤度 (Log Probability): {logprob}")
    print(f"最も尤もらしい状態系列 (0=s1, 1=s2): {state_sequence + 1}") 
    
    # 4. モデルから新しいデータをサンプリング(生成)
    X_gen, Z_gen = model.sample(n_samples=5)
    print("\n--- モデルからのサンプリング結果 ---")
    print(f"生成された状態系列: {Z_gen + 1}")
    print(f"生成された観測系列: {X_gen.flatten()}")
    
    --- Viterbiアルゴリズムの結果 ---
    対数尤度 (Log Probability): -2.777271670144163
    最も尤もらしい状態系列 (0=s1, 1=s2): [2 2 1]
    
    --- モデルからのサンプリング結果 ---
    生成された状態系列: [1 2 1 1 1]
    生成された観測系列: [1 0 0 1 1]



    本記事のもくじはこちら:


    あなたへのおすすめ