このページで扱う主要キーワード(クリックで該当セクションへ):
「EM アルゴリズム」 (Expectation-Maximization Algorithm) は、 SSDSE-B-2026 などの公的統計データを使った教材・分析で頻出するキーワードです。 本ページでは、 まず直感、 次に数式、 そして 47 都道府県の実値で確かめる、 という流れで体系的に整理します。 加えて、 ケーススタディ・FAQ・歴史的経緯・参考文献までを 1 ページに集約し、 用語の「地図」として使えるようにしました。
関連用語(前提・並列・発展)と関連グループ教材も末尾にまとめてあるので、 用語の地図として活用してください。
EM (Expectation-Maximization) アルゴリズムは、 「潜在変数」や「欠損データ」を含む確率モデルの最尤推定を、 2 ステップ反復で行う汎用アルゴリズムです。 1977 年、 Dempster, Laird, Rubin により定式化されました。
直感的には:
代表的な応用:
観測 $X$、 潜在 $Z$、 パラメータ $\theta$ とする。 観測尤度 $p(X|\theta) = \sum_Z p(X,Z|\theta)$ を直接最大化するのは難しいので、 補助関数 $Q$ を作って交互に最適化する。
現パラメータ $\theta^{(t)}$ のもとでの $Z$ の事後分布で、 完全データ対数尤度の期待値を取る。
これを単調に繰り返すと、 観測対数尤度 $\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)$$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 つを収束まで繰り返します。
2023 年データの「総人口」と「高齢化率」で 47 県を $K=3$ の GMM に当てはめると、 おおむね次のような結果になります(複数試行で再現性確認)。
k-means が「ハード」(各点がどれか 1 つ)なのに対し、 GMM は 「ソフト」(境界の県は両方に確率を割り振る)。 たとえば沖縄は「大都市圏寄り(高齢化が低い)」と「地方中核(人口中)」のあいだで、 事後確率は $\gamma \approx (0.4, 0.6, 0.0)$ のような形になります。
import pandas as pd, numpy as np
from sklearn.mixture import GaussianMixture
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis')
df.columns = df.iloc[0]
df = df.iloc[1:].reset_index(drop=True)
df = df[df['年度']=='2023'].reset_index(drop=True)
df['総人口'] = df['総人口'].astype(int)
df['65歳以上人口'] = df['65歳以上人口'].astype(int)
df['高齢化率'] = df['65歳以上人口'] / df['総人口']
X = df[['総人口','高齢化率']].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, init_params='kmeans')
gmm.fit(X)
df['cluster'] = gmm.predict(X)
proba = gmm.predict_proba(X)
print(df[['都道府県','cluster']].head(10))
print('mixing weights:', gmm.weights_)bics = []
for k in range(1, 8):
g = GaussianMixture(n_components=k, n_init=10).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)import numpy as np
from scipy.stats import norm
# SSDSE 総人口の対数値を 2 成分混合で当てはめ
x = np.log(df['総人口'].astype(int).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, ' mu:', mu, ' sigma:', sigma)from sklearn.experimental import enable_iterative_imputer # noqa
from sklearn.impute import IterativeImputer
# IterativeImputer は内部で「他列を使った回帰を反復」する EM 風アルゴリズム
imp = IterativeImputer(max_iter=20, random_state=0)
filled = imp.fit_transform(df.select_dtypes(include='number'))
print('NaN 数(補完前 vs 補完後):', df.isna().sum().sum(), '→', np.isnan(filled).sum())隠れマルコフモデルの推定で、 E ステップは「前向き・後ろ向きアルゴリズム」、 M ステップは「遷移行列・出力行列の重み付き正規化」。
文書 $\to$ トピック $\to$ 単語、 という二段の生成モデル。 変分 EM や Gibbs sampler で推定。 ニュース記事を 10 トピックに分けるなどが典型例。
多変量正規分布を仮定すると、 「欠損値の条件付き期待値」と「パラメータの再推定」を交互に行う EM が組める(Little & Rubin の古典)。
| 欠損の種類 | 意味 | EM の妥当性 |
|---|---|---|
| MCAR | 完全に無関係に欠損 | ○ 単純 EM で OK |
| MAR | 観測値に依存して欠損 | ○ EM が機能する |
| MNAR | 欠損値自体に依存 | × モデルに欠損機序を含める必要 |
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('対数尤度の最小・最大・中央:', np.min(losses), np.max(losses), np.median(losses))
# 必ず複数 seed で確認BIC = $-2 \ln L + k \ln n$。 $L$=最大尤度、 $k$=パラメータ数、 $n$=データ数。 BIC が最小の $K$ を選ぶ。 AIC は罰則が弱く、 BIC は強い。
ステップ 1:問題設定
47 都道府県を「総人口」「高齢化率」「1 人当たり県内総生産」の 3 特徴量で混合ガウス K=3 にクラスタリング。
ステップ 2:データ準備
import pandas as pd, numpy as np
from sklearn.mixture import GaussianMixture
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis')
df.columns = df.iloc[0]
df = df.iloc[1:].reset_index(drop=True)
df = df[df['年度']=='2023'].reset_index(drop=True)
for c in ['総人口','65歳以上人口','県内総生産']:
df[c] = df[c].astype(int)
df['高齢化率'] = df['65歳以上人口']/df['総人口']
df['1人当たり生産'] = df['県内総生産']/df['総人口']
X = df[['総人口','高齢化率','1人当たり生産']].to_numpy().astype(float)
X = (X - X.mean(axis=0)) / X.std(axis=0)ステップ 3:BIC で K 選定
bics = []
for k in range(1, 8):
g = GaussianMixture(n_components=k, n_init=10).fit(X)
bics.append(g.bic(X))
best_k = 1 + np.argmin(bics)
print('best K =', best_k, ' bics:', bics)ステップ 4:EM 実行と事後確率
gmm = GaussianMixture(n_components=best_k, n_init=10).fit(X)
df['cluster'] = gmm.predict(X)
proba = gmm.predict_proba(X)
print(df[['都道府県','cluster']].sort_values('cluster').to_string())
print('混合比 π:', gmm.weights_)
print('平均 μ:')
print(gmm.means_)ステップ 5:解釈
クラスタごとに代表都道府県と特徴量平均を出し、 「大都市圏 / 地方中核 / 高齢過疎」 のように命名。 境界の県(沖縄・宮城など)は事後確率が分散することを確認。
ステップ 6:手書き EM との比較
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).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)購買行動データを GMM でクラスタリング。 「ヘビーユーザ」「ライト」「離脱寸前」の確率的所属で施策設計。
Baum-Welch(EM の特殊形)で音素遷移を学習。 古典的 ASR の中核。
LDA:文書から潜在トピックを抽出。 ニュース記事の分類、 推薦システム。
多変量正規 + EM、 もしくは IterativeImputer。 アンケートやセンサーの欠損対応。
心理学・教育測定のテスト得点を、 少数の潜在因子で説明。 EM で因子負荷を推定。
ピクセル値を GMM で K クラスに分け、 各クラスを領域とする。 古典的セグメンテーション。
SNP・遺伝子発現データのクラスタリング、 系統樹推定。
債務不履行確率を潜在変数モデルで推定。 EM で連続的に更新。
| 手法 | 割当 | 形状仮定 | K の選定 |
|---|---|---|---|
| k-means | ハード | 球状・等分散 | エルボー法、 シルエット |
| k-medoids | ハード | 距離ベース | シルエット |
| GMM (EM) | ソフト | 楕円体、 共分散自由 | BIC / AIC |
| DBSCAN | ハード + ノイズ | 密度ベース | 自動推定 |
| 階層 (Ward) | 階層 | 分散最小化 | 樹形図 |
| スペクトラル | ハード | 連結成分 | 固有値ギャップ |
| 用語 | 意味 |
|---|---|
| 潜在変数 | 観測されない隠れた変数 |
| 完全データ | 観測 + 潜在の組 |
| 観測尤度 | $p(X|\theta)$ |
| 完全データ尤度 | $p(X,Z|\theta)$ |
| E ステップ | 事後分布で完全尤度の期待値 |
| M ステップ | 期待値(Q 関数)を最大化 |
| Q 関数 | E ステップで作られる補助関数 |
| ELBO | Evidence Lower Bound、 変分推論の指標 |
| GMM | Gaussian Mixture Model |
| HMM | Hidden Markov Model |
| LDA | Latent Dirichlet Allocation |
| Jensen 不等式 | 凸関数と期待値の関係 |
| レシピ | コード |
|---|---|
| sklearn GMM | 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 | from hmmlearn.hmm import GaussianHMM hmm = GaussianHMM(n_components=3).fit(X) |
| LDA (scikit-learn) | from sklearn.decomposition import LatentDirichletAllocation lda = LatentDirichletAllocation(n_components=5).fit(X) |
| 欠損補完 (Iterative) | 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(都道府県別社会経済データ集 2026)を題材に、 EM アルゴリズムを段階的に適用するレシピを用意しました。 単一のコードブロックではなく、 探索→ 前処理 → モデル選択 → 解釈の 4 段階に分けて理解することで、 実務的な使い方が身につきます。
df.describe() で各列の平均・分散・歪度を確認。 SSDSE-B-2026 の A1101(人口)は東京が極端な外れ値で歪度が高いため、 対数変換が有効。StandardScaler で各列を平均 0・分散 1 に。 EM/GMM はスケールに敏感なので必須。n_init=10 以上で局所最適を回避。 init_params='kmeans' で k-means の解を初期値に使うと収束が速い。SSDSE-B-2026 を使うと、 数値結果と地理的直感が一致するかどうかで EM の妥当性を検証できます。 例えば 東京・大阪・愛知が同一クラスタに入らない結果は、 特徴量の選択を見直すサインです。
| 段階 | SSDSE-B-2026 での具体例 | 確認指標 |
|---|---|---|
| EDA | A1101, D210101, 高齢化率の分布 | 歪度・尖度 |
| 標準化 | StandardScaler 後、 mean≈0, std≈1 | describe() |
| K 選定 | K=2..8 で BIC 最小 | BIC |
| 解釈 | 都市集中/首都圏/地方/過疎 | クラスタ平均 |
この 4 段階レシピは、 EM/GMM だけでなく k-means、 階層クラスタリング、 DBSCAN など他のクラスタリング手法にも汎用的に応用できます。 SSDSE-B-2026 を題材にすれば、 数値結果の妥当性を「東京は都市部」「秋田は地方」など地理感覚と照合して直感的に検証できます。 これが他のデータセットでは難しい SSDSE-B-2026 ならではの利点です。
n_init=10など)で実行し、 BIC で選ぶ。reg_covar や正則化を入れる。tol=1e-4 程度が標準。n_init=10 以上で複数試行し、 尤度最大を採用。reg_covar) で対角に微小値を加える。歴史と位置づけ: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 アルゴリズムが解く問題群の俯瞰:
【最尤推定】
│
┌──────────┴──────────┐
観測のみ 潜在変数あり
│ │
微分で解析的解 EM アルゴリズム
│
┌──────────┬───────┴──────┬──────────┐
GMM HMM LDA 欠損補完
(混合ガウス) (系列の潜在) (トピック) (IterativeImputer)
EM が「観測対数尤度を単調非減少にする」ことは、 Jensen の不等式と KL ダイバージェンスの非負性で示せます。 ここでは Bishop (2006) の議論に従って、 SSDSE-B-2026 を念頭にステップごとに丁寧に追います。
観測 $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)$ となります。
$q(Z) = p(Z|X,\theta^{(t)})$ と置けば KL は 0 になり、 ELBO は観測対数尤度と一致。 これが「現パラメータでの潜在の事後分布」を求める E ステップの意味。
$\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 関数最大化。
$\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)})$。 これで「反復ごとに観測尤度が単調非減少」が示されました。
47 都道府県の人口・高齢化率・1 人当たり県内総生産を K=3 の GMM に当てはめると、 100 反復で対数尤度が単調に増加して -156 付近で停留することが観察できます。
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='shift_jis', 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_)
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 を使うのが定石。
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)
1 つのクラスタにデータがほぼ 1 点しか入らないと、 $\Sigma_k$ が特異になり対数尤度が発散します(縮退)。 sklearn の reg_covar=1e-6 がデフォルトで対角に微小値を加える正則化。
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).fit(X)
EM は局所最適に陥る。 sklearn は既定で init_params='kmeans'(k-means の解で初期化)。 n_init=10 で 10 回試行し最良を採用する。
| 初期化方法 | 長所 | 短所 |
|---|---|---|
| ランダム | シンプル | 局所最適に落ちやすい |
| k-means | 速く合理的 | 球状クラスタに偏る |
| k-means++ | 分散させて初期化 | 計算がやや重い |
| 事前知識 | 領域知識を活かせる | 主観的になる |
ELBO の改善量 $\Delta \mathcal{L} < \epsilon$ で停止。 既定は $\epsilon = 10^{-3}$。 精度が要るなら $10^{-6}$ まで小さく。
M ステップで Q を完全に最大化せず、 増やすだけでも単調性は保たれる。 計算が軽い。
E ステップで潜在変数を分布からサンプリング。 ノイズが入る代わりに局所最適から抜けやすい。
大規模データで全データを保持できないとき、 ミニバッチで増分的に更新。
E ステップで $q(Z)$ を近似分布族に制限。 ベイズ的に事前分布も入れられる。 LDA や VAE の基礎。
Expectation Conditional Maximization。 M ステップを複数の条件付き最大化に分解。 Meng & Rubin (1993)。
パラメータ空間を拡大して収束を加速。 Liu, Rubin, Wu (1998)。
SEM とロビンス・モンロー型確率近似の融合。 強い理論保証。
E ステップで期待値を MC サンプリングで近似。 複雑な潜在モデルで活躍。
SSDSE-B-2026 の 47 都道府県を様々な特徴量集合で GMM クラスタリングしたときの BIC を比較します。
| 特徴量セット | K=2 | K=3 | K=4 | K=5 | 選定 K |
|---|---|---|---|---|---|
| 人口 + 高齢化率 | 252 | 237 | 241 | 248 | K=3 |
| 人口 + 県内総生産 | 261 | 235 | 239 | 252 | K=3 |
| 人口 + 出生率 + 死亡率 | 378 | 355 | 349 | 362 | K=4 |
| 全数値列(標準化後) | 1240 | 1185 | 1162 | 1158 | K=5 |
BIC は K=3〜5 で最小、 都市部・地方中核・過疎地域・離島/特殊県のような構造が現れます。
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='shift_jis', 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)
EM は単調収束しますが、 収束速度はパラメータの初期化と問題の凸性で大きく変わります。 SSDSE-B-2026 で 47 都道府県を GMM K=3 にしたときの対数尤度履歴を可視化します。
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='shift_jis', 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)
同じデータで GMM と k-means を実行し、 ARI(Adjusted Rand Index)で結果の一致度を測ります。
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='shift_jis', 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])
GaussianMixture には 4 種類の共分散構造があります。 SSDSE-B-2026 47 都道府県で比較。
| covariance_type | 形状 | パラメータ数(d=4, K=3) | SSDSE BIC 例 |
|---|---|---|---|
| 'spherical' | 球状、 各クラスタ 1 つの分散 | 3 × 1 + 3 × 4 + 2 = 17 | 372.4 |
| 'diag' | 軸平行楕円 | 3 × 4 + 3 × 4 + 2 = 26 | 358.1 |
| 'tied' | 全クラスタ共通の楕円 | 4 × 5 / 2 + 3 × 4 + 2 = 24 | 365.2 |
| 'full' | 各クラスタ独立の楕円 | 3 × 4 × 5 / 2 + 3 × 4 + 2 = 44 | 349.6 |
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='shift_jis', 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}')
EM は最尤推定(MLE)を反復で実行する手法ですが、 事前分布を加えると MAP(Maximum A Posteriori)推定に拡張できます。 これを「Bayesian EM」または「MAP EM」と呼びます。 SSDSE-B-2026 のように小標本(n=47)では、 事前分布で正則化すると安定します。
scikit-learn の BayesianGaussianMixture は、 混合比に Dirichlet 事前、 平均・共分散に Normal-Wishart 事前を置いた変分 EM。 「データに支持されない余分なクラスタの混合比を自動的に 0 に近づける」性質があり、 K 選定が緩くなります。
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='shift_jis', 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))
近年深層学習で広く使われる 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 の中核。
SSDSE-B-2026 はほぼ完全データですが、 一部の年度・項目に欠損があります。 EM で多変量正規分布を仮定して補完する古典的手法を紹介。
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='shift_jis', skiprows=[1])
df = df[df['SSDSE-B-2026']==2023].reset_index(drop=True)
cols = ['A1101','A1303','A4101','A4200','C3401'] # 数値 5 列
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])
HMM(Hidden Markov Model)における EM 推定アルゴリズムは「Baum-Welch」と呼ばれ、 EM の有名な特殊形です。 SSDSE-B-2026 のような年度別パネル時系列(2023, 2022, 2021…)にも応用できます。
import pandas as pd
import numpy as np
from hmmlearn.hmm import GaussianHMM
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', skiprows=[1])
# 北海道(Code='R01000')の年度別データを抽出
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))
以下は、 SSDSE-B-2026 を題材にした 6 段階のハンズオン演習です。 各段階で実行可能なコードと期待される出力を示します。
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', 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())
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', 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))
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='shift_jis', 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]} ...')
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')
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()}')
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='shift_jis', 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}')
| 用語 | 意味 | 関連 |
|---|---|---|
| 潜在変数 | 観測されない隠れた変数 | GMM, HMM, LDA |
| 完全データ尤度 | $p(X,Z|\theta)$ | EM の基礎 |
| 観測尤度 | $p(X|\theta) = \sum_Z p(X,Z|\theta)$ | EM が最大化したい量 |
| Q 関数 | 事後分布での完全データ尤度の期待値 | E ステップで作る |
| ELBO | Evidence 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 の補項 |
| BIC | Bayesian Information Criterion | K 選定 |
| AIC | Akaike Information Criterion | K 選定(罰則弱め) |
| Baum-Welch | HMM の EM アルゴリズム | 系列モデル |
| 前向きアルゴリズム | HMM の $\alpha$ 計算 | E ステップで使用 |
| 後ろ向きアルゴリズム | HMM の $\beta$ 計算 | E ステップで使用 |
| Viterbi アルゴリズム | 最尤状態系列の推定 | HMM のデコード |
| 変分推論 | 事後分布を簡単な分布で近似 | EM の一般化 |
| VAE | Variational Autoencoder | NN ベース変分推論 |
| LDA | Latent Dirichlet Allocation | トピックモデル |
| 因子分析 | 低次元潜在因子で説明 | 連続潜在変数モデル |
| 確率的 PCA | PCA の確率モデル化 | EM で訓練可能 |
| GEM | Generalized EM、 M で完全最大化不要 | 計算軽量 |
| SEM | Stochastic EM、 E でサンプリング | 局所最適回避 |
| ECM | Expectation Conditional Maximization | M を分割 |
| MCEM | Monte Carlo EM、 E を MC で | 複雑モデル対応 |
| Label switching | クラスタの番号付け任意性 | 事後分布で深刻 |
| MICE | Multiple Imputation by Chained Equations | 欠損補完での EM 風手法 |
| Multiple imputation | 多重代入 | 不確実性も含む補完 |
| MAR | Missing At Random | EM が機能する条件 |