主成分分析で「累積寄与率80%まで残す」を実測したら、捨てた寄与率1.88%の成分だけで精度0.95が出た
約11分で読めます

主成分分析で「累積寄与率80%まで残す」を実測したら、捨てた寄与率1.88%の成分だけで精度0.95が出た


主成分分析(PCA)をかけたあと、必ず決めなければならないことがある。何本残すかだ。

定番の答えは決まっている。

累積寄与率が80%(あるいは90%)を超えるところまで残す。

私もそうしてきた。3行で書けるし、根拠を聞かれても「元の情報の8割を保っています」と言える。

引っかかっていたのは、寄与率が「何の」8割なのかだ。実測したら、寄与率88.57%の成分に分類の手がかりが1つも入っておらず、捨てられる寄与率1.88%の成分だけで精度0.95が出た。

実験に使ったコードの全文はGitHubに置いています

主成分分析が何をしているか、30秒で

先に言葉を揃える。すでにご存じなら次の節まで飛ばしてください。

100個の項目があるアンケートデータを想像してほしい。100列もあると扱いにくいので、似た動きをする列をまとめて、少ない本数で表し直したい。これがPCAの目的だ。

PCAは、データがいちばん大きく散らばっている方向を探す。その方向を第1主成分と呼ぶ。次に、それと直角な方向のうちで最も散らばっている方向を第2主成分とする。以下同様に並べていく。

  • 寄与率 … その方向がデータ全体の散らばりの何%を担っているか
  • 累積寄与率 … 上位k本ぶんの寄与率の合計

だから「累積寄与率80%まで残す」は、データの散らばりの8割を説明できるところまで残すという意味になる。

ここで問題になるのが、散らばりが大きいことと、こちらが知りたいことが入っていることは別だという一点だ。

身長と体重のデータで男女を見分けたいとする。いちばん散らばっているのは「体が大きい/小さい」の方向かもしれない。でもそれは男女どちらにもいる。男女を分けているのは、もっと散らばりの小さい別の方向かもしれない。PCAは前者を優先して残し、後者を捨てる。

これが起きるとどうなるのか。実際にやってみた。

まず、いちばん意地悪なデータを作る

20列のデータを用意した。作り方はこうだ。

  • 全部の列に共通で乗る強い変動を1つ置く。これが散らばりのほとんどを生む。工場のセンサーで言えば「その日の気温」のような、全部の測定値を一斉に押し上げ下げする要因
  • クラスAとクラスBを分けているのは、その変動と直角の方向(前半10列と後半10列の差)に入れた小さなズレだけ

つまり「大きく動いている方向」と「知りたいことが入っている方向」を、意図的に別々にした。

主成分分析をかけると、こうなった。

上下2段のヒストグラム。上段は第1主成分で、寄与率88.57%、青とピンクの2クラスの分布が完全に重なっていて見分けがつかず、この1本だけでの分類精度は0.486。下段は第2主成分で、寄与率わずか1.88%だが、青とピンクが0を境にきれいに左右へ分かれており、分類精度は0.952

上下とも、青がクラスA、ピンクがクラスBの分布だ。

上段の第1主成分は、散らばりの88.57%を説明していて、分類は何もできない。 2色が完全に重なっている。この1本で分類すると精度0.486——コインを投げるのと同じだ。

下段の第2主成分は寄与率1.88%。 それなのに2色がきれいに分かれていて、この1本だけで精度0.952が出る。

さて、累積寄与率で切るとどうなるか。第1主成分だけで88.57%あるので、80%の基準は k=1 で満たされてしまう。そこで打ち切ると、残るのは分類に何の役にも立たない1本だけだ。

残し方累積寄与率分類精度
累積80%で打ち切り(k=1)88.6%0.486
全20成分100%0.947
第2主成分だけ(k=1)1.9%0.952

「情報の88%を残した」1本より、「情報の2%しかない」1本のほうが強い。

主成分ごとに、その1本だけで分類したときの精度を並べるとこうなる。

横軸が主成分の番号1から20、灰色の棒が寄与率(対数目盛)、ピンクの折れ線がその主成分1本だけでの分類精度。第1主成分の棒だけが88.57%と突出して高く、以降は1.88%から0.5%程度に落ちる。折れ線は第2主成分だけが0.95に跳ね上がり、他はすべて0.5前後の水平線上にある。左端に累積寄与率80%で切ったときの範囲(k=1)が薄く塗られている

灰色の棒(寄与率)と、ピンクの折れ線(役に立つか)。この2つに何の関係もない。

実データでは、80%はむしろ余裕がある

意地悪なデータで壊れるのは当然だと言われそうなので、実データでも測った。手書き数字の画像(64列)と、乳がんの診断データ(30列)。どちらもよく使われる練習用のデータセットだ。

主成分の数kを1本ずつ増やしながら、そのつど分類精度を交差検証で測った。

左右2枚の折れ線グラフ。左が手書き数字64次元、右が乳がん30次元。どちらも青線が累積寄与率、ピンク線が分類精度。手書き数字では累積寄与率がゆるやかに立ち上がってk=21で80%に達するのに対し、分類精度はもっと急峻でk=15あたりで0.90に頭打ちになる。乳がんでは累積寄与率がk=5で80%を超え、分類精度はk=2の時点で0.95に達している。どちらも精度の曲線が寄与率の曲線より左側にある

青が累積寄与率、ピンクが分類精度だ。

データ全部使った精度累積80%になる kそのときの精度
手書き数字 (64列)0.9188210.9054
乳がん (30列)0.980750.9719

壊れなかった。 それどころか、ピンクの線が青い線より左側にある。乳がんは k=2(累積63.2%)の時点で精度0.953、手書き数字は k=15(累積70.9%)で0.904にもう届いている。80%まで残すのは、この2つではむしろ余分だ。

ここが大事なところだと思う。80%が安全だったのではなく、たまたま散らばりの大きい方向にラベルの情報が乗っていただけだ。

手書き数字で散らばりが大きい方向は「線の太さ」や「傾き」で、それは数字の見分けにも効く。散らばりの源とラベルの源が同じものから来ているから、両者が一致する。自然なデータではそうなりやすい。

合成データでやったのは、その一致を壊すことだった。そして現実のデータでも、共通の強い変動要因があってラベルがそれと無関係なとき、同じことが起きる。センサーの個体差、撮影日の照明、部署ごとの記入クセ——散らばりは大きいが知りたいこととは関係ない要因は、実務のデータにいくらでもある。

標準化を飛ばすと、第1主成分の正体は「面積」になる

もうひとつ、実務で踏みやすい落とし穴も測った。

乳がんデータは列によって単位が違う。面積は3桁の値を取り、なめらかさは0.1未満だ。PCAは「散らばりの大きい方向」を探すので、単位が大きいというだけでその列が主役になってしまう。これを防ぐのが標準化(各列を平均0・標準偏差1に揃える前処理)だ。

飛ばすとどうなるか。

左右2枚の横棒グラフ。左は標準化しない場合で、第1主成分の寄与率は98.2%、効いている列はworst areaが0.852、mean areaが0.517と面積の2列が突出し、他は0.06以下。右は標準化した場合で、第1主成分の寄与率は44.3%、mean concave points 0.261、mean concavity 0.258、worst concave points 0.251と複数の列が同じくらいの重みで並んでいる

第1主成分の寄与率80%到達に必要な kそのときの精度
標準化しない98.2%10.9069
標準化する44.3%50.9719

標準化しないと、第1主成分の寄与率が98.2%になる。数字だけ見れば「1本で全部説明できた」ように見える。

中身を開けると、worst area(重み0.852)と mean area(0.517)——面積の2列だけでできていた。この2列は標準偏差が568.9と351.6で、他の列より2桁大きい。

PCAが見つけたのは、データの構造ではなく単位だった。

そして「累積寄与率80%」の基準はこのとき k=1 を返す。精度は0.9069。標準化してから同じ基準で切ると k=5 で0.9719になるので、標準化を飛ばした代償が6.5ポイントということになる。

何を基準にすればいいのか

実測から言えることを整理する。

累積寄与率は「元のデータを復元できるか」の指標であって、「やりたいことができるか」の指標ではない。 この2つは、散らばりの源とラベルの源が同じときだけ一致する。一致するかどうかは、寄与率を見ても分からない。

なので、分類や予測をやっているなら、切る本数はそのタスクの成績で決めるのが素直だ。kを振って交差検証を回すだけなので、上の図はどちらも数十秒で描ける。寄与率を眺めて悩む時間より短い。

from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

for k in range(1, 31):
    Z = PCA(n_components=k).fit_transform(X)
    print(k, cross_val_score(LogisticRegression(), Z, y, cv=5).mean())

寄与率が役に立つ場面がないわけではない。ラベルがない場合(可視化、圧縮、ノイズ除去)は復元の良し悪しがそのまま目的なので、寄与率で切るのは筋が通っている。目的が復元なら寄与率、目的が予測なら予測の成績、という当たり前のところに戻る。

まとめ

  • 相関の強い20列のデータで、第1主成分は寄与率88.57%で分類精度0.486(当てずっぽう)。累積80%の基準はここで満たされるので、打ち切ると精度が0.947から0.486に落ちる
  • 捨てられる第2主成分は寄与率1.88%、それ1本で精度0.952
  • 手書き数字と乳がんの実データでは80%は壊れず、むしろ余裕がある。精度は乳がんでk=2(累積63.2%)、手書き数字でk=15(累積70.9%)でほぼ頭打ち
  • 壊れなかったのは基準が正しいからではなく、散らばりの大きい方向にラベル情報が乗っていたから。共通の変動要因(照明、個体差、記入クセ)がある場面ではこの一致が崩れる
  • 標準化を省くと第1主成分の寄与率が98.2%になるが、中身は面積の2列だけ。単位を拾っているだけで、80%基準での精度は6.5ポイント下がる
  • 教師ありなら、切る本数はkを振って交差検証で決めればいい。数十秒で終わる

88.57%は、嘘をついていない。 そこが嫌だった。

第1主成分は本当に散らばりの88.57%を説明している。計算は正しいし、報告しても誰も文句を言わない。問題は、その数字が答えている質問が「このデータはどれだけ復元できますか」であって、こちらが聞きたかった「このデータで分類できますか」ではない、という一点だけだ。

質問と答えが1つずれているとき、答えのほうは正しいので気づけない。むしろ数字が大きいほど安心してしまう。88.57%は、55%より疑いにくい。

仕事で見る数字にも同じ形のものがある気がする。稼働率、消化率、対応件数。どれも正確に測れていて、正確に測れているからこそ、それが「知りたかったこと」とずれていても検算では見つからない。測り方の誤りは数字を見れば分かるが、質問の取り違えは数字を見ても分からない。

唯一の確かめ方は、知りたかったことを直接測ってみることだった。この記事でやったのも、寄与率をいくら睨む代わりに、分類精度を実際に測っただけのことだ。