本ページは 混合ガウスモデル(Gaussian Mixture Model)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。
「gaussian mixture」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「gaussian mixture」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「gaussian mixture の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
複数の山を組み合わせたようなモデルです。
データの集まりをグループ分けするために使います。
スマホの利用時間でユーザーを分ける時に便利です。
この手法の特徴と使いどころを学びます。
GMM は強力ですが、 すべての問題に向くわけではありません。 苦手なケースをまとめておきます:
SSDSE 都道府県人口は 「対数空間でほぼ正規」「47 という適度なサンプル数」「1〜3 次元なら次元の呪い回避」と GMM に好都合な条件が揃っています。 初学者の練習素材として最適です。
GMM は単独で使うほか、 機械学習パイプラインの 前処理・特徴量生成に組み込まれることが多い手法です:
| 用途 | 使い方 |
|---|---|
| 特徴量生成 | 各サンプルの γ(K 次元ベクトル)を特徴量に追加 |
| クラスタラベル付与 | predict のラベルを「グループ ID」として使用 |
| 対数尤度を特徴量に | score_samples の値を「典型度スコア」に |
| セミ教師あり学習 | 少量のラベル + GMM の密度で擬似ラベル生成 |
| 層化サンプリング | GMM クラスタを層として、 各層から比例サンプリング |
| クラスタごとモデル | GMM で分割 → 各クラスタで別々の回帰モデル(Mixture-of-Experts) |
SSDSE で「県のタイプ別に売上予測モデルを変える」のような Mixture-of-Experts 構造を組めば、 単一モデルより精度が改善する場合があります。
sklearn の GaussianMixture を内部から理解するため、 1 次元の EM をスクラッチで書いてみると、 「E ステップで γ、 M ステップでパラメータ」という対称構造が腑に落ちます:
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 | import numpy as np import pandas as pd from scipy.stats import norm df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) x = np.log10(df[df['年度']==2023]['総人口'].values.astype(float)) # K=3 で初期値(k-means の代わりに分位点) K = 3 pi = np.ones(K) / K mu = np.quantile(x, [0.17, 0.50, 0.83]) sigma = np.array([x.std()] * K) for it in range(50): # E ステップ: 負担率 probs = np.stack([pi[k] * norm.pdf(x, mu[k], sigma[k]) for k in range(K)], axis=1) gamma = probs / probs.sum(axis=1, keepdims=True) # M ステップ N_k = gamma.sum(axis=0) pi = N_k / len(x) mu = (gamma * x.reshape(-1, 1)).sum(axis=0) / N_k sigma = np.sqrt(((x.reshape(-1,1) - mu)**2 * gamma).sum(axis=0) / N_k) print('π:', pi.round(3)) print('μ:', mu.round(3)) print('σ:', sigma.round(3)) |
📤 50 反復後の出力:
→ sklearn と ほぼ同じ結果が得られます。 自前で実装すると「E ステップ = 各クラスタの正規密度に重みを掛けて正規化」「M ステップ = 重み付き平均と分散」という対称的な構造が見えて、 EM のエレガントさが体感できます。
GMM の結果は 初期値・ランダム性に依存するため、 安定性を評価する習慣が大事です:
SSDSE は 47 サンプルと小さいので、 ブートストラップで 「県境界が変わるか」を見るのが有効です。 安定性が低いクラスタは「データに対して K が大きすぎる」サイン。
GMM の最大の悩みは 「混合成分の数 K をいくつにするか」です。 ここでは情報量基準 BIC (ベイズ情報量基準) と AIC (赤池情報量基準) を使って、 SSDSE-B-2026 の 47 都道府県を最適な K でクラスタリングする例を見せます。 K を増やすほど fit は良くなりますが、 BIC/AIC は 「複雑さのペナルティ」を加えて過剰適合を防ぎます。
| 基準 | 数式 | ペナルティ強度 | 用途 |
|---|---|---|---|
| AIC | $-2 \log L + 2k$ | 弱い | 予測重視・少し多めの K |
| BIC | $-2 \log L + k \log n$ | 強い (n が大きいほど) | 真のモデル探索・少なめの K を選ぶ |
このコードでやること: SSDSE-B-2026 の都道府県データに対して、 K=1 から K=8 まで GaussianMixture を fit し、 BIC と AIC が最小になる K を自動探索する。 47 サンプルしかないので BIC は厳しいペナルティをかけ、 だいたい K=2-3 を選ぶ。
📥 入力データ (SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd import numpy as np from sklearn.mixture import GaussianMixture from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == 2023].copy() df['高齢人口'] = df['65歳以上人口'] df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100 X = StandardScaler().fit_transform(df[['総人口', '高齢人口', '高齢化率']]) # K=1..8 で BIC / AIC を計算 results = [] for k in range(1, 9): gmm = GaussianMixture(n_components=k, random_state=42, n_init=5).fit(X) results.append({'K': k, 'BIC': round(gmm.bic(X), 1), 'AIC': round(gmm.aic(X), 1)}) print(pd.DataFrame(results).to_string(index=False)) best_bic = min(results, key=lambda r: r['BIC']) print('\nBIC 最小の K =', best_bic['K']) |
📤 実行結果:
💬 BIC は K=2 で最小 (362.8) になり、 「東京・大阪などの大都市圏 vs その他」の 2 クラスタが最適と判定された。 AIC も同じく K=2 で最小。 K=3 以降は BIC が増加するので、 サンプル数 47 では混合成分を 3 つ以上に分けても情報量的に得がない。 これが BIC による自動 K 選択の威力です。
注意: BIC は厳しいペナルティのため K を小さく見積もる傾向があります。 サンプル数が増えると (n→∞) BIC は一致性 (真のモデルを選ぶ確率→1) を持ちますが、 AIC は予測性能を重視するため少し多めの K を選びがち。 SSDSE のような小サンプルでは BIC を優先するのが安全です。
full/diag 等)によって最小 BIC を取る K は変わります(人口系の列は相関が強く、 full だと共分散が退化して BIC が単調減少することもあります)。 「BIC 最小の K」を鵜呑みにせず、 n_init を増やして複数条件で走査し、 ドメイン解釈と併せて K を決めるのが実務的です。ここまで「数式」「Python 実装」「BIC によるクラスタ数選択」を個別に見てきました。 ここではそれらを 1 本のパイプラインに統合し、 SSDSE-B-2026 を題材に「データ取り込み → 探索的可視化 → 標準化 → GMM 学習 → クラスタ解釈 → 異常検出」までを 1 ファイルで回し切る、 実務想定のワークフローを提示します。 各ステップで「なぜそれをやるのか」を 2-3 行で明示するので、 自分の卒研データに置き換える際の判断軸として使えます。
SSDSE-B-2026 の人口関連 2 変数 (例: 「総人口」「高齢人口」) の散布図を見ると、 多くの県が原点近くに密集し、 東京・大阪などが右上に離れた位置に来ます。 これは「単一の正規分布では説明できない」シグナルで、 混合ガウス (GMM) を検討する典型的な動機になります。
図 R504-1: SSDSE-B-2026 の人口関連 2 変数の散布図。 右上の数県が独立した「大都市クラスタ」を形成しているように見える。
このコードでやること: SSDSE-B-2026 から人口関連 2 変数を読み込み、 都道府県別に散布図を描いて「単峰か多峰か」を視覚的に確認する。 GMM を当てはめる前の「目で確認」の作業。
📥 入力データ (SSDSE-B-2026, 先頭 5 行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == 2023].copy() df['高齢人口'] = df['65歳以上人口'] x = df['総人口'] y = df['高齢人口'] plt.figure(figsize=(6, 4.5)) plt.scatter(x, y, s=30, alpha=0.7) plt.xlabel('総人口') plt.ylabel('高齢人口') plt.title('SSDSE-B-2026 人口関連 2 変数の散布図') plt.tight_layout() plt.show() # 右上に離れた数点が「大都市クラスタ」候補 print(f"右上に離れた県 (人口 > 900 万): {df[df['総人口'] > 9_000_000]['都道府県'].tolist()}") |
📤 実行すると次の出力が得られる:
💬 散布図上で右上に離れた 2 県 (東京都・神奈川県) が「大都市クラスタ」、 残りの 45 県が「地方クラスタ」を形成する 2 山構造が示唆される。 → GMM の K=2 が有望と判断できる。
散布図で「多峰っぽい」と感じても、 周辺分布 (各軸を独立に見たヒストグラム) で確認するとより確信が持てます。 SSDSE-B-2026 の総人口は右に長い裾を持ち、 単一正規分布の鐘形からは大きく逸脱します。 これも GMM (= 複数の正規分布の重ね合わせ) を採用すべき根拠になります。
図 R504-2: SSDSE-B-2026 総人口のヒストグラム。 右に長い裾 (heavy right tail) が見え、 単一正規分布では当てはまりが悪い。
このコードでやること: 総人口のヒストグラムを描き、 平均・中央値・歪度を計算して「単峰正規分布で十分か」を統計的に検証する。
📥 入力データ: SSDSE-B-2026 の「総人口」列 (47 都道府県)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd from scipy import stats import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == 2023].copy() pop = df['総人口'] print(f"平均 : {pop.mean():,.0f}") print(f"中央値 : {pop.median():,.0f}") print(f"歪度 : {pop.skew():.2f}") # Shapiro-Wilk 検定 (帰無仮説: データは正規分布に従う) W, p = stats.shapiro(pop) print(f"Shapiro W = {W:.3f}, p = {p:.6f}") |
📤 実行すると次の出力が得られる:
💬 平均 (265 万) >> 中央値 (155 万) で歪度 2.29 (強い右歪み)。 Shapiro-Wilk 検定で p < 0.001 → 正規性を強く棄却。 → 単一正規分布では絶対に説明できず、 GMM (複数成分の重ね合わせ) が必要と結論できる。
GMM は共分散行列を学習するため、 変数のスケールや分散が大きく異なると「分散の大きい変数だけで距離が決まる」現象が起きます。 SSDSE-B-2026 の総人口 (平均約 265 万人) と高齢人口 (平均約 77 万人) は水準も分散も数倍違うため、 StandardScaler で標準化 (平均 0、 分散 1) してから GMM に渡すのが鉄則です。 単位が同じ「人」でも、 分散の大きい総人口が距離を支配しないように揃えます。
| 変数 | 原スケール (平均) | 原スケール (標準偏差) | 標準化後 (平均/分散) |
|---|---|---|---|
| 総人口 | 2,645,809 人 | 2,767,630 人 | 0.0 / 1.0 |
| 高齢人口 | 770,830 人 | 686,418 人 | 0.0 / 1.0 |
標準化前の状態で GMM を回すと、 総人口の桁数が大きい (10⁶〜10⁷ オーダー) ため、 共分散行列の対角成分が極端に大きくなり数値計算が不安定化します (det 行列式が小さくなりすぎて log-likelihood が -inf になる)。 標準化は単なる前処理ではなく、 GMM の収束安定性を担保する必須工程です。
このコードでやること: scikit-learn の StandardScaler で 2 変数を標準化し、 変換前後の統計量を表示する。
📥 入力データ: Step 1 で読み込んだ df から「総人口」「高齢人口」の 2 列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == 2023].copy() df['高齢人口'] = df['65歳以上人口'] X = df[['総人口', '高齢人口']].values scaler = StandardScaler() X_std = scaler.fit_transform(X) print("=== 原スケール ===") print(f"平均: {X.mean(axis=0)}") print(f"標準偏差: {X.std(axis=0)}") print("=== 標準化後 ===") print(f"平均: {X_std.mean(axis=0).round(3)}") print(f"標準偏差: {X_std.std(axis=0).round(3)}") |
📤 実行すると次の出力が得られる:
💬 標準化後は両変数とも平均 0、 標準偏差 1 になり、 スケールの影響を完全に取り除けた。 これで GMM の共分散行列が両変数を「対等に」扱えるようになり、 収束も安定する。
Step 1-3 で「K=2 が妥当」と分かったので、 ここでは sklearn.mixture.GaussianMixture(n_components=2) で 2 成分 GMM を学習します。 共分散タイプは "full" (各成分が独立した完全共分散行列) を採用し、 ランダムシードを固定して再現性を担保します。
このコードでやること: 標準化済みデータに GMM (K=2) を当てはめ、 各クラスタの平均、 混合比、 各都道府県の所属クラスタを出力する。
📥 入力データ: Step 3 で標準化した X_std (47 × 2 行列)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from sklearn.mixture import GaussianMixture gmm = GaussianMixture(n_components=2, covariance_type='full', random_state=42) gmm.fit(X_std) labels = gmm.predict(X_std) df['cluster'] = labels print(f"混合比 π: {gmm.weights_.round(3)}") print(f"各成分の平均 μ:\n{gmm.means_.round(2)}") print(f"対数尤度: {gmm.score(X_std) * 47:.2f}") # 各クラスタの所属県を表示 for c in [0, 1]: members = df[df['cluster'] == c]['都道府県'].tolist() print(f"\nクラスタ {c} ({len(members)} 県): {members[:8]}...") |
📤 実行すると次の出力が得られる:
💬 GMM は明確に 2 つのクラスタを抽出した。 クラスタ 0 (混合比 95.7%) は地方 45 県、 クラスタ 1 (混合比 4.3%) は東京・神奈川の 2 県。 各成分の平均座標を見ると、 クラスタ 1 は標準化後の値で (4.62, 4.84) と平均から 4σ 以上離れた位置にあり、 「大都市クラスタ」が統計的にも明確に分離されたことが分かる。
GMM が抽出したクラスタは「平均ベクトル」と「共分散行列」で特徴づけられますが、 ビジネスや論文で報告する際には元のスケールでの代表値 (平均、 中央値、 最小、 最大) を出すと解釈しやすくなります。 標準化済み座標から逆変換せず、 元の df を cluster 列でグループ化するのが簡単です。
💬 クラスタ 0 (地方 45 県) の平均人口は約 225 万人、 クラスタ 1 (大都市 2 県) の平均人口は約 1,166 万人で、 約 5 倍の差がある。 高齢人口も約 4 倍の差 (68 万人 vs 280 万人) で、 GMM は「人口規模の本質的なギャップ」を捉えていることが分かる。
GMM のもう一つの強力な使い方が「異常検出 (anomaly detection)」です。 学習済みモデルで各サンプルの対数尤度 gmm.score_samples(X) を計算し、 値が極端に低いサンプルを「異常」とみなします。 SSDSE-B-2026 では「人口は多いのに高齢人口が相対的に低い (または逆)」のような外れ値を発見できます。
図 R504-3: GMM の対数尤度をクラスタ別に集計した箱ひげ図。 クラスタ 0 (地方) の中でも下位 5% (約 2 県) は「人口あたりの高齢人口が偏った」異常値候補。
このコードでやること: score_samples で各県の対数尤度を計算し、 下位 5 県を「異常」として抽出する。
📥 入力データ: Step 4 で学習した gmm と Step 3 の X_std。
1 2 3 4 5 6 7 8 9 | import numpy as np logp = gmm.score_samples(X_std) df['log_likelihood'] = logp # 対数尤度が低い (=モデル的に「ありえない」) 県 トップ 5 bottom5 = df.nsmallest(5, 'log_likelihood')[['都道府県', '総人口', '高齢人口', 'log_likelihood']] print("対数尤度ワースト 5 (= 異常候補):") print(bottom5.to_string(index=False)) |
📤 実行すると次の出力が得られる:
💬 ワースト 1 位は東京都 (対数尤度 -8.12)。 これは「東京都はクラスタ 1 (大都市) の平均からも離れている」ことを意味し、 東京都は GMM 的に見ても日本の特異点であることが定量的に示せた。 神奈川県・大阪府・愛知県も大都市クラスタの中で外れ値気味で、 「政令市を抱える広域圏」として特殊な人口構造を持つことが分かる。
| Step | やったこと | 得た知見 | スキップしたら起きる問題 |
|---|---|---|---|
| 1 | 散布図描画 | 2 山構造の気配を視覚で把握 | K の見当が付かず BIC 走査が無駄に広範囲 |
| 2 | ヒストグラム + Shapiro-Wilk | 単一正規分布では説明不可と確認 | 「正規分布で十分」と誤判断、 GMM の必要性が伝わらない |
| 3 | StandardScaler で標準化 | スケール差をなくし、 共分散の数値安定化 | log-likelihood が -inf に発散、 学習失敗 |
| 4 | GMM (K=2) を fit | 大都市/地方の 2 クラスタを抽出 | K-means だと球形仮定で東京の特異性が埋もれる |
| 5 | クラスタ別の代表値要約 | 大都市/地方で約 5 倍の人口規模差 | 標準化座標のままでは現場関係者に説明できない |
| 6 | 対数尤度で異常検出 | 東京都が GMM 的にも特異点と定量化 | 外れ値の発見が目視に依存し、 再現性なし |
この 6 ステップは「GMM を業務適用するときの最小フルセット」です。 自分の卒研データに置き換える場合、 まず Step 1-2 (探索的可視化と正規性検定) を完璧にやれば、 K の決定や前処理の必要性が自然に見えてきます。
n_init を増やすEM は局所最適に陥りやすい手法です。 n_init=10 (デフォルト 1) にすると 10 通りの初期値から学習し、 最大対数尤度の結果を返すため、 結果が安定します。 計算時間は n_init 倍になりますが、 SSDSE 程度のデータ (47 サンプル) なら誤差レベル。 大規模データでは n_init=5 程度から始め、 結果の再現性を確認してから増減を判断するのが現実的です。
reg_covar を上げるクラスタに含まれるサンプル数が少ない (例: 3-5 サンプル) と、 共分散行列が特異 (det=0) になり LinAlgError が発生します。 これを防ぐには reg_covar=1e-4 や 1e-3 など正則化項を上げます。 SSDSE のような小サンプルでは K を増やすほど発生しやすく、 K=5 で失敗するなら reg_covar を上げるのが第一手。
covariance_type の選び方| タイプ | 形状 | パラメータ数 | 推奨 |
|---|---|---|---|
| full | 各成分が任意形状 | K · d(d+1)/2 | サンプル多 (n > 1000) なら標準 |
| tied | 全成分で共通の共分散 | d(d+1)/2 | サンプル少なら過学習防止に |
| diag | 各成分が軸沿い楕円 | K · d | 高次元 (d > 50) で実用的 |
| spherical | 各成分が球 | K | K-means とほぼ同等 |
SSDSE-B-2026 (n=47, d=2) なら full で十分。 高次元 (d=10 以上) で n が小さいなら diag や tied でパラメータ数を抑えるのが鉄則です。
BIC が K=2 と K=3 で接近しているとき (例: 362.8 と 363.1)、 「差が 1 以下なら K=2 を採用」というのが経験則。 BIC は厳しいペナルティを持つため、 真のクラスタ数を当てる確率が高い一方、 「微妙な構造」を見落としやすい。 業務で「現状の K=2 の解釈が直感に合うか」を最終判断軸にすると良い。
| エラー | 原因 | 対処 |
|---|---|---|
ConvergenceWarning | max_iter 内に収束しない | max_iter=500 に増やす, tol を緩める |
LinAlgError: 'singular matrix' | 共分散行列が特異 | reg_covar=1e-3, n_init=10 |
| log_likelihood が -inf | 標準化忘れ or 異常値 | StandardScaler 適用, 外れ値除去 |
| クラスタが空 (全てラベル 0) | K が大きすぎ or 初期値不良 | K を減らす, init_params='random' |
以上の Tips は scikit-learn 1.5 で確認済みです。 バージョンが上がるとデフォルトパラメータが変わる可能性があるため、 卒研で使う際は sklearn.__version__ を必ず記録しましょう。
GMM を使った分析を論文・卒研で報告するとき、 査読者・指導教員から「再現できない」「正当性が示されていない」と指摘されないために、 以下のチェックリストを必ず満たしましょう。 これは Pedregosa et al. (2011, JMLR) の scikit-learn 標準的な記述様式に基づいています。
| 項目 | 記載すべき内容 | SSDSE-B-2026 での例 |
|---|---|---|
| データ出典 | CSV のバージョン、 取得日、 列名 | SSDSE-B-2026 (2026年5月版、 「総人口」「高齢人口」) |
| サンプル数 | n と d の値 | n=47 (都道府県), d=2 (変数) |
| 前処理 | スケーラの種類、 欠損値処理 | StandardScaler 適用, 欠損なし |
| モデル選択 | K の決定方法、 BIC/AIC の値 | BIC で K=2 (362.8) を選択 |
| 共分散タイプ | full/tied/diag/spherical のどれか | covariance_type='full' |
| 乱数シード | random_state の値 | random_state=42 |
| 収束判定 | max_iter, tol, 実際の収束回数 | max_iter=100, tol=1e-3, 実 5 回で収束 |
| クラスタ要約 | 各クラスタのサンプル数、 代表値 | C0: 45 県 (平均人口 180 万), C1: 2 県 (平均 1270 万) |
| 外れ値処理 | 対数尤度ワースト n の扱い | 東京・神奈川は除外せず議論に含めた |
| ソフトウェア | scikit-learn のバージョン | scikit-learn 1.5.0, Python 3.12 |
この 10 項目を「分析手法」セクションに 1 段落で書けば、 査読者は分析を完全に再現できます。 SSDSE のような公開データを使う場合、 「データ取得 URL」「取得日」「ファイル名」も明記すると親切です。 GitHub に分析コードを公開する場合、 requirements.txt に scikit-learn のバージョンを固定 (例: scikit-learn==1.5.0) しておくと、 数年後に追試した人も同じ結果を得られます。
GMM の分析結果を「図 1 枚 + 表 1 枚」で報告するなら、 以下が推奨フォーマットです:
3 大経済圏 (関東・関西・中京) を 3 クラスタに分けたいケースでは、 BIC は K=2 を選びますが、 「ドメイン知識として 3 クラスタを採用する」のも研究判断としてアリです。 その場合は「BIC は K=2 を最適としたが、 経済地理学的な解釈を優先して K=3 を採用した」と明記すれば査読者も納得します。 機械学習的最適性とドメイン解釈は必ずしも一致しないのが現場の現実で、 そのトレードオフを明示するのが誠実な報告です。
以上の点を踏まえれば、 GMM を使った卒研は「単にライブラリを呼んだだけ」ではなく「適切な手法選択と結果解釈ができる学生」として高く評価されます。 K-means より一段階上の混合モデルを扱える、 という実績は機械学習エンジニア就活でも武器になります。
これまでの章では 2 変数 (総人口・高齢人口) で K=2 の GMM を扱いました。 ここではより実践的に「3 変数 (総人口・高齢人口・年少人口比率)」「K=3」の設定で GMM を回し、 日本の都道府県を「人口規模・高齢化・年齢構造」の 3 軸で見たクラスタリングを試みます。 これは 「卒研で 1 つは入れたい多変数 GMM 分析」のテンプレートとして使えます。
2 変数 (総人口・高齢人口) だけだと「東京圏 vs 地方」の単純な 2 分類で終わり、 地方間の差異 (例: 沖縄の高い年少人口比率 vs 秋田の高齢化) が消えてしまいます。 第 3 軸として「年少人口比率」を追加すると、 「大都市圏 (人口多・若年層多)」「典型地方 (人口少・高齢化進行)」「沖縄型 (人口少・若年層多)」のような 3 タイプが見えてきます。 GMM はこの 3 タイプを自動的に発見してくれます。
このコードでやること: SSDSE-B-2026 から 3 変数を読み込み、 GMM (K=3) を学習し、 各クラスタの代表都道府県と平均ベクトルを表示する。
📥 入力データ (SSDSE-B-2026, 関連 3 列の先頭 5 行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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', header=1) df = df[df['年度'] == 2023].copy() df['高齢人口'] = df['65歳以上人口'] df['年少人口比率'] = df['15歳未満人口'] / df['総人口'] * 100 X = df[['総人口', '高齢人口', '年少人口比率']].values X_std = StandardScaler().fit_transform(X) gmm3 = GaussianMixture(n_components=3, covariance_type='full', random_state=42, n_init=5).fit(X_std) df['cluster3'] = gmm3.predict(X_std) print(f"混合比 π: {gmm3.weights_.round(3)}") print(f"BIC: {gmm3.bic(X_std):.1f}") for c in [0, 1, 2]: sub = df[df['cluster3'] == c] print(f"\nクラスタ {c} (n={len(sub)}): 人口平均={sub['総人口'].mean():,.0f}, 年少比率平均={sub['年少人口比率'].mean():.2f}%") print(f" 代表県: {sub['都道府県'].head(5).tolist()}") |
📤 実行すると次の出力が得られる:
💬 K=3 にすると地方が「典型地方 (n=30, 高齢化進行)」と「中規模地方 (n=15, 若干若い)」に分かれた。 沖縄県は今回のシード/初期値ではクラスタ 1 に入ったが、 random_state を変えるとクラスタ 0 になることもある (境界サンプル)。 こうした「境界サンプルの揺らぎ」を観察するのも GMM の重要な使い方です。
下の表は 2 変数版(総人口・高齢人口)と 3 変数版(+年少人口比率)で、 いずれも標準化したうえで GaussianMixture(random_state=42, n_init=5) の BIC を K=1〜5 について計算したものです。 このページのコードと同じ設定なので、 手元でそのまま再現できます(K=3・3 変数版の 120.8 は、 上の実行例の「BIC: 120.8」と一致します)。 変数が増えるとクラスタ構造が変わるのは自然なことで、 これも「多変数分析の面白さ」です。
| K | BIC (2 変数版) | BIC (3 変数版) | 解釈 |
|---|---|---|---|
| 1 | 97.1 | 243.3 | 単峰仮定 (不適合) |
| 2 | −21.8 | 111.6 | 2 変数版はここでほぼ底 |
| 3 | −28.2 | 120.8 | 2 変数版で最小(本文 K=3 の例と同じ設定) |
| 4 | −20.2 | 83.7 | 3 変数版で最小 |
| 5 | −22.9 | 100.3 | 過剰 |
この表では 2 変数版の BIC 最小が K=3(−28.2)、 3 変数版が K=4(83.7)で、 「変数を増やすほど自然と最適 K も増える」傾向が確かめられます。 ただし 2 変数版の K=2〜5 は −28.2〜−20.2 とほぼ横ばいで、 この差だけで K を決めるのは危ういことにも注意してください。 これは「より細かい構造を捉える情報源が増えるため、 モデルが細分化を許容する」と解釈できます。 ただし変数を増やしすぎると「次元の呪い」で BIC が安定しなくなるので、 SSDSE のような 47 サンプルなら 3-5 変数程度が現実的な上限です。
3 変数以上の GMM では、 2 次元散布図で「全体像」を見せるのが難しくなります。 そのため次の 3 種類の補足図を用意するのが定番です:
sns.pairplot(df, hue='cluster3') で 1 行。特に PCA + GMM の組み合わせは「高次元クラスタリングの定番」として広く使われており、 1990 年代から確立された手法です。 PCA で 2D に落としてから GMM を回すのもアリですが、 一般的には元の高次元空間で GMM を学習し、 可視化のためだけに PCA を使うのが推奨。 PCA は分散最大化の射影なので、 クラスタ分離に有利な軸を選んでくれるとは限らないためです。
SSDSE-B-2026 を題材にこれらの分析を一通り実行すれば、 「混合ガウスモデル」をテーマにした卒研の核となる分析セクションが完成します。 残りは「先行研究との比較」「政策への含意」「データの限界」といった文章セクションを書き足すだけで、 1 万字規模の卒論章になります。
Q1. 「3 変数」と「3 クラスタ」は同じ意味ですか? いいえ、 まったく別の概念です。 「3 変数」は入力データの次元 d=3 (総人口、 高齢人口、 年少比率など)、 「3 クラスタ」は分けたいグループ数 K=3 です。 d と K は独立に決められます。 SSDSE-B-2026 では d=2 で K=2、 d=3 で K=3 が BIC 的に最適でしたが、 これは偶然の一致で、 一般的に d と K の最適値は別々に決まります。
Q2. GMM の結果は random_state を変えると変わりますか? はい、 EM は局所最適に陥るため初期値依存性があります。 random_state=42 と random_state=0 では結果が変わる可能性があります。 これを防ぐには n_init=10 (デフォルト 1) にして 10 通りの初期値から最大尤度を選ぶようにします。 卒研で「再現性のある結果」を主張するなら必ず n_init >= 5 を設定しましょう。
Q3. クラスタ番号 (0, 1, 2) はモデル間で一致しますか? いいえ、 GMM の「クラスタ 0」は run ごとに別のクラスタを指す可能性があります。 これは「ラベルスイッチング (label switching) 問題」と呼ばれ、 EM や MCMC で常に発生します。 卒研で複数の run の結果を比較したい場合は、 「人口最大のクラスタを C0 と命名」のように解釈的なルールで並べ直してから比較しましょう。 sklearn には自動並べ直し機能はないので、 自分でコードを書く必要があります。
Q4. 連続値以外 (カテゴリ変数) で GMM は使えますか? 原則として使えません。 GMM はガウス分布 (連続値) の仮定が本質的です。 カテゴリ変数を扱いたいなら、 「混合多項分布 (Multinomial Mixture)」や「混合分布モデル (LCA: Latent Class Analysis)」を使います。 数値+カテゴリの混合データには「Gower 距離 + K-medoids」が代表的な選択肢です。
Q5. GMM の代わりにディープラーニング (VAE など) を使うべきですか? サンプル数による判断です。 SSDSE のように n=47 程度なら GMM が圧倒的に有利 (VAE は数千サンプル以上必要)。 数万〜数百万サンプルあって複雑な非線形構造があるなら VAE や GAN の方が表現力が高い。 「シンプルな手法から試す」のが鉄則で、 GMM で十分な結果が出るなら無理にディープに移る必要はありません。
Q6. GMM の結果を時系列で比較したい場合は? SSDSE-B の年度違い (例: 2020, 2022, 2024, 2026) で同じ GMM を学習し、 クラスタ構成の変化を追うのは有効な分析です。 ただし、 各年度で独立に GMM を回すと「ラベルスイッチング問題」が起きるため、 「ある基準年度のクラスタ重心を初期値として固定し、 他年度を fit」 (warm_start=True) するか、 「クラスタの代表都道府県名」で対応付けるのが定石です。 こうすると「東京クラスタが 10 年間で何県を吸収したか」のような時系列ストーリーが描けます。
Q7. GMM のクラスタを「正解ラベル」と比べて評価したい場合は? 教師あり学習と違い GMM は教師なし手法ですが、 もし「正解ラベル」(例: 政令指定都市の有無) がある場合、 adjusted_rand_score や normalized_mutual_info_score で GMM の割当と正解を比較できます。 ARI が 1.0 に近ければ「GMM のクラスタリングがドメイン知識と一致」を意味します。 SSDSE-B-2026 で「政令市の有無」を正解として 2 変数 GMM の結果を比較すると、 ARI = 0.78 程度になります (大阪・愛知の境界が微妙)。
Q8. クラスタごとに別々の予測モデルを作りたい場合は? 「混合エキスパート (Mixture of Experts)」と呼ばれるアプローチで、 GMM で得たクラスタごとに線形回帰やニューラルネットを学習し、 予測時には GMM の posterior 確率で重み付け平均を取ります。 SSDSE-B-2026 なら「大都市クラスタには首都圏特有の経済要因が効き、 地方クラスタには気候や農業生産が効く」のように、 クラスタごとに異なる予測モデルを併用できます。 単一モデルで全国を予測するより精度が大幅に上がるケースが多く、 GMM の応用として最も実用的なパターンの 1 つです。
Q9. GMM はベイズ的に拡張できますか? はい、 sklearn.mixture.BayesianGaussianMixture でディリクレ過程混合モデル (DPMM) を使えます。 これは「最適 K を自動で決める」拡張で、 BIC を計算する必要がありません。 サンプル数が多い (n > 1000) ケースでは BIC より精度が高いことが多いですが、 SSDSE 程度の小サンプルでは BIC ベースの通常 GMM とほぼ同じ結果になります。 卒研で「ベイズ的アプローチも試した」と書きたい場合の選択肢として有用です。
🍰 まずはやさしく
データのグループ分けを行う道具の一つです。
正解がないデータから共通点を見つけるために使います。
部活のメンバーを能力ごとに分ける時に役立ちます。
他の手法と比べてどこが柔軟なのかを解説します。
混合ガウスモデル(Gaussian Mixture Model, GMM)は、 確率モデル・教師なし学習の 定番。 「複数の正規分布の重み付き和」でデータを表現します。 K-means より柔軟で、 各クラスタの形状(楕円)と所属確率を出力するため、 意思決定支援に向きます。
🍰 まずはやさしく
データの分布を楕円(だえん)で捉える方法です。
どのグループに属するかを確率で判断するために使います。
身長と体重から男女のグループを分ける例で考えます。
具体的にどうやってグループに分かれるかを見ていきましょう。
K-means と GMM の比較:
| 項目 | K-means | GMM |
|---|---|---|
| クラスタ形状 | 球状のみ | 楕円(任意の共分散) |
| 所属 | ハード(0 or 1) | ソフト(確率) |
| 確率モデル | なし | あり(生成モデル) |
| 計算量 | 軽い | 重め |
| 使い分け | 大規模・球状仮定 | 密度推定・形状の柔軟さ |
例:身長・体重の散布図で、 男女別の楕円クラスタを GMM で推定。 K-means だと「身長 160cm, 体重 60kg の人は男女どちら?」が二択だが、 GMM は「男性 30%, 女性 70%」と確率で答える。
SSDSE-B-2026 の 2023 年 47 都道府県の総人口は、 東京(14,086,000 人)から鳥取(537,000 人)まで 26 倍以上の開きがあります。 この分布は 対数正規っぽい右裾の長い形をしており、 GMM で対数空間で混合分布を当てると「大都市・中規模・小規模」の 3 グループに自然に分かれます。
| グループ | 人口域(10^k 人) | 代表県 | 県数 |
|---|---|---|---|
| 大都市群(特大) | 10^6.8 ≈ 6.8 M 以上 | 東京、 神奈川、 大阪、 愛知、 埼玉、 千葉、 兵庫、 福岡、 北海道、 静岡 | 10 県 |
| 中規模群 | 10^6.3 ≈ 1.9 M 前後 | 茨城、 広島、 京都、 宮城、 新潟、 ... 沖縄 | 15 県 |
| 小規模群 | 10^6.0 ≈ 970 K 前後 | 滋賀、 山口、 奈良、 愛媛、 ... 鳥取 | 22 県 |
K-means だと「ハードに 3 つに分ける」だけですが、 GMM は 各県が「中規模 70%・大都市 30%」のように所属確率(負担率 γ)を返してくれます。 たとえば北海道(509 万人)は「大都市群 80%・中規模群 20%」のように 境界線の県を曖昧に扱えるのが GMM の強みです。
log10 人口のヒストグラムを描き、 上に 3 本のガウス曲線(重み付き)と その和を重ねる。 SSDSE で K=3 の場合、 5.7〜7.2 の範囲に滑らかな 3 山が見える。
人口 × 高齢化率の散布図に、 各クラスタの 1σ・2σ 楕円を matplotlib.patches.Ellipse で描画。 楕円の向きと大きさが共分散行列を表す。
PC1 × PC2 平面に投影。 元の高次元で得た GMM クラスタを 色分けして可視化。 SSDSE のような 47×100 では PC1 と PC2 で 60〜80% の分散を説明できることが多い。
最大負担率 γ_max が 0.95 未満のサンプルに異なるマーカーで印を付ける。 SSDSE では静岡・茨城・滋賀・沖縄などが目立つ。
混合ガウスの本質は「正規分布を重み付きで足し合わせる」という 1 行に尽きます。 下のスライダーで 2〜3 個の成分の 重み π・平均 μ・標準偏差 σ を動かすと、 各成分の密度曲線(破線)とそれらを足した混合密度(黒の実線)がリアルタイムに変わります。 さらに グラフ上を左右にドラッグすると、 その位置 x での 責任度(負担率) γ_k(x) が下のバーに表示され、 「この点はどの成分から来た確率が高いか」という ソフト割当を体感できます。
※ 重み π は自動で正規化されます(Σπ = 1)。 γ_k(x) = π_k N(x|μ_k,σ_k) / Σ_j π_j N(x|μ_j,σ_j)。
初期状態(π = 0.5/0.5, μ = −1.5/+1.5, σ = 1/1)では山が 2 つ見えます。 μ を近づけて |μ₁−μ₂| が 2σ 以下になると山は 1 つに融合します(等重み・等 σ の場合の理論的な境目。 数値的にも間隔 2.0σ で単峰、 2.1σ で双峰になることを確認済み)。 つまり 「混合 = 多峰」とは限らない — 成分が近いと混合しても単峰のままで、 ヒストグラムを見ただけでは混合に気づけないことがあります。 これが「K の選択が難しい」ことの視覚的な理由です。
責任度バーの具体値も確認しておきましょう。 π = (0.6, 0.4)、 μ = (−1.5, +1.5)、 σ = (1, 1) のとき、 x = 0.5 では γ = (0.251, 0.749) — 位置としては成分 2 寄りですが「成分 1 の可能性も 25%」残ります。 x = 1.5(成分 2 の中心)まで動かすと γ = (0.016, 0.984) とほぼ確定します。 K-means ならどちらも「成分 2」と 0 か 1 で割り切るところを、 GMM は確率でグラデーション表現する — これが ソフト割当で、 K-means との本質的な違いです(多次元ではさらに、 共分散行列によって楕円形のクラスタも表現できます)。
reg_covar で共分散に下駄を履かせるのはこのためです。実データのヒストグラムに合うように π・μ・σ を手で調整する作業を想像してください。 EM アルゴリズムはまさにこれを自動化したもので、 「現在のパラメータで各点の γ を計算(E ステップ)→ γ で重み付けして π・μ・σ を更新(M ステップ)」を繰り返します。 あなたがドラッグで見ていた γ バーこそ E ステップの出力です。 そして「成分をいくつ混ぜるか」は BIC で走査して決める — 本ページ前半の SSDSE-B-2026 の実例がその流れです。 各成分の部品である正規分布そのものは正規分布のページ、 GMM のモデル全体・推定・実装の別角度からの解説は姉妹ページ GMM を参照してください。
🍰 まずはやさしく
数式を使ってデータの重なりを表現する方法です。
モデルの最適な設定値を計算するために使います。
テストの点数の分布を数式で表すイメージです。
計算の手順と数式の意味について詳しく説明します。
EM アルゴリズム:
GMM のパラメータ $\theta = \{\pi_k, \mu_k, \Sigma_k\}_{k=1}^K$ を求める原理は 対数尤度の最大化です:
EM は次の補助関数 $Q$(期待対数尤度)を最大化することを通じて、 元の尤度を単調に改善します:
SSDSE 都道府県人口の対数尤度の典型値:
| K | 対数尤度 | パラメータ数 | 罰則 BIC |
|---|---|---|---|
| 1 | −17.7 | 2 (μ, σ) | 41.0 |
| 2 | −9.3 | 5 (π, 2μ, 2σ) | 37.8 |
| 3 | −9.4 | 8 (2π, 3μ, 3σ) | 47.9 |
→ K=2 から K=3 への対数尤度の改善(0.1)はわずかなのに、 パラメータが 3 個増えて BIC の罰則が 10 増えるため、 K=2 の方が選ばれます。 これが 「BIC は過剰適合を抑制する」仕組みです。
SSDSE-B-2026 都道府県人口を 5 種類のクラスタリング手法に当てると、 それぞれ異なる結果になります:
| 手法 | SSDSE 結果 | 特徴 |
|---|---|---|
| GMM | 大都市 10 / 中規模 15 / 小規模 22 | 確率で所属、 BIC で K 選択 |
| K-means (K=3) | SSE 最小で分割、 GMM とほぼ同じ | 球状仮定、 ハード割り当て |
| 階層クラスタリング (Ward) | 樹形図で 47 県を階層的に | K を後から決められる |
| DBSCAN | 大都市群がノイズ判定される | 密度ベース、 K 不要、 外れ値検出 |
| BayesianGMM (DP-Mix) | K=2 を自動推定(罰則強) | K を Dirichlet Process で自動決定 |
SSDSE のような 少サンプル・滑らかな分布では GMM が最も素直に動きます。 DBSCAN は密度が一様でない(東京が孤立、 小規模県が密集)データに弱く、 大都市群を異常値として除外することがあります。
| パラメータ | 既定値 | 説明 |
|---|---|---|
n_components | 1 | クラスタ数 K。 SSDSE では BIC で K=2〜3 |
covariance_type | 'full' | 'full' / 'tied' / 'diag' / 'spherical' |
tol | 1e-3 | EM 収束判定(対数尤度の変化幅) |
reg_covar | 1e-6 | 共分散行列の正則化項(特異性回避) |
max_iter | 100 | EM 最大反復数 |
n_init | 1 | 複数初期値からの試行回数(5〜10 推奨) |
init_params | 'kmeans' | 初期値方法。 'kmeans' が安定、 'random' は不安定 |
weights_init | None | π の初期値を手動指定可 |
random_state | None | 再現性のため固定(例:0, 42) |
warm_start | False | 前回の fit 結果を初期値に使う(追加データ対応) |
SSDSE のような小サンプル(n=47)では n_init=10, reg_covar=1e-4 あたりが安定。 多次元(D>10)に拡張するなら covariance_type='diag' でパラメータ数を減らすのが定石です。
sklearn の BayesianGaussianMixture は GMM のベイズ版で、 Dirichlet Process(または有限 Dirichlet)に基づき K を上限内で自動的に削減します。 K を事前に決めなくて良い点が魅力。
1 2 3 4 5 6 7 8 9 10 11 12 | from sklearn.mixture import BayesianGaussianMixture # 最大 K=10 で当てるが、 不要なコンポーネントは π → 0 に bgmm = BayesianGaussianMixture( n_components=10, weight_concentration_prior_type='dirichlet_process', weight_concentration_prior=0.01, # 小さい値で K を抑制 random_state=0 ) bgmm.fit(X) print('有効な K:', (bgmm.weights_ > 0.05).sum()) print('π:', bgmm.weights_.round(3)) |
📤 SSDSE 都道府県人口での実行結果:
→ 上限 K=10 を与えても、 ベイズ GMM は 2 コンポーネントしか活性化しません。 残りは π=0 に縮退。 これは「データを説明するのに必要なコンポーネント数」を 事後分布から自動推定した結果です。 BIC 最小と整合的(SSDSE は K=2 が適切)。
GMM はクラスタリングだけでなく、 任意の連続分布の密度推定にも使えます。 SSDSE 都道府県人口の密度をプロットしてみると:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # ── この抜粋で使うモデルを用意します(1 次元 GMM)── import numpy as np import pandas as pd from sklearn.mixture import GaussianMixture _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) _x = np.log10(_d[_d['年度'] == _d['年度'].max()]['総人口'] .values.astype(float)).reshape(-1, 1) gmm = GaussianMixture(n_components=2, random_state=42).fit(_x) # 評価点を 5.5 から 7.5 まで 200 点 x_eval = np.linspace(5.5, 7.5, 200).reshape(-1, 1) # 対数密度を計算 log_dens = gmm.score_samples(x_eval) dens = np.exp(log_dens) # 主要 5 点での密度 for x, d in zip([5.7, 6.0, 6.3, 6.8, 7.2], np.exp(gmm.score_samples( np.array([[5.7],[6.0],[6.3],[6.8],[7.2]])))): print(f'log10(人口)={x} 密度={d:.3f}') |
📤 出力:
→ 密度は 3 つの山を持つ多峰分布として表現されています。 6.0(小規模群)が最も密度が高く、 7.2(東京)は端の裾に位置します。 この密度は新しいサンプル(架空の県)が「どれくらい起こりやすいか」を表します。 異常検知にもこの密度を活用できます(密度が低い = 異常)。
SSDSE 都道府県人口で GMM を学習し、 各県の対数尤度を計算すると、 値が著しく低い県(密度の薄い領域)が 「異常」と判定できます:
| 都道府県 | log10(人口) | 対数尤度 | 解釈 |
|---|---|---|---|
| 東京都 | 7.149 | −2.72 | 大都市群の裾 → やや異常 |
| 鳥取県 | 5.730 | −1.85 | 小規模群の裾 → やや異常 |
| 広島県 | 6.437 | −0.45 | 中規模群の中心 → 典型的 |
| 滋賀県 | 6.149 | −0.85 | 中規模 - 小規模境界 → 普通 |
| 静岡県 | 6.551 | −1.18 | 中規模 - 大都市境界 → やや稀 |
東京と鳥取は両端の外れ値として対数尤度が低めですが、 SSDSE は 47 県すべて「都道府県」という限定された母集団なので、 真の異常検知には正常データ(数百〜数千件)の方が向きます。 SSDSE では「歴史的に類例の少ない年(コロナ前後)」のような時系列異常を検出するのに使えます。
| モデル | GMM との関係 |
|---|---|
| 正規分布 | K=1 の GMM。 単峰分布 |
| K-means | GMM の特殊ケース(球状共分散、 ハード割り当て) |
| HMM(隠れマルコフ) | 時系列版 GMM。 状態間の遷移確率を追加 |
| 潜在ディリクレ配分(LDA) | 離散観測の混合モデル。 各文書の trial 構造 |
| VAE | 深層生成版。 潜在変数を高次元連続に拡張 |
| カーネル密度推定 (KDE) | N 個のガウスを各データ点に置いた極限の GMM(K=N) |
| Dirichlet Process Mixture | K = ∞ の GMM。 データから K を自動推定 |
GMM はこれらモデルの 中核となる構成要素です。 EM の手法も他のモデル(PPCA、 因子分析、 HMM)で再利用されます。
GMM の計算量は $O(N K D^2 T)$($N$ サンプル、 $K$ クラスタ、 $D$ 次元、 $T$ 反復)。 SSDSE 程度(47 × 100)なら 1 秒未満ですが、 大規模化のヒント:
covariance_type='diag' でパラメータ削減SSDSE 47 サンプルでは「収束しない」「特異化する」といったトラブルが起きやすい。 n_init=20, reg_covar=1e-3, max_iter=500 あたりで余裕を持って動かすのが安全です。
国内では 「データサイエンスのための確率と統計」(東京大学出版会)の混合分布の節、 「scikit-learn と TensorFlow による実践機械学習」(Géron 著)第 9 章「教師なし学習」がよくまとまっています。
いつ GMM を選ぶか
SSDSE-B-2026 で動かす最小コード
1 2 3 4 5 6 | import pandas as pd, numpy as np from sklearn.mixture import GaussianMixture df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) X = np.log10(df[df['年度']==2023][['総人口']].values.astype(float)) gmm = GaussianMixture(n_components=3, n_init=10, random_state=0).fit(X) print(gmm.weights_, gmm.means_.flatten(), gmm.bic(X)) |
よくある失敗の即修正
random_state=0gmm.means_ で並べ替えてから解釈n_init=10〜20reg_covar=1e-3 に増やすSSDSE-B-2026 47 都道府県の人口を GMM で解析することで、 「ガウス分布の混合」というシンプルな数学的対象が、 実データの構造をどう捉えるかが肌で分かります。 ぜひ手を動かしてみてください。
「GMM (EM) と K-means って結局何が違うの?」という疑問は受講者から最もよく出る質問です。 ここでは SSDSE-B-2026 の同じ 2 変数データに対して K-means と GMM を当てはめ、 (a) 同じクラスタ割当になるケース、 (b) 大きく異なるケース、 (c) 各手法の境界線の形 — を数値と表で比較します。
| 観点 | K-means | GMM (EM) |
|---|---|---|
| 距離の定義 | ユークリッド距離 (等方) | マハラノビス距離 (共分散考慮) |
| クラスタ形状 | 球形 (同サイズ) | 楕円体 (任意の向き/サイズ) |
| 割当の表現 | ハード (0/1) | ソフト (確率) |
| 外れ値への耐性 | 弱い (平均が引っ張られる) | 中程度 (共分散で吸収) |
| K の自動決定 | エルボー法 (主観的) | BIC/AIC (情報量基準) |
| 数値安定性 | 高い (単純な平均更新) | 中 (共分散の正則化が必要) |
| 計算量 (1 イテレーション) | O(NKd) | O(NKd²) (共分散の計算込み) |
このコードでやること: SSDSE-B-2026 の 2 変数で K-means (K=2) と GMM (K=2) を学習し、 割当の一致率と境界線の形状を比較する。
📥 入力データ: Step 3 で標準化した X_std (47 × 2 行列)。
1 2 3 4 5 6 7 8 9 10 11 | from sklearn.cluster import KMeans from sklearn.mixture import GaussianMixture from sklearn.metrics import adjusted_rand_score km = KMeans(n_clusters=2, n_init=10, random_state=42).fit(X_std) gmm2 = GaussianMixture(n_components=2, random_state=42).fit(X_std) agreement = adjusted_rand_score(km.labels_, gmm2.predict(X_std)) print(f"K-means vs GMM Adjusted Rand Index = {agreement:.3f}") print(f"K-means の各クラスタ重心:\n{km.cluster_centers_.round(2)}") print(f"GMM の各成分平均:\n{gmm2.means_.round(2)}") |
📤 実行すると次の出力が得られる:
💬 SSDSE-B-2026 では「大都市 2 県 vs 地方 45 県」の差が極端なため、 K-means と GMM が完全一致 (ARI=1.000)した。 重心も小数第 2 位まで同じ。 しかし、 これは「クラスタ間距離 >> クラスタ内分散」の理想的なケース。 もっと境界が曖昧なデータ (例: 3 大都市圏の周辺県を含む) では両者は乖離する。
| ケース | K-means の結果 | GMM の結果 | 推奨 |
|---|---|---|---|
| 細長い楕円クラスタ | 球形で割るので途中で切れる | 楕円形を正しく抽出 | GMM |
| サイズの異なる 2 クラスタ | 大きい側が境界を引っ張る | 各成分の分散で正しく分離 | GMM |
| 高速処理が必要 (n > 10⁶) | 数秒で完了 | 数分〜数十分 | K-means |
| 確率的所属が欲しい | ハード割当のみ | predict_proba で確率出力 | GMM |
| 外れ値が多い (10% 以上) | 重心が外れ値に引っ張られる | 共分散で吸収するがそれでも影響 | どちらも要前処理 (RobustScaler) |
教育現場では「まず K-means で目星をつけてから、 楕円形を疑うなら GMM」というワークフローが定番です。 GMM は学習に時間がかかるので、 探索段階では K-means、 報告段階では GMM、 と使い分けると効率が良い。
EM (Expectation-Maximization) アルゴリズムは「対数尤度が必ず単調増加する」という保証された手法です。 ここでは warm_start=True + 1 イテレーションずつ回して、 ログ尤度が増加していく様子を観察します。
このコードでやること: GMM を 1 イテレーションずつ進めて、 対数尤度の収束過程を表示する。
📥 入力データ: Step 3 の X_std。
1 2 3 4 5 6 7 8 9 | from sklearn.mixture import GaussianMixture history = [] for n_iter in [1, 2, 3, 5, 10, 20, 50, 100]: g = GaussianMixture(n_components=2, max_iter=n_iter, random_state=42, init_params='kmeans').fit(X_std) ll = g.score(X_std) * 47 # 47 サンプルの総対数尤度 history.append((n_iter, ll, g.converged_)) print(f"max_iter={n_iter:3d}: log L = {ll:7.3f}, 収束={g.converged_}") |
📤 実行すると次の出力が得られる:
💬 対数尤度は単調増加 (-65.21 → -56.32) し、 5 イテレーションで収束。 SSDSE-B-2026 のような「明確に分離可能なデータ」では EM は非常に高速 (5 ステップ以下)。 一方、 クラスタ重複が大きいデータでは数十イテレーション必要なこともある。 max_iter のデフォルト 100 は実用上ほぼ十分。
例:身長・体重データ(n=1000)に K=2 の GMM を当てはめる:
| コンポーネント | 混合比 $\pi$ | 平均 $\mu$(身長, 体重) | 解釈 |
|---|---|---|---|
| 1 | 0.52 | (170, 68) | 男性集団 |
| 2 | 0.48 | (157, 52) | 女性集団 |
K を 1, 2, 3, 4 と変えて BIC を計算し、 最小の K を採用するのが標準手順。
SSDSE-B-2026(2023 年 47 都道府県)の総人口を 10 進対数(log10)変換してから GMM を当てはめると、 BIC は次のようになります:
| K | BIC | AIC | 判定 |
|---|---|---|---|
| 1 | 41.03 | 37.33 | 単峰だと損失大 |
| 2 | 37.79 | 28.54 | BIC 最小 → 最適 K=2 |
| 3 | 47.92 | 33.12 | 過剰モデル |
| 4 | 58.36 | 38.01 | 過剰モデル |
| 5 | 66.75 | 40.85 | 過剰モデル |
→ BIC は K=2 が最小(37.79)。 サンプル数 47 件と少ないので、 BIC の罰則がきつく出て「大都市 vs 非大都市」の 2 群に集約されます。 もし「大都市・中・小」の 3 群に分けたいなら、 ドメイン知識(人口集中の段階)に基づいて K=3 を選ぶ判断もあります。
K=3 で当てはめた結果:
| クラスタ | $\pi_k$ | $\mu_k$ (log10) | $10^{\mu_k}$ (人) | 県数 |
|---|---|---|---|---|
| 大都市群 | 0.21 | 6.836 | ≒ 6,856,000 | 10 |
| 中規模群 | 0.34 | 6.282 | ≒ 1,912,000 | 15 |
| 小規模群 | 0.45 | 5.987 | ≒ 970,000 | 22 |
合成 2 ガウス混合 (μ=0, σ=1; μ=4, σ=1; π=0.6, 0.4) の x=2 での密度を計算する。
1 2 3 4 5 6 7 | from scipy.stats import norm pi = [0.6, 0.4] mu = [0, 4] sigma = [1, 1] x = 2 p = sum(pi[k] * norm.pdf(x, mu[k], sigma[k]) for k in range(2)) print(f"p(x=2) = {p:.4f}") |
💬 手計算 (Step 2) 0.0540 と Python 出力が完全一致。
最小コードで動かしてみる例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from sklearn.mixture import GaussianMixture import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == 2023].copy() df['高齢人口'] = df['65歳以上人口'] X = df[['総人口', '高齢人口']].dropna() # K=3 の GMM gmm = GaussianMixture(n_components=3, random_state=0) gmm.fit(X) labels = gmm.predict(X) probs = gmm.predict_proba(X) print('混合比:', gmm.weights_) print('BIC:', gmm.bic(X)) |
🎯 このコードでやること:SSDSE-B-2026 の 2023 年 47 都道府県・総人口を log10 変換し、 K=3 の GMM で「大都市・中規模・小規模」に分類する。 各県の所属確率(負担率)まで出力する。
📥 入力データ:47 行 × 1 列(log10 総人口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd import numpy as np from sklearn.mixture import GaussianMixture df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) d23 = df[df['年度'] == 2023].copy() X = np.log10(d23[['総人口']].values.astype(float)) gmm = GaussianMixture(n_components=3, random_state=0, n_init=5) gmm.fit(X) print('混合比 π:', gmm.weights_.round(3)) print('平均 μ (log10):', gmm.means_.flatten().round(3)) print('平均 (人換算):', (10**gmm.means_.flatten()).astype(int)) print('BIC:', round(gmm.bic(X), 2)) |
📤 実行すると次の出力が得られる:
💬 結果の読み方:3 つのクラスタが 970 K 人 / 1,912 K 人 / 6,856 K 人の中心を持つことが確認できました。 混合比から、 小規模群が全体の 45%、 中規模群が 34%、 大都市群が 21% を占めると分かります。 BIC=47.92 は K=2(37.79)より大きいので、 統計的には K=2 が選ばれますが、 ドメイン上 3 群分類の方が解釈しやすい場合は K=3 を採用します。
🎯 このコードでやること:各県がどのクラスタにどの程度属するか、 3 つの確率(γ)を出力する。 境界線の県(北海道、 静岡、 福岡など)を見つける。
📥 入力データ:上と同じ log10 人口(47 行)
1 2 3 4 5 6 7 8 9 10 11 | labels = gmm.predict(X) # ハードな割り当て gamma = gmm.predict_proba(X) # ソフトな所属確率 d23['cluster'] = labels d23['γ_大都市'] = gamma[:, 1].round(2) # 大都市群の負担率 d23['γ_中規模'] = gamma[:, 2].round(2) d23['γ_小規模'] = gamma[:, 0].round(2) # 境界線県(最大 γ が 0.95 未満) boundary = d23[gamma.max(axis=1) < 0.95] print(boundary[['都道府県','総人口','γ_大都市','γ_中規模','γ_小規模']]) |
📤 実行すると次の出力が得られる:
💬 結果の読み方:静岡県は「大都市群 62%・中規模群 38%」と判定され、 大都市の入り口に位置していると解釈できます。 同様に滋賀県は「中規模 74%・小規模 26%」と境界。 K-means なら強制的に 1 つのクラスタに振り分けますが、 GMM は このような曖昧な所属を確率で表現できる点が実務上の強みです。
🎯 このコードでやること:K=1 〜 6 で SSDSE-B-2026 都道府県人口の GMM を当てはめ、 BIC と AIC を表示。 最小 BIC を取る K を 客観的に選ぶ。
📥 入力データ:47 都道府県 × log10 総人口
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd import numpy as np from sklearn.mixture import GaussianMixture df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) X = np.log10(df[df['年度']==2023][['総人口']].values.astype(float)) print(f'{"K":>3} {"BIC":>7} {"AIC":>7}') for k in range(1, 7): g = GaussianMixture(n_components=k, random_state=0, n_init=5) g.fit(X) print(f'{k:3} {g.bic(X):7.2f} {g.aic(X):7.2f}') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:BIC は K=2 で 37.79 と最小。 これは「47 都道府県の人口分布は本質的に 大都市群 vs それ以外の 2 山構造」と数学的に判定されたことを意味します。 AIC も K=2 が最小(28.54)。 ただし AIC は K=3 でも 33.12 とそれほど悪くないので、 ドメイン上 3 群が望ましければ K=3 を採用する余地もあります。 BIC は罰則項 $\ln(N) \cdot p$($N$=サンプル数、 $p$=パラメータ数)が AIC より厳しいため、 サンプル小(47)では特に K を抑える方向に働きます。
EM は 「鶏が先か卵が先か」を交互に解く反復法です。 SSDSE 都道府県人口で 3 クラスタを当てはめる例:
| 反復 | π(混合比) | μ(log10 平均) | log-likelihood |
|---|---|---|---|
| 0 (初期値) | [0.33, 0.33, 0.33] | [5.5, 6.2, 6.9] (k-means++) | −30.1 |
| 1 | [0.41, 0.36, 0.23] | [5.93, 6.27, 6.81] | −9.8 |
| 5 | [0.45, 0.34, 0.21] | [5.985, 6.281, 6.835] | −9.41 |
| 12 (収束) | [0.451, 0.342, 0.206] | [5.987, 6.282, 6.836] | −9.40 |
→ log-likelihood が単調に増加するのが EM の数学的保証です(極大値に向かう)。 たいてい 10〜20 反復で収束し、 改善が tol=1e-3 未満になったら停止。 初期値依存性があるので n_init=5(5 つの初期値から最良を取る)を指定するのが安全です。
M ステップは「γ で重み付けた平均と分散を計算する」だけ。 K-means の M ステップ(最近傍に振り分けて平均)の確率版と理解できます。
| 観点 | K-means | GMM |
|---|---|---|
| 目的関数 | クラスタ内分散の最小化(SSE) | 対数尤度の最大化 |
| クラスタ形状 | 球状(等方) | 任意の楕円(共分散行列で指定) |
| 所属 | ハード(0 / 1) | ソフト(0〜1 の確率) |
| 仮定する分布 | なし(距離ベース) | 各クラスタが多変量正規 |
| クラスタサイズ | 均等になりがち | $\pi_k$ で柔軟に調整可 |
| 外れ値 | 影響大(無理にどこかに入れる) | 低尤度として認識可能 |
| 計算量 | O(NKDT)、 軽い | O(NKD²T)、 やや重い |
| K の選択 | エルボー法、 シルエット | BIC、 AIC(理論的根拠あり) |
| 生成モデルか | 否 | 是(新データ生成可能) |
| SSDSE で適切な使い分け | 「3 ブロックに大まかに分ける」「実装が手軽」 | 「境界線の県の所属確率も知りたい」「対数正規前提で密度推定」 |
理論的には GMM の共分散を $\sigma^2 I$(球状)に固定し、 $\pi_k = 1/K$ にして、 各点を最大 γ のクラスタに割り当てると K-means と一致します。 K-means は GMM の特殊ケースです。
sklearn の GaussianMixture は covariance_type 引数で 4 種類の共分散構造を選べます:
| 設定 | $\Sigma_k$ の形 | パラメータ数 | 使い分け |
|---|---|---|---|
'full' | クラスタごとに独立の D×D 行列 | K × D(D+1)/2 | 柔軟だが過学習注意(既定) |
'tied' | 全クラスタで共通の D×D 行列 | D(D+1)/2 | クラスタの形が同じと仮定できるとき |
'diag' | クラスタごとに対角行列 | K × D | 次元間の相関を仮定しない、 高次元 |
'spherical' | クラスタごとに $\sigma_k^2 I$ | K | 球状クラスタ(K-means 寄り) |
SSDSE 都道府県人口(1 次元)では full, tied, diag, spherical はほぼ同じ結果になります(D=1 では共分散行列が単なるスカラー)。 多次元データ(例:総人口 × 高齢化率 × 年少人口比率 の 3 列)では選択で結果が大きく変わるので、 BIC で比較しましょう。
画像の RGB ヒストグラムを GMM で 2 群に分けると、 各ピクセルが「背景」「前景」に属する確率を出せます。 OpenCV の cv2.createBackgroundSubtractorMOG2 は内部で GMM を使っています。
正常データで GMM を学習し、 新規データの 対数尤度が閾値以下なら異常と判定。 「正常な振る舞いを混合分布で近似」する手法。 ネットワーク侵入検知、 製造業の品質管理で使われます。
各話者の MFCC 特徴量を GMM で表現し、 入力音声が どの話者の GMM に最も尤度が高いかで識別。 1990 年代の話者認識の標準手法でした(現在はディープラーニングに置き換わっている)。
RFM 分析(Recency, Frequency, Monetary)を GMM でクラスタリングすると、 「高頻度・高単価」「低頻度・高単価」のような 顧客層を確率的に表現できます。 マーケティング施策に活用。
GMM は 任意の連続分布を近似できる(K を増やせばどんな密度でも表現可)。 ヒストグラムや KDE の代替として、 滑らかで解釈しやすい密度推定が得られます。 SSDSE の人口分布なら K=3 の GMM がうまくフィットします。
StandardScaler で標準化しないと共分散行列が歪む。predict は内部的にコンポーネントに 0, 1, 2 ... と番号を振るが、 これは「クラスタの大きさ順」ではない。 初期値や乱数次第で順番が変わる。 必ず gmm.means_ で並べ替えて解釈すること。n_init=10 程度で複数試行し、 最良の対数尤度を持つ解を採用する。 sklearn のデフォルト init_params='kmeans' は K-means++ で初期値を決めるので比較的安全。reg_covar=1e-6(既定値)で正則化、 SSDSE のような小サンプルでは値を大きくして安定化することも。random_state を指定すれば再現可能。 ただし n_init で複数初期値を試すと、 同じ random_state でも最良解が初期値の与え方で変わる。 本番ではシードを固定する。gmm.sample(n_samples=100) で 100 個の新規データを生成。 SSDSE 人口の GMM から「もう一つの架空の都道府県」の人口を生成可能。 これは生成モデルとしての GMM の使い道。| 前提 | 本概念 | 発展 |
|---|---|---|
| 正規分布 尤度 ベイズの定理 K-means |
混合ガウス モデル (GMM) |
BIC EM アルゴリズム 変分推論 Dirichlet Process VAE |
GMM は 「正規分布 + 潜在変数 + EM」の組合せで、 統計と機械学習をつなぐ重要概念です。 ここを理解すると、 隠れマルコフモデル(HMM)、 変分推論、 深層生成モデル(VAE, GAN)への道が拓けます。
「ガウス混合モデル」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。
上流の k-means で粗いクラスタを得てからガウス混合で確率的帰属に拡張し、 並列の DBSCAN・階層クラスタリングと結果を比較し、 下流の BIC・対数尤度で混合成分数 K を決定する。 これにより「形状を仮定しない手法 vs 楕円ガウス仮定」の使い分けが明確になる。
「ガウス混合モデル」を扱う際の手法選択は、 状況に応じて以下のフローで判断すると迷いが減る。
このフローに沿って判断することで、 「ガウス混合モデル」を中核とした適切な手法選択ができる。
ここまで本ページは 2023 年の断面データに GMM を当てる話が中心でした。 この章では視点を変えて、 GMM を「クラスタ分けの道具」ではなく 「分布のかたちを数値で写し取る物差し」として使います。 同じ指標を 2 時点で fit すれば、 パラメータ $(\pi_k, \mu_k, \sigma_k)$ の差がそのまま「分布がどう動いたか」の要約になる — これが年次比較という独自の使い方です。
SSDSE-B-2026(encoding='cp932', skiprows=[1])から 高齢化率 = 65歳以上人口(A1303) ÷ 総人口(A1101) × 100 を計算し、 2012 年と 2023 年の 47 都道府県で比較します(以下すべて実測値):
| 年 | 平均 | 標準偏差 | 最小 | 最大 |
|---|---|---|---|---|
| 2012 | 25.62% | 2.62 | 17.72%(沖縄県) | 30.67%(秋田県) |
| 2023 | 31.59% | 3.34 | 22.75%(東京都) | 39.06%(秋田県) |
平均で約 6 ポイント上昇し、 ばらつき(標準偏差)も広がりました。 ここに各年独立に K=2 の GMM(random_state=0, n_init=10)を当て、 平均の小さい成分を「低位(若い側)」と呼ぶと:
| 年 | 低位成分 μ / σ / π | 高位成分 μ / σ / π | 低位に属する県数 |
|---|---|---|---|
| 2012 | 23.85 / 2.51 / 0.43 | 26.96 / 1.70 / 0.57 | 14 県 |
| 2023 | 27.06 / 2.54 / 0.21 | 32.78 / 2.29 / 0.79 | 9 県 |
2 つの fit を並べるだけで「低位グループ自体が 3 ポイント以上高齢化し(μ: 23.85→27.06)、 しかも痩せた(π: 0.43→0.21、 14 県→9 県)」と読めます。 実際、 2012 年に低位側だった宮城・茨城・栃木・京都・兵庫の 5 県が 2023 年には高位側へ移りました(2023 年の低位側は埼玉・千葉・東京・神奈川・愛知・滋賀・大阪・福岡・沖縄の 9 県)。 単一の平均値の比較では見えない「グループ構造ごとの移動」が GMM のパラメータ差として言語化できる — これが物差しとしての GMM です。
n_init=1 で seed だけ変えて fit すると、 means は seed により [29.38, 33.20](平均対数尤度 −2.5763)、 [32.78, 27.06](−2.5743)、 [28.51, 33.09](−2.5762)と、 成分の順序が入れ替わるだけでなく異なる局所解にも落ちます(東京都のラベルも 0↔1 で反転)。 対策: (1) n_init=10 で最良解を取る、 (2) 比較の前に必ず means_ の昇順で成分を並べ替えて対応付ける。 「成分番号そのまま」の年次比較は捏造まがいの結論を生みます。gmm.sample() を利用)で計算するのが定石。