判別分析 — フィッシャーの線形判別とベイズ判別

医療検査で2種類の数値(たとえば血糖値と血圧)が得られたとき、その患者が「健康」なのか「疾患あり」なのかを判定したいとします。2つの変数をそれぞれ別々に閾値で切って判定することもできますが、2つの変数をうまく組み合わせて1つのスコアにすることで、より正確な判別ができるのではないでしょうか。

この「複数の変数を最適に組み合わせて、グループを最もよく分離する方向を見つける」という問いに答えるのが判別分析(Discriminant Analysis)です。1936年にロナルド・フィッシャーが提案した線形判別分析(LDA: Linear Discriminant Analysis)は、多変量統計学の中でも最も古典的で重要な手法の一つです。

判別分析を理解すると、以下のような応用が開けます。

  • 医療診断: 複数の検査値から疾患の有無を判定する
  • パターン認識: 顔画像の「フィッシャーフェイス」による顔認識
  • マーケティング: 顧客属性から購入・非購入を予測する
  • 生物学: 形態測定値から生物種を分類する(フィッシャーの元の応用)
  • 機械学習の前処理: 教師あり次元削減としてPCAの代替に使用する

本記事では、フィッシャーの線形判別を「クラス間分散とクラス内分散の比の最大化」として定式化し、一般化固有値問題に帰着することを示します。さらにベイズ判別との関係を明らかにし、Pythonで実装して動作を確認します。

本記事の内容

  • フィッシャーの線形判別の直感的な理解
  • クラス間分散行列とクラス内分散行列の定義
  • レイリー商の最大化による導出
  • ベイズ判別法との関係
  • 多クラスへの拡張
  • Pythonでの実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

フィッシャーの線形判別 — 直感的な理解

「分離度」を最大化する

2つのクラスに属するデータがあるとします。各データは $p$ 個の変数で記述されています。フィッシャーの線形判別のアイデアは、データを1次元に射影する方向 $\bm{w}$ を見つけて、射影後のデータで2つのクラスが最もよく分離されている状態を実現することです。

「よく分離されている」とはどういうことでしょうか。直感的には、以下の2つの条件を同時に満たすことです。

  1. クラスの中心(平均)が離れている: 射影後の2つのクラスの平均が遠いほどよい
  2. クラス内のばらつきが小さい: 射影後の各クラスのデータが密集しているほどよい

平均が離れていてもデータが広く散らばっていれば分離は困難ですし、データが密集していても平均が近ければ分離できません。フィッシャーは、この2つの要件をとして定量化しました。

$$ J(\bm{w}) = \frac{\text{クラス間の散らばり}}{\text{クラス内の散らばり}} $$

この比 $J(\bm{w})$ をフィッシャーの判別基準と呼び、これを最大化する方向 $\bm{w}$ が最適な判別方向になります。

2次元での図解

2次元のデータを1次元に射影する場面を想像してください。方向 $\bm{w}$ を変えると、射影後の1次元ヒストグラムの形が変わります。ある方向では2つのクラスのヒストグラムが大きく重なり、別の方向ではヒストグラムがきれいに分離します。フィッシャーの線形判別は、このヒストグラムの重なりが最も小さくなる方向を見つける操作に対応しています。

この直感を数学的に定式化しましょう。

数学的定式化

クラス間分散行列とクラス内分散行列

$K$ 個のクラス $C_1, C_2, \dots, C_K$ があり、クラス $k$ のデータ数を $n_k$、全データ数を $n = \sum_{k=1}^K n_k$ とします。

各クラスの平均ベクトルと全体の平均ベクトルを定義します。

$$ \begin{equation} \bm{\mu}_k = \frac{1}{n_k}\sum_{\bm{x}_i \in C_k}\bm{x}_i, \quad \bm{\mu} = \frac{1}{n}\sum_{i=1}^{n}\bm{x}_i = \frac{1}{n}\sum_{k=1}^{K}n_k\bm{\mu}_k \end{equation} $$

クラス内分散行列(within-class scatter matrix)$\bm{S}_W$ は、各クラス内のデータの散らばりの合計です。

$$ \begin{equation} \bm{S}_W = \sum_{k=1}^{K}\sum_{\bm{x}_i \in C_k}(\bm{x}_i – \bm{\mu}_k)(\bm{x}_i – \bm{\mu}_k)^\top = \sum_{k=1}^{K}n_k\bm{S}_k \end{equation} $$

ここで $\bm{S}_k$ はクラス $k$ の共分散行列です。

クラス間分散行列(between-class scatter matrix)$\bm{S}_B$ は、クラス平均の散らばりを測ります。

$$ \begin{equation} \bm{S}_B = \sum_{k=1}^{K}n_k(\bm{\mu}_k – \bm{\mu})(\bm{\mu}_k – \bm{\mu})^\top \end{equation} $$

$\bm{S}_B$ は $K$ 個のランク1行列の和なので、ランクは高々 $K – 1$ です(全体平均の制約で自由度が1減る)。

全分散行列は $\bm{S}_T = \bm{S}_W + \bm{S}_B$ と分解されます。

フィッシャーの判別基準(2クラスの場合)

まず $K = 2$ の場合を考えます。データを方向 $\bm{w}$ に射影したとき、射影後のクラス平均は $\tilde{\mu}_k = \bm{w}^\top\bm{\mu}_k$ です。射影後のクラス内分散は $\tilde{s}_k^2 = \bm{w}^\top\bm{S}_k\bm{w}$ です。

フィッシャーの判別基準は次のように定義されます。

$$ \begin{equation} J(\bm{w}) = \frac{(\tilde{\mu}_1 – \tilde{\mu}_2)^2}{\tilde{s}_1^2 + \tilde{s}_2^2} = \frac{(\bm{w}^\top(\bm{\mu}_1 – \bm{\mu}_2))^2}{\bm{w}^\top\bm{S}_W\bm{w}} \end{equation} $$

分子は射影後のクラス平均の差の2乗(クラス間の散らばり)、分母は射影後のクラス内分散の和(クラス内の散らばり)です。

$(\bm{w}^\top(\bm{\mu}_1 – \bm{\mu}_2))^2 = \bm{w}^\top(\bm{\mu}_1 – \bm{\mu}_2)(\bm{\mu}_1 – \bm{\mu}_2)^\top\bm{w}$ と書けることに注意すると、2クラスの場合 $\bm{S}_B$ は次のように簡略化されます。

$$ \bm{S}_B = (\bm{\mu}_1 – \bm{\mu}_2)(\bm{\mu}_1 – \bm{\mu}_2)^\top $$

($n_1, n_2$ の重みを吸収して定数倍の違いはあるが、最大化の方向には影響しない)

したがって、フィッシャーの判別基準はレイリー商(Rayleigh quotient)の形になります。

$$ \begin{equation} J(\bm{w}) = \frac{\bm{w}^\top\bm{S}_B\bm{w}}{\bm{w}^\top\bm{S}_W\bm{w}} \end{equation} $$

この形は一般化固有値問題に帰着することが知られています。次に導出を行いましょう。

レイリー商の最大化による導出

2クラスの場合

レイリー商 $J(\bm{w}) = \frac{\bm{w}^\top\bm{S}_B\bm{w}}{\bm{w}^\top\bm{S}_W\bm{w}}$ を最大化するために、分母を制約して分子を最大化する問題に変換します。

$$ \max_{\bm{w}} \quad \bm{w}^\top\bm{S}_B\bm{w} \quad \text{subject to} \quad \bm{w}^\top\bm{S}_W\bm{w} = 1 $$

ラグランジュ関数を構成して停留条件を求めます。

$$ \mathcal{L}(\bm{w}, \lambda) = \bm{w}^\top\bm{S}_B\bm{w} – \lambda(\bm{w}^\top\bm{S}_W\bm{w} – 1) $$

$\bm{w}$ で微分してゼロとおくと次の条件が得られます。

$$ 2\bm{S}_B\bm{w} – 2\lambda\bm{S}_W\bm{w} = \bm{0} $$

整理すると一般化固有値問題が得られます。

$$ \begin{equation} \bm{S}_B\bm{w} = \lambda\bm{S}_W\bm{w} \end{equation} $$

2クラスの場合、$\bm{S}_B\bm{w} = (\bm{\mu}_1 – \bm{\mu}_2)(\bm{\mu}_1 – \bm{\mu}_2)^\top\bm{w}$ は $(\bm{\mu}_1 – \bm{\mu}_2)$ の方向を向くスカラー倍です。したがって $\bm{S}_W\bm{w}$ も $(\bm{\mu}_1 – \bm{\mu}_2)$ の方向でなければならず、$\bm{w}$ は次のように求まります。

$$ \begin{equation} \bm{w}^* = \bm{S}_W^{-1}(\bm{\mu}_1 – \bm{\mu}_2) \end{equation} $$

(定数倍の自由度があるが、方向だけが重要)

この結果には美しい直感的な解釈があります。もしクラス内分散行列が単位行列($\bm{S}_W = \bm{I}$)であれば、最適な判別方向はクラス平均の差 $\bm{\mu}_1 – \bm{\mu}_2$ の方向です。$\bm{S}_W \neq \bm{I}$ のときは、$\bm{S}_W^{-1}$ が「クラス内の散らばりを正規化する」役割を果たし、散らばりが大きい方向を縮め、小さい方向を伸ばした上でクラス平均の差の方向を計算しています。

多クラスの場合

$K \geq 3$ のクラスの場合、$\bm{S}_B$ のランクは高々 $K – 1$ なので、最大 $K – 1$ 個の有意な判別方向が存在します。

一般化固有値問題 $\bm{S}_B\bm{w} = \lambda\bm{S}_W\bm{w}$ を解き、固有値の大きい順に $K – 1$ 個の固有ベクトル $\bm{w}_1, \bm{w}_2, \dots, \bm{w}_{K-1}$ を求めます。

あるいは、$\bm{S}_W$ が正則であれば、標準的な固有値問題に変換できます。

$$ \begin{equation} \bm{S}_W^{-1}\bm{S}_B\bm{w} = \lambda\bm{w} \end{equation} $$

この固有ベクトルがフィッシャーの判別関数(Fisher discriminant function)です。データを $K – 1$ 次元以下の空間に射影して可視化したり、その空間で分類を行ったりします。

次に、フィッシャーの判別分析と確率的なアプローチであるベイズ判別の関係を見ていきましょう。

ベイズ判別法

ベイズの判別規則

フィッシャーの線形判別はクラス間・クラス内分散の比を最大化するという基準に基づく手法でしたが、別の観点としてベイズの定理に基づく判別法があります。

ベイズ判別では、新しいデータ $\bm{x}$ が与えられたとき、各クラスの事後確率 $P(C_k | \bm{x})$ を計算し、事後確率が最大のクラスに分類します。

ベイズの定理より、事後確率は次のように書けます。

$$ \begin{equation} P(C_k | \bm{x}) = \frac{p(\bm{x} | C_k)P(C_k)}{p(\bm{x})} \end{equation} $$

ここで $p(\bm{x} | C_k)$ はクラス $k$ の尤度(class-conditional density)、$P(C_k)$ は事前確率です。

線形判別分析との等価性

各クラスのデータが多変量正規分布に従い、すべてのクラスで共分散行列が等しい($\bm{\Sigma}_1 = \bm{\Sigma}_2 = \dots = \bm{\Sigma}_K = \bm{\Sigma}$)と仮定します。

$$ p(\bm{x} | C_k) = \frac{1}{(2\pi)^{p/2}|\bm{\Sigma}|^{1/2}}\exp\left(-\frac{1}{2}(\bm{x} – \bm{\mu}_k)^\top\bm{\Sigma}^{-1}(\bm{x} – \bm{\mu}_k)\right) $$

2つのクラスの事後確率の対数比を取ると、$\bm{x}$ に依存しない項が消え、$\bm{x}$ の線形関数が得られます。

$$ \ln\frac{P(C_1|\bm{x})}{P(C_2|\bm{x})} = \ln\frac{P(C_1)}{P(C_2)} – \frac{1}{2}(\bm{\mu}_1 + \bm{\mu}_2)^\top\bm{\Sigma}^{-1}(\bm{\mu}_1 – \bm{\mu}_2) + \bm{x}^\top\bm{\Sigma}^{-1}(\bm{\mu}_1 – \bm{\mu}_2) $$

この式の中で、$\bm{x}$ に依存する項は $\bm{x}^\top\bm{\Sigma}^{-1}(\bm{\mu}_1 – \bm{\mu}_2)$ です。判別は $\bm{w}^\top\bm{x}$ の値が閾値を超えるかどうかで行われ、$\bm{w} = \bm{\Sigma}^{-1}(\bm{\mu}_1 – \bm{\mu}_2)$ はまさにフィッシャーの判別方向です。

つまり、等共分散の正規分布を仮定したベイズ判別は、フィッシャーの線形判別と同じ結果を与えるのです。フィッシャーの判別方向は分布の仮定なしに導出しましたが、正規分布の仮定のもとでベイズ最適であることが示されます。

二次判別分析(QDA)

共分散行列がクラスごとに異なる場合($\bm{\Sigma}_1 \neq \bm{\Sigma}_2$)は、対数事後確率比に $\bm{x}$ の二次項が含まれ、判別境界は二次曲面になります。これを二次判別分析(QDA: Quadratic Discriminant Analysis)と呼びます。

$$ \begin{equation} \delta_k(\bm{x}) = -\frac{1}{2}\ln|\bm{\Sigma}_k| – \frac{1}{2}(\bm{x} – \bm{\mu}_k)^\top\bm{\Sigma}_k^{-1}(\bm{x} – \bm{\mu}_k) + \ln P(C_k) \end{equation} $$

QDAはLDAよりも柔軟ですが、推定すべきパラメータが多く(各クラスの共分散行列)、データが少ない場合には過学習のリスクが高まります。

理論の内容を踏まえ、Pythonで実装して確認しましょう。

Pythonによる実装と可視化

フィッシャーの線形判別の実装

まず、2クラスのデータに対してフィッシャーの線形判別を実装し、判別方向と射影後のヒストグラムを可視化します。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# --- 2クラスの合成データ ---
n1, n2 = 100, 120
mu1 = np.array([2, 3])
mu2 = np.array([5, 4])
# 共通の共分散行列
Sigma = np.array([[2.0, 1.2],
                  [1.2, 1.5]])

X1 = np.random.multivariate_normal(mu1, Sigma, n1)
X2 = np.random.multivariate_normal(mu2, Sigma, n2)

# --- フィッシャーの判別方向 ---
# クラス内分散行列
S1 = (X1 - mu1).T @ (X1 - mu1)
S2 = (X2 - mu2).T @ (X2 - mu2)
S_W = S1 + S2

# 判別方向
w = np.linalg.inv(S_W) @ (mu1 - mu2)
w = w / np.linalg.norm(w)  # 正規化

# --- 射影 ---
proj1 = X1 @ w
proj2 = X2 @ w

# --- PCA方向(比較用) ---
X_all = np.vstack([X1, X2])
X_centered = X_all - X_all.mean(axis=0)
S_total = X_centered.T @ X_centered / len(X_all)
eigvals, eigvecs = np.linalg.eigh(S_total)
w_pca = eigvecs[:, np.argmax(eigvals)]

fig, axes = plt.subplots(1, 3, figsize=(17, 5))

# (a) データと判別方向
ax = axes[0]
ax.scatter(X1[:, 0], X1[:, 1], c="blue", alpha=0.4, s=20, label="Class 1")
ax.scatter(X2[:, 0], X2[:, 1], c="red", alpha=0.4, s=20, label="Class 2")
ax.plot(mu1[0], mu1[1], "b+", markersize=15, markeredgewidth=2)
ax.plot(mu2[0], mu2[1], "r+", markersize=15, markeredgewidth=2)

origin = X_all.mean(axis=0)
scale = 3.0
ax.annotate("", xy=origin + scale*w, xytext=origin,
            arrowprops=dict(arrowstyle="->", color="green", lw=2.5))
ax.text(origin[0]+scale*w[0]+0.2, origin[1]+scale*w[1]+0.2,
        "LDA", fontsize=11, color="green", fontweight="bold")
ax.annotate("", xy=origin + scale*w_pca, xytext=origin,
            arrowprops=dict(arrowstyle="->", color="purple", lw=2.5))
ax.text(origin[0]+scale*w_pca[0]+0.2, origin[1]+scale*w_pca[1]-0.3,
        "PCA", fontsize=11, color="purple", fontweight="bold")

ax.set_xlabel("$x_1$", fontsize=11)
ax.set_ylabel("$x_2$", fontsize=11)
ax.set_title("(a) Data and discriminant direction", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_aspect("equal")

# (b) LDA射影のヒストグラム
ax = axes[1]
ax.hist(proj1, bins=20, alpha=0.5, color="blue", density=True, label="Class 1")
ax.hist(proj2, bins=20, alpha=0.5, color="red", density=True, label="Class 2")
ax.set_xlabel("LDA projection", fontsize=11)
ax.set_ylabel("Density", fontsize=11)
ax.set_title("(b) Projection onto LDA direction", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

# (c) PCA射影のヒストグラム(比較)
proj1_pca = X1 @ w_pca
proj2_pca = X2 @ w_pca
ax = axes[2]
ax.hist(proj1_pca, bins=20, alpha=0.5, color="blue", density=True, label="Class 1")
ax.hist(proj2_pca, bins=20, alpha=0.5, color="red", density=True, label="Class 2")
ax.set_xlabel("PCA projection", fontsize=11)
ax.set_ylabel("Density", fontsize=11)
ax.set_title("(c) Projection onto PCA direction", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("lda_vs_pca.png", dpi=150, bbox_inches="tight")
plt.show()

この3つのグラフから、LDAとPCAの根本的な違いが明確に読み取れます。

  1. 図(a): LDAの判別方向(緑)とPCAの第1主成分方向(紫)が異なる方向を指しています。PCAはデータ全体の分散が最大の方向を選ぶのに対し、LDAはクラス間の分離が最大の方向を選びます。両者が一致する場合もありますが、一般には異なります

  2. 図(b): LDA方向に射影すると、2つのクラスのヒストグラムがよく分離しています。重なりが小さく、適切な閾値を設定すれば高い精度で判別できます

  3. 図(c): PCA方向に射影すると、データ全体の広がりは大きいですが、2つのクラスのヒストグラムの重なりがLDAよりも大きくなっています。PCAはクラスの分離ではなく、全分散の最大化を目指すため、判別には必ずしも最適ではありません

多クラスLDAの実装

次に、アイリスデータセット(3クラス、4変数)に対して多クラスLDAを実装します。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris

# --- データの読み込み ---
iris = load_iris()
X = iris.data
y = iris.target
n, p = X.shape
K = len(np.unique(y))

# --- クラスごとの統計量 ---
means = []
S_W = np.zeros((p, p))
mu_total = X.mean(axis=0)

for k in range(K):
    X_k = X[y == k]
    mu_k = X_k.mean(axis=0)
    means.append(mu_k)
    S_W += (X_k - mu_k).T @ (X_k - mu_k)

# --- クラス間分散行列 ---
S_B = np.zeros((p, p))
for k in range(K):
    n_k = np.sum(y == k)
    diff = (means[k] - mu_total).reshape(-1, 1)
    S_B += n_k * diff @ diff.T

# --- 一般化固有値問題: S_W^{-1} S_B w = lambda w ---
eigvals, eigvecs = np.linalg.eig(np.linalg.inv(S_W) @ S_B)
eigvals = np.real(eigvals)
eigvecs = np.real(eigvecs)
idx = np.argsort(eigvals)[::-1]
eigvals = eigvals[idx]
eigvecs = eigvecs[:, idx]

# K-1 = 2 個の判別関数
W = eigvecs[:, :K-1]

# --- LDAスコア ---
LDA_scores = X @ W

# --- 比較: PCAスコア ---
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
PCA_scores = pca.fit_transform(X)

fig, axes = plt.subplots(1, 2, figsize=(13, 5.5))
colors = ["#e41a1c", "#377eb8", "#4daf4a"]

# (a) LDA
ax = axes[0]
for k in range(K):
    mask = y == k
    ax.scatter(LDA_scores[mask, 0], LDA_scores[mask, 1],
               c=colors[k], alpha=0.6, s=30, label=iris.target_names[k])
ax.set_xlabel("LD1", fontsize=11)
ax.set_ylabel("LD2", fontsize=11)
ax.set_title("(a) LDA projection (supervised)", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

# (b) PCA
ax = axes[1]
for k in range(K):
    mask = y == k
    ax.scatter(PCA_scores[mask, 0], PCA_scores[mask, 1],
               c=colors[k], alpha=0.6, s=30, label=iris.target_names[k])
ax.set_xlabel("PC1", fontsize=11)
ax.set_ylabel("PC2", fontsize=11)
ax.set_title("(b) PCA projection (unsupervised)", fontsize=12)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("lda_multiclass_iris.png", dpi=150, bbox_inches="tight")
plt.show()

# 寄与率
total_eigval = np.sum(eigvals[:K-1])
for k in range(K-1):
    print(f"LD{k+1}: 固有値 = {eigvals[k]:.4f}, "
          f"寄与率 = {eigvals[k]/total_eigval:.4f}")

LDAとPCAの射影結果を比較すると、重要な違いが見えてきます。

  1. LDA射影(図a): 3つの品種がより明確に分離されています。LDAはクラスラベルの情報を使って、クラス間の分離を最大化する方向に射影するため、分類タスクに最適化された表現が得られます。特に、versicolorとvirginicaの分離がPCAよりも改善されています

  2. PCA射影(図b): PCAはラベル情報を使わず全分散を最大化するため、クラスの分離は「副産物」として得られるに過ぎません。この例ではPCAでもかなりの分離が見られますが、LDAほど鮮明ではありません

  3. LDAの次元: 3クラスの場合、LDAは最大 $K – 1 = 2$ 次元の判別空間を与えます。4次元データを2次元に削減しつつ、クラスの分離を最大限に保持しています

LDAとQDAの判別境界の比較

最後に、LDAとQDAの判別境界を2次元データ上で可視化し、共分散行列の等質性仮定の影響を確認します。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis

np.random.seed(42)

# --- 共分散が異なる2クラスのデータ ---
n1, n2 = 150, 150
mu1 = np.array([1, 1])
mu2 = np.array([4, 3])
Sigma1 = np.array([[1.5, 0.5], [0.5, 1.0]])
Sigma2 = np.array([[0.5, -0.3], [-0.3, 2.0]])  # 異なる共分散

X1 = np.random.multivariate_normal(mu1, Sigma1, n1)
X2 = np.random.multivariate_normal(mu2, Sigma2, n2)
X = np.vstack([X1, X2])
y = np.array([0]*n1 + [1]*n2)

# --- LDA, QDA ---
lda = LinearDiscriminantAnalysis()
qda = QuadraticDiscriminantAnalysis()
lda.fit(X, y)
qda.fit(X, y)

# --- 判別境界の描画 ---
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 300),
                      np.linspace(y_min, y_max, 300))
grid = np.c_[xx.ravel(), yy.ravel()]

fig, axes = plt.subplots(1, 2, figsize=(13, 5.5))

for idx, (model, name) in enumerate([(lda, "LDA"), (qda, "QDA")]):
    ax = axes[idx]
    Z = model.predict(grid).reshape(xx.shape)
    ax.contourf(xx, yy, Z, alpha=0.15, cmap="RdBu")
    ax.contour(xx, yy, Z, levels=[0.5], colors="k", linewidths=1.5)
    ax.scatter(X1[:, 0], X1[:, 1], c="blue", alpha=0.4, s=15, label="Class 1")
    ax.scatter(X2[:, 0], X2[:, 1], c="red", alpha=0.4, s=15, label="Class 2")
    acc = model.score(X, y)
    ax.set_title(f"({chr(97+idx)}) {name} (accuracy={acc:.3f})", fontsize=12)
    ax.set_xlabel("$x_1$", fontsize=11)
    ax.set_ylabel("$x_2$", fontsize=11)
    ax.legend(fontsize=9)
    ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("lda_vs_qda_boundary.png", dpi=150, bbox_inches="tight")
plt.show()

判別境界の比較から、以下の知見が得られます。

  1. LDA(図a): 判別境界は直線です。LDAはすべてのクラスで共分散行列が等しいと仮定するため、境界は常に線形(超平面)になります。この例ではクラスの共分散が実際には異なるため、境界がデータのパターンを完全にはフォローできていません

  2. QDA(図b): 判別境界は曲線です。QDAはクラスごとに異なる共分散行列を推定するため、境界が二次曲面になり、データの非対称なパターンにより柔軟に適応しています。この例では、共分散の違いを正しく反映した曲線的な境界が得られています

  3. 精度の比較: QDAのほうがLDAよりも高い精度を示しています。これは、真の生成過程がクラスごとに異なる共分散を持つため、QDAの仮定がより正確であることの帰結です。ただし、データが少ない場合はQDAの方がパラメータ数が多いため過学習のリスクがあります

まとめ

本記事では、判別分析の理論を2つの視点から解説しました。

  • フィッシャーの線形判別: クラス間分散とクラス内分散の比(レイリー商)を最大化する方向を一般化固有値問題として求めます。2クラスの場合は $\bm{w}^* = \bm{S}_W^{-1}(\bm{\mu}_1 – \bm{\mu}_2)$ という閉形式の解が得られます
  • ベイズ判別: 各クラスが等共分散の多変量正規分布に従うと仮定すると、ベイズ最適な判別はフィッシャーの線形判別と一致します
  • PCAとの違い: PCAは教師なしで全分散を最大化する方向を探すのに対し、LDAは教師ありでクラスの分離を最大化する方向を探します
  • QDA: 共分散行列がクラスごとに異なる場合には二次判別分析を使い、曲線的な判別境界を実現します

次のステップとして、以下の記事も参考にしてください。