主成分分析:データが一番広がっている向きを探す
2つの特徴量を、1つにまとめられないか
第10章で作った、200人の身長と体重のデータ(相関係数0.746)を思い出してください。身長が高い人は体重も重い傾向があるので、点は右上がりの帯のように並んでいました。この2つの数を、「体の大きさ」のような1つの数にまとめられないでしょうか。
点の散らばりを見ると、右上がりの向きには大きく広がり、それと直角の向きには少ししか広がっていません。一番大きく広がっている向き に沿った1本の軸を引き、各点をその軸に投影した位置(軸の上の目盛り)を使えば、2つの数を1つにまとめても、点どうしの違いの大部分を残せます。
このように、データが一番広がっている(分散が一番大きい)向きを順に探して、新しい軸を作る方法を 主成分分析(PCA)と呼びます。1番目の軸を 第1主成分、それと直角な向きのうち次に広がっている軸を 第2主成分 と呼びます。
固有ベクトルが、その向き
一番広がっている向きは、前のレッスンの道具で求められます。共分散の表(第10章)の固有ベクトルが主成分の向き、固有値がその向きの分散になります。
第1主成分は、身長と体重が両方とも増える向き((0.642, 0.766)、ほぼ斜め右上)で、データの分散全体の87.5%を占めていました。第1主成分だけで、2つの特徴量の違いの87.5%を表せる ということです。この割合を 寄与率 と呼びます。
2つの主成分の向きは、直角に交わります(対称な行列の固有ベクトルだから)。scikit-learn の PCA でも、同じ向きと寄与率(0.875・0.125)が得られました(向きは符号が逆になることがありますが、同じ軸です)。
実際の手順
- 各特徴量から平均を引く(データの中心を原点に移す)
- 共分散の表を作り、固有値・固有ベクトルを求める(実際の計算では、同じ結果になる特異値分解を使うことが多い)
- 固有値の大きい順に、必要な数の固有ベクトルを選ぶ
- データを、選んだ固有ベクトルの向きに投影する(内積を計算する)
値の大きさがそろっていない特徴量は、k近傍法と同じく、先に標準化します。そうしないと、値の大きい特徴量の向きが「一番広がっている向き」になってしまいます。
手書き数字を、少ない数の特徴量にまとめる
手書き数字の画像は、8×8 = 64個の画素の濃さ(64個の特徴量)でできています。主成分分析で、64個の主成分を求めました。
寄与率と、累積寄与率
手書き数字の第1〜第10主成分の寄与率。第1主成分が0.149で最も大きく、第2が0.136、第3が0.118と続き、第10主成分は0.031まで小さくなる
寄与率を大きい順に足していったものを 累積寄与率 と呼びます。「最初の何個の主成分で、元のデータの分散の何割を表せるか」を表します。
使う主成分の数(横軸、0〜64個)と、累積寄与率(縦軸)。最初は急に増え、10個で0.738、21個で0.903(○)と、0.9の水平な線を超える。その後はゆるやかになり、40個で0.988、50個でほぼ1.0に達する
64個の特徴量のうち、21個の主成分で、分散の90%を表せます。残りの43個は、全部合わせても10%です。このように、主成分分析で少ない数の特徴量にまとめることを 次元圧縮(次元削減)と呼びます。
まとめた特徴量で分類する
主成分分析で次元を減らしてから、k近傍法(k=5)で分類しました。
20個にまとめても、64個全部を使ったときと同じ0.981でした。計算が約3分の1で済み、レッスン1の次元の呪いも和らぎます。一方、2個まで減らすと0.580に下がりました。
2次元にすると、データを「見る」ことができる
2個の主成分では分類には足りませんが、散布図に描ける という大きな利点があります。0・1・4・7の4つの数字について、第1・第2主成分の値を描きました(各25枚)。
手書き数字の0・4・7・1(各25枚)を、第1主成分(横軸)と第2主成分(縦軸)で描いた散布図。0(○)は主に下の方(縦軸−30〜−12)に、4(■)は主に右の方(横軸15〜32)に、それぞれまとまっている。7(△)は主に左上(横軸−13〜10、縦軸10〜22)に集まる。1(◆)は主にその右側の上の方(横軸0〜18、縦軸10〜23)に広がるが、一部は左の方(縦軸−11〜6)にも散らばっている。7と1は一部が重なっている
正解を使わずに求めた2つの軸なのに、0・4・7はそれぞれまとまって分かれています。一方、7と1は重なっていて、2次元では見分けにくいことが分かります(2次元の正解率が0.580だった理由の1つです)。64次元のデータを目で見る方法として、主成分分析はよく使われます。
主成分分析は、データを「まっすぐな軸」に投影するので、曲がった形の構造は表しきれません。2次元で見ることだけが目的なら、t-SNE のように、「元の空間で近い点どうしが、2次元でも近くなるように」配置する方法もよく使われます。t-SNE は見るための方法で、軸そのものには意味がなく、新しいデータにそのまま使うこともできません。
多重共線性、Pythonで主成分分析、振り返り
多重共線性:ほとんど同じ特徴量が2つあると、係数がでたらめになる
主成分分析が役に立つ、もう1つの場面を見ます。特徴量 x1 と、x1 にごく小さな誤差を足しただけの x2(相関係数0.99995)を作り、正解を y = 3 × x1 + 誤差 として、線形回帰(第4章)の係数を求めました。データを作る乱数を変えて、5回試しています。
x1 だけなら、係数は本当の値3の近くに安定しています。ところが、ほとんど同じ x2 も入れると、係数は (−9.3, 12.3) のように、大きなプラスとマイナスに振り回されました。2つの係数の 合計 はどれも3くらいで、予測の値はほとんど同じです。x1 と x2 がほぼ同じなので、「x1 に3」でも「x1 に−9.3、x2 に12.3」でも、同じ予測になってしまい、どちらか1つに決められないのです。
このように、特徴量どうしが強く相関していて、係数が不安定になる問題を 多重共線性 と呼びます。予測の精度は落ちにくいのですが、「どの特徴量がどれだけ効いているか」を係数から読み取ることができなくなります(第9章の、似た特徴量で重要度が分かれる問題と同じ根っこです)。
対策には、次のようなものがあります。
- 強く相関する特徴量の一方を取り除く
- 主成分分析で、相関する特徴量を1つの主成分にまとめる(主成分どうしは相関しない)
- 第8章のL2正則化(Ridge)で、係数が大きくなりすぎるのを防ぐ
Pythonで主成分分析を使う
from sklearn.decomposition import PCA
pca = PCA(n_components=21) # 21個の主成分を使う
Z = pca.fit_transform(X) # 64個の特徴量 → 21個の主成分の値
print(pca.explained_variance_ratio_.sum()) # 累積寄与率 → 0.903
pca90 = PCA(n_components=0.9) # 0から1の数を渡すと、「累積寄与率がその値を超える数」を自動で選ぶ
pca90.fit(X)
print(pca90.n_components_) # → 21
分類の前に使うときは、make_pipeline(StandardScaler(), PCA(20), KNeighborsClassifier()) のようにつなげ、主成分の向きを訓練データだけから求めます(第9章のデータリーク対策)。
よくある誤解
- 「主成分分析は、正解を使って、分類に役立つ向きを探す」: 主成分分析は正解を使わない、教師なし学習 です。「一番広がっている向き」が、分類に役立つ向きとは限りません
- 「寄与率の小さい主成分は、捨てても必ず問題ない」: 分散が小さくても、分類に大切な違いが含まれていることがあります。まとめた後の正解率で確かめます
- 「多重共線性があると、予測が大きく外れる」: 予測はあまり悪くなりません。困るのは、係数の大きさや向き(プラスかマイナスか)が信用できなくなることです
振り返り
- 主成分分析は、データが一番広がっている向き(共分散の表の固有ベクトル)を順に探し、少ない数の軸にまとめる。寄与率は、その軸が表す分散の割合
- 手書き数字は、64個の特徴量を21個の主成分にまとめても、分散の90%が残る。2次元にまとめると、散布図で見られる
- 多重共線性は、強く相関する特徴量のせいで係数が不安定になる問題。特徴量を減らす、主成分分析、正則化で和らげる