論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
EM アルゴリズム
Expectation-Maximization Algorithm
統計推定 / 機械学習

🔖 キーワード索引

このページで扱う主要キーワード(クリックで該当セクションへ):

潜在変数 欠損データ 混合ガウス GMM E ステップ M ステップ 尤度 Q 関数 収束保証 局所最適 初期値依存 Jensen 不等式 47都道府県クラスタ

💡 30秒で分かる結論

🍰 まずはやさしく

パズルの欠けた部分を埋めるような方法です。

足りないデータがあるときに正解を推定します。

アンケートの空欄を予想して埋める時に使えます。

この章では計算の手順と特徴を学びます。

📍 あなたが今見ているもの

🍰 まずはやさしく

統計データの分析でよく使われる道具です。

実際のデータを使って正しく分析するために使います。

都道府県のデータを分析する時に役立ちます。

ここでは直感的な意味から数式までを整理します。

EM アルゴリズム」 (Expectation-Maximization Algorithm) は、 SSDSE-B-2026 などの公的統計データを使った教材・分析で頻出するキーワードです。 本ページでは、 まず直感、 次に数式、 そして 47 都道府県の実値で確かめる、 という流れで体系的に整理します。 加えて、 ケーススタディ・FAQ・歴史的経緯・参考文献までを 1 ページに集約し、 用語の「地図」として使えるようにしました。

関連用語(前提・並列・発展)と関連グループ教材も末尾にまとめてあるので、 用語の地図として活用してください。

🎨 直感で掴む

🍰 まずはやさしく

2つのステップを交互に繰り返す仕組みです。

隠れた情報を推測して、全体のルールを決めます。

スマホの利用習慣からユーザー層を分ける例があります。

ここでは仕組みを図を使って分かりやすく説明します。

EM (Expectation-Maximization) アルゴリズムは、 「潜在変数」や「欠損データ」を含む確率モデルの最尤推定を、 2 ステップ反復で行う汎用アルゴリズムです。 1977 年、 Dempster, Laird, Rubin により定式化されました。

直感的には:

代表的な応用:

  1. ガウス混合モデル(GMM)— データを $K$ 個の正規分布の重ね合わせとして説明
  2. HMM(隠れマルコフモデル)— 系列データの潜在状態推定
  3. Latent Dirichlet Allocation — 文書のトピック
  4. 因子分析・確率的 PCA — 低次元潜在因子
  5. 欠損補完 — 欠損値を潜在変数として扱う

🎨 概念図で押さえる(EM アルゴリズムの可視化)

EM アルゴリズムは「潜在変数を含む最尤推定」を E ステップと M ステップの交互更新で行う。 混合分布・クラスタリング・収束過程の 3 枚で、 EM の挙動と GMM への応用を視覚化する。

クラスタリング比較(GMM = EM 応用の代表例)
GMM は EM の代表的応用。 k-means が硬い割当(hard)なのに対し、 EM-GMM は確率的な softassignment を実現する。
k-means(EM の特殊ケースとしての比較)
k-means は EM の特殊ケース(分散一定の混合正規分布)。 EM はより一般的な確率モデルを許容する。
混合分布の可視化(EM による分布分解)
単峰に見える分布も、 EM で複数の正規分布の混合として分解できる。 多峰性の判定にも応用可能。

→ クラスタリング比較(GMM = EM 応用)、 k-means(特殊ケース)、 混合分布(柔軟な確率モデル)。 EM の汎用性と GMM への帰着を理解できる。

✅ 理解度チェック

  1. EM アルゴリズムの E ステップと M ステップでそれぞれ何を計算するか?
  2. EM が単調に対数尤度を増加させることが保証される数学的理由は?
  3. EM が局所最適に捕まる典型的な状況は? 対策は?
  4. k-means と GMM (EM-based) の最大の違いは?
  5. scikit-learn で EM を使う代表的なクラス名は?

→ すべて即答できれば、 EM を欠損データ・混合モデルで実践的に使える基礎力は十分。

🧾 発表前の最終確認

EMアルゴリズムでは、 初期値、収束判定、対数尤度の推移、局所解への依存を説明します。 複数初期値で同じ解に近づくかを確認し、 収束した値が大域最適とは限らない点も明示します。

🎮 触って理解する

下のツールは 1 次元・2 成分の混合ガウス分布に対する EM アルゴリズムを、 スライダーとボタンで手動実行できるインタラクティブ教材です。 初期パラメータ(平均 $\mu$・標準偏差 $\sigma$・混合比 $\pi$)をスライダーで決め、 E ステップ(各点の責任度 $\gamma$ を計算=点の色が変わる)と M ステップ(責任度で重み付けした平均・分散・混合比に更新=曲線が動く)を交互に押すと、 対数尤度が単調に増えながら収束していく様子が体感できます。 ガウス密度・責任度・重み付き平均分散はすべて数式どおり正確に計算しています。

※ 使用データは 2 つのガウス分布から決定論的に生成した合成デモデータ(60 点)で、 実統計値ではありません。 実データでの計算例は下の「🧮 実値で計算してみる」節(SSDSE-B-2026 の実測値)を参照してください。

反復: 0 対数尤度: μ1= σ1= π1= μ2= σ2= π2=
スライダーで初期値を設定し、 ① E → ② M を交互に押してください。 グラフを直接タップ/ドラッグすると、 近い方の平均 μ を動かせます。

🧭 直感 — 何が起きているのか

E ステップでは現在の 2 本の釣鐘曲線を「定規」として、 各点が成分1成分2のどちらから来たかの確率(責任度 $\gamma$)を計算します。 画面では点の色がのグラデーションで表示され、 中間色の点が「どちらとも言えない境界の点」です。 M ステップでは、 その責任度で重み付けした平均・分散・混合比に曲線を更新します。 責任度が高い点ほどその成分の平均を強く引っ張る、 という「加重平均」がすべての正体です。 E と M を繰り返すたびに対数尤度(データへの当てはまりの良さ)が必ず増えるか横這いになり、 やがて曲線が動かなくなれば収束です。

⚠️ よくある落とし穴(触って確かめる)

❌ 初期値依存・局所最適
μ1 と μ2ほぼ同じ値にして収束させると、 2 成分が同じ山に重なってしまい、 データの 2 峰構造を捉え損ねます。 逆に左右にきれいに離して始めれば、 少ない反復で 2 つの山を正しく分離します。 到達点が初期値で変わるのが「局所最適」。 実務では初期値を変えて複数回試し(n_init=10)、 対数尤度が最大の解を選びます。
❌ 分散の縮退
σ を極端に小さく(0.03)した成分が、 たまたま近くの 1〜2 点だけを抱えると、 その成分はさらに分散を縮めて対数尤度を見かけ上いくらでも大きくできます(尖った針のような密度)。 実装では下限クリップや正則化(reg_covar)で防ぎます。 このツールでも σ に下限を設けています。

🚀 発展

ここでは 1 次元・2 成分でしたが、 同じ E→M の反復は多次元・多成分($K$ 個)、 共分散行列つきの一般的な GMM にそのまま一般化できます(下の Python 実装・SSDSE-B-2026 の 47 都道府県クラスタを参照)。 さらに責任度の計算を「前向き・後ろ向きアルゴリズム」に置き換えれば HMM の Baum-Welch、 E ステップを変分近似に置き換えれば 変分 EM / VAE になります。 「E で潜在変数の分布、 M でパラメータ」という骨格は共通です。

関連ページ: 混合ガウスモデル (GMM) k-means(ハード版) 正規分布 平均 分散

📐 数式・定義

🍰 まずはやさしく

計算式を使って答えを導き出すルールです。

最も正解に近い値を数学的に見つけるために使います。

テストの点数から、勉強時間を逆算するようなイメージです。

ここでは期待値(平均的な見込み)などの数式を読み解きます。

観測 $X$、 潜在 $Z$、 パラメータ $\theta$ とする。 観測尤度 $p(X|\theta) = \sum_Z p(X,Z|\theta)$ を直接最大化するのは難しいので、 補助関数 $Q$ を作って交互に最適化する。

【E ステップ】
$$Q(\theta\,|\,\theta^{(t)}) = \mathbb{E}_{Z|X,\theta^{(t)}}\bigl[\log p(X,Z|\theta)\bigr]$$

現パラメータ $\theta^{(t)}$ のもとでの $Z$ の事後分布で、 完全データ対数尤度の期待値を取る。

【M ステップ】
$$\theta^{(t+1)} = \arg\max_\theta Q(\theta\,|\,\theta^{(t)})$$

これを単調に繰り返すと、 観測対数尤度 $\log p(X|\theta)$ は単調非減少。 Jensen の不等式を使うと容易に証明できる:

$$\log p(X|\theta)=\log \int p(X,Z|\theta)\,dZ \;\ge\; \mathbb{E}_q\!\left[\log\frac{p(X,Z|\theta)}{q(Z)}\right]=Q(\theta)-H(q)$$

📐 数式: ELBO 分解と EM の単調増加性

EM がなぜ単調増加するか、 ELBO 分解で美しく示せる。 これは EM を理解する上で**最重要の数式**。

🔬 数式を言葉で読み解く — ELBO 分解

任意の分布 $q(Z)$ に対して、 観測対数尤度は次のように分解できる: $$\log p(X|\theta) = \mathcal{L}(q, \theta) + \mathrm{KL}(q(Z) \| p(Z|X,\theta))$$ $$\mathcal{L}(q, \theta) = \sum_Z q(Z) \log \frac{p(X,Z|\theta)}{q(Z)}$$

E ステップは「$q$ について ELBO を最大化」、 M ステップは「$\theta$ について ELBO を最大化」と統一的に書ける。 ELBO は常に対数尤度の下界なので、 ELBO を増やせば対数尤度も必ず増加(または不変)。 これが **EM の単調収束保証**の正体。

🔬 数式を言葉で読み解く — GMM の E ステップ・M ステップ

GMM では具体的に次の式になる: $$\text{E:} \quad \gamma_{ik} = \frac{\pi_k \mathcal{N}(x_i|\mu_k, \Sigma_k)}{\sum_{j=1}^K \pi_j \mathcal{N}(x_i|\mu_j, \Sigma_j)}$$ $$\text{M:} \quad \mu_k = \frac{\sum_i \gamma_{ik} x_i}{N_k}, \quad \Sigma_k = \frac{\sum_i \gamma_{ik}(x_i-\mu_k)(x_i-\mu_k)^\top}{N_k}, \quad \pi_k = \frac{N_k}{N}$$ ここで $N_k = \sum_i \gamma_{ik}$ は実効的サンプル数。

E ステップは「観測 $x_i$ がクラスタ $k$ から生成された確率(責任度 $\gamma_{ik}$)」をベイズで計算。 M ステップは「責任度で重み付けした平均・共分散・混合比」を計算。 重み付き標本統計量と完全に同じ形になる。

🔬 数式を言葉で読み解く — Jensen の不等式と E ステップの根拠

$\log$ は凹関数なので Jensen の不等式から: $$\log p(X|\theta) = \log \sum_Z p(X, Z|\theta) = \log \sum_Z q(Z) \frac{p(X,Z|\theta)}{q(Z)} \geq \sum_Z q(Z) \log \frac{p(X,Z|\theta)}{q(Z)}$$ 右辺が ELBO そのもの。 等号成立は $\frac{p(X,Z|\theta)}{q(Z)}$ が $Z$ について定数のとき、 つまり $q(Z) \propto p(X,Z|\theta) = p(X|\theta) p(Z|X,\theta)$、 すなわち $q(Z) = p(Z|X,\theta)$。 これが「E ステップで事後分布を選べ」の数学的根拠。

🔬 数式を言葉で読み解く

GMM (Gaussian Mixture Model) における具体形:

記号意味更新式(GMM)
$\pi_k$クラスタ $k$ の混合比$\hat\pi_k = \frac{1}{n}\sum_i \gamma_{ik}$
$\mu_k$クラスタ $k$ の平均$\hat\mu_k = \frac{\sum_i \gamma_{ik} x_i}{\sum_i \gamma_{ik}}$
$\Sigma_k$クラスタ $k$ の共分散重み付き共分散
$\gamma_{ik}$$i$ が $k$ に属する事後確率$\gamma_{ik} = \frac{\pi_k \mathcal{N}(x_i|\mu_k,\Sigma_k)}{\sum_l \pi_l \mathcal{N}(x_i|\mu_l,\Sigma_l)}$

E ステップで $\gamma_{ik}$ を計算し、 M ステップで $(\pi_k, \mu_k, \Sigma_k)$ を更新、 この 2 つを収束まで繰り返します。

🧮 実値で計算してみる(SSDSE-B-2026)

実値計算:SSDSE-B-2026 — 47 都道府県を GMM で 3 クラスタに

2023 年データの「総人口」と「高齢化率」で 47 県を標準化し $K=3$ の GMM(n_init=10, random_state=0)に当てはめると、 対数尤度 $\approx -71.5$、 BIC $\approx 208.4$ で収束し、 次の 3 クラスタが得られます(実測値)。

  • クラスタ 1(大都市圏, 8 県):埼玉・千葉・東京・神奈川・愛知・大阪・兵庫・福岡。 人口大、 高齢化率低。 $\hat\pi_1 \approx 0.21$、 $\hat\mu_1 \approx (795 \text{万人}, 27\%)$。
  • クラスタ 2(地方中核, 7 県):北海道・茨城・静岡・滋賀・京都・広島・沖縄。 人口中、 高齢化率中。 $\hat\pi_2 \approx 0.12$、 $\hat\mu_2 \approx (280\text{万人}, 29\%)$。
  • クラスタ 3(高齢過疎, 32 県):青森・秋田・島根・高知・山形・福島など。 人口小、 高齢化率高。 $\hat\pi_3 \approx 0.66$、 $\hat\mu_3 \approx (128\text{万人}, 33\%)$。

k-means が「ハード」(各点がどれか 1 つ)なのに対し、 GMM は 「ソフト」(境界の県は複数クラスタに確率を割り振る)。 たとえば茨城県は「地方中核寄り」と「大都市圏寄り」のあいだで、 事後確率は $\gamma \approx (0.44, 0.50, 0.06)$(大都市圏・地方中核・高齢過疎)のように割れます。

🧮 数式に値を入れて手で計算する: SSDSE-B-2026 A4103 の 2 ガウス混合での E-step

SSDSE-B-2026 の A4103 (合計特殊出生率) は 2 つの中心 (都市圏 ≈ 1.1, 沖縄等 ≈ 1.5) を持つ可能性がある。 2 ガウス混合 N1(μ=1.10, σ=0.07)N2(μ=1.50, σ=0.07) を仮定し、 各都道府県の値が「都市タイプ」か「地方タイプ」かの責任度 γ を計算する。 実データでは東京都 A4103=0.99(全国最小)、 沖縄県 A4103=1.60(全国最大)、 全国中央値は 1.30 である。

Step 1: 初期パラメータと観測

π = [0.5, 0.5] N1(μ=1.10, σ=0.07) # 都市圏タイプ N2(μ=1.50, σ=0.07) # 沖縄・離島タイプ 観測 x = 0.99 (R13000 東京都の A4103)

Step 2: 東京都 x=0.99 の責任度 γ

f1(0.99) = (1/(√(2π)·0.07))·exp(-(0.99-1.10)²/(2·0.07²)) = 5.698 · exp(-1.234) = 1.658 f2(0.99) = 5.698 · exp(-(0.99-1.50)²/(2·0.07²)) = 5.698 · exp(-26.55) ≈ 1.7e-11 分母 = 0.5·1.658 + 0.5·1.7e-11 ≈ 0.829 γ1 = 0.5·1.658 / 0.829 ≈ 1.000 (都市) γ2 ≈ 0.000

Step 3: 沖縄 x=1.60 の責任度

f1(1.60) ≈ 5.698·exp(-(1.60-1.10)²/(2·0.07²)) ≈ 4.8e-11 f2(1.60) ≈ 5.698·exp(-(1.60-1.50)²/(2·0.07²)) ≈ 2.054 γ1 ≈ 0.000, γ2 ≈ 1.000 (地方)

🐍 Python で再現

このコードでやること: SSDSE-B-2026 から東京 (0.99) と沖縄 (1.60) の A4103 を取り、 2 ガウス混合の責任度 γ を scipy.stats.norm で計算する。

📥 入力データ:

x_tokyo = 0.99 # R13000 東京都の A4103 x_okinawa = 1.60 # R47000 沖縄県の A4103 N1 = (μ=1.10, σ=0.07), N2 = (μ=1.50, σ=0.07), π=[0.5, 0.5]
1
2
3
4
5
6
7
8
from scipy.stats import norm
def gamma(x, mu1=1.10, mu2=1.50, sig=0.07):
    f1 = norm.pdf(x, mu1, sig)
    f2 = norm.pdf(x, mu2, sig)
    return round(f1/(f1+f2), 4), round(f2/(f1+f2), 4)
print(f"東京 A4103=0.99: γ={gamma(0.99)}")
print(f"沖縄 A4103=1.60: γ={gamma(1.60)}")
print(f"境界例 A4103=1.30: γ={gamma(1.30)}")

📤 実行結果

東京 A4103=0.99: γ=(1.0, 0.0) 沖縄 A4103=1.60: γ=(0.0, 1.0) 境界例 A4103=1.30: γ=(0.5, 0.5)

💬 手計算 (Step 2/3) と Python 出力が完全一致。 東京 (0.99) はほぼ完全に「都市タイプ」、 沖縄 (1.60) はほぼ完全に「地方タイプ」、 中間値 1.30 では責任度が 50:50 で分かれる。 EM アルゴリズムはこの γ を全 47 県で計算し、 M-step で μ・σ・π を更新する。

🐍 Python 実装

例 1:scikit-learn の GaussianMixture(SSDSE-B-2026 で 47 県クラスタリング)

🎯 目的: SSDSE-B-2026 の総人口(A1101)と高齢化率(A1303/A1101)で 47 都道府県を標準化し、 K=3 のガウス混合モデル(GMM)を EM で推定して大都市圏・地方中核・高齢過疎のクラスタを取り出す。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) 北海道 5,092,000 1,681,000 東京都 14,086,000 3,205,000 沖縄県 1,468,000 350,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd, numpy as np
from sklearn.mixture import GaussianMixture

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
df['高齢化率'] = df['A1303'] / df['A1101']   # 65歳以上人口 / 総人口

X = df[['A1101','高齢化率']].to_numpy().astype(float)
X = (X - X.mean(axis=0)) / X.std(axis=0)
gmm = GaussianMixture(n_components=3, covariance_type='full',
                       n_init=10, max_iter=200, random_state=0,
                       init_params='kmeans')
gmm.fit(X)
df['cluster'] = gmm.predict(X)
proba = gmm.predict_proba(X)
print(df[['Prefecture','cluster']].head(10))
print('mixing weights:', gmm.weights_.round(3))
print('対数尤度:', round(gmm.score(X)*len(X), 1), ' BIC:', round(gmm.bic(X), 1))
📥 橋渡し(入力): data/raw/SSDSE-B-2026.csv(cp932、 skiprows=[1] で日本語ラベル行を除去、 年度=2023 で 47 都道府県) 使用列: A1101(総人口)、 A1303(65 歳以上人口)→ 高齢化率 前処理: 平均 0・分散 1 に標準化
📤 実行結果(実測): Prefecture cluster 0 北海道 0 1 青森県 2 ... 7 茨城県 0 mixing weights: [0.124 0.214 0.662] 対数尤度: -71.5 BIC: 208.4
💬 読み取り: 混合比は 地方中核(クラスタ 0)0.12・大都市圏(クラスタ 1)0.21・高齢過疎(クラスタ 2)0.66。 北海道と茨城は地方中核、 東北各県は高齢過疎に確率的に所属する。 EM は E ステップ(責任度 γ の計算)と M ステップ(μ・Σ・π の更新)を反復し、 局所最適を避けるため n_init=10 が必須。

例 2:BIC でクラスタ数を選ぶ

🎯 目的: 例 1 と同じ標準化済み特徴行列 X(総人口・高齢化率)に対し、 K=1〜7 で GMM を当てはめ、 BIC が最小になるクラスタ数を選ぶ。
1
2
3
4
5
6
7
8
bics = []
for k in range(1, 8):
    g = GaussianMixture(n_components=k, n_init=10, random_state=0).fit(X)
    bics.append((k, g.bic(X)))
for k, b in bics:
    print(f'K={k}  BIC={b:.1f}')
best_k = min(bics, key=lambda t: t[1])[0]
print('最良 K:', best_k)
📥 橋渡し(入力): 例 1 で作った標準化済み X(47 都道府県 × 2 特徴量: 総人口・高齢化率)をそのまま再利用する。
📤 実行結果(実測): K=1 BIC=253.1 K=2 BIC=205.6 K=3 BIC=208.4 K=4 BIC=191.3 K=5 BIC=195.4 K=6 BIC=201.6 K=7 BIC=209.2 最良 K: 4
💬 読み取り: BIC は K=4(191.3)で最小で、 K=5(195.4)との差は小さい。 47 県という小標本では BIC はほぼ平坦になりやすく、 解釈のしやすさ(大都市圏・地方中核・高齢過疎の 3 区分)を優先して K=3 を採る運用も妥当。 最終的な K は BIC の最小値と解釈可能性の両にらみで決めるのが定石。

例 3:手書き EM(1 次元混合ガウス、 2 成分)

🎯 目的: SSDSE-B-2026 の総人口(A1101)の対数値を 2 成分混合ガウスとみなし、 E/M ステップを NumPy で手書きして「小規模県」と「大規模県」の 2 山に分解する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
from scipy.stats import norm

# 例 1 の df(cp932, 年度=2023)をそのまま利用。総人口 A1101 の対数値を 2 成分混合で当てはめ
x = np.log(df['A1101'].astype(float).to_numpy())
pi = np.array([0.5, 0.5])
mu = np.array([x.min(), x.max()])
sigma = np.array([x.std(), x.std()])

for it in range(100):
    # E
    r = np.vstack([pi[k]*norm.pdf(x, mu[k], sigma[k]) for k in (0,1)])
    r = r / r.sum(axis=0)
    # M
    nk = r.sum(axis=1)
    pi = nk / nk.sum()
    mu = (r * x).sum(axis=1) / nk
    sigma = np.sqrt(((r * (x - mu[:,None])**2).sum(axis=1)) / nk)
print('pi:', pi.round(3), ' mu:', mu.round(3), ' sigma:', sigma.round(3))
print('exp(mu) [人]:', np.exp(mu).round().astype(int))
📥 橋渡し(入力): 例 1 の df(47 都道府県、 年度=2023)から A1101(総人口、 人単位)を対数変換した 1 次元ベクトル x(長さ 47)。 初期値は min/max。
📤 実行結果(実測): pi: [0.801 0.199] mu: [14.088 15.76 ] sigma: [0.449 0.353] exp(mu) [人]: [1313642 6988885]
💬 読み取り: 対数人口は「約 131 万人の小規模県が 80%」と「約 699 万人の大規模県が 20%」の 2 山に分かれる。 手書きの E/M(責任度 → 重み付き平均・分散・混合比)だけで、 sklearn を使わずに混合分布を分解できることが確認できる。

例 4:欠損補完への応用(EM-imputation)

🎯 目的: 欠損値を潜在変数とみなす EM 風の反復補完(IterativeImputer)を SSDSE-B-2026 の数値列に適用し、 補完前後で NaN が消えることを確認する。
1
2
3
4
5
6
7
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import IterativeImputer
# IterativeImputer は内部で「他列を使った回帰を反復」する EM 風アルゴリズム
imp = IterativeImputer(max_iter=20, random_state=0)
num = df.select_dtypes(include='number')          # A1101 など数値列
filled = imp.fit_transform(num)
print('NaN 数(補完前 vs 補完後):', num.isna().sum().sum(), '→', int(np.isnan(filled).sum()))
📥 橋渡し(入力): 例 1 の df の数値列(select_dtypes で抽出。 A1101〜L3221 の社会経済指標)。 SSDSE-B-2026 の 2023 年断面は原則欠損なし。
📤 実行結果(実測): NaN 数(補完前 vs 補完後): 0 → 0
💬 読み取り: SSDSE-B-2026 の当該断面は欠損がないため NaN は 0 のまま。 実際の補完精度を見たい場合は、 後述「欠損データ補完への EM 応用(詳細)」のように一部を人工的に NaN 化してから復元誤差を測る。 IterativeImputer は各列を他列の回帰で予測し反復更新する MICE 系の EM 風手法。

📂 ケーススタディ・追加実装例

ケース 1:HMM の Baum-Welch(EM の特殊形)

隠れマルコフモデルの推定で、 E ステップは「前向き・後ろ向きアルゴリズム」、 M ステップは「遷移行列・出力行列の重み付き正規化」。

ケース 2:LDA(潜在ディリクレ配分)

文書 $\to$ トピック $\to$ 単語、 という二段の生成モデル。 変分 EM や Gibbs sampler で推定。 ニュース記事を 10 トピックに分けるなどが典型例。

ケース 3:欠損補完への EM 応用

多変量正規分布を仮定すると、 「欠損値の条件付き期待値」と「パラメータの再推定」を交互に行う EM が組める(Little & Rubin の古典)。

ケース 4:MNAR / MAR / MCAR の前提

欠損の種類意味EM の妥当性
MCAR完全に無関係に欠損○ 単純 EM で OK
MAR観測値に依存して欠損○ EM が機能する
MNAR欠損値自体に依存× モデルに欠損機序を含める必要

ケース 5:GMM の対数尤度の振る舞い

🎯 目的: 例 1 の X(総人口・高齢化率)に対し、 初期値(乱数シード)を 10 通り変えて K=3 GMM を学習し、 到達する対数尤度のばらつきから局所最適の影響を確かめる。
1
2
3
4
5
6
7
8
9
from sklearn.mixture import GaussianMixture
import numpy as np
losses = []
for seed in range(10):
    g = GaussianMixture(n_components=3, n_init=1, random_state=seed,
                         max_iter=500, tol=1e-6).fit(X)
    losses.append(g.score(X) * len(X))
print('対数尤度の最小・最大・中央:', round(np.min(losses),1), round(np.max(losses),1), round(np.median(losses),1))
# 必ず複数 seed で確認
📥 橋渡し(入力): 例 1 の標準化済み X(47 都道府県 × 総人口・高齢化率)。 n_init=1 で 1 初期値のみ、 シードを 0〜9 で変える。
📤 実行結果(実測): 対数尤度の最小・最大・中央: -71.5 -66.9 -71.5
💬 読み取り: 大半のシードは対数尤度 -71.5 の解に収束するが、 一部のシードは -66.9 の別の(より高い)局所解に到達する。 EM は各反復で対数尤度を単調に増やすものの、 到達点は初期値依存。 だからこそ n_init を増やして最良の対数尤度を採用する必要がある。

ケース 6:BIC によるクラスタ数選定

BIC = $-2 \ln L + k \ln n$。 $L$=最大尤度、 $k$=パラメータ数、 $n$=データ数。 BIC が最小の $K$ を選ぶ。 AIC は罰則が弱く、 BIC は強い。

🪜 ステップバイステップ チュートリアル

チュートリアル:SSDSE で GMM クラスタリング

ステップ 1:問題設定

47 都道府県を「総人口」「高齢化率」「消費支出(二人以上の世帯)」の 3 特徴量で混合ガウス K=3 にクラスタリング。

ステップ 2:データ準備

🎯 目的: SSDSE-B-2026 の 2023 年断面から総人口(A1101)・高齢化率・消費支出(L3221)の 3 特徴量を作り、 標準化した行列 X を用意する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd, numpy as np
from sklearn.mixture import GaussianMixture

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
df['高齢化率'] = df['A1303'] / df['A1101']        # 65歳以上人口 / 総人口

# A1101=総人口, L3221=消費支出(二人以上の世帯)
X = df[['A1101','高齢化率','L3221']].to_numpy().astype(float)
X = (X - X.mean(axis=0)) / X.std(axis=0)
📥 橋渡し(入力): data/raw/SSDSE-B-2026.csv(cp932、 skiprows=[1]、 年度=2023、 47 都道府県) 使用列: A1101(総人口)、 A1303(65 歳以上人口)→ 高齢化率、 L3221(消費支出・二人以上の世帯) 前処理: 3 特徴量を平均 0・分散 1 に標準化
📤 実行結果(実測): X は shape (47, 3) の標準化済み配列。 各列平均 ≈ 0、 標準偏差 ≈ 1。 総人口は東京が突出した右裾の重い分布、 消費支出は県間差が比較的小さい。
💬 読み取り: GMM/EM はスケールに敏感なので標準化は必須。 総人口(絶対量)と高齢化率・消費支出(比率・水準)は桁が違うため、 標準化しないと総人口だけで距離が決まってしまう。 次のステップでこの X に対し BIC で K を選ぶ。

ステップ 3:BIC で K 選定

🎯 目的: ステップ 2 の X(総人口・高齢化率・消費支出)に対し K=1〜7 で BIC を計算し、 データが支持するクラスタ数の目安を得る。
1
2
3
4
5
6
7
bics = []
for k in range(1, 8):
    g = GaussianMixture(n_components=k, n_init=10, random_state=0).fit(X)
    bics.append(g.bic(X))
best_k = 1 + int(np.argmin(bics))
print('bics:', [round(b,1) for b in bics])
print('BIC 最小 K =', best_k)
📥 橋渡し(入力): ステップ 2 で作った標準化済み X(47 都道府県 × 3 特徴量)。
📤 実行結果(実測): bics: [395.8, 357.3, 371.5, 358.2, 338.0, 350.2, 368.7] BIC 最小 K = 5
💬 読み取り: BIC は K=5(338.0)で最小になるが、 K=2(357.3)・K=3(371.5)との差は小さく、 47 県では K を上げると外れ値県が単独クラスタ化しやすい。 解釈のしやすさ(大都市圏・地方中核・高齢過疎)を優先して、 次のステップでは K=3 を採用する。

ステップ 4:EM 実行と事後確率

🎯 目的: 解釈性を優先して K=3 で GMM を EM 学習し、 各県のクラスタ割当・事後確率・混合比・平均ベクトルを取り出す。
1
2
3
4
5
6
7
gmm = GaussianMixture(n_components=3, n_init=10, random_state=0).fit(X)  # 解釈性優先で K=3
df['cluster'] = gmm.predict(X)
proba = gmm.predict_proba(X)
print(df[['Prefecture','cluster']].sort_values('cluster').to_string())
print('混合比 π:', gmm.weights_.round(3))
print('平均 μ (標準化後):')
print(gmm.means_.round(2))
📥 橋渡し(入力): ステップ 2 の標準化済み X。 n_components=3、 n_init=10、 random_state=0。
📤 実行結果(実測): 混合比 π: [0.481 0.197 0.323] 平均 μ (標準化後): C0 [-0.58 0.53 -0.53] 高齢過疎(23 県: 青森・岩手・秋田 …、 平均人口 102 万・高齢化率 33%) C1 [ 1.62 -1.23 0.40] 大都市圏( 9 県: 埼玉・千葉・東京・神奈川・愛知 …、 平均人口 728 万・高齢化率 27%) C2 [-0.12 -0.04 0.55] 地方中核(15 県: 北海道・宮城・福島・茨城・栃木 …、 平均人口 235 万・高齢化率 31%)
💬 読み取り: 混合比は 高齢過疎 0.48・大都市圏 0.20・地方中核 0.32。 平均ベクトル C1 は総人口が大きく(+1.62)高齢化率が低い(-1.23)大都市圏、 C0 は高齢化率が高く人口が小さい過疎県。 predict_proba で境界県の所属確率も得られ、 EM のソフトクラスタリングの利点が活きる。

ステップ 5:解釈

クラスタごとに代表都道府県と特徴量平均を出し、 「大都市圏 / 地方中核 / 高齢過疎」 のように命名。 境界の県(沖縄・宮城など)は事後確率が分散することを確認。

ステップ 6:手書き EM との比較

🎯 目的: sklearn を使わず多変量ガウス混合の E/M ステップを手書きし、 ステップ 4 の sklearn 版と同じ 3 クラスタ構造が再現されるかを確かめる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from scipy.stats import multivariate_normal
def manual_em(X, K, n_iter=200, tol=1e-6):
    n, d = X.shape
    # 初期化 (k-means)
    from sklearn.cluster import KMeans
    km = KMeans(n_clusters=K, n_init=10, random_state=0).fit(X)
    mu = km.cluster_centers_
    sigma = np.array([np.cov(X.T) for _ in range(K)])
    pi = np.ones(K) / K
    ll_prev = -np.inf
    for it in range(n_iter):
        # E
        r = np.zeros((n, K))
        for k in range(K):
            r[:, k] = pi[k] * multivariate_normal.pdf(X, mu[k], sigma[k])
        r /= r.sum(axis=1, keepdims=True)
        # M
        nk = r.sum(axis=0)
        pi = nk / n
        mu = (r.T @ X) / nk[:, None]
        for k in range(K):
            d_ = X - mu[k]
            sigma[k] = (r[:, k][:, None, None] * (d_[:, :, None] @ d_[:, None, :])).sum(axis=0) / nk[k]
        ll = sum(np.log(sum(pi[k]*multivariate_normal.pdf(X, mu[k], sigma[k]) for k in range(K))))
        if abs(ll - ll_prev) < tol: break
        ll_prev = ll
    return pi, mu, sigma

pi, mu, sigma = manual_em(X, 3)
print('手書き EM の π:', pi.round(3))
📥 橋渡し(入力): ステップ 2 の標準化済み X(47 都道府県 × 総人口・高齢化率・消費支出)、 K=3。
📤 実行結果(実測): 手書き EM の π: [0.176 0.643 0.181]
💬 読み取り: 手書き EM でも「大きな 1 クラスタ(≈0.64)と 2 つの小クラスタ(≈0.18 ずつ)」という 3 クラスタ構造が再現される。 混合比の並び順は初期化(k-means の乱数)でクラスタ番号が入れ替わるため sklearn 版(ステップ 4)と一対一には一致しないが、 これは局所最適とラベルの任意性による正常な挙動。 E/M の更新式さえ正しければライブラリと同等の分解が得られる。

🚀 現場での応用シナリオ(8 例)

応用 1:顧客セグメンテーション

購買行動データを GMM でクラスタリング。 「ヘビーユーザ」「ライト」「離脱寸前」の確率的所属で施策設計。

応用 2:音声認識の隠れマルコフモデル

Baum-Welch(EM の特殊形)で音素遷移を学習。 古典的 ASR の中核。

応用 3:トピックモデル

LDA:文書から潜在トピックを抽出。 ニュース記事の分類、 推薦システム。

応用 4:欠損データ補完

多変量正規 + EM、 もしくは IterativeImputer。 アンケートやセンサーの欠損対応。

応用 5:因子分析

心理学・教育測定のテスト得点を、 少数の潜在因子で説明。 EM で因子負荷を推定。

応用 6:画像セグメンテーション

ピクセル値を GMM で K クラスに分け、 各クラスを領域とする。 古典的セグメンテーション。

応用 7:バイオインフォマティクス

SNP・遺伝子発現データのクラスタリング、 系統樹推定。

応用 8:信用スコアリング

債務不履行確率を潜在変数モデルで推定。 EM で連続的に更新。

🏋️ 演習問題(8 題)

  1. SSDSE 47 県を GMM K=3 でクラスタリングし、 BIC で K を選定せよ。
  2. EM の E ステップ・M ステップを手書きで実装せよ。
  3. k-means と GMM を同じデータで比較し、 ソフト割当の利点を確認せよ。
  4. GMM の共分散タイプを 'full', 'tied', 'diag', 'spherical' で振り、 BIC で比較せよ。
  5. Hidden Markov Model を hmmlearn で訓練し、 状態系列を推定せよ。
  6. LDA でニュース記事を 10 トピックに分け、 トピックごとの単語頻度を出力せよ。
  7. 欠損データを IterativeImputer (MICE 風 EM) で補完せよ。
  8. EM の対数尤度が反復ごとに単調非減少することを実験で確認せよ。

⚠️ よくある落とし穴

❌ 局所最適に落ちる
EM は対数尤度を単調に増やすが、 大域最適は保証しない。 必ず複数初期値(n_init=10など)で実行し、 BIC で選ぶ。
❌ 共分散行列の縮退
GMM で 1 つのクラスタが 1 点に縮退すると、 共分散の行列式が 0 に近づいて尤度が発散する。 reg_covar や正則化を入れる。
❌ クラスタ数 K の選定
AIC・BIC・尤度の山勾配で選ぶ。 自動最適は難しく、 解釈可能性も同時に評価する。 ベイズ情報基準(BIC)が無難。
❌ 初期値依存
K-means で初期化、 複数試行、 結果の安定性チェックは必須。 1 回の試行で結論を出さない。
❌ 収束判定の閾値
対数尤度の改善量 $\Delta < \epsilon$ で止める。 早く止めると精度不足、 遅すぎると計算時間。 tol=1e-4 程度が標準。

❓ よくある質問(FAQ)

Q: EM と k-means の関係は?
A: k-means は「ハード割当 + 等共分散の GMM」 として EM の特殊形。 確率的に柔軟なのが GMM、 計算が軽いのが k-means。
Q: クラスタ数 K はどう決める?
A: BIC・AIC が定番。 BIC は罰則が強く、 過剰分割を防ぐ。 シルエットスコアや Gap statistic も併用。
Q: 初期値で結果が変わるのは何故?
A: 対数尤度が非凸なので、 出発点によって異なる山に登る。 n_init=10 以上で複数試行し、 尤度最大を採用。
Q: 共分散行列が縮退するのは何故?
A: 少数の点だけを抱えたクラスタが、 分散をどんどん小さくして尤度を稼ぐ。 正則化 (reg_covar) で対角に微小値を加える。
Q: 変分推論とどう違う?
A: EM はパラメータの「点推定」、 変分推論は「分布推定」。 不確実性を扱いたいなら変分。
Q: SSDSE-B-2026 の都道府県データに EM を適用する意義は?
A: 47 都道府県のうち、 「人口規模」「高齢化率」「消費支出」などを変数として GMM をフィットすると、 大都市圏 (東京・大阪・愛知)・地方中核 (北海道・宮城・広島)・人口減少地域 (秋田・高知・島根) といった潜在的なグループ構造が自動抽出できる。 k-means と違い確率的所属度を返すので、 「茨城県は大都市圏と地方中核の中間 (確率 0.44 と 0.50)」のような柔軟な解釈が可能。 政策立案では、 こうしたソフトクラスタリングが境界事例の議論に有用となる。
Q: 欠損データへの応用は?
A: EM は元々 欠損データ解析 のために提唱された汎用枠組みであり、 欠損値を E ステップで補完しつつ M ステップでパラメータ推定を反復する形で、 MAR 仮定下では尤度最大化推定が得られる。 多重代入法 (Multiple Imputation) の理論的基礎にもなっている。
Q: EM アルゴリズムの収束は厳密に保証されるのか?
A: 対数尤度の単調非減少性は厳密に保証される (各反復で必ず増えるか同じ)。 これは Jensen 不等式と Q 関数の性質から数学的に証明可能。 ただし大域最適への収束は保証されない。 局所最適や鞍点に捕まる可能性があるため、 複数初期値での試行と BIC による解の選択が標準的な対処法である。 SSDSE のような小サンプル (n=47) ではこの問題が顕著なので注意が必要。

📜 歴史と背景

歴史と位置づけ:EM アルゴリズムは 1977 年、 Dempster, Laird, Rubin による論文「Maximum Likelihood from Incomplete Data via the EM Algorithm」(Journal of the Royal Statistical Society, Series B)で統一的に定式化されました。 ただし、 個別の問題(混合分布・欠損データ・隠れマルコフ)に対する反復解法はそれ以前から複数の研究者が独立に発見していました(例:Baum-Welch、 1970)。

主な発展:

EM は「最尤推定を 反復的・分解的 に実行する」普遍的な道具立てとして、 統計・機械学習・信号処理・バイオインフォマティクスに広く浸透しました。

⚠️ EM を実装する前に知っておくべき 7 つの落とし穴

  1. 初期値依存で局所最適に捕まる — EM は対数尤度を**局所**最大化するだけで大域最適は保証しない。 sklearn の n_init=10(10 通りの初期値から始めて最良を選ぶ)、 k-means++ 初期化、 焼きなまし併用が標準対策。
  2. 共分散の縮退(singular)で尤度が無限大に発散 — 1 クラスタが 1 点に縮退すると共分散がゼロ→対数尤度が +∞。 sklearn の reg_covar=1e-6 で対角に微小値を加える「数値正則化」が必須。
  3. Label switching でクラスタ番号が走査の度に入れ替わる — EM のクラスタ番号は順序を持たないので、 並列実行で「クラスタ 1」が指す対象が変わる。 比較したいなら混合比でソート、 もしくはアラインメント手続き(ハンガリアン法)で番号を揃える。
  4. K 選定で AIC は過大、 BIC は過小気味 — AIC は罰則 2k、 BIC は罰則 k log n。 n が大きいほど BIC は K を小さく選ぶ。 ICL(Integrated Completed Likelihood)が GMM では推奨されることも。
  5. 共分散構造の誤指定で歪んだクラスタが出る — covariance_type='spherical' で実際は楕円形のデータを当てると、 球形に押し込められて変な分割になる。 まず full で試し、 BIC で他構造と比較。
  6. 収束判定が緩いと尤度が低い解で止まる — sklearn の tol=1e-3 はやや緩め。 厳しい応用では tol=1e-6 + max_iter=1000 に。 ただし無限ループ気味になることがあるので双方併用。
  7. MAR(Missing At Random)でないと EM は無効 — 欠損値補完で EM を使う場合、 欠損メカニズムが MAR でないと最尤推定が偏る。 完全な MNAR(Missing Not At Random)には選択モデルや pattern-mixture モデルが必要。

🔁 EM 派生・拡張アルゴリズム — ECM / GEM / MCEM / Stochastic EM

EM はモジュラーで、 E と M をそれぞれ「近似」したり「分割」することで多様な派生が生まれる。

📊 表: 主要派生アルゴリズム

アルゴリズムE ステップM ステップ用途
標準 EM事後分布を完全計算Q を完全最大化GMM, HMM
GEM完全Q を増加させるだけM が解析不能なとき
ECM完全パラメータ別に分割最大化高次元パラメータ
MCEMMC サンプリングで近似完全事後が解析不能
SEM1 サンプリングで近似完全局所最適回避
Online EM逐次更新指数加重ストリーミング
変分 EM変分近似 $q(Z)$ で代用ELBO 最大化LDA, VAE

🐍 ECM 風の実装 — 平均と分散を別ステップで更新

このコードでやること: 1D GMM(K=2)の EM を素朴に手書きし、 E ステップ → 平均更新 → 分散更新と分割。 SSDSE 47 県の人口を 2 つの正規分布の混合と仮定。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np
import pandas as pd

# latest はこのあとのブロックで作っているので、ここでも用意しておく
_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
latest = _df[_df['SSDSE-B-2026'] == 2023].copy()

from scipy.stats import norm

pop = latest['A1101'].values.astype(float)
n = len(pop)

# 初期化
mu = np.array([pop.min(), pop.max()])
sigma = np.array([pop.std(), pop.std()])
pi = np.array([0.5, 0.5])

for it in range(50):
    # E step: 責任度
    r0 = pi[0] * norm.pdf(pop, mu[0], sigma[0])
    r1 = pi[1] * norm.pdf(pop, mu[1], sigma[1])
    gamma = np.column_stack([r0, r1]) / (r0 + r1)[:, None]
    Nk = gamma.sum(axis=0)
    # M step 1: 平均
    mu = (gamma * pop[:, None]).sum(axis=0) / Nk
    # M step 2: 分散
    var = (gamma * (pop[:, None] - mu) ** 2).sum(axis=0) / Nk
    sigma = np.sqrt(var)
    # M step 3: 混合比
    pi = Nk / n

print(f"μ = {(mu/1e4).round(1)} (万人)")
print(f"σ = {(sigma/1e4).round(1)} (万人)")
print(f"π = {pi.round(3)}")

📤 実行例:

μ = [136.5 639.3] (万人) σ = [ 55.5 321.5] π = [0.745 0.255]

💬 47 県の人口は「平均 136 万人の小規模県 75%」と「平均 639 万人の大規模県 25%」の混合と解釈できる。 ECM 風に M を分解しても sklearn の同時最大化と同じ結果に収束する。

🛠 実装チェックリスト — EM を初めて書く前に

🗺 学習ロードマップ

🗺 学習ロードマップ

  1. レベル 1 — 潜在変数の概念。 観測尤度と完全データ尤度の区別。
  2. レベル 2 — E ステップ・M ステップの 1 反復を手書きで(1 次元混合ガウス)。
  3. レベル 3 — sklearn の GaussianMixture で SSDSE クラスタリング。 BIC で K 選定。
  4. レベル 4 — HMM、 LDA、 因子分析、 確率的 PCA — 各種潜在変数モデル。
  5. レベル 5 — 変分 EM、 オンライン EM、 Stochastic EM。
  6. レベル 6 — VAE、 GAN、 拡散モデルでの潜在表現と EM の関係。

📊 比較表(兄弟手法・選択肢)

クラスタリング手法の比較

手法割当形状仮定K の選定
k-meansハード球状・等分散エルボー法、 シルエット
k-medoidsハード距離ベースシルエット
GMM (EM)ソフト楕円体、 共分散自由BIC / AIC
DBSCANハード + ノイズ密度ベース自動推定
階層 (Ward)階層分散最小化樹形図
スペクトラルハード連結成分固有値ギャップ

📖 用語ミニ辞典

用語意味
潜在変数観測されない隠れた変数
完全データ観測 + 潜在の組
観測尤度$p(X|\theta)$
完全データ尤度$p(X,Z|\theta)$
E ステップ事後分布で完全尤度の期待値
M ステップ期待値(Q 関数)を最大化
Q 関数E ステップで作られる補助関数
ELBOEvidence Lower Bound、 変分推論の指標
GMMGaussian Mixture Model
HMMHidden Markov Model
LDALatent Dirichlet Allocation
Jensen 不等式凸関数と期待値の関係

🍳 コードレシピ(コピペ用 15 連発)

レシピコード
sklearn GMM
1
2
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(3).fit(X)
クラスタ予測
labels = gmm.predict(X)
事後確率
proba = gmm.predict_proba(X)
BIC
gmm.bic(X)
AIC
gmm.aic(X)
共分散タイプ
GaussianMixture(3, covariance_type='diag')
初期値固定 (k-means)
GaussianMixture(3, init_params='kmeans', n_init=10)
収束判定
GaussianMixture(3, tol=1e-6, max_iter=500)
尤度
gmm.score(X)  # 1 サンプル当たりの平均対数尤度
HMM
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import numpy as np
import pandas as pd

# X(時系列として扱う観測値)を用意する。hmmlearn は 2 次元を求める
_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_tokyo = _d[_d['Prefecture'] == '東京都'].sort_values('SSDSE-B-2026')
X = _tokyo[['A1101', 'A4101']].astype(float).to_numpy()

from hmmlearn.hmm import GaussianHMM
hmm = GaussianHMM(n_components=3).fit(X)
LDA (scikit-learn)
1
2
from sklearn.decomposition import LatentDirichletAllocation
lda = LatentDirichletAllocation(n_components=5).fit(X)
欠損補完 (Iterative)
1
2
3
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
filled = IterativeImputer().fit_transform(X)
EM の対数尤度履歴
gmm.lower_bound_  # ELBO 風
クラスタ中心
gmm.means_
混合比
gmm.weights_

📊 SSDSE-B-2026 で EM を回す詳細レシピ

SSDSE-B-2026(都道府県別社会経済データ集 2026)を題材に、 EM アルゴリズムを段階的に適用するレシピを用意しました。 単一のコードブロックではなく、 探索→ 前処理 → モデル選択 → 解釈の 4 段階に分けて理解することで、 実務的な使い方が身につきます。

  1. 探索的データ分析(EDA): df.describe() で各列の平均・分散・歪度を確認。 SSDSE-B-2026 の A1101(人口)は東京が極端な外れ値で歪度が高いため、 対数変換が有効。
  2. 標準化: StandardScaler で各列を平均 0・分散 1 に。 EM/GMM はスケールに敏感なので必須。
  3. K の選定: K=2〜10 で BIC を計算し最小点を選ぶ。 都道府県データでは K=3〜5 が解釈しやすいことが多い。
  4. 初期値多試行: n_init=10 以上で局所最適を回避。 init_params='kmeans' で k-means の解を初期値に使うと収束が速い。
  5. 解釈: 各クラスタの平均ベクトルから「これは都市部」「これは地方」とラベル付け。 シルエット係数や ARI で他手法と比較。

SSDSE-B-2026 を使うと、 数値結果と地理的直感が一致するかどうかで EM の妥当性を検証できます。 例えば 東京・大阪・愛知が同一クラスタに入らない結果は、 特徴量の選択を見直すサインです。

段階SSDSE-B-2026 での具体例確認指標
EDAA1101, L3221, 高齢化率の分布歪度・尖度
標準化StandardScaler 後、 mean≈0, std≈1describe()
K 選定K=2..8 で BIC 最小BIC
解釈都市集中/首都圏/地方/過疎クラスタ平均

この 4 段階レシピは、 EM/GMM だけでなく k-means、 階層クラスタリング、 DBSCAN など他のクラスタリング手法にも汎用的に応用できます。 SSDSE-B-2026 を題材にすれば、 数値結果の妥当性を「東京は都市部」「秋田は地方」など地理感覚と照合して直感的に検証できます。 これが他のデータセットでは難しい SSDSE-B-2026 ならではの利点です。

EM アルゴリズムが解く問題群の俯瞰:

                        【最尤推定】
                             │
                  ┌──────────┴──────────┐
            観測のみ                潜在変数あり
                 │                       │
            微分で解析的解        EM アルゴリズム
                                          │
                      ┌──────────┬───────┴──────┬──────────┐
                     GMM        HMM            LDA       欠損補完
                  (混合ガウス) (系列の潜在)  (トピック)   (IterativeImputer)

🔍 理論深掘り:EM が単調収束する理由(Jensen → Q 関数)

EM が「観測対数尤度を単調非減少にする」ことは、 Jensen の不等式と KL ダイバージェンスの非負性で示せます。 ここでは Bishop (2006) の議論に従って、 SSDSE-B-2026 を念頭にステップごとに丁寧に追います。

ステップ 1:観測尤度の下界(ELBO)を作る

観測 $X$、 潜在 $Z$、 パラメータ $\theta$。 任意の分布 $q(Z)$ について、 観測対数尤度を分解できます:

$$\log p(X|\theta) = \mathcal{L}(q,\theta) + \mathrm{KL}(q\,\|\,p(Z|X,\theta))$$

ここで $\mathcal{L}(q,\theta) = \mathbb{E}_q[\log p(X,Z|\theta)] - \mathbb{E}_q[\log q(Z)]$ は ELBO(Evidence Lower Bound)。 KL は常に非負なので、 $\log p(X|\theta) \ge \mathcal{L}(q,\theta)$ となります。

ステップ 2:E ステップで KL を 0 にする

$q(Z) = p(Z|X,\theta^{(t)})$ と置けば KL は 0 になり、 ELBO は観測対数尤度と一致。 これが「現パラメータでの潜在の事後分布」を求める E ステップの意味。

ステップ 3:M ステップで ELBO を最大化

$\theta^{(t+1)} = \arg\max_\theta \mathcal{L}(q,\theta) = \arg\max_\theta \mathbb{E}_{p(Z|X,\theta^{(t)})}[\log p(X,Z|\theta)]$。 これが Q 関数最大化。

ステップ 4:単調性の確認

$\log p(X|\theta^{(t+1)}) \ge \mathcal{L}(q^{(t)}, \theta^{(t+1)}) \ge \mathcal{L}(q^{(t)}, \theta^{(t)}) = \log p(X|\theta^{(t)})$。 これで「反復ごとに観測尤度が単調非減少」が示されました。

SSDSE-B-2026 での実証

47 都道府県の総人口・65 歳以上人口(A1101・A1303)を K=3 の GMM に当てはめると、 数反復で対数尤度(ELBO)が単調に増加して収束することが観察できます。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) 北海道 5,092,000 1,681,000 東京都 14,086,000 3,205,000 沖縄県 1,468,000 350,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
X = df[['A1101','A1303']].astype(float).to_numpy()
X = StandardScaler().fit_transform(X)
gmm = GaussianMixture(n_components=3, n_init=10, max_iter=200,
                       random_state=0, warm_start=False).fit(X)
print('対数尤度の下界:', gmm.lower_bound_)
print('反復回数:', gmm.n_iter_)
print('収束:', gmm.converged_)
📥 入力例: data/raw/SSDSE-B-2026.csv(年度=2023、 47 都道府県) 使用列: A1101(総人口)、 A1303(65 歳以上人口) 前処理: StandardScaler で標準化
📤 実行例: 対数尤度の下界: 0.996(サンプル平均) 反復回数: 3(収束まで) 収束: True
💬 読み方: 総人口と 65 歳以上人口はほぼ比例(相関が高い)ため実効的な分離が容易で、 EM はわずか 3 反復で収束する。 対数尤度(ELBO)は単調に増加し、 警告なしで converged=True なら局所最適に到達している。

🧪 数値計算上の注意点(実装で必ず踏む罠)

1. log-sum-exp トリック

E ステップで $\gamma_{ik} = \pi_k \mathcal{N}(x_i|\mu_k,\Sigma_k) / \sum_l \pi_l \mathcal{N}(x_i|\mu_l,\Sigma_l)$ を計算するとき、 高次元では分子・分母ともに極端に小さく、 オーバーフロー/アンダーフローを起こします。 ログ空間で計算して log-sum-exp を使うのが定石。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import pandas as pd

# log_normal_pdf / X / pi / mu / sigma を用意する(1 次元 2 成分の混合)
def log_normal_pdf(x, mu, sigma):
    x = np.asarray(x).reshape(-1, 1)
    return -0.5 * np.log(2 * np.pi * sigma ** 2) - (x - mu) ** 2 / (2 * sigma ** 2)

_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_d = _d[_d['SSDSE-B-2026'] == 2023]
X = np.log(_d['A1101'].astype(float).to_numpy()).reshape(-1, 1)   # sklearn は 2 次元を求める
pi = np.array([0.5, 0.5])
mu = np.array([X.min(), X.max()])
sigma = np.array([X.std(), X.std()])

import numpy as np
from scipy.special import logsumexp
# log γ_ik = log π_k + log N(x|μ_k,Σ_k) - logsumexp over k
log_w = np.log(pi)[None, :] + log_normal_pdf(X, mu, sigma)  # (n,K)
log_gamma = log_w - logsumexp(log_w, axis=1, keepdims=True)
gamma = np.exp(log_gamma)
🎯 解説: 多次元ガウスの確率密度は次元が高いと指数的に小さくなる。 log 空間で計算して logsumexp で正規化することで、 IEEE 754 の浮動小数点精度範囲内で安定計算できる。 SSDSE-B-2026 のように 10 次元以上の特徴量を使うときには必須。
📥 入力例: 標準化済み (n=47, d=10) の特徴量行列 X、 K=3、 mu (3,10)、 sigma (3,10,10)
📤 実行例: log_gamma の各行は最大 0、 exp すれば責任度 γ (sum=1) が得られる。 オーバーフロー警告なし。
💬 読み方: scipy.special.logsumexp(a) = log(sum(exp(a)))。 内部で max を引いてから exp するので、 大きな値があっても overflow しない。 sklearn.mixture.GaussianMixture も内部で同じテクニックを使っている。

2. 共分散行列の正則化

1 つのクラスタにデータがほぼ 1 点しか入らないと、 $\Sigma_k$ が特異になり対数尤度が発散します(縮退)。 sklearn の reg_covar=1e-6 がデフォルトで対角に微小値を加える正則化。

1
2
3
4
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=5, covariance_type='full',
                       reg_covar=1e-4,  # 既定 1e-6 では小さすぎる場合
                       n_init=20, max_iter=500, random_state=0).fit(X)
🎯 解説: SSDSE-B-2026 の 47 都道府県で K=5 以上にすると、 北海道や沖縄のような外れ値が独立クラスタになり、 共分散が縮退する。 reg_covar を 1e-4 程度まで上げて安定化させる。
📥 入力例: SSDSE-B-2026 の 47 都道府県、 K=5、 全特徴量を標準化
📤 実行例: 5 クラスタすべてに 5 件以上の都道府県が振り分けられ、 共分散縮退の警告なし
💬 読み方: reg_covar はリッジ正則化と同じ発想で、 Σ → Σ + λI とすることで逆行列の計算を安定化させる。 過剰に大きい λ はクラスタを球状に潰すので、 1e-4〜1e-2 を試す。

3. 初期化の重要性

EM は局所最適に陥る。 sklearn は既定で init_params='kmeans'(k-means の解で初期化)。 n_init=10 で 10 回試行し最良を採用する。

初期化方法長所短所
ランダムシンプル局所最適に落ちやすい
k-means速く合理的球状クラスタに偏る
k-means++分散させて初期化計算がやや重い
事前知識領域知識を活かせる主観的になる

4. 収束判定の閾値

ELBO の改善量 $\Delta \mathcal{L} < \epsilon$ で停止。 既定は $\epsilon = 10^{-3}$。 精度が要るなら $10^{-6}$ まで小さく。

🌀 EM の変種・拡張

1. Generalized EM (GEM)

M ステップで Q を完全に最大化せず、 増やすだけでも単調性は保たれる。 計算が軽い。

2. Stochastic EM (SEM)

E ステップで潜在変数を分布からサンプリング。 ノイズが入る代わりに局所最適から抜けやすい。

3. Online EM

大規模データで全データを保持できないとき、 ミニバッチで増分的に更新。

4. 変分 EM (VBEM)

E ステップで $q(Z)$ を近似分布族に制限。 ベイズ的に事前分布も入れられる。 LDA や VAE の基礎。

5. ECM / ECME

Expectation Conditional Maximization。 M ステップを複数の条件付き最大化に分解。 Meng & Rubin (1993)。

6. PX-EM (Parameter Expansion)

パラメータ空間を拡大して収束を加速。 Liu, Rubin, Wu (1998)。

7. SAEM (Stochastic Approximation EM)

SEM とロビンス・モンロー型確率近似の融合。 強い理論保証。

8. MCEM (Monte Carlo EM)

E ステップで期待値を MC サンプリングで近似。 複雑な潜在モデルで活躍。

📈 経験的振る舞い:SSDSE-B-2026 での K 選定

SSDSE-B-2026 の 47 都道府県を様々な特徴量集合で GMM クラスタリングしたときの BIC を比較します。

特徴量セットK=2K=3K=4K=5選定 K
人口 + 高齢化率205.6208.4191.3195.4K=4
人口 + 消費支出232.4237.4228.7224.1K=5
人口 + 出生率 + 死亡率329.2325.2313.5336.4K=4
全数値列(標準化後)5578.522347.046850.668766.6K=2

特徴量セットによって BIC 最小の K は 2〜5 と変わり、 都市部・地方中核・過疎地域・特殊県のような構造が現れます。 なお「全数値列」(112 列)は n=47 に対して次元が大きすぎ、 full 共分散の GMM が過剰適合して BIC が K とともに発散します(次元の呪い)。 実務では少数の解釈可能な特徴量に絞るのが鉄則です。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) A4200(死亡数) 北海道 5,092,000 1,681,000 24,430 75,120 東京都 14,086,000 3,205,000 86,348 137,241 沖縄県 1,468,000 350,000 12,549 15,110 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
cols = ['A1101','A1303','A4101','A4200']  # 人口・65 歳以上・出生・死亡
X = StandardScaler().fit_transform(df[cols].astype(float))

bics = []
for k in range(1, 9):
    g = GaussianMixture(n_components=k, n_init=20, max_iter=500,
                         random_state=0).fit(X)
    bics.append((k, g.bic(X), g.aic(X)))
for k, b, a in bics:
    print(f'K={k}  BIC={b:.1f}  AIC={a:.1f}')
best_k = min(bics, key=lambda t: t[1])[0]
print('best K (BIC):', best_k)
🎯 解説: BIC は -2 logL + k log n でモデル複雑度に強いペナルティを課す。 K=3 または K=4 で最小になることが多い。 解釈の容易さと BIC の最小値を両立する K を選ぶ。
📥 入力例: SSDSE-B-2026 2023 年 47 都道府県、 4 列(人口・65 歳以上・出生・死亡)、 標準化済み
📤 実行例: K=1 BIC=-131.0 AIC=-156.9 K=2 BIC=-289.7 AIC=-343.4 K=3 BIC=-307.1 AIC=-388.6 K=4 BIC=-302.0 AIC=-411.1 K=5 BIC=-332.0 AIC=-468.9 K=6 BIC=-321.2 AIC=-485.9 K=7 BIC=-318.2 AIC=-510.6 K=8 BIC=-334.7 AIC=-554.9 best K (BIC): 8
💬 読み方: 4 特徴量(人口・65 歳以上・出生・死亡)は相関が強く、 BIC は K=5(-332.0)と K=8(-334.7)がほぼ同値で「谷が平坦」。 AIC は罰則が弱く K を増やすほど下がり続ける。 情報量規準の最小値だけでは決められないので、 解釈しやすい K=3(都市集中・中堅・高齢過疎)を採るのが実務的。

📉 収束モニタリング:対数尤度の軌跡を見る

EM は単調収束しますが、 収束速度はパラメータの初期化と問題の凸性で大きく変わります。 SSDSE-B-2026 で 47 都道府県を GMM K=3 にしたときの対数尤度履歴を可視化します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
X = StandardScaler().fit_transform(df[['A1101','A1303']].astype(float))

histories = []
for seed in range(5):
    history = []
    gmm = GaussianMixture(n_components=3, n_init=1, max_iter=1,
                          warm_start=True, random_state=seed)
    for it in range(50):
        gmm.fit(X)
        history.append(gmm.score(X) * len(X))
    histories.append(history)

fig, ax = plt.subplots(figsize=(8, 5))
for seed, h in enumerate(histories):
    ax.plot(h, label=f'seed={seed}')
ax.set_xlabel('反復回数')
ax.set_ylabel('対数尤度')
ax.set_title('SSDSE 47 都道府県 GMM K=3 — 初期値ごとの収束')
ax.legend()
plt.savefig('em_convergence.png', dpi=100)
🎯 解説: 5 つの異なる乱数シードで EM を 50 反復実行し、 対数尤度の軌跡を描く。 全シードで単調増加するが、 最終値は初期値依存で多少ずれる。
📥 入力例: SSDSE-B-2026 47 都道府県(人口・65 歳以上人口)、 標準化済み、 K=3
📤 実行例(実測) em_convergence.png が保存される(標準出力は無い)。各 seed の対数尤度は seed=0: 36.5 → 39.8 seed=1: 46.8 → 46.9 seed=2: 35.7 → 35.7 seed=3: 36.5 → 39.8 seed=4: 46.8 → 46.9 と単調増加し、40 前後(seed 0,3)と 47 前後(seed 1,4)の 2 つの局所解に 分かれて収束する。初期値で最終値が変わる。
💬 読み方: 「単調増加して横這いになったら収束」と判断。 グラフを見て「速い・遅い」「最終値の安定性」を視覚的に確認するのが定石。 n_init=10 で複数試行し、 最良値を採用する。

⚔️ GMM vs k-means:SSDSE-B-2026 で比較

同じデータで GMM と k-means を実行し、 ARI(Adjusted Rand Index)で結果の一致度を測ります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd
from sklearn.mixture import GaussianMixture
from sklearn.cluster import KMeans
from sklearn.metrics import adjusted_rand_score, silhouette_score
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
X = StandardScaler().fit_transform(df[['A1101','A1303']].astype(float))

gmm = GaussianMixture(n_components=3, n_init=20, random_state=0).fit(X)
km = KMeans(n_clusters=3, n_init=20, random_state=0).fit(X)
labels_g = gmm.predict(X)
labels_k = km.labels_

print('ARI:', adjusted_rand_score(labels_g, labels_k))
print('GMM シルエット:', silhouette_score(X, labels_g))
print('k-means シルエット:', silhouette_score(X, labels_k))
print('GMM 事後確率の最大(境界の県):')
import numpy as np
margin = gmm.predict_proba(X).max(axis=1)
border_idx = np.argsort(margin)[:5]
print(df.iloc[border_idx][['Prefecture']].values.ravel())
print('確率:', margin[border_idx])
🎯 解説: GMM は「確率的所属」を返すので、 境界の県を特定できる。 k-means は「ハード割当」のみで、 境界の情報が失われる。 SSDSE-B-2026 では境界の県(沖縄・宮城・新潟など)が興味深い。
📥 入力例: SSDSE-B-2026 2023 年 47 都道府県、 2 特徴量、 標準化済み、 K=3
📤 実行例: ARI: 1.0(完全一致) GMM シルエット: 0.777 k-means シルエット: 0.777 境界の県: 静岡・茨城・広島・新潟・京都 確率: 0.572, 0.997, 0.999, 0.999, 1.0
💬 読み方: 人口・65 歳以上人口の 2 次元では GMM と k-means の割当が完全一致(ARI=1.0)し、 シルエットも同値。 それでも GMM は事後確率を返すので、 静岡県だけは確率 0.57 と低く「境界の県」と分かる。 政策決定で「グレーゾーン」を扱うなら GMM が有用。

🎼 GMM 共分散タイプの比較

GaussianMixture には 4 種類の共分散構造があります。 SSDSE-B-2026 47 都道府県で比較。

covariance_type形状パラメータ数(d=4, K=3)SSDSE BIC 例
'spherical'球状、 各クラスタ 1 つの分散3 × 1 + 3 × 4 + 2 = 1773.9
'diag'軸平行楕円3 × 4 + 3 × 4 + 2 = 26104.3
'tied'全クラスタ共通の楕円4 × 5 / 2 + 3 × 4 + 2 = 24-228.8
'full'各クラスタ独立の楕円3 × 4 × 5 / 2 + 3 × 4 + 2 = 44-307.1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
X = StandardScaler().fit_transform(
    df[['A1101','A1303','A4101','A4200']].astype(float))

for ct in ['spherical','diag','tied','full']:
    g = GaussianMixture(n_components=3, covariance_type=ct,
                         n_init=20, random_state=0).fit(X)
    print(f'{ct:10s}  BIC={g.bic(X):.1f}  AIC={g.aic(X):.1f}')
🎯 解説: 共分散タイプは「クラスタ形状の仮定」。 'spherical' は最も単純、 'full' は最も柔軟。 BIC が最小のものを選ぶが、 SSDSE のような小標本(n=47)では 'full' が過剰適合する可能性も。
📥 入力例: SSDSE-B-2026 2023 年 47 都道府県、 4 特徴量、 標準化済み、 K=3
📤 実行例: spherical BIC= 73.9 AIC= 42.5 diag BIC=104.3 AIC= 56.2 tied BIC=-228.8 AIC=-273.2 full BIC=-307.1 AIC=-388.6
💬 読み方: この 4 特徴量では 'full'(-307.1)が最良 BIC で、 相関を捉える 'tied'(-228.8)が省パラメータの次点。 一方 'spherical'/'diag' は相関を無視するため大きく劣る。 特徴量間に強い相関がある社会経済データでは full/tied が有利だが、 n=47 では過剰適合に注意し 'tied' を選ぶのも手堅い。

🧭 ベイズ的視点:EM = MAP の特殊形

EM は最尤推定(MLE)を反復で実行する手法ですが、 事前分布を加えると MAP(Maximum A Posteriori)推定に拡張できます。 これを「Bayesian EM」または「MAP EM」と呼びます。 SSDSE-B-2026 のように小標本(n=47)では、 事前分布で正則化すると安定します。

BayesianGaussianMixture との関係

scikit-learn の BayesianGaussianMixture は、 混合比に Dirichlet 事前、 平均・共分散に Normal-Wishart 事前を置いた変分 EM。 「データに支持されない余分なクラスタの混合比を自動的に 0 に近づける」性質があり、 K 選定が緩くなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import pandas as pd
from sklearn.mixture import BayesianGaussianMixture
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
X = StandardScaler().fit_transform(
    df[['A1101','A1303','A4101','A4200']].astype(float))

bgm = BayesianGaussianMixture(n_components=10,  # 大きめに設定
                                weight_concentration_prior_type='dirichlet_process',
                                weight_concentration_prior=0.1,  # 小さいほど少数クラスタを好む
                                n_init=10, max_iter=500,
                                random_state=0).fit(X)
print('実効クラスタ数(重み > 0.01):', (bgm.weights_ > 0.01).sum())
print('weights:', bgm.weights_.round(3))
🎯 解説: BayesianGaussianMixture は「最大クラスタ数を大きめに設定し、 実効的に使われるクラスタ数をデータから自動決定」する。 Dirichlet Process 事前で疎な解(少数クラスタ)が出る。
📥 入力例: SSDSE-B-2026 47 都道府県、 4 特徴量、 標準化済み、 n_components=10
📤 実行例: 実効クラスタ数: 4 weights: [0.809 0.132 0.029 0.019 0.005 0.005 0. 0. 0. 0.] → 大きな 4 つだけが実質的(特に第 1 クラスタが 8 割)
💬 読み方: weight_concentration_prior が小さいほど(例 0.1)少数のクラスタで説明しようとする。 1.0 なら均等。 実測では重み > 0.01 のクラスタが 4 つ残り、 残り 6 つは 0 に潰れる。 SSDSE で K の事前知識がなくても、 自動的にデータ駆動で実効 K=4 という答えを得られる。

🤖 EM と VAE(変分オートエンコーダ)の橋渡し

近年深層学習で広く使われる VAE(Variational Autoencoder)は、 EM の精神を引き継ぎ、 ニューラルネットワークでパラメータ化したものと見なせます。

項目EM (GMM)VAE
潜在変数離散カテゴリ $Z \in \{1,...,K\}$連続ベクトル $z \in \mathbb{R}^d$
E ステップ事後確率 $\gamma_{ik}$ 計算encoder で $q_\phi(z|x)$ 推定
M ステップ$\theta$ の解析的更新decoder $p_\theta(x|z)$ の SGD 更新
下界Q 関数 (=ELBO の特殊形)ELBO 直接最大化
規模数 100 〜 数万サンプル100 万 〜 数億サンプル
非凸性局所最適に注意SGD のノイズで局所最適を抜けやすい

SSDSE-B-2026 のように小標本では GMM/EM が圧勝。 画像・テキストのような大規模・高次元では VAE が優位。 「EM の発想 → 変分推論 → VAE」というロードマップは、 現代統計&ML の中核。

🧩 欠損データ補完への EM 応用(詳細)

SSDSE-B-2026 はほぼ完全データですが、 一部の年度・項目に欠損があります。 EM で多変量正規分布を仮定して補完する古典的手法を紹介。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pandas as pd
import numpy as np
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import IterativeImputer
from sklearn.linear_model import BayesianRidge

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
cols = ['A1101','A1303','A4101','A4200','L3221']  # 数値 5 列(人口・65歳以上・出生・死亡・消費支出)
sub = df[cols].astype(float)

# 5% を人工的に欠損させる(実データ忠実なので「無作為に 5% 削除」のみ)
sub_missing = sub.copy()
mask = np.tri(len(sub), len(cols), dtype=bool)  # 三角マスクで代用
# 注: 実 SSDSE は元来欠損がほぼなく、 ここでは方法論のデモ
sub_missing.iloc[10:15, 2] = np.nan

# IterativeImputer は EM 風の反復補完(各列を他列で予測 → 繰り返し)
imp = IterativeImputer(estimator=BayesianRidge(), max_iter=30, tol=1e-4)
filled = imp.fit_transform(sub_missing)
print('補完前 NaN 数:', sub_missing.isna().sum().sum())
print('補完後 NaN 数:', np.isnan(filled).sum())
# 真値との比較(行 10-14、 列 2)
print('真値:', sub.iloc[10:15, 2].to_numpy())
print('補完値:', filled[10:15, 2])
🎯 解説: IterativeImputer は内部で「他列を使った回帰モデルで各欠損列を予測」→「予測値で更新して再学習」を反復する EM 風アルゴリズム。 BayesianRidge をベース推定器とすると安定で、 多変量正規 EM の近似になる。
📥 入力例: SSDSE-B-2026 2023 年、 5 数値列、 5 行 × 1 列を人工的に NaN 化(実データ性は維持)
📤 実行例: 補完前 NaN 数: 5 補完後 NaN 数: 0 真値 (A4101 出生数、 埼玉・千葉・東京・神奈川・新潟): [42108 35658 86348 53991 10916] 補完値: [45568 38515 101987 60952 10779] → 平均絶対誤差率 (MAPE) は約 9.7%
💬 読み方: 5 件を人工欠損させて復元すると MAPE は約 10%。 出生数は人口とほぼ比例するため他列(特に総人口)からの回帰で概ね復元できるが、 大都市の絶対量が大きい行では誤差も大きめに出る。 相関の強い社会経済データでは IterativeImputer/EM 補完が有効。 注意:MNAR(欠損が値自体に依存)では補完が偏るので、 欠損機序の確認が先決。

⛓ HMM の Baum-Welch(EM の系列版)

HMM(Hidden Markov Model)における EM 推定アルゴリズムは「Baum-Welch」と呼ばれ、 EM の有名な特殊形です。 SSDSE-B-2026 のような年度別パネル時系列(2023, 2022, 2021…)にも応用できます。

HMM の構造

Baum-Welch の流れ

  1. 前向き $\alpha_t(i)$:$P(X_{1:t}, S_t=i|\lambda)$
  2. 後ろ向き $\beta_t(i)$:$P(X_{t+1:T}|S_t=i,\lambda)$
  3. E ステップ:$\gamma_t(i) = \alpha_t(i)\beta_t(i) / \sum_j \alpha_t(j)\beta_t(j)$、 $\xi_t(i,j) = \alpha_t(i) A_{ij} B_j(X_{t+1}) \beta_{t+1}(j) / Z$
  4. M ステップ:$\hat A_{ij} = \sum_t \xi_t(i,j) / \sum_t \gamma_t(i)$、 $\hat\pi_i = \gamma_1(i)$、 $\hat B_i$ は重み付き再推定
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd
import numpy as np
from hmmlearn.hmm import GaussianHMM

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
# 東京都(Code='R13000')の年度別データ(2012〜2023 の 12 年分)を抽出
tokyo = df[df['Code']=='R13000'].sort_values('SSDSE-B-2026')
X = tokyo[['A1101','A1303']].astype(float).to_numpy()

# 3 状態 HMM
hmm = GaussianHMM(n_components=3, covariance_type='full',
                   n_iter=100, random_state=0)
hmm.fit(X)
print('遷移行列 A:')
print(hmm.transmat_.round(3))
print('状態系列:', hmm.predict(X))
print('対数尤度:', hmm.score(X))
🎯 解説: 東京の年度別「人口・65 歳以上人口」を 3 状態 HMM に当てはめる。 Baum-Welch(EM)で遷移行列と出力分布を推定し、 各年度の潜在状態を Viterbi で復号する。
📥 入力例: SSDSE-B-2026 東京 (Code=R13000) の年度別、 2 列(A1101・A1303)× 12 年分(2012〜2023)の時系列。 別途 pip install hmmlearn が必要。
📤 実行例(模式): わずか 12 時点への 3 状態 HMM 当てはめは自由度が高く数値的に不安定だが、 収束すると遷移行列は対角優位(各行の対角成分 ≈ 0.9)になり、 状態の「慣性」が読み取れる。 例: 遷移行列 A ≈ [[0.92, 0.05, 0.03], [0.04, 0.91, 0.05], [0.02, 0.06, 0.92]]
💬 読み方: 対角要素が大きいほど「状態の慣性」が強い。 SSDSE の年度別パネルは長期トレンドが支配的なので、 「人口拡大期」「停滞期」「縮小期」の 3 状態が自然に現れる。 ただし 12 時点では推定が不安定なので、 実務では複数県を束ねる・年数を増やす等が必要。

🎓 上級者向け FAQ(10 問)

Q: EM の収束速度は?
A: 線形収束。 Newton 法のような二次収束ではない。 高次元ではしばしば遅い。 加速法(Aitken、 Quasi-Newton)が研究されている。
Q: EM の標準誤差はどう計算する?
A: 観測情報量行列(Louis の方法)または、 ブートストラップ。 sklearn には標準誤差を直接返す機能はないので、 重ね合わせて計算する。
Q: ラベル切り替え(Label Switching)問題とは?
A: 混合モデルではクラスタの番号が任意(K! 通りの同じ尤度を持つ解がある)。 ベイズ的 MCMC では深刻、 EM では初期化で固定するだけで済む。
Q: EM は MCMC とどう違う?
A: EM は「パラメータの点推定」、 MCMC は「事後分布のサンプリング」。 EM は速く決定論的、 MCMC は遅いが不確実性も得られる。
Q: 凸 EM はあるか?
A: 一部の制約付き設定(例:指数族+共役事前)では凸化される。 「Tensor decomposition」によるグローバル最適 EM(Anandkumar 2014)など、 近年理論研究も進んでいる。
Q: EM と勾配上昇法の関係は?
A: M ステップが解析的に解けない場合、 1 ステップの勾配上昇でも OK(Generalized EM = GEM)。 NN ベースの VAE は実質これ。
Q: 小サンプルでも EM は使える?
A: SSDSE のような n=47 では、 BayesianGaussianMixture(事前分布で正則化)が頑健。 共分散タイプを 'diag' に制限するのも有効。
Q: ELBO ≠ 対数尤度?
A: ELBO は対数尤度の下界。 E ステップで $q(Z)=p(Z|X,\theta)$ にすれば KL=0 で ELBO = 対数尤度。 変分推論では $q$ が制限され、 ELBO < 対数尤度。
Q: EM はオンラインで実行できる?
A: できる。 Cappé & Moulines (2009) の Online EM。 sklearn の MiniBatchKMeans の確率版に相当。 ストリーミングデータに有効。
Q: K=1 のとき EM は何をする?
A: 1 クラスタの正規分布フィッティングに退化。 反復不要で、 解析解(標本平均と共分散)が 1 ステップで得られる。

🛠 ハンズオン演習:SSDSE-B-2026 で EM 完全攻略

以下は、 SSDSE-B-2026 を題材にした 6 段階のハンズオン演習です。 各段階で実行可能なコードと期待される出力を示します。

段階 1:データのロードと探索

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) A4200(死亡数) 北海道 5,092,000 1,681,000 24,430 75,120 東京都 14,086,000 3,205,000 86,348 137,241 沖縄県 1,468,000 350,000 12,549 15,110 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
print('行数:', len(df), ' 列数:', df.shape[1])
print('年度の範囲:', df['SSDSE-B-2026'].min(), '〜', df['SSDSE-B-2026'].max())
print('都道府県数:', df['Prefecture'].nunique())

# 2023 年だけに絞る
d23 = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
print('2023 年データ:', len(d23), '件')
print(d23[['Prefecture','A1101','A1303','A4101','A4200']].head())
🎯 解説: SSDSE-B-2026 のロード。 skiprows=[1] で日本語ラベル行をスキップ。 都道府県 47 × 年度数の縦持ちフォーマット。
📥 入力例: data/raw/SSDSE-B-2026.csv(CSV、 cp932、 112 列)
📤 実行例: 行数: 564 列数: 112 年度の範囲: 2012 〜 2023 都道府県数: 47 2023 年データ: 47 件
💬 読み方: SSDSE-B-2026 は 2012〜2023 年の 12 年間 × 47 県=564 行。 列は社会経済の主要指標 112 列。 EM/GMM では普通、 1 年度に絞って分析する。

段階 2:特徴量エンジニアリング

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)

# 派生特徴量
d23['aging_rate'] = d23['A1303'].astype(float) / d23['A1101'].astype(float)
d23['birth_rate'] = d23['A4101'].astype(float) / d23['A1101'].astype(float) * 1000
d23['death_rate'] = d23['A4200'].astype(float) / d23['A1101'].astype(float) * 1000
print(d23[['Prefecture','aging_rate','birth_rate','death_rate']].head())
print(d23[['aging_rate','birth_rate','death_rate']].describe().round(3))
🎯 解説: 絶対値(人口)は東京が極端に大きいので、 比率(高齢化率・出生率・死亡率)に変換。 比率はスケールが揃いやすく、 クラスタリングに適している。
📥 入力例: SSDSE-B-2026 2023 年、 47 都道府県、 A1101/A1303/A4101/A4200 列
📤 実行例: Prefecture aging_rate birth_rate death_rate 北海道 0.330 4.80 14.75 青森県 0.352 4.81 17.60 ... describe(): aging_rate mean=0.316 std=0.033 birth_rate mean=5.73 std=0.70 death_rate mean=14.10 std=2.09
💬 読み方: 高齢化率は 23%〜39% の範囲。 出生率は 3.95‰〜8.55‰、 死亡率は 9.7‰〜19.2‰。 「アクティブな県」と「高齢過疎県」が比率データで明確に区別される。

段階 3:標準化と GMM 訓練

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.mixture import GaussianMixture

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d23 = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
d23['aging_rate'] = d23['A1303'].astype(float) / d23['A1101'].astype(float)
d23['birth_rate'] = d23['A4101'].astype(float) / d23['A1101'].astype(float) * 1000
d23['death_rate'] = d23['A4200'].astype(float) / d23['A1101'].astype(float) * 1000

scaler = StandardScaler()
X = scaler.fit_transform(d23[['aging_rate','birth_rate','death_rate']])
gmm = GaussianMixture(n_components=4, covariance_type='full',
                       n_init=20, max_iter=500, random_state=42).fit(X)
d23['cluster'] = gmm.predict(X)
d23['confidence'] = gmm.predict_proba(X).max(axis=1)
for k in range(4):
    members = d23[d23['cluster']==k]['Prefecture'].tolist()
    print(f'クラスタ {k}: {len(members)} 県 — {members[:5]} ...')
🎯 解説: K=4 の GMM を 20 回試行から最良を採用。 predict_proba で各県の所属確率(confidence)を計算。 0.5 付近の県が境界。
📥 入力例: 47 都道府県 × 3 特徴量(高齢化率・出生率・死亡率)、 標準化済み、 K=4
📤 実行例: クラスタ 0: 7 県 — 青森・岩手・秋田・山形・福島 ... (高齢過疎) クラスタ 1: 30 県 — 北海道・宮城・茨城・栃木・群馬 ... (平均・中堅) クラスタ 2: 9 県 — 埼玉・千葉・東京・神奈川・愛知 ... (大都市圏) クラスタ 3: 1 県 — 沖縄 ... (特殊:低死亡率・高出生率)
💬 読み方: 4 クラスタが地理的に解釈可能。 「高齢過疎・平均/中堅・大都市圏」に加えて、 出生率が突出して高く死亡率が低い沖縄が単独クラスタとして分離される。 confidence が 0.7 以上ならクラスタ所属が確定的。

段階 4:クラスタ可視化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import matplotlib.pyplot as plt

# 2D に PCA で射影してプロット
from sklearn.decomposition import PCA
pca = PCA(n_components=2).fit(X)
Xp = pca.transform(X)
fig, ax = plt.subplots(figsize=(9, 7))
for k in range(4):
    mask = d23['cluster']==k
    ax.scatter(Xp[mask, 0], Xp[mask, 1], s=80, label=f'C{k}', alpha=0.7)
    for i, prefname in d23[mask][['Prefecture']].iterrows():
        ax.annotate(prefname['Prefecture'], (Xp[i, 0], Xp[i, 1]),
                    fontsize=8, alpha=0.7)
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.legend()
ax.set_title('SSDSE-B-2026 47 都道府県 GMM K=4 クラスタ(PCA 投影)')
plt.savefig('em_clusters.png', dpi=120, bbox_inches='tight')
🎯 解説: 3 次元の特徴量を PCA で 2 次元に圧縮し、 クラスタを色分けプロット。 県名を annotate で重ねて可読性を確保。
📥 入力例: 標準化済み X (47, 3)、 d23['cluster'] (47,)、 d23['Prefecture']
📤 実行例: em_clusters.png に 4 色で 47 県プロット。 PC1 軸は「人口動態の活発さ」、 PC2 軸は「死亡率の高さ」が主成分。
💬 読み方: 都市部クラスタ(東京・大阪・愛知)が右上に密集、 過疎県(秋田・高知)が左下に。 PCA 軸の解釈は「PC1=都市性、 PC2=高齢性」など、 各軸の負荷で読み取る。

段階 5:クラスタ意味づけと指標

1
2
3
4
5
6
7
8
9
import pandas as pd
# クラスタごとの平均で意味づけ
cluster_stats = d23.groupby('cluster')[['aging_rate','birth_rate','death_rate']].agg(['mean','std']).round(3)
print(cluster_stats)
print()
print('クラスタごとの代表的都道府県(事後確率最大の 3 件):')
for k in range(4):
    sub = d23[d23['cluster']==k].sort_values('confidence', ascending=False).head(3)
    print(f'  C{k}: {sub["Prefecture"].tolist()}')
🎯 解説: 各クラスタの特徴量平均で「これは都市部、 これは過疎地域」とラベル付け。 信頼度(事後確率)が高い県をクラスタの代表とする。
📥 入力例: d23(47 件、 cluster 列付き)
📤 実行例: C0: aging=0.355, birth=4.88, death=17.03 → 高齢過疎(7 県) C1: aging=0.321, birth=5.77, death=14.36 → 平均・中堅(30 県) C2: aging=0.277, birth=5.93, death=11.38 → 大都市圏(9 県) C3: aging=0.238, birth=8.55, death=10.29 → 沖縄(特殊, 1 県)
💬 読み方: 各クラスタの平均特徴量で意味づけが完了。 高齢化率が上がるほど死亡率が上がり出生率が下がる勾配が明瞭。 沖縄は高出生率・低死亡率で単独クラスタ。 政策の優先順位設定(過疎対策など)に直接使える。

段階 6:時系列での追跡(2014→2023)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.mixture import GaussianMixture

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

years = [2014, 2018, 2023]
for yr in years:
    d = df[df['SSDSE-B-2026']==yr].reset_index(drop=True)
    d['aging'] = d['A1303'].astype(float) / d['A1101'].astype(float)
    d['birth'] = d['A4101'].astype(float) / d['A1101'].astype(float) * 1000
    X = StandardScaler().fit_transform(d[['aging','birth']])
    gmm = GaussianMixture(n_components=3, n_init=10, random_state=42).fit(X)
    labels = gmm.predict(X)
    print(f'年度 {yr}: 平均高齢化率 {d["aging"].mean():.3f}')
    for k in range(3):
        n_in_cluster = (labels==k).sum()
        mean_aging = d.loc[labels==k, 'aging'].mean()
        print(f'  C{k}: {n_in_cluster} 県, 高齢化率 {mean_aging:.3f}')
🎯 解説: 同じ EM/GMM を 2014, 2018, 2023 の 3 年度で独立に実行し、 クラスタ構造の時系列変化を観察。 「都市と地方の格差」が拡大しているか否かが BIC で評価できる。
📥 入力例: SSDSE-B-2026 全 564 行を年度ごとにフィルタ、 各年度 47 件 × 2 特徴量
📤 実行例: 年度 2014: 平均高齢化率 0.275 年度 2018: 平均高齢化率 0.300 年度 2023: 平均高齢化率 0.316 → 9 年で約 4 ポイント高齢化が進行
💬 読み方: 高齢化が全国一斉に進行している。 クラスタの中心位置がシフトするだけで、 クラスタ数(K=3)の構造は維持される。 「進行する全国的高齢化」と「都市と地方の構造維持」を両立して示せる。

📖 拡張用語集(EM 周辺の専門用語 30 件)

用語意味関連
潜在変数観測されない隠れた変数GMM, HMM, LDA
完全データ尤度$p(X,Z|\theta)$EM の基礎
観測尤度$p(X|\theta) = \sum_Z p(X,Z|\theta)$EM が最大化したい量
Q 関数事後分布での完全データ尤度の期待値E ステップで作る
ELBOEvidence Lower Bound、 観測尤度の下界変分推論、 VAE
事後分布$p(Z|X,\theta)$、 観測の下で潜在の分布E ステップ
責任度$\gamma_{ik} = p(z_i=k|x_i,\theta)$GMM のクラスタ所属確率
混合比$\pi_k = p(Z=k)$、 クラスタ $k$ の事前確率GMM のパラメータ
共分散縮退1 点の分散がゼロに近づく現象正則化で防ぐ
Jensen の不等式$\log E[X] \ge E[\log X]$ELBO の導出
KL ダイバージェンス2 つの分布間の差ELBO の補項
BICBayesian Information CriterionK 選定
AICAkaike Information CriterionK 選定(罰則弱め)
Baum-WelchHMM の EM アルゴリズム系列モデル
前向きアルゴリズムHMM の $\alpha$ 計算E ステップで使用
後ろ向きアルゴリズムHMM の $\beta$ 計算E ステップで使用
Viterbi アルゴリズム最尤状態系列の推定HMM のデコード
変分推論事後分布を簡単な分布で近似EM の一般化
VAEVariational AutoencoderNN ベース変分推論
LDALatent Dirichlet Allocationトピックモデル
因子分析低次元潜在因子で説明連続潜在変数モデル
確率的 PCAPCA の確率モデル化EM で訓練可能
GEMGeneralized EM、 M で完全最大化不要計算軽量
SEMStochastic EM、 E でサンプリング局所最適回避
ECMExpectation Conditional MaximizationM を分割
MCEMMonte Carlo EM、 E を MC で複雑モデル対応
Label switchingクラスタの番号付け任意性事後分布で深刻
MICEMultiple Imputation by Chained Equations欠損補完での EM 風手法
Multiple imputation多重代入不確実性も含む補完
MARMissing At RandomEM が機能する条件
EM アルゴリズム HMM(Baum-Welch) LDA トピックモデル 因子分析 (FA) 確率的 PCA 欠損値補完 (MICE) Tobit 回帰

🔗 隣接手法への橋渡し

EM アルゴリズムは単独の最適化手法ではなく、 潜在変数モデル (混合正規 ・隠れマルコフ ・LDA) を統一的に扱う一般枠組みである。 MAP 推定 ・変分ベイズ ・MCMC との位置関係を意識して使う。

EM アルゴリズムは「潜在変数モデルの最尤推定を反復で解く」一般手法で、 上流のモデル仮定 (混合正規・隠れマルコフ等) で E/M 式が決まり、 並列の MAP・変分ベイズと比較され、 下流のクラスタリング・欠測値補完で広く使われる。

🌳 手法選択フロー

EM を採るかは「潜在変数の存在・分布の解析的扱いやすさ・他手法 (変分・MCMC) との比較」の 3 軸で決まる。 共役な指数族なら EM が高速、 複雑分布なら変分推論や MCMC が必要になる。

  1. 欠測や隠れ変数があるか
    あるなら EM の出番。 混合分布の所属クラスタ、 欠測値、 潜在因子などが典型。 全部観測できているなら、 素直に最尤推定すればよい。
  2. 初期値をどう決めるか
    EM は局所最適に落ちるので、 初期値で結果が変わる。 複数の初期値から走らせ、 対数尤度が最大になったものを採る。
  3. 収束をどう判定するか
    対数尤度の増分が閾値を下回ったら止める。 尤度は単調増加するので、 減ったら実装の誤り。
  4. 結果をどう検証するか
    クラスタ数のような設定は EM 自体では決まらない。 AIC・BIC や交差検証で外側から選ぶ。

SSDSE-B-2026 の消費支出(L3221)を 2-3 個の正規分布の混合とみなしてクラスタリングするとき、 まず初期化を 2 通り試して同じ解に収束するか確認し、 BIC で混合数を決めるのが安定した運用手順である。

📝 補足:EM をもう一歩深く理解する

※ 本節は既存の解説を壊さずに追記した「深掘り補足」です。 上の各節と重複する話題もありますが、 直感・落とし穴・発展を一望できるまとめとして活用してください。

🎨 直感:「鶏と卵」を反復で断ち切る

EM の本質は、 潜在変数がある最尤推定でぶつかる「鶏と卵」問題を、 交互更新で解くところにあります。 「各データがどのクラスタから来たか(=潜在変数 $Z$)が分かればパラメータ $\theta$ は簡単に推定できる」。 逆に「$\theta$ が分かれば $Z$ の分布は簡単に計算できる」。 どちらも相手が分からないと決まらない — これが鶏と卵です。

EM はこれを、 E(期待)ステップで「今の $\theta$ のもとで潜在変数の期待値(責任度 $\gamma$)を埋める」→ M(最大化)ステップで「その期待値を真値とみなして $\theta$ を更新する」という反復で断ち切ります。 重要なのは、 この 2 ステップを繰り返すたびに観測データの対数尤度が単調に増える(または横這い)ことが数学的に保証されている点です(上の「理論深掘り」節の ELBO 分解を参照)。 だから「毎回良くなるのに、 なぜ止まると分かるのか」に悩まなくてよい — 増える一方で上に有界なので、 必ずどこかに収束します。

⚠️ 落とし穴(重要):EM が「うまくいったように見えて外している」典型

❌ 局所最適・初期値依存(最重要)
対数尤度が単調に増えることと、 大域最適に着くことは別物です。 EM は非凸な尤度面を「今いる山の頂上」まで登るだけなので、 出発点(初期値)が変われば別の頂上に着きます。 上の「ケース 5」でも、 同じデータ・同じ K でシードを変えるだけで到達する対数尤度が割れる実測が示されています。 対策は n_init=10 以上での多重初期化と、 init_params='kmeans'/k-means++ 初期化、 そして最良の対数尤度の解を採用すること。
❌ 対数尤度は増えても「正解」の保証はない
尤度が高い解=解釈として正しい解、 とは限りません。 尤度最大の解が、 外れ値県を単独クラスタに切り出しただけ、 ということも起こります。 数値指標(尤度・BIC)と地理的・実務的な解釈の両にらみで判断してください。
❌ 収束が遅い(特に成分が重なるとき)
2 つの成分の平均・分散が近いと、 責任度 $\gamma$ がどの点でも 0.5 付近になり、 更新量が小さくなって反復数が跳ね上がります(尤度面が平坦で「谷底が広い」状態)。 tol を緩めすぎると中途半端な解で止まり、 厳しくすると無限ループ気味になるので、 tolmax_iter の両方を設定するのが安全です。
❌ 混合数 K の選択に唯一の正解はない
K はデータからは一意に決まりません。 上の「経験的振る舞い」節のとおり、 特徴量セットや共分散タイプを変えると BIC 最小の K は 2〜8 まで揺れます。 小標本(n=47)・高次元では情報量規準の谷が平坦になりやすいので、 BIC/AIC の最小値だけを鵜呑みにせず、 解釈可能性(大都市圏・地方中核・高齢過疎など)を優先する運用が現実的です。
❌ 特異解(分散 → 0)による尤度の発散
ある成分がたった 1 点(や近接数点)だけを抱えると、 その成分は分散を限りなく小さくして密度を尖らせ、 対数尤度を見かけ上いくらでも大きく(+∞ に)できます。 これは真の最尤解ではなく「病的な特異点」です。 reg_covar(既定 1e-6、 必要なら 1e-4)で共分散の対角に微小値を足す正則化が必須。 上のインタラクティブ教材でも σ に下限を設けて再現を防いでいます。
❌ ラベルスイッチング(クラスタ番号の入れ替わり)
GMM のクラスタ番号には本来意味のある順序がありません。 初期値やシードが変わると「クラスタ 0」が指す集団が入れ替わるため、 実行ごとに番号だけ見て比較すると混乱します(上の「ステップ 6:手書き EM との比較」で、 混合比の並びが sklearn 版と一対一に一致しないのも同じ理由)。 比較・集計するときは混合比や平均でソートするか、 ハンガリアン法で番号を揃えてから対応付けます。

🚀 発展:EM の骨格が見えると視界が広がる

🔗 関連ページ(前提・並列・発展)

EM の理解を広げる関連用語ページ:

混合ガウスモデル (GMM) ガウス混合分布 k-means(ハード版) クラスタリング 階層クラスタリング スペクトラルクラスタリング 最尤推定(点推定) 正規分布 平均 分散 因子分析 確率的 PCA ベイズ/変分推論 欠損値補完 クラスタリング(グループ教材)

※ 「最尤推定(の尤度そのもの)」「潜在変数」の独立した用語ページは本用語集には未整備のため、 前提概念としてはテキストで補足します:尤度はパラメータをデータで評価する関数、 潜在変数は観測されない隠れ変数(クラスタ所属・欠測値など)で、 EM はこの潜在変数を含む尤度の最大化を反復で解く手法です。