ラビットチャレンジ　レポート（その6　機械学習-主成分分析）

## 主成分分析
多変量データの持つ構造をより少数個の指標に圧縮する手法。
この際、**できるだけ情報の損失が少なくなるように**変数を減らす。
これを利用することで、少数変数を利用した分析や可視化ができる。

### 流れ
学習データを

```math
\boldsymbol{x}_{i}=(x_{i1},x_{i2}, \dots,x_{im}) \in \mathbb{R}^m, i=1,2,\dots, n
```
として、各データから平均ベクトルを引いた、データ行列

```math
\bar X = (\boldsymbol{x}_1-\boldsymbol{\bar x}, \dots, \boldsymbol{x}_n-\boldsymbol{\bar x})^\top \in \mathbb{R}^{n \times m}
```
を定義する。これに対して線形変換をした後のベクトルは、

```math
\boldsymbol{s}_j = (s_{1j}, \dots, s_{nj})^\top = \bar X \boldsymbol{a}_j , \boldsymbol{a}_j \in \mathbb{R}^m
```
と表される。
線形変換後の分散は、

```math
Var(\boldsymbol{s}_j) = \boldsymbol{a}_j^\top Var(\bar X)\boldsymbol{a}_j
```
と表される。
主成分分析では、情報の多さを**分散の大きさ**と捉えて、線形変換後の分散が最大となるような射影軸を探していく。
ノルムが1となる制約条件の下で、上式を最大化するような$\boldsymbol{a}_j$を探すので、これは制約条件付き最適化問題として、以下のラグランジュ関数を用いる。

```math
E(\boldsymbol{a}_j) = \boldsymbol{a}_j^\top Var(\bar X)\boldsymbol{a}_j - \lambda (\boldsymbol{a}_j^\top \boldsymbol{a}_j - 1)
```
これを解くと、以下の解が得られる。

```math
Var(\bar X)\boldsymbol{a}_j = \lambda \boldsymbol{a}_j
```

### 寄与率
こうして分散共分散行列を計算して、それに対する固有値問題を解いたとき、$m$個の固有値と固有ベクトルのペアが得られる。
固有値を昇順に並べて、対応する固有ベクトルを第k主成分と呼ぶことにすると、第1～第m主成分の分散は元のデータの分散と一致する。

主成分分析では、どの主成分まで圧縮するかで情報量がどの程度残るかが肝になるので、その際に使う指標が**寄与率**である。

  * 寄与率：第k主成分の分散の全分散に対する割合
  * 累積寄与率：第1～k主成分までに圧縮した際の情報損失量の割合

### ハンズオン
乳がん検査データをロジスティック回帰モデルを作成。
主成分を利用して、2次元空間に次元圧縮した上でうまく判別できるか確認

```python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import confusion_matrix
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
%matplotlib inline

cancer_df = pd.read_csv('./cancer.csv')

#不要な行削除
cancer_df.drop('Unnamed: 32', axis=1, inplace=True)

# 目的変数の抽出
y = cancer_df.diagnosis.apply(lambda d: 1 if d == 'M' else 0)
# 説明変数の抽出
X = cancer_df.loc[:, 'radius_mean':]

# 学習用とテスト用でデータを分離
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 標準化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# ロジスティック回帰で学習
logistic = LogisticRegressionCV(cv=10, random_state=0)
logistic.fit(X_train_scaled, y_train)

# 検証
print('Train score: {:.3f}'.format(logistic.score(X_train_scaled, y_train)))
# Train score: 0.988

print('Test score: {:.3f}'.format(logistic.score(X_test_scaled, y_test)))
# Test score: 0.972

print('Confustion matrix:\n{}'.format(confusion_matrix(y_true=y_test, y_pred=logistic.predict(X_test_scaled))))
```
Train score:0.988、Test score:0.972となるので、判別は出来てそう。

ここからが主成分分析。

```python
pca = PCA(n_components=30)
pca.fit(X_train_scaled)
plt.bar([n for n in range(1, len(pca.explained_variance_ratio_)+1)], pca.explained_variance_ratio_)
```

![主成分分析-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/424091ee-f964-838e-83d9-2aa8264308e2.png)

寄与率を見ると、上位2つで60%くらいの累積寄与率になっている。
2次元に圧縮してみて、データをプロットしてみる。

```python
# PCA
# 次元数2まで圧縮
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_scaled)
print('X_train_pca shape: {}'.format(X_train_pca.shape))
# X_train_pca shape: (426, 2)

# 寄与率
print('explained variance ratio: {}'.format(pca.explained_variance_ratio_))
# explained variance ratio: [ 0.43315126  0.19586506]

# 散布図にプロット
temp = pd.DataFrame(X_train_pca)
temp['Outcome'] = y_train.values
b = temp[temp['Outcome'] == 0]
m = temp[temp['Outcome'] == 1]
plt.scatter(x=b[0], y=b[1], marker='o') # 良性は○でマーク
plt.scatter(x=m[0], y=m[1], marker='^') # 悪性は△でマーク
plt.xlabel('PC 1') # 第1主成分をx軸
plt.ylabel('PC 2') # 第2主成分をy軸
```

![主成分分析-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/615ac96c-49f4-eacc-e429-b8a344aa6eca.png)

この2次元データを用いて、ロジスティック回帰を行う。

```python
# ロジスティック回帰で学習
logistic = LogisticRegressionCV(cv=10, random_state=0)
logistic.fit(X_train_pca, y_train)

X_test_pca = pca.fit_transform(X_test_scaled)

# 検証
print('Train score: {:.3f}'.format(logistic.score(X_train_pca, y_train)))
print('Test score: {:.3f}'.format(logistic.score(X_test_pca, y_test)))
```
Train score:0.965、Test score:0.916なので、2次元データでもかなりの精度で判別が可能。（課題）