PCA – 主成分分析

概要

主成分分析(principal component analysis: PCA)は教師なし学習の手法の一つでもあり、その考え方は、特徴量の線形組み合わせの中で、最もデータの情報を多く含む組み合わせを発見し、それを主成分として分析を行うものである。

具体的には、特徴量空間の中でベクトルを考え、そのベクトル沿いのデータの分散が最も大きくなるようにベクトルを定める。その考え方と定式化については、主成分分析の定式化にまとめた。

クラス分類データへの適用

Irisデータセット

Irisデータセットにscikit-learnのPCAを適用した例。

PCA – Irisデータセット

教師なし学習として、クラス分類のターゲットデータを用いることなく、特徴量の分析だけでクラスがうまく分離できるような主成分が得られる。

Breast cancerデータセット

Breast cancerデータセットにscikit-learnのPCAを適用した例。

PCA – Breast cancerデータセット

Irisデータの場合と同じく、教師なし学習として、クラス分類のターゲットデータを用いることなく、特徴量の分析だけでクラスがうまく分離できるような主成分が得られる。

ここでは、主成分を構成する各特徴量の寄与をヒートマップによって視覚化している。

LFW peopleデータセット

著名人の顔画像データを集めたLFW peopleデータセットにscikit-learnのPCAを適用した例。

PCA – LFWデータセット

主成分の可視化、次元圧縮後の画像の再現など、様々な角度でPCAの特性を見ている。

回帰データへの適用

Boston house pricesデータセット

Boston house pricesデータセットにPCAを適用して、ターゲットが連続量で与えられた回帰系の問題への適用性を確認する。

PCA – Boston house pricesデータセット

このデータセットに関する限り、PCAで明確な関係は見いだせなかった。

なお、このデータセットには属性データが含まれ、前処理としてone-hot encodingを行っている。

 

PCA – LFWデータセット

概要

Scikit-learnで提供されているLFW peopleデータセットを、主成分分析を使って分析する。

データの読み込みと確認

LFWデータセットは世界の著名人の顔画像を、その名前とそれに対応するクラスデータとともに格納したものである。

書籍”Pythonではじめる機械学習”に沿って、画像サイズを0.7にし、20枚以上の画像がある人物を抽出する。

画像の人物は書籍と同じだが顔画像は異なっている。書籍執筆後画像データが追加/変更されたものと思われる。

画像の枚数の絞り込み

元のコード

LFW peopleの画像データは、人物によって枚数にばらつきがある(特にGeorge Bushだけ500枚を超えている)。画像データの多寡によるばらつきを抑えるため、書籍では画像の数を50枚までとし、それ以上の画像は切り落としている。

このコードがちょっとわかり難かったので、別にこちらで整理している

k-近傍法との組み合わせによる精度の確認

書籍では、画像を50枚以下に制限したデータについて、k-近傍法(knn)を適用したときのスコア、元データを主成分分析によって変換した場合のknnのスコアを確認している。

その過程をトレースしてみた

  • 画像データを最近傍データ1つで判定する1-nnの実行結果は、スコアは0.23と低い
  • 元の画像データを100個の主成分で変換したデータに対しては、1-nnのスコアは0.31と若干向上
  • PCAインスタンス生成時にwhiten=Trueを指定しない場合、PCA変換後もスコアは向上しなかった

主成分の可視化

PCA.fit()を実行すると、PCA.components_に主成分が格納される。components_は2次元配列で、[主成分の数, 元の特徴量数]という形になっている。たとえば今回のデータの場合、主成分の数はn_componentsで指定した100、特徴量の数は画像のピクセル数87×65=5655となり、components_は100×5655の2次元配列になっている。

(1)    \begin{equation*} \tt{components_} = \left[ \begin{array}{ccc} (p_{0, 0} & \cdots & p_{0, 5654} ) \\ & \vdots &\\ (p_{99, 0} & \cdots & p_{99, 5654}) \end{array} \right] = \left[ \begin{array}{c} \boldsymbol{p}_0 \\ \vdots \\ \boldsymbol{p}_{99} \end{array} \right] \end{equation*}

components_に収められた主成分はそれぞれが画像データと同じサイズの配列なので、これらを画像として表示させてみる。

たとえばComponent-0は顔と背景のコントラスト、Component-2は顔の左右の明るさの差をコーディングしているように見える、と書籍では解説している。その他にも、Component-5は目の下の出っ張った部分、Component-11は鼻筋のあたりを表現しているかもしれないといった想像はできる。

上の画像は以下のコードで表示させたが、要点は以下の通り。

  • 最低20枚の画像を持つ人物のみ読み込んでいる
  • 画像の最大数を50枚以下に制限している
  • 訓練データとテストデータに分割し、訓練データを主成分分析にかけている
  • components_プロパティーの主成分配列のうち、15行分を取り出して表示させている
  • 表示にあたって、リニアな5655の要素を画像の形(87, 65)に変形している
  • components_の形状が、100行×5655の2次元配列であることを確認

次元圧縮された主成分からの復元

概要

主成分の意味の一つとして、元のデータは主成分の線形和で表せるという解釈がある。

(2)    \begin{equation*} \boldsymbol{x} = (x_0, ..., x_n) = a_0 \boldsymbol{p}_0 + a_1 \boldsymbol{p}_1 + a_2 \boldsymbol{p}_2 + \cdots \end{equation*}

LFWの顔画像データで考えると、components_に収められた主成分の重みによって、元のそれぞれの人物の画像を再現しようとすることになる。

そこで、限られた主成分だけを用いて元の顔画像を再現してみる。

顔画像の選定

まず、特に有名な人物の顔画像をいくつか表示させてみた。選んだ人物は、Arnold Schwarzenegger, Tiger Woods, Vladimir Putinの3人。

これらの画像から、一旦次元削減して復元する画像を選ぶ。Shwalzzeneggerは正面少し左向きの31番、Tiger Woodsは少し右側から撮った歯を出している683番、Putinは左を向いた顔をほぼ正面から撮った372番を選んだ。

次元削減後の逆変換

そして次元数を変化させながらPCAモデルに全データを学習させ、それらのモデルで3枚の画像を変形し、逆変換する。

10個の主成分では、3人とも似たような顔になっているが、30個になると顔の方向や葉を出しているかどうかといった特徴が表れ始めている。

70個から100個にかけて、ShwaltzeneggerとWoodsはかなり元の顔に近いが、Putinはあまり判然としない。前者2人が「濃い」顔立ちなのに比べると、Putinの顔立ちは平板だということだろうか。

この画像は、以下の手順で作成した。

  1. 20枚以上の画像を持つ人物を選び、画像の枚数を50枚以下に制限
  2. 3人の顔画像について、次元数を10、30、70、100と変化させて以下を実行
    1. 設定された次元数で全データを学習
    2. 学習済みモデルで各顔画像を変換(ここで次元が削減される)
    3. 設定された次元数で元の顔画像に逆変換

同一人物の画像

さらに、3人について1人ずつ、3枚の顔画像について同様のことを行った結果が以下の通り。

Shwalzeneggerの後半2枚は向きが逆だが口元などがよく似ていて、目元と口元の特徴が強調されている。1枚目の画像はこの2枚と特徴が違うが、主成分30個あたりではよく似た感じともいえる。

Tiger Woodsも、主成分30個のところで173と683の画像が似ている。だが、535については一貫して他の2つと異なっているように見える。個人の特徴よりも顔の表情に大きく引きずられているようだ。

Putinは60と372の画像が割に似ているが、239の画像はかなり異なり、コントラストが強調されているようだ。60や372では、そもそも顔画像が平板なせいなのか、主成分を増やしても明確な画像が得られていない(他の人物との区別も難しいのではないだろうか)。

第2主成分までによるクラスの分布

第1主成分と第2主成分だけを使って、各クラスの分布をみてみる。62人の人物の各画像データが1つの点に対応している。2つの主成分だけでは人物が明確なクラスターとしては認識し難い(というよりもクラスが多すぎて識別も難しい)。

試しに表示するクラスを5つに限定してみる。やはり2つの主成分では明確なクラスターは確認できない。先ほどの変換・逆変換の結果でも、主成分10個でも個々の顔の識別は困難だったので、2つの主成分では難しいのは自明だが。

以上の可視化のコードは以下の通り。

 

Python – 配列要素の重複数を制限する

概要

文字で表現するとわかり難いが、要するに次のようなことを想定している。たとえば次のような1次元の配列があるとする。

この配列には20個の要素があり、0~4の数値がそれぞれ5個、5個、2個、2個、4個、4個、順不同で含まれている。

この配列において、各数値の数を最大でも3個以内となるように切り落としたい、というのが目標。

たとえば、機械学習の教師データの数がターゲットごとにばらついている場合、各ターゲットのデータ数をある程度の数以下に抑えたいときが想定される。

上の例で仮に早く出現した準から3つまでを残して後は捨てるとすれば、以下のような配列になる。

内容:4 0 3 3 3 1 3 2 4 0 0 4 2 1 0 1 1 0 1 4
個数:1 1 1 2 3 1 4 1 2 2 3 3 2 2 4 3 4 5 5 4

なお、単に1つの配列の要素を切り落とすだけでなく、これと対応する配列が別にあって、その要素についても同時に切り落とすことも想定する。これは、機械学習のターゲット配列でデータを制限するのに、これに紐づけられた画像データなどを格納した配列も同時に操作するイメージ。

手順

ターゲットごとのインデックスの取得

targetsの20個のデータのうちid=0について考える。targetsの要素のうち値が0のものは5個あり、それらのインデックスは(1, 9, 10, 14, 17)。同様にid=1についても5個あり、インデックスは(5, 13, 15, 16, 18)。

このようにしてid=0~4についてインデックスを書き出すと以下の通りになる。

0:1, 9, 10, 14, 17
1:5, 13, 15, 16, 18
2:7, 12
3:2, 3, 4, 6
4:0, 8, 11, 19

各idに対応する配列はnumpy.where()関数を用いて以下のように得られる。

上の例では、ループのidを0~4と変化させていくのにrange(5)を使っている。ところが一般的には、番号が連続して存在しているとは限らず、またその上限もわからない。

そこで、targetsに出てくる要素を重なりなく、かつ全て使うためにnumpy.unique()関数を使っている。unique()関数は引数の配列の要素の重複を除き、昇順・辞書順に並べてくれる。この引数にtargetsを渡して、要素の重なりを除けば、targets中の要素を重なりなく1つずつ参照できる。

取り出す要素の制限

次に、すべてのターゲットのデータ数が3個以下になるようにすることを考える。

これらのデータで各idの個数を3個以下にするのに、出現順位の早いものから3個を選び出すことを考える。

0:1, 9, 10, 14, 17
1:5, 13, 15, 16, 18
2:7, 12
3:2, 3, 4, 6
4:0, 8, 11, 19

各配列の最初の3個を取り出すには、各idに対応する配列の先頭から3個目までをスライスで取り出せばよい。

これで値の最大3個までとするのに取り出すべきtargets中のインデックスが得られた。

要素の抽出

targets配列の要素の個数を制限するには、上で絞り込まれたインデックスに対応する要素を残し、それ以外の要素を切り捨てる。そのためには、残すべきインデックス位置の値がTrue、その他のインデックス位置の値がFalseであるbool配列をつくり、これをtargetsの引数とすればよい。

この配列を例えばmaskという名前とすると、targetsと同じサイズですべての要素がFalseである配列としてmaskを準備し、先ほどの切り落とすべきインデックスの位置のみTrueにするとよい。

以下では、まず全要素がFalsetargetsと同じサイズのbool配列を準備し、各idに対して3つ目までの要素の位置をTrue(1)としている。

ループの1回目で1、9、10番目がTrueになり、2回目で5、13、15番目がTrueに代わっていき、ループを重ねるごとに、取り出すべき要素の位置がTrueになっていることが確認できる。

なおbool配列の初期化では、Falseが数値の0と等価なため、numpy.zeros()関数を使っている。同じ理由で、numpy.where()Trueをセットするときに、数値の1をセットしている。

最後に、このbool配列をtargetsに適用して、取り出すべき要素の配列を得る。

他の配列の同時操作

mask配列は、targetsと同じサイズを持つ次元の配列に繰り返し適用できるので、たとえば機械学習でtargetsの各要素に紐づけられた画像データなどを格納した配列などについても、targetsと整合させながら必要な分だけ切出すことができる。

LFWデータセット – k近傍法(PCA変換付き)

概要

“Pythonではじめる機械学習”の主成分分析(PCA)のところで、著名人の顔画像データ(LFW peopleデータセット)に対するk-近傍法の精度を確認している。

  • LFW peopleのデータを、最低20枚以上の画像がある人物で絞り込んで読み込み
  • 各人物の画像を最大でも50枚以内となるよう制限(配列要素数の制限手順についてはこちらを参照
  • 2063人の人物について、87×65ピクセルを1次元化した5,655個の数値配列を特徴量データ(X_people)、各画像の人物の番号を収めた配列(y_people)をターゲットデータとする
  • 画像データを訓練データとテストデータに分割
  • このデータセットをそのまま1-nnで予測したスコアは、0.2程度でそれほどよくない
  • 画像データに主成分分析(PCA)を用いて、100個の主成分で変換したデータについても1-nnを適用していて、この場合のスコアは0.31

コード例ではPCAインスタンス生成時の引数としてwhiten=Trueを指定しているが、これを指定しない場合、データ変換後のスコアは0.23でこうじょうしなかった。

なお、スコアが書籍掲載値と異なるが、画像データの内容も書籍と今回実行時で異なっている。

コードと実行結果

 

numpy – bincount

概要

numpy.bincount()関数の仕様

  • 整数型の配列を引数にとる
  • 配列中、同じ値の要素の個数をカウントする
  • 0~要素の最大値を要素とし、各要素番号に対応する値の個数を要素とする配列を返す
  • 元のデータの要素ごとの重みを指定することができる

使い方

基本形

引数で与えた整数型配列中の同じ値をカウントして、各値ごとの個数を要素とする配列を返す。

上の結果の意味は、0が1個、1が2個、2が3個、3が2個。

値が飛んでいる場合

引数の配列中、0~最大値までの整数値に対する数をカウントする。値が存在しない場合の個数は0。

上の例では、0~5までの個数がカウントされ、0, 2, 4は配列中に存在しないので0となっている。

順不同

引数の配列中の要素は昇順である必要はない。

weightsの意味

引数にweightsを指定する場合。

  • データの配列と同じ要素数のweightsの配列を与える。
  • 要素をカウントの場合に1ずつ足すのではなく、各要素の位置に対応した重みが加算されていく

上の例では以下のように動作している。

  • 0は存在しないので0
  • 1は0番目に1つだけ存在し、その位置のweightsの値は0.1
  • 2は1番目と3番目に存在するので、weightsの第1要素0.2と第3要素0.4を加えて0.6
  • 3は存在しないので0
  • 4は2番目、4番目、5番目にあるので、weightsの第2要素0.3、第4要素0.5、第5要素0.6を加えて1.4

 

pyplot.imshow – 画像表示

概要

matplotlib.pyplot.imshow()は画像表示用のメソッドで、表示対象として、画像ファイルや画像情報を格納した配列を指定する。

pyplotやsubplotで直接実行するほか、Axesオブジェクトのメソッドとしても実行できる。

ピクセルデータのレンジのデフォルト設定と与えるデータのレンジによって予期しない結果になることもあり、vminvmaxを明示的に指定した方がよい。

画像ファイルの表示

以下のコードは、JPEGファイルを読み込んで表示する。

ここではpyplot.subplotのメソッドとしてimshow()を実行している。画像が1つの場合、pyplot.imshow()でもよい。

1つは画像ファイルをそのまま引数にし、もう1つは画像ファイルを配列の形にしてから引数に渡している。画像の配列の形については後述。

配列の画像表示

基本形

imshow()は配列を引数にとることができる。

以下の例では、カラーマップを指定して2×2=4要素の2次元配列を表示している。最小値0がカラーマップbwrの青に、最大値255が赤に対応し、その間の数値の大きさに応じたカラーマップ上の色が選択されている(デフォルトのcmapvirいdis)。

なお、この例ではpyplotから直接imshow()を実行している。

レンジ

imshow()に配列を渡して描画させるとき、数値のレンジに留意する必要がある。

デフォルトでは、imshow()は渡された配列の中の最小値と最大値をカラーマップの下限値と上限値に対応させ、線形にマッピングする。

なお、この例ではarray-likeとして2次元のリストを渡していて、Axesからimshow()を呼び出している。

4つの配列はそれぞれ最小値と最大値が異なり、かつその中央の値を持つ。値は異なるが全て最小値がカラーマップbwrの下限値に対応する青、最大値が上限値に対応する赤、中央値は白となっている(特段フランス国旗を意図したものではない)。

viminとvmax

imshow()の引数でvminvmaxを設定すると、配列の値に関わらず、vminvmiaxをカラーマップの下限値と上限値に対応させる。

以下の例では最小値0、最大値1の2要素の配列を、vminvmaxを変えてカラーマップbwrで描画させている。

左上はデフォルトなので、最小値0がカラーマップ下限値に対応した青に、最大値1が上限値に対応した赤になっている。

右上はvmin=0で配列の最小値0と同じだが、vmax=2としている。このため配列の0はカラーマップ下限の青で、配列の1はカラーマップ中央の白になっている。

左下はvmin=-1も設定されているので、配列の0、1はカラーマップの左から1/3、2/3に相当する色となっている。

右下はvminvmaxが配列の最小値と最大値の範囲より内側にある。このため、配列の最小値・最大値はそれぞれカラーマップの下限・上限に対応する青・赤となっている。

RGB

array-likeの次元が3次元になると、RGB/RGBA形式だと認識される。

[rows, cols, 3]
3次元目のサイズが3の時はRGB表現と認識される。1次元目と2次元目はそれぞれ画像の行数と列数とみなされ、3次元目は3つの列がR, G, Bの値に対応する。
[rows, cols, 4]
3次元目のサイズが4の時はRGBA表現と認識される。1次元目と2次元目はそれぞれ画像の行数と列数とみなされ、3次元目は3つの列がR, G, Bの値に対応し、4つ目の列が透明度に対応する。

R, G, B, Aの値は、配列のdtypeint形式の時には0~255、floatの時には0~1の範囲が想定される。

以下の例の内容。

  • 画像サイズを2行×4列として、R, G, Bごとに画像のピクセルデータを設定→shape=(3, 2, 4)
  • ピクセル並び替え後の配列を4つ準備
  • forループでピクセル並び替え
  • 画像表示とデータ内容の表示

3次元配列のピクセルの並び替えは、泥臭くforループで回しているが、もっとエレガントな方法があるかもしれない(もとから(3, rows, cols)の形にしてくれればよかったのに)。

 

imshow()に渡す配列のdtypeint型の時は、ピクセルデータのレンジが0~255になる。

  • 左上は元の配列のままR, G,Bが0か255なので、想定した組み合わせの色となっている
  • 右上は想定されているレンジに対して0.0~1.0の値を与えていることから、どのピクセルともR, G, Bが0か1(ほぼゼロ)となり黒くなっている(そのまま実行され、特にメッセ維持は出ない)

配列のdtypeがfloatの時は、ピクセルデータの想定レンジは0.0~1.0になる。

  • 左下は最小値0と最大255を与えているが、結果は左上と同じで、imshow()のデフォルトのレンジ0~255に変更されているようである(特にメッセージは出ない)
  • 右下は与えるピクセルデータを0.0~1.0としたところ、”入力データをクリップしている”というメッセージが出たが、レンジが修正されたらしく結果は意図通り

並べ替えた後の配列は、直感的にはわかりにくい形になっている。

グレースケール

グレースケールの場合は、cmap='gray'を指定する。vminvmaxは省略しても同じ結果となるが念のため。

 

PCA – Boston house-pricesデータセット

概要

scikit-learnの主成分分析モデル(PCA)をBiston housing pricesデータに適用して、その挙動を確認する。

主成分が適切に発見されてよい相関が得られることを期待したが、IrisデータBreast cancerデータの場合のようなクラス分類データにおける良好な結果は得られなかった。

ただし、Boston housing pricesデータはIrisやcancerのデータよりも複雑な社会行動に関するものであり、その指標も限定されていることから、これをもってPCAが回帰系のデータに不向きとまでは言い切れない。

なお、Boston housing pricesデータの特徴量には属性データ(カテゴリーデータ、クラスデータ)が含まれることから、DataFrameget_dummis()メソッドによるone-hot encodingを行っている。

計算の手順

  1. 必要なパッケージをインポート
  2. Boston housing pricesデータセットを準備
  3. データセットをスケーリング/エンコーディング
    1. 属性データの列を取り出して、get_dummiesでone-hot化
    2. StandardScalerで残りの特徴量データを標準化
    3. 上記2つを結合して前処理済みデータとして準備
  4. PCAモデルのインスタンスを生成
    • 引数n_components=2として、2つの特徴量について計算
  5. fit()メソッドにより、モデルにデータを学習させる
  6. 主成分やその寄与率を確認
    • 主成分はPCA.comonents_を、寄与率はPCA.explained_variance_ratio_を確認
  7. transform()メソッドによって、主成分に沿ってデータを変換
  8. 3つの主成分について3次元可視化
  9. 2つの主成分について2次元可視化

前処理

特徴量のうちの1つCHASについては、「チャールズ川に関するダミー変数(1:川沿い、0:それ以外)」~”Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)“となっていて、0か1の属性変数である。この変数をDataFrameget_dummies()メソッドでone-hot化する。

また、その他のデータについてはStandardScalerで標準化する。

  1. CHASのデータのみone-hot化
  2. CHASの列を除いたデータをStandardScalerで標準化
  3. 上記2つのデータをjoin()で結合

可視化

2次元

ここではまず、2次元可視化の結果を確認する。

クラス分類の場合は2次元で2つの主成分を確認できるが、回帰データの場合はターゲットの量を確認する必要があるため、グラフの軸を1つ消費する。このため、2次元による表現では1つの主成分による説明性を確認することになる。

  • 各点の色や大きさをターゲットの値によって変化させ、2つの軸を2つの主成分に割り当てる方法も考えられるが、直感的にとらえにくくなる。

この結果を見る限り、あまり美しい結果とはなっていない。データを俯瞰した際、各特徴量であまりいい説明ができなかったが、その中でもある程度関係がみられたMDEVやLSTATとの相関と変わらないくらい。

3次元

そこで3次元の可視化にして、2つの主成分による説明性を確認する。

これでもあまりいい結果にならない。ただしグラフを見ると、大きく2つの塊に分かれているように見える。ターゲットである住居価格とは別に、特徴量の組み合わせに隠れている、性質の違うグループがあるのかもしれない。

主成分と寄与率

2つの主成分と寄与率について表示させてみる。

寄与率は第1主成分が50%程度で低いため高い相関が出ないといえるかもしれない。だが、Breast cancerデータセットのクラス分類では第1主成分の寄与率が40%台だが、明確なクラス分類ができていた。やはり回帰系の問題にはPCAは不向きなのかもしれない。

主成分の要素について、先の散布図が第1主成分と負の相関があることから、第1主成分の各特徴量は価格低下に寄与するものはプラス、価格上昇に寄与するものはマイナスとなるはずである。

たとえばZNRMがマイナスなのは頷けるが、DISがマイナスなのは微妙。TAXPRATIOがプラスなのも逆のような気がする。

先にも書いたが、Boston housing pricesデータセットで取りそろえられた特徴量は、住居の価格以外の何かを特徴づける傾向が強いのかもしれない。

 

PCA – Breast cancerデータセット

概要

scikit-learnの主成分分析モデル(PCA)をBreast cancerデータセットに適用して、その挙動を確認する。

30個の特徴量(全て連続量)を持つ569個の腫瘍データについて、悪性(marignant)/良性(benign)がターゲットとして与えられている。PCAによって特徴量のみの分析で、少ない主成分によってある程度明確な分離が可能なことが示される。

手順

以下の手順・コードで計算した。

  1. パッケージをインポート
  2. Breast cancerデータセットを準備
  3. データセットをスケーリング
    • StandardScalerで特徴量データを標準化している
  4. PCAモデルのインスタンスを生成
    • 引数n_components=3で3つの主成分まで計算させている
  5. fit()メソッドによって、モデルにデータを学習させる
  6. 成分やその寄与率を確認
    • 主成分はPCA.comonents_を、寄与率はPCA.explained_variance_ratio_を確認
  7. transform()メソッドによって、主成分に沿ってデータを変換
  8. 3つの主成分について3次元可視化
  9. 2つの主成分について2次元可視化

主成分と寄与率

以下に主成分と寄与率を計算するまでのコードを示す。

寄与率は第1主成分が44%、第2主成分が19%、第3主成分が9%。第3成分まで3/4の情報を説明していることになる。

また、第1主成分は全ての特徴量がプラス方向で寄与している。

主成分をヒートマップで視覚化してみると、各主成分の符号や大きさが直感的に把握しやすくなるが、第2~第3主成分がmeanとworst系の特徴量が小さい方が影響が大きい点、3つの主成分についてerrorが大きいほど影響が大きい点など、意味づけは難しい。

可視化

3次元

3つの主成分について3次元で可視化してみると、悪性/良性がかなりはっきりと分離されている。

2次元

2つの主成分のみでも、悪性/良性がよく区分されている。

まとめ

Irisデータの場合と同じく、特徴量分析のみでクラスの別がよくあぶりだされている。

 

PCA – Irisデータセット

概要

scikit-learnの主成分分析モデル(PCA)をIrisデータに適用して、その挙動を確認する。

クラス分類のターゲットを用いていないにもかかわらず、少ない主成分でクラスがかなり明確に分類されることがわかる。

計算の手順

以下の手順・コードで計算した。

  1. 必要なパッケージをインポート
  2. Irisデータセットを準備
  3. データセットをスケーリング
    • StandardScalerで特徴量データを標準化している
  4. PCAモデルのインスタンスを生成
    • 引数n_componentsを指定せず、4つの特徴量全てを計算
  5. モデルにデータを学習させる
    • fit()メソッドのみでよいが、後のグラフ化のためにfit_transform()メソッドを実行
    • X_transに主成分によって変換したデータを格納
  6. 主成分やその寄与率を確認
    • 主成分はPCA.comonents_を、寄与率はPCA.explained_variance_ratio_を確認
  7. 3つの主成分について3次元可視化
  8. 2つの主成分について2次元可視化

主成分と寄与率

以下に、主成分と寄与率を計算するまでのコードを示す。

寄与率の方を見てみると、第1主成分で約73%、第2主成分で23 %と、2つの主成分で特徴をほぼ説明しきっている(第3、第4主成分の寄与はほとんど無視できる)。

第1主成分の各要素の符号を見てみる。萼の長さ、花弁の長さと幅は同程度でプラス方向に効いていて、萼の幅はマイナス方向の効果を持っている。このことから、萼の細長さと花弁の全体的な大きさによって、アヤメの花が特徴づけられていると考えられる。また第2主成分は、萼の幅で殆ど特徴が決まっている。

可視化

3次元

4つの主成分のうち3つについて3次元で可視化してみると、3つのアヤメの種類がかなりきれいに分離されているのがわかる。

2次元

第2主成分まででほとんどの特徴を説明できそうなので、2次元の散布図で表示してみる。

実際、2つの主成分だけでかなりきれいに3つのクラスが分かれている。少し重なっている部分があるが、先の主成分を3つの3次元グラフで傾きを調整すると、より明確にクラスが分けられる。

なお今回の計算では、PCAのモデルインスタンス生成時にn_components=2としている。その結果は以下の通りで、1つ前の結果と同じ値になっている。

主成分分析の特徴

IrisデータセットへのPCAの適用結果から、以下のようにまとめられる。

  • 主成分分析の計算において、ターゲットのクラス分類は全く用いていない(特徴量データのみを用いている)
  • ターゲットのクラス分類は、散布図を描くときの色分けにのみ利用している
  • それにも関わらず、散布図において3つのクラスがかなりきれいに分離されている
  • 特徴量の線形和に沿った分散の最大化、という問題設定で、その背後にあるアヤメの種類がうまく分類されている