K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

LDA

中級

02.7.3

LDA

最終更新 2020-02-26 読了時間 3 分
まとめ
線形判別分析(LDA)は、クラスラベルを使って「同じクラスの点は近く、違うクラスの点は遠く」なる射影軸を学習する教師あり次元削減です。クラス内散布行列 \(S_W\)(各クラスの中のばらつき)を小さく、クラス間散布行列 \(S_B\)(クラス平均同士の離れ具合)を大きくする比 \(w^\top S_B w / w^\top S_W w\) を最大化するので、分散が大きいだけの方向を選ぶ PCA とは軸の向きが変わります。ラベルを使うため分類の前処理として有効で、LinearDiscriminantAnalysis(n_components=2) で 3 クラスの 3 次元データを 2 次元に落とすと、PCA より塊がはっきり分かれます。ただし取り出せる次元は最大でクラス数 − 1 で、各クラスが同じ共分散の正規分布に従うという仮定が崩れると性能が落ちます。

直感 #

3 色の団子が 3 つの塊で置かれた机を、1 枚の写真に撮って色ごとに分けて見せたいとします。PCA は「団子が最も広く散らばって見える角度」を選びます。その角度で 2 色が重なることもあります。LDA は色(ラベル)を知っているので、「同じ色はまとまり、違う色は離れて見える角度」を直接選びます。分散が大きい軸を探す PCA と異なり、分類しやすさそのものを目的にしている点が LDA の要です。

何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:点を軸に射影した散らばりが最大の向きが第 1 主成分。情報を最も失わない 1 本の軸。LDA はこの「散らばり」をクラス間の分離に置き換えて軸を選ぶ。

詳細な解説 #


1. 直感:クラスを分けやすい方向を探す #

PCA はラベルを見ずに、データ全体の「ばらつき」が大きい方向を探します。LDA はラベルを使い、クラス平均同士の距離が大きく、各クラスの中のばらつきが小さくなる方向を探します。2 つの軸は一致することもありますが、クラスの違いが分散の小さい方向に隠れているデータでは大きくずれます。


2. 数式でみる LDA #

クラス \(C_1, C_2, \dots, C_k\) があるとき、2 種類の「ばらつき」を行列で定義します。

まずクラス内散布行列(within-class scatter)は、各クラスの点がそのクラス平均 \(\mu_j\) からどれだけ散らばっているかの合計で、図の上では各塊の太さに対応します。

$$ S_W = \sum_{j=1}^k \sum_{x_i \in C_j} (x_i - \mu_j)(x_i - \mu_j)^\top $$

次にクラス間散布行列(between-class scatter)は、各クラス平均 \(\mu_j\) が全体平均 \(\mu\) からどれだけ離れているかをクラスの点数 \(n_j\) で重み付けした合計で、塊同士の離れ具合に対応します。

$$ S_B = \sum_{j=1}^k n_j (\mu_j - \mu)(\mu_j - \mu)^\top $$

目的は、射影ベクトル \(w\) を求めて次の比を最大化することです。分子が「射影後のクラス間の離れ具合」、分母が「射影後のクラス内のばらつき」なので、「クラス間の分離を大きくし、クラス内のばらつきを小さくする」基準になっています。

$$ J(w) = \frac{w^\top S_B w}{w^\top S_W w} $$

この最大化は一般化固有値問題 \(S_B w = \lambda S_W w\) に帰着し、固有値の大きい順に取った固有ベクトルが LD1, LD2, … です。\(S_B\) のランクは最大でクラス数 − 1 なので、3 クラスなら軸は 2 本までしか取れません。


3. 実験用のデータ #

make_blobs で 3 クラス・3 次元の 600 点を作り、クラスを色にして 3D で描きます。cluster_std=4 と散らばりを大きくしてあるので、角度によっては塊が重なります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

X, y = make_blobs(
    n_samples=600, n_features=3, random_state=11711, cluster_std=4, centers=3
)

fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(projection="3d")
ax.scatter(X[:, 0], X[:, 1], X[:, 2], marker="o", c=y)
ax.set_xlabel("$x_1$")
ax.set_ylabel("$x_2$")
ax.set_zlabel("$x_3$")

実験用のデータの図


4. LDAで二次元に次元削減 #

LinearDiscriminantAnalysis(n_components=2) にデータとラベルの両方を渡して学習し、LD1・LD2 の平面に描き直します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA

lda = LDA(n_components=2).fit(X, y)
X_lda = lda.transform(X)

plt.figure(figsize=(8, 8))
plt.scatter(X_lda[:, 0], X_lda[:, 1], c=y, alpha=0.5)
plt.xlabel("LD1")
plt.ylabel("LD2")
plt.title("LDAによる2次元化")
plt.show()

LDAで二次元に次元削減の図


5. PCAとLDAの比較 #

同じデータを PCA(n_components=2) で 2 次元化します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

plt.figure(figsize=(8, 8))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, alpha=0.5)
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title("PCAによる2次元化")
plt.show()

同じデータをPCAで2次元化した場合:の図

PCAは「ばらつき最大化」、LDAは「クラス分離最大化」という違いがある。

実行結果の読み方 #

LDA の図では 3 色が LD1 に沿って左・中・右に並び、重なりは塊のすそだけです。PCA の図では分散が最大の向きに点が伸び、2 色が部分的に重なります。同じ 600 点を 2 次元に落としただけで分かれ方が変わるのは、LDA がラベルを使って軸を選んだからです。


6. 実務での応用 #

LDA の使い道は 3 つあります。1 つ目は分類問題の前処理としての次元削減で、クラス数 − 1 次元まで落とした座標を後段のモデルに渡します。2 つ目は可視化です。ラベル付きの高次元データを 2 次元に落としてラベルごとにプロットすると、PCA より塊が分かれた図になります。3 つ目は顔認識の Fisherfaces で、PCA で次元を落としてから LDA で判別軸を学習する 2 段構成が有名です。


発展:LDAの制約 #

LDA の最大次元は「クラス数 − 1」で、3 クラスなら最大 2 次元までしか落とせません。これは \(S_B\) のランクがクラス数 − 1 で止まるためです。また、各クラスの分布が「正規分布で共分散が等しい」と仮定しているので、クラスごとに広がりの向きが違うデータでは軸がずれます。多クラスにも使えますが、サンプル数が特徴量数より少ないと \(S_W\) が逆行列を持たず不安定になるので、solver="eigen" と shrinkage で正則化するか、先に PCA で次元を落とします。


近傍数と埋め込み #

スライダーで近傍数を 3, 5, 10, 20, 40 と変えると、3 クラスのデータの 2D 射影がどう変化するかを確認できます。近傍数が小さいと局所の並びが強調されて塊が細かく割れ、大きくすると大域的な配置に近づいて 3 つの塊がまとまります。