趣味のPython・深層学習

中級者のための実装集

EMアルゴリズムと潜在変数

今回は機械学習の重要なトピックである「潜在変数モデル」と「EMアルゴリズム」について、実装例を交えながら丁寧に解説していきます。

潜在変数モデルとは?

潜在変数モデルでは、観測できる変数(データ)と観測できない潜在変数(隠れた要因)の間に何らかの関係があると仮定します。例えば、以下のようなモデルが考えられます。

  • 人の収入(観測変数)は、能力や努力度合い(潜在変数)に影響を受ける
  • 天気予報の適切さ(観測変数)は、予報士の能力(潜在変数)に左右される
  • 製品の品質(観測変数)は、工場の設備状況(潜在変数)による

このように、潜在変数モデルでは観測できないが重要な要因(潜在変数)が、観測できるデータ(観測変数)に影響を与えていると考えられています。しかし、潜在変数は直接観測できないため、その値を推定するのは難しい問題となります。

EMアルゴリズムによる潜在変数モデルの解析

そこで登場するのが、EMアルゴリズム(Expectation-Maximization algorithm)です。EMアルゴリズムは、潜在変数を含むモデルのパラメータを推定するための一般的なアルゴリズムです。 具体例として、ある集団にはハッピー層とアンハッピー層の2つの潜在的なグループ(潜在変数)が存在すると考えられます。しかし、我々は個人の幸福度(観測変数)しか観測できません。EMアルゴリズムを使うことで、観測された幸福度からグループ分けのパラメータ(平均や分散など)を推定することができます。 EMアルゴリズムは以下の2ステップを繰り返し行うことで、パラメータを推定します。

  • E-step (Expectation step): 現在のパラメータを使って、潜在変数の事後確率分布を計算する。
  • M-step (Maximization step): 事後確率分布を使って、対数尤度を最大化するようにパラメータを更新する。

この2ステップを交互に繰り返すことで、潜在変数を含むモデルのパラメータを徐々に良い値に近づけていきます。 次に、Pythonでの実装例を見ていきましょう。

import numpy as np

# データ生成過程
# zは潜在変数、xは観測変数
# P(z=0) = 0.5、P(z=1) = 0.5 
# z = 0の時、x ~ N(0, 1)
# z = 1の時、x ~ N(5, 1)

def sample_data(n_samples):
    z = np.random.binomial(1, 0.5, n_samples)  # 潜在変数zをサンプリング
    x = np.zeros(n_samples)
    x[z == 0] = np.random.normal(0, 1, np.sum(z == 0))  # z=0の時、x ~ N(0, 1)
    x[z == 1] = np.random.normal(5, 1, np.sum(z == 1))  # z=1の時、x ~ N(5, 1)
    return x, z

# EMアルゴリズム
def em(x: np.ndarray, n_iter: int = 100) -> tuple[float, float]:
    """EMアルゴリズムを実行し、最終的な平均と分散を返す"""
    n_samples = len(x)
    
    # 初期値
    mu0 = 0.0  # 平均の初期値
    mu1 = 5.0  
    sigma0 = 1.0  
    sigma1 = 1.0 
    pi = 0.5  # 事前確率の初期値
    
    for _ in range(n_iter):
        # E-step: 事後確率を計算
        p0 = pi * np.exp(-0.5 * ((x - mu0) / sigma0) ** 2) / (np.sqrt(2 * np.pi * sigma0))
        p1 = (1 - pi) * np.exp(-0.5 * ((x - mu1) / sigma1) ** 2) / (np.sqrt(2 * np.pi * sigma1))
        gamma = p0 / (p0 + p1)
        
        # M-step: パラメータを更新
        mu0 = np.sum(x * (1 - gamma)) / np.sum(1 - gamma)
        mu1 = np.sum(x * gamma) / np.sum(gamma)
        sigma0 = np.sqrt(np.sum((x - mu0) ** 2 * (1 - gamma)) / np.sum(1 - gamma))
        sigma1 = np.sqrt(np.sum((x - mu1) ** 2 * gamma) / np.sum(gamma))
        pi = np.mean(gamma)
        
    return mu0, sigma0, mu1, sigma1

# サンプルデータを生成
x, z = sample_data(1000)  

# EMアルゴリズムを実行し、推定された平均と分散を出力
mu0, sigma0, mu1, sigma1 = em(x)
print(f"z=0の時の推定平均: {mu0:.2f}, 推定分散: {sigma0:.2f}") 
print(f"z=1の時の推定平均: {mu1:.2f}, 推定分散: {sigma1:.2f}")

このコードでは、潜在変数zと観測変数xのデータ生成過程が定義されています。EMアルゴリズムの関数emでは、以下の処理が行われています。

平均、分散、事前確率の初期値を設定します。 E-stepで、現在のパラメータから潜在変数の事後確率gammaを計算します。 M-stepで、事後確率gammaを使って平均、分散、事前確率を更新します。 2と3を指定された反復回数(n_iter)分繰り返します。 最終的な平均と分散を返します。

実行すると、潜在変数zが0と1の2つの値を取る場合の、推定された平均と分散が出力されます。 このように、EMアルゴリズムを使うことで、潜在変数を含むモデルのパラメータを推定することができます。機械学習の分野をはじめ、様々な分野で幅広く利用されているアルゴリズムです。