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

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

言語 JA

PCA

入門

02.7.1

PCA

最終更新 2020-01-29 読了時間 4 分
まとめ
主成分分析(PCA)は、データの散らばり(分散)が最も大きい向きを第 1 主成分、それと直交する中で次に大きい向きを第 2 主成分として、座標軸をまるごと回転させる手法です。回転後の軸を分散の大きい順に n_components 本だけ残せば、情報の損失を抑えながら列の数を減らせます。計算は共分散行列の固有値分解で、固有ベクトルが軸の向き、固有値がその軸に沿った分散です。各固有値が全体に占める割合が寄与率で、累積寄与率が 80〜90% を超える本数を残すのが定番の決め方です。分散をそのまま情報量として扱うので、列ごとに単位が違うときは StandardScaler で平均 0・分散 1 にそろえないと、値の範囲が広い 1 列が第 1 主成分を独占します。

直感 #

3 色の団子が 3 つの塊で置かれた机を 1 枚の写真に撮るとき、真上からでは 2 つが重なって見えても、斜めから撮れば 3 つがはっきり分かれる角度があります。PCA はこの「いちばん散らばって見える角度」を計算で選ぶ手法です。点の雲が最も長く伸びている向きを新しい第 1 軸にし、それと直角の中で次に伸びている向きを第 2 軸にします。元の 3 本の軸のどれでもない回転した軸に描き直すので、3 次元の塊が 2 次元の写真でも分かれて見えます。

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

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

見るポイント:点を軸に射影した散らばりが最大の向きが第 1 主成分。情報を最も失わない 1 本の軸。

詳細な解説 #


1. 直感:データの「一番ばらつく方向」を探す #

高次元データを扱うと、30 列もあれば散布図は描けず、距離の計算も列の数だけ重くなります。PCA は、データが最も広がっている方向(分散が最大の方向)を探して、その方向を新しい軸(主成分)として使います。分散の小さい方向は「どの点もほぼ同じ値」なので捨てても点の区別に効かず、情報をなるべく失わずに次元を減らせます。


2. 数式でみる PCA #

データ行列 \(X \in \mathbb{R}^{n \times d}\) を、各列の平均を引いて平均 0 に正規化したとします。計算は 3 段階です。

まず共分散行列を計算します。対角成分が各列の分散、非対角成分が列同士の共分散で、「点の雲がどの向きに伸びているか」を \(d \times d\) の行列で表したものです。

$$ \Sigma = \frac{1}{n} X^\top X $$

次に固有値分解を行います。\(v_j\) が主成分ベクトル(固有ベクトル)で軸の向き、\(\lambda_j\) が対応する分散(固有値)で、その軸に点を射影したときの散らばりです。ステップ図で「射影した散らばりが最大の向き」を探していた操作が、この固有値問題を解くことに当たります。

$$ \Sigma v_j = \lambda_j v_j $$

最後に主成分得点(低次元データ)を求めます。\(V_k\) は固有値の大きい順に \(k\) 個の固有ベクトルを列に並べた \(d \times k\) の行列で、\(Z\) の各行が新しい座標です。

$$ Z = X V_k $$

3. 実験用のデータ #

make_blobs で 3 列(3 次元)・600 点の塊データを作り、3D 散布図で確認します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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=117117)

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. PCAで二次元に次元削減する #

標準化してから PCA(n_components=2, whiten=True) をかけ、PC1・PC2 の平面に同じ色で描き直します。

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

pca = PCA(n_components=2, whiten=True)
X_pca = pca.fit_transform(StandardScaler().fit_transform(X))

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

PCAで二次元に次元削減するの図

実行結果の読み方 #

PC1・PC2 の平面で 3 色の塊が互いに重ならずに並びます。3D 散布図では見る角度によって重なっていた塊が、「最も散らばる 2 方向」に描き直すだけで分かれるのが PCA の効果です。軸の数値は whiten=True のため分散 1 に正規化されており、元の列の単位ではありません。

主成分数と累積寄与率 #

スライダーで主成分の数を増やすと累積寄与率がどう変化するか確認してください。30 列のデータでは第 1 主成分だけで全分散 30.05 のうち 13.3(約 44%)を説明し、2 本で 63%、3 本で 73% と増え、90% ラインを超えるのは 7 本目です。90% が実務でよく使われる目安で、残りの 23 本を捨てても情報の 1 割しか失いません。


5. 正規化の効果 #

PCA は「分散の大きさ」を情報量として扱うので、スケールの違う特徴量を持つデータでは正規化(標準化)が必須です。

PCAは分散の大きさをそのまま「情報量」として扱うため、スケールの異なる特徴量(例: 単位がメートルとキロメートルが混在)を標準化せずに適用すると、大きいスケールの変数が第1主成分をほぼ独占してしまいます。

例1: 正規化なし vs 正規化あり #

2 列目を 1000 倍、3 列目を 0.01 倍にしてスケールをわざと崩したデータに、標準化なしとありで PCA をかけて並べます。

 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
from sklearn.preprocessing import StandardScaler

# 特徴量のスケールがバラバラなデータ
X, y = make_blobs(
    n_samples=200, n_features=3, random_state=11711, centers=3, cluster_std=2.0
)
X[:, 1] = X[:, 1] * 1000
X[:, 2] = X[:, 2] * 0.01

X_ss = StandardScaler().fit_transform(X)

# PCA実行
pca = PCA(n_components=2).fit(X)
X_pca = pca.transform(X)

pca_ss = PCA(n_components=2).fit(X_ss)
X_pca_ss = pca_ss.transform(X_ss)

plt.figure(figsize=(10, 5))
plt.subplot(121)
plt.title("正規化なし")
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, marker="x", alpha=0.6)
plt.subplot(122)
plt.title("正規化あり")
plt.scatter(X_pca_ss[:, 0], X_pca_ss[:, 1], c=y, marker="x", alpha=0.6)
plt.show()

例1: 正規化なし vs 正規化ありの図


6. 実務での応用 #

PCA の使い道は 4 つに分けられます。可視化では、数十列の高次元データを PC1・PC2 の 2 次元に落としてプロットします。ノイズ除去では、寄与率の小さい主成分を削ると測定ノイズが主に落ちるので、再構成したデータで下流モデルの精度が上がることがあります。特徴量圧縮では、n_components=0.9 のように累積寄与率で指定して列数を減らし、学習時間の短縮と過学習の抑制を狙います。前処理としては、k-means や回帰・分類の前段に置いて相関の強い列をまとめます。

累積寄与率が80〜90%を超える次元数を選ぶのが一般的な目安です。pca.explained_variance_ratio_.cumsum() の出力をプロットして「肘(elbow)」の箇所を確認すると、情報損失を抑えた次元数を視覚的に判断できます。

まとめ #

PCA は共分散行列 \(\Sigma\) の固有値分解で、分散が最も大きい直交軸(主成分)を固有値の大きい順に求め、n_components 本を残して座標を描き直します。3 次元の make_blobs を PCA(n_components=2) にかけると、3D では重なって見えた塊が PC1・PC2 の平面で分かれ、第 3 主成分を捨てても塊の区別は失われませんでした。残す本数は explained_variance_ratio_ の累積が 80〜90% を超える所で決め、上のスライダーの 30 列のデータでは 7 本が目安です。列の単位が違うときは StandardScaler が前提で、省くと第 1 主成分が分散の大きい 1 列に乗っ取られます。


発展:PCAのさらに深い話題 #

1. 寄与率と累積寄与率 #

各主成分の寄与率は、その固有値 \(\lambda_j\) が全固有値の和に占める割合で計算されます。scikit-learn では explained_variance_ratio_ がこの値です。

$$ \text{寄与率} = \frac{\lambda_j}{\sum_i \lambda_i} $$

寄与率を第 1 主成分から順に足した累積寄与率が 80〜90% を超える本数を残すのが一般的な目安です。

2. SVDによる計算 #

実装上は共分散行列を作らず、データ行列を直接「特異値分解 (SVD)」で計算することが多く、scikit-learn の PCA もこの方式です。\(X = U \Sigma V^\top\) の形に分解し、\(V\) の列ベクトルが主成分に対応します。詳細は 特異値分解(SVD) を参照してください。

3. カーネルPCA #

非線形な構造をとらえるために「カーネル法」を使う拡張版が Kernel-PCA です。同心円のようにクラスタが曲線状に広がる場合、軸を回転させるだけの PCA では分けられない構造を扱えます。


  • 特異値分解(SVD) — PCA の内部計算手法
  • Kernel-PCA — カーネル行列の固有値分解で非線形な構造へ拡張
  • LDA — 教師あり次元削減
  • UMAP — 近傍グラフに基づく非線形な次元削減手法