ラビットチャレンジレポート2 機械学習

## 機械学習
### 1. 線形回帰モデル
#### 1.1 要点
回帰問題とは、ある入力から出力を予測する問題のことである。直線で予測する場合を線形回帰、曲線で予測する場合は非線形回帰と呼ぶ。
**線形回帰モデル**は回帰問題を解くための教師あり学習の1つであり、
入力とパラメータの線形結合を出力するモデル
入力$\boldsymbol{x}$、パラメータ$\boldsymbol{w}$を、

```math
\boldsymbol{x}=(x_1,x_2,_\cdots,x_m)^T\\
\boldsymbol{w}=(w_1,w_2,_\cdots,w_m)^T
```
としたとき、出力$\hat{y}$(予測値)は、

```math
\hat{y}=\boldsymbol{w}^T\boldsymbol{x}+w_0=\sum_{j=1}^mw_jx_j+w_0
```
と表される。  
説明変数$x_i$が1つのとき**単回帰**と呼び、2つ以上のとき**重回帰**と呼ぶ。
次のような連立方程式を考える。

```math
y_1=w_0+w_1x_{11}+w_2x_{12}+\cdots+w_mx_{1m}+\epsilon_1\\
y_2=w_0+w_1x_{21}+w_2x_{22}+\cdots+w_mx_{2m}+\epsilon_2\\
\vdots\\
y_n=w_0+w_1x_{n1}+w_2x_{n2}+\cdots+w_mx_{nm}+\epsilon_n
```
これを行列表現で表すと以下のようになる。

```math
\boldsymbol{y}=\boldsymbol{X}\boldsymbol{w}+\boldsymbol{\epsilon}\\
\boldsymbol{X}=(\boldsymbol{x_1},\cdots,\boldsymbol{x_n})^T\\
\boldsymbol{y}=(y_1,\cdots,y_n)^T\\
\boldsymbol{x_i}=(1,x_{i1},\cdots,x_{im})^T\\
\boldsymbol{\epsilon}=(\epsilon_1,\cdots,\epsilon_n)^T\\
\boldsymbol{w}=(w_0,w_1,\cdots,w_m)^T
```

このとき$\boldsymbol{X}$は**計画行列**と呼ぶ。
#### 最小二乗法
パラメータ$\boldsymbol{w}$は**最小二乗法**で決められる。
**平均二乗誤差（残差平方和）**とは、データとモデル出力の二乗誤差の和であり、小さいほど直線とデータの距離が近い。  

```math
MSE_{train}=\frac{1}{n_{train}}\sum_{i=1}^{n_{train}}(\hat{y}_i^{(train)}-y_i^{(train)})^2
```

ここで、**最小二乗法**による$\boldsymbol{w}$の推定を行う。  
MSEを最小化するには、$\boldsymbol{w}$に関して微分したものが0になる点を求めればよい。  

```math
\frac{\partial{}}{\partial \boldsymbol{w}}\biggl\{\frac{1}{n_{train}}\sum_{i=1}^{n_{train}}(\hat{y}_i^{(train)}-y_i^{(train)})^2 \biggr\}=0
```

```math
\frac{1}{n_{train}}\frac{\partial{}}{\partial \boldsymbol{w}}\bigl\{(\boldsymbol{X}^{(train)}\boldsymbol{w}-\boldsymbol{y}^{(train)})^T(\boldsymbol{X}^{(train)}\boldsymbol{w}-\boldsymbol{y}^{(train)}) \bigr\}=0
```

```math
2\boldsymbol{X}^{(train)T}\boldsymbol{X}^{(train)}\boldsymbol{w}-2\boldsymbol{X}^{(train)T}\boldsymbol{y}^{(train)}=0
```

```math
\hat{\boldsymbol{w}}=(\boldsymbol{X}^{(train)T}\boldsymbol{X}^{(train)})^{-1}\boldsymbol{X}^{(train)T}\boldsymbol{y}^{(train)}
```
予測値$\hat{\boldsymbol{y}}$は  

```math
\begin{align}
\hat{\boldsymbol{y}}&=\boldsymbol{X}\hat{\boldsymbol{w}}\\
&=\boldsymbol{X}・(\boldsymbol{X}^{(train)T}\boldsymbol{X}^{(train)})^{-1}\boldsymbol{X}^{(train)T}\boldsymbol{y}^{(train)}
\end{align}
```
となる。

#### 1.2 実装
shikit-learnを用いて線形回帰モデルの実装を行う。
ここで使用するデータはボストン近郊の住宅価格のデータセットであり、これはshikit-learnで用意されているものである。

```python
from sklearn.datasets import load_boston
import pandas as pd
import numpy as np

boston = load_boston()

df = pd.DataFrame(boston.data, columns=boston.feature_names)
df['PRICE'] = boston.target
df.head()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/cdfa9843-4a88-07ee-e2d7-6123a5bf26b9.png)

ここでは、目的変数は住宅価格であり、説明変数として、犯罪発生率(CRIM)、一戸当たりの平均部屋数(RM)を使用する。

```python
data = df.loc[:, ['CRIM', 'RM']].values
target = df.loc[:,['PRICE']].values
```

モデルの作成をする。
線形回帰モデルはshikit-learnのLinearRegressionを使って実装できる。

```python
from sklearn.linear_model import LinearRegression
model = LinearRegression()

model.fit(data, target)
```

そして「predict」で与えた説明変数に対する予測結果を出力することができる。

```python
model.predict([[0.2, 7]])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/ebaa125b-6e64-d550-3b90-3cf0e24ec09b.png)

切片と回帰係数の確認を行う。

```python
print('回帰係数: ', model.coef_)
print('切片: ', model.intercept_)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/9b04e50b-ac90-5668-aecf-7eeedfb7ab78.png)

### 2. 非線形回帰モデル
#### 2.1 要約
**非線形回帰**とは、

```math
y = w_0+w_1x+w_2x^2+w_3x^3\\
y = w_0+w_1\sin{x}+w_2\cos{x}+w_3\log{x}
```
などのように、
線形回帰での$x$の代わりに$x$の関数$\phi (x)$を用いる回帰モデルのこと。
ただし、パラメータ$\boldsymbol{w}$については線形の関係であることは重回帰モデルと変わりはない。  

#### 基底展開法  
回帰関数として、基底関数と呼ばれる既知の非線形関数とパラメータベクトルの線形結合を使用  

```math
y_i=w_0+\sum_{j=1}^mw_j\phi(\boldsymbol{x_i})+\epsilon_j
```
よく使われる基底関数には多項式関数やガウス基底関数などがある。
基底展開法も線形回帰と同じ枠組みで推定が可能である。
説明変数を

```math
\boldsymbol{x}_i=(x_{i1},x_{i2},\cdots,x_{im})
```
非線形関数ベクトルを

```math
\boldsymbol{\phi}(\boldsymbol{x}_i)=(\phi_1(\boldsymbol{x}_i),\phi_2(\boldsymbol{x}_i),\cdots,\phi_k(\boldsymbol{x}_i))
```
としたとき、計画行列は

```math
\Phi^{(train)}=(\boldsymbol{\phi}(\boldsymbol{x}_1),\boldsymbol{\phi}(\boldsymbol{x}_2),\cdots,\boldsymbol{\phi}(\boldsymbol{x}_n))
```
となることから、予測値は最尤法により

```math
\hat{\boldsymbol{y}}=\boldsymbol{\Phi}(\Phi^{(train)T}\Phi^{(train)})^{-1}\Phi^{(train)T}\boldsymbol{y}^{(train)}
```
と計算できる。
#### 未学習と過学習
学習データに対して十分小さな誤差が得られないモデルを**未学習**と呼ぶ。
小さな誤差であるが、テストデータに対する誤差との差が大きいモデルを**過学習**と呼ぶ。
対策としては、

- 学習データの数を増やす
- 不要な基底関数を削除(特徴量選択) 
モデルの複雑さが変化

- 正則化法を利用
モデルの複雑さに従って、値が大きくなる**正則化項(罰則項)**を課した関数を最小化

などがある。
#### 正則化項
- L2ノルム　Ridge推定量と呼ばれパラメータが0に近づくように推定される
- L1ノルム　Lasso推定量と呼ばれいくつかのパラメータを正確に0に推定される
    
正則化パラメータは大きいほど制約面が小さくなり、パラメータが0に近くなるように推定される。

モデルが未学習か過学習しているかの判断としては、

- 訓練誤差もテスト誤差もどちらも小さい　　　　　汎化している
- 訓練誤差は小さいがテスト誤差が大きい　　　　　過学習
- 訓練誤差もテスト誤差もどちらも小さくならない　未学習

#### 精度評価
#### ホールドアウト法
データを学習用データとテスト用に分割し、予測精度や誤り率を推定するために使用
手元にデータが大量にある場合を除いて、よい性能評価を与えないという欠点がある
  
#### クロスバリデーション
データを複数のグループに分割し、
それぞれのグループを検証用データ、その他のデータを学習用データとしたモデルを作成し精度評価を行う。
これらの精度の平均値を**CV値**と呼ぶ。
#### パラメータのチューニング
#### グリッドサーチ
- すべてのチューニングパラメータの組み合わせで評価値を算出
- 最も良い評価値を持つチューニングパラメータを持つ組み合わせを採用
  
#### 2.2 実装
未学習と過学習の様子を示す。
次数が低いと表現力は低く、次数が高くなると表現力は高い分複雑なモデルとなる。

```python
import matplotlib.pyplot as plt
import japanize_matplotlib

x = np.arange(0,10, 0.2)

def poly(x):
    return 1 + 0.8*x + 0.3*x**2 - 0.04*x**3 + 3*np.random.rand(len(x))

X1 = np.array([x]).T
X2 = np.array([x, x**2, x**3]).T
X3 = np.array([x, x**2, x**3, x**4, x**5, x**6, x**7, x**8, x**9, x**10]).T

lr1 = LinearRegression()
lr2 = LinearRegression()
lr3 = LinearRegression()

lr1.fit(X1, poly(x))
lr2.fit(X2, poly(x))
lr3.fit(X3, poly(x))

plt.plot(poly(x), 'o')
plt.plot(lr1.predict(X1), color='red', label='1次')
plt.plot(lr2.predict(X2), color='green', label='3次')
plt.plot(lr3.predict(X3), color='blue', label='10次')
plt.legend();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/4154ec97-966d-7d47-8eaf-2579af32a646.png)


Ridge回帰の様子を確認する。
ここでは大きな違いは見られなかった。

```python
from sklearn.linear_model import Ridge

ridge1 = Ridge(alpha=0.1)
ridge2 = Ridge(alpha=1.0)
ridge3 = Ridge(alpha=10.0)

ridge1.fit(X3, poly(x))
ridge2.fit(X3, poly(x))
ridge3.fit(X3, poly(x))

plt.plot(poly(x), 'o')
plt.plot(ridge1.predict(X3), color='red', label='alpha=0.1')
plt.plot(ridge2.predict(X3), color='green', label='alpha=1.0')
plt.plot(ridge3.predict(X3), color='blue', label='alpha=10.0')
plt.legend();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/a03765bb-83a5-f75a-1f73-296cc45fd8f4.png)


それぞれの係数を確認する。
正則化のパラメータが高くなるにつれて係数が小さくなっていくことが確認できた。

```python
plt.plot(ridge1.coef_, 'o', color='red', label='alpha=0.1')
plt.plot(ridge2.coef_, 'o', color='green', label='alpha=1.0')
plt.plot(ridge3.coef_, 'o', color='blue', label='alpha=10.0')
plt.plot(lr3.coef_, 'o', color='orange', label='alpha=0')
plt.legend();
plt.xlabel('xの次数');
plt.ylabel('係数');
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/6f9a21f5-e493-eb73-8e14-6be0c450ed10.png)

同様にLassoでも確認を行う。

```python
from sklearn.linear_model import Lasso

lasso1 = Lasso(alpha=0.1)
lasso2 = Lasso(alpha=1.0)
lasso3 = Lasso(alpha=10.0)

lasso1.fit(X3, poly(x))
lasso2.fit(X3, poly(x))
lasso3.fit(X3, poly(x))

plt.plot(poly(x), 'o')
plt.plot(lasso1.predict(X3), color='red', label='alpha=0.1')
plt.plot(lasso2.predict(X3), color='green', label='alpha=1.0')
plt.plot(lasso3.predict(X3), color='blue', label='alpha=10.0')
plt.legend();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/49242122-4174-23bb-b7f9-731a80684715.png)

基の回帰係数に比べほとんどの係数が0になっていることが確認できた。

```python
plt.plot(lasso1.coef_, 'o', color='red', label='alpha=0.1')
plt.plot(lasso2.coef_, 'o', color='green', label='alpha=1.0')
plt.plot(lasso3.coef_, 'o', color='blue', label='alpha=10.0')
plt.plot(lr3.coef_, 'o', color='orange', label='alpha=0')
plt.legend();
plt.xlabel('xの次数');
plt.ylabel('係数');
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/50a074b3-a293-1d40-7e15-119109281e76.png)


次に、交差検証の例を示す。ボストン住宅データを使用し、5分割交差検証を行う。

```python
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error

data = df.loc[:, ['CRIM', 'RM']].values
target = df.loc[:,['PRICE']].values

x_train, x_test, y_train, y_test = train_test_split(data, target)


kf = KFold(n_splits=5)

train_loss = []
valid_loss = []

for train_index, valid_index in kf.split(x_train):
    x_train_kf, x_valid_kf = x_train[train_index, :], x_train[valid_index, :]
    y_train_kf, y_valid_kf = y_train[train_index, :], y_train[valid_index, :]
    
    ridge = Ridge(alpha=0.1)
    ridge.fit(x_train_kf, y_train_kf)
    trainingLoss = mean_squared_error(y_train_kf, ridge.predict(x_train_kf))
    train_loss.append(trainingLoss)
    
    validLoss = mean_squared_error(y_valid_kf, ridge.predict(x_valid_kf))
    valid_loss.append(validLoss)
    
    print('train loss: ',trainingLoss,  'validation loss: ', validLoss)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/45ba1fda-4095-3b28-020b-df43ecd31331.png)


最後にグリッドサーチの例を示す。  
ここではRidge回帰の正則化パラメータの値のチューニングを行う。

```python
from sklearn.model_selection import GridSearchCV

param_grid = {'alpha':[0.001, 0.01, 0.1, 1.0, 10.0, 100.0]}

# refit=Trueで訓練データ全体を使って最適な設定で自動的に再適合する
gridsearch = GridSearchCV(Ridge(), param_grid, cv=5, verbose=0, refit=True, scoring='neg_mean_squared_error')

best_model = gridsearch.fit(x_train, y_train)
```

```python
print(-best_model.best_score_)
print(best_model.best_params_)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/519ebcb2-020d-46da-1a2c-9d64dba34432.png)

### 3. ロジスティック回帰モデル
#### 3.1 要約
**分類問題**とは、ある入力からクラスに分類する問題のことである。
分類問題に対するアプローチ

- 識別的アプローチ
　$p(C_k|\boldsymbol{x})$を直接モデル化
- 生成的アプローチ
　$p(C_k)$と$p(\boldsymbol{x}|C_k)$をモデル化し、Bayesの定理を用いて$p(C_k|\boldsymbol{x})$を求める

ロジスティック回帰は**識別的アプローチ**である。
入力$\boldsymbol{x}$、パラメータ$\boldsymbol{w}$を、

```math
\boldsymbol{x}=(x_1,x_2,_\cdots,x_m)^T\\
\boldsymbol{w}=(w_1,w_2,_\cdots,w_m)^T
```
としたとき、これらの線形結合$\hat{y}$は、

```math
\hat{y}=\boldsymbol{w}^T\boldsymbol{x}+w_0=\sum_{j=1}^mw_jx_j+w_0
```
となる。
この線形結合をシグモイド関数に入力することで、y=1になる確率を出力する。
**シグモイド関数**とは、

```math
\sigma{(x)}=\frac{1}{1+\exp{(-ax)}}
```
で表される関数で、出力は必ず0-1の値となる。
#### シグモイド関数の微分

```math
\sigma{(z)}=\frac{1}{1+\exp{(-z)}}
```
の微分は、

```math
\frac{\partial \sigma{(z)}}{\partial z}=(-1) * (1+\exp{(-z)})^{-2}*\exp{(-z)}*(-1)\\
=\sigma{(z)}(1-\sigma{(z)})
```
と計算できる。
ロジステック回帰モデルは、シグモイド関数を使用して

```math
P(Y=1|\boldsymbol{x})=\sigma(w_0+w_1x_1+\cdots+w_mx_m)
```
と表せる。
このときY=1になる確率が0.5以上のとき1、0.5未満のときに0とするなど判断できる。
#### 最尤推定
y1～ynのデータが得られた際の尤度関数は

```math
\begin{align}
P(y_1,y_2,\cdots,y_n;p)&=\prod_{i=1}^np^{y_i}(1-p)^{1-y_i}\\
&=\prod_{i=1}^n\sigma{(\boldsymbol{w^T}\boldsymbol{x_i})}^{y_i}(1-\sigma{(\boldsymbol{w^T}\boldsymbol{x_i})})^{1-y_i}\\
&=L(\boldsymbol{w})
\end{align}
```
となり、この尤度関数を最大化するパラメータを探索する。
対数をとることで微分の計算が可能となる。

```math
\begin{align}
E(w_0,w_1,\cdots,w_m)&=-\log{L(\boldsymbol{w})}\\
&=\sum_{i=1}^n\{y_i\log{p_i}+(1-y_i)\log{(1-p_i)}\}
\end{align}
```

#### 勾配降下法
反復学習によりパラメータを逐次的に更新する
学習率$\eta$の調整により収束のしやすさを調整

ここで対数尤度の微分を考える。

```math
\begin{align}
\frac{\partial E(\boldsymbol{w})}{\partial \boldsymbol{w}}&=-\sum_{i=1}^n\frac{\partial E_i}{\partial p_i}・\frac{\partial p_i}{\partial z_i}・\frac{\partial z_i}{\partial \boldsymbol{w}}\\
&=-\sum_{i=1}^n\bigr(\frac{y_i}{p_i}-\frac{1-y_i}{1-p_i} \bigl)p_i(1-p_i)\boldsymbol{x}\\
&=-\sum_{i=1}^n(y_i(1-p_i)-(1-y_i)p_i)\boldsymbol{x}\\
&=-\sum_{i=1}^n(y_i-p_i)\boldsymbol{x}
\end{align}
```

このとき勾配降下法による重みパラメータの更新は、

```math
\begin{align}
\boldsymbol{w}(k+1)&=\boldsymbol{w}(k)-\eta\frac{\partial E(\boldsymbol{w})}{\partial \boldsymbol{w}}\\
&=\boldsymbol{w}(k)+\eta\sum_{i=1}^n(y_i-p_i)\boldsymbol{x}
\end{align}
```
と書くことができ、更新されなくなったとき勾配が0になったということとなる。
**確率的勾配降下法**とは、データを１つずつランダムに選びパラメータを更新

```math
\boldsymbol{w}(k+1)=\boldsymbol{w}(k)+\eta(y_i-p_i)\boldsymbol{x}
```
#### 性能指標
予測結果と真のラベルクロス集計表を**混同行列**と呼ぶ。

|||||
|-|-|-|-|
|||正解|ラベル|
|||positive|negetive|
|予測|positive|真陽性|偽陽性|
|ラベル|negetive|偽陰性|真陽性|

- 正解率　正解した数/予測対象全体の数
　予測対称クラスに偏りがある場合などに問題がある

- 再現率　真陽性/本当に陽性であった数(真陽性+偽陰性)
- 適合率　真陽性/陽性と予測した数(真陽性+偽陽性)

再現率と適合率はトレードオフの関係にるため、これらの調和平均である**F値**を使うことがある。
F = 1/(1/再現率 + 1/適合率)

#### 3.2 実装
ダミーデータを用いて動作の確認を行う。

```python
n_sample = 100
harf_n_sample = 50
var = .2

def gen_data(n_sample, harf_n_sample):
    x0 = np.random.normal(size=n_sample).reshape(-1, 2) - 1.
    x1 = np.random.normal(size=n_sample).reshape(-1, 2) + 1.
    x_train = np.concatenate([x0, x1])
    y_train = np.concatenate([np.zeros(harf_n_sample), np.ones(harf_n_sample)]).astype(np.int)
    return x_train, y_train

def plt_data(x_train, y_train):
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, facecolor="none", edgecolor="b", s=50, label="training data")
    plt.legend()

#データ作成
x_train, y_train = gen_data(n_sample, harf_n_sample)
#データ表示
plt_data(x_train, y_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/e5c643c7-b66b-d149-18d9-1912bf5f433e.png)

予測モデルの作成を行い、分類結果を図示する。  
データ点以外の領域についても分類結果を出力することでモデルの様子を確認できる。

```python
from sklearn.linear_model import LogisticRegression

xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

model=LogisticRegression(fit_intercept=True)
model.fit(x_train, y_train)
proba = model.predict_proba(xx)
y_pred = (proba > 0.5).astype(np.int)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(xx0, xx1, proba[:, 0].reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/7023759a-492d-2ae1-a461-962437ee06a7.png)

タイタニックの生存データを用いた予測モデルの構築を行う。  
課題としては「30歳の男性の生き残る確率」を求めることである。

```python
import pandas as pd

data_train =  pd.read_csv('C:/Users/tanak/study/data/titanic_train.csv')
data_train.head()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b16839f2-d0ee-27f7-bf62-8321873a2e4f.png)

目的変数は「Suevived」であり、1が生存、0が死亡である。  
今回は、簡単のため説明変数としてAge(年齢)とSex(性別)のみ扱うものとする。

```python
y_train = data_train['Survived'].values
x_train = data_train[['Age', 'Sex']]
```

欠損値の確認を行う。

```python
x_train.isna().sum()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/e6e0171a-6ca0-0822-ff63-a9406e1c8e70.png)

Ageの欠損値は平均値を使って補完を行うことにする。

```python
x_train.loc[x_train['Age'].isna(), 'Age'] = int(x_train['Age'].mean())
x_train.isna().sum()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/ae0c7e7f-2b69-7672-5585-378575c43c7d.png)

Sexが文字列のままであるとモデルの作成が行えないので、maleなら1、femaleなら0とする。

```python
x_train.loc[x_train['Sex']=='male', 'Sex'] = 1
x_train.loc[x_train['Sex']=='female', 'Sex'] = 0
```

```python
from sklearn.metrics import f1_score, accuracy_score, confusion_matrix

LR = LogisticRegression()

LR.fit(x_train, y_train)
pred_train = LR.predict(x_train)

print('正解率: ', accuracy_score(y_train, pred_train))
print('F1値: ', f1_score(y_train, pred_train))
print('混同行列')
confusion_matrix(y_train, pred_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/39367e56-6c30-dbe8-061f-649d12ca7a1e.png)

30代男性の生き残る確率を出力する。

```python
LR.predict_proba([[30, 1]])[0][0]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/a91dcab9-a4ce-535d-109c-2329782fc1dd.png)

### 4. 主成分分析
#### 4.1 要約
多変量のデータをより少数のデータに圧縮
分散が最大となる射影軸を探索

学習データを、

```math
\boldsymbol{x_i}=(x_{i1},x_{i2},\cdots,x_{im})
```
としたとき、平均ベクトルは

```math
\hat{\boldsymbol{x}}=\frac{1}{n}\sum_{i=1}^{n}\boldsymbol{x_i}
```
と表すことができる。
平均値で中心化を行ったデータ行列を,

```math
\bar{\boldsymbol{X}}=(\boldsymbol{x}_1-\hat{\boldsymbol{x}},\cdots,\boldsymbol{x}_n-\hat{\boldsymbol{x}})
```
としたときに、分散共分散行列は

```math
\Sigma = Var(\bar{\boldsymbol{X}})=\frac{1}{n}\bar{\boldsymbol{X}}^T\bar{\boldsymbol{X}}
```
線形変換後のベクトルは、

```math
\boldsymbol{s_j}=(s_{1j},\cdots,s_{nj})=\bar{\boldsymbol{X}}\boldsymbol{a}_j
```
となる。  
  
線形変換後の分散を計算すると、

```math
Var(\boldsymbol{s_j})=\frac{1}{n}\boldsymbol{s_j^T}\boldsymbol{s_j}=\frac{1}{n}\boldsymbol{a_j^T}\bar{\boldsymbol{X}}^T\bar{\boldsymbol{X}}\boldsymbol{a_j}=\boldsymbol{a_j^T}Var(\bar{\boldsymbol{X}})\boldsymbol{a_j}
```

以下の制約付き最適化問題を解くことで、線形変換後の分散が最大となる射影軸を求める。  

```math
\arg\max_{\boldsymbol{a}\in R^m}\ \boldsymbol{a}_j^TVar(\bar{\boldsymbol{X}})\boldsymbol{a_j}\\
subject\ to\ \boldsymbol{a_j^T}\boldsymbol{a_j}=1
```

詳細は割愛するが、この解は、

```math
Var(\bar{\boldsymbol{X}})\boldsymbol{a_j}=\lambda_j\boldsymbol{a_j}
```

となり、射影先の分散は固有値と一致することが確認できる。  

```math
Var(\boldsymbol{s_j})=\boldsymbol{a_j^T}Var(\bar{\boldsymbol{X}})\boldsymbol{a_j}=\lambda_j\boldsymbol{a_j^T}\boldsymbol{a_j}=\lambda_j
```
#### 寄与率
第1～元次元の主成分の分散は、元のデータの分散と一致。

```math
V_{total}=\sum_{i=1}^m\lambda_i
```
**寄与率**($c_k$)は、第k成分の分散の全分散に対する割合のことである。
**累積寄与率**($r_k$)は、第1～第k主成分まで圧縮した際の情報損失量の割合である。

```math
c_k=\frac{\lambda_k}{\sum_{i=1}^m\lambda_i}\\
r_k=\frac{\sum_{i=j}^k\lambda_j}{\sum_{i=1}^m\lambda_i}
```
#### 4.2 実装
乳がん検査データを用いたロジスティック回帰を行う。
主成分成分分析により、2次元空間上に圧縮し分類できるか確認する。

```python
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression

cancer = load_breast_cancer()

scaler = StandardScaler()
scaler.fit(cancer.data)
y = cancer.target

X_std = scaler.transform(cancer.data)


pca = PCA(n_components=2, whiten=True) # whiten 白色化
lr = LogisticRegression(solver='lbfgs')

X_pca = pca.fit_transform(X_std)
lr.fit(X_pca, y)
```

```python
xx0, xx1 = np.meshgrid(np.linspace(-5, 6, 100), np.linspace(-5, 6, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

proba = lr.predict_proba(xx)
y_pred = (proba > 0.5).astype(np.int)

plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y)
plt.contourf(xx0, xx1, proba[:, 0].reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/e05508af-9520-e20d-30a5-e32bb5e856a0.png)

寄与率を確認すると、2つの主成分で全体の分散のおよそ63%の分散を捉えていることがわかる。

```python
pca.explained_variance_ratio_
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b8003bbb-735a-2fc1-ab01-66fefc263547.png)

### 5. サポートベクターマシン
#### 5.1 要約
SVMの目的は、**マージン**の最大化である。
マージンとは、決定境界と、この境界に最も近い訓練データの間の距離として定義される。
この決定境界に最も近い訓練データは**サポートベクトル**と呼ばれる。
ロジスティック回帰に比べ外れ値の影響を受けにくい。

ここで、決定境界に沿った正と負の超平面を見ていく。
これらの超平面は、次のように表せる。

```math
w_0+\boldsymbol{w}^T\boldsymbol{x}_{pos}=1\\
w_0+\boldsymbol{w}^T\boldsymbol{x}_{neg}=-1
```

この2つの方程式の差をとる。

```math
\boldsymbol{w}^T(\boldsymbol{x}_{pos}-\boldsymbol{x}_{neg})=2
```

この式の両辺をベクトルの長さ$|\boldsymbol{w}|$で割る。

```math
\frac{\boldsymbol{w}^T(\boldsymbol{x}_{pos}-\boldsymbol{x}_{neg})}{|\boldsymbol{w}|}=\frac{2}{|\boldsymbol{w}|}
```

この式の左辺は、正の超平面と負の超平面の距離(すなわちマージン)として解釈できる。
SVMの目的関数を最大化する問題は、$\frac{2}{|\boldsymbol{w}|}$を最大化する問題に帰着する。
実際には、逆数をとって2乗した$\frac{1}{2}|\boldsymbol{w}|^2$を最小化するほうが簡単である。
ただし、これにはデータ点が正しく分類されているという制約がある。

```math
w_0+\boldsymbol{w}^T\boldsymbol{x}^{(i)}\geq1\ (y^{(i)}=1)\\
w_0+\boldsymbol{w}^T\boldsymbol{x}^{(i)}\leq-1\ (y^{(i)}=-1)\\
i=1\cdots N
```

簡潔に書くと、

```math
y^{(i)}(w_0+\boldsymbol{w}^T\boldsymbol{x}^{(i)})\geq1
```
となる。  
  
#### ソフトマージン分類
**スラック変数**$\xi$を導入することで、適切なペナルティを科した上で、
誤分類が存在する状態のまま最適化問題を収束させることが可能となった。

```math
w_0+\boldsymbol{w}^T\boldsymbol{x}^{(i)}\geq1-\xi^{(i)}\ (y^{(i)}=1)\\
w_0+\boldsymbol{w}^T\boldsymbol{x}^{(i)}\leq-1+\xi^{(i)}\ (y^{(i)}=-1)\\
i=1\cdots N
```

最小化すべき対象は次のようになる。

```math
\frac{1}{2}|\boldsymbol{w}|^2+C\biggl(\sum_{i}\xi^{(i)} \biggl)
```

Cの値が大きい場合は、誤分類のペナルティが大きく、Cの値が小さいときはペナルティが小さい。
これにより、Cを使ってマージンの幅を制御できる。

#### カーネルSVM
線形分離不能なデータの処理をする方法に、**カーネル法**がある。
射影関数$\phi$を使ってそれらの組み合わせを高次元へ射影し、線形分離できるようにする。
例えば、

```math
\phi(x_1,x_2)=(z_1,z_2,z_3)=(x_1,x_2,x_1^2+x_2^2)
```

のような変換である。
ただし、新しい特徴量を生成する計算コストが非常に高いという問題がある。
ここで、**カーネルトリック**と呼ばれる手法があり、**カーネル関数**を定義して、データ間の距離を直接計算する。

```math
K(\boldsymbol{x}^{(i)}, \boldsymbol{x}^{(j)})=\phi(\boldsymbol{x}^{(i)})^T\phi(\boldsymbol{x}^{(j)})
```

最もよく使われるカーネルの1つに**動径基底関数カーネル(ガウスカーネル)**がある。

```math
K(\boldsymbol{x}^{(i)}, \boldsymbol{x}^{(j)})=\exp\biggl(-\frac{|\boldsymbol{x}^{(i)}-\boldsymbol{x}^{(j)}|^2}{2\sigma^2} \biggr)
```

これは、次のように簡略化される。

```math
K(\boldsymbol{x}^{(i)}, \boldsymbol{x}^{(j)})=\exp\bigl(-\gamma|\boldsymbol{x}^{(i)}-\boldsymbol{x}^{(j)}|^2 \bigr)
```

ここで、$\gamma=\frac{1}{2\sigma^2}$はハイパーパラメータである。

#### 5.2 実装
numpyによる実装とschikit-learnによる実装の両方を乗せる。
#### 線形分離可能

```python
def gen_data():
    x0 = np.random.normal(size=50).reshape(-1, 2) - 2.
    x1 = np.random.normal(size=50).reshape(-1, 2) + 2.
    X_train = np.concatenate([x0, x1])
    ys_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)
    return X_train, ys_train

X_train, ys_train = gen_data()
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/846b8760-3886-a69e-49d2-c44308594b48.png)

```python
t = np.where(ys_train == 1.0, 1.0, -1.0)

n_samples = len(X_train)
# 線形カーネル
K = X_train.dot(X_train.T)

eta1 = 0.01
eta2 = 0.001
n_iter = 500

H = np.outer(t, t) * K

a = np.ones(n_samples)
for _ in range(n_iter):
    grad = 1 - H.dot(a)
    a += eta1 * grad
    a -= eta2 * a.dot(t) * t
    a = np.where(a > 0, a, 0)

index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]

term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
```

```python
xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
    for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
        y_project[i] += a * sv_t * sv.dot(X_test[i])
y_pred = np.sign(y_project)

# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])


# マージンと決定境界を可視化
plt.quiver(0, 0, 0.1, 0.35, width=0.01, scale=1, color='pink')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/e4b8cd66-98ef-72e7-f2e4-d0dd4c5a90c3.png)


```python
from sklearn.svm import SVC 

svc = SVC(kernel='linear', degree=2, probability=True) # Cはペナルティ
svc.fit(X_train, ys_train)

xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_pred = svc.predict(X_test)

support_vectors = svc.support_vectors_

# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, svc.decision_function(X_test).reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])


# マージンと決定境界を可視化
plt.quiver(0, 0, 0.1, 0.35, width=0.01, scale=1, color='pink')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/8578289d-af6f-0578-abe5-025848c348e0.png)


#### ソフトマージンSVM

```python
x0 = np.random.normal(size=50).reshape(-1, 2) - 1.
x1 = np.random.normal(size=50).reshape(-1, 2) + 1.
x_train = np.concatenate([x0, x1])
y_train = np.concatenate([np.zeros(25), np.ones(25)]).astype(np.int)

plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/f8bbc2d8-f084-dd26-8aad-770ea58dc014.png)


```python
X_train = x_train
t = np.where(y_train == 1.0, 1.0, -1.0)

n_samples = len(X_train)
# 線形カーネル
K = X_train.dot(X_train.T)

C = 1
eta1 = 0.01
eta2 = 0.001
n_iter = 1000

H = np.outer(t, t) * K

a = np.ones(n_samples)
for _ in range(n_iter):
    grad = 1 - H.dot(a)
    a += eta1 * grad
    a -= eta2 * a.dot(t) * t
    # ここが変わる
    a = np.clip(a, 0, C)

index = a > 1e-8
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]

term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
```

```python
xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
    for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
        y_project[i] += a * sv_t * sv.dot(X_test[i])
y_pred = np.sign(y_project)

# 訓練データを可視化
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/7c6b87b8-c44a-dcaa-06a5-060ae7dbc6f6.png)


```python
svc = SVC(C=1, kernel='linear', degree=2, probability=True) # Cはペナルティ
svc.fit(X_train, ys_train)

xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_pred = svc.predict(X_test)

support_vectors = svc.support_vectors_

# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, svc.decision_function(X_test).reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/bb59ef15-61a7-a78d-4666-495c81d6e951.png)

#### カーネルSVM

```python
factor = .2
n_samples = 50
linspace = np.linspace(0, 2 * np.pi, n_samples // 2 + 1)[:-1]
outer_circ_x = np.cos(linspace)
outer_circ_y = np.sin(linspace)
inner_circ_x = outer_circ_x * factor
inner_circ_y = outer_circ_y * factor

X = np.vstack((np.append(outer_circ_x, inner_circ_x),
               np.append(outer_circ_y, inner_circ_y))).T
y = np.hstack([np.zeros(n_samples // 2, dtype=np.intp),
               np.ones(n_samples // 2, dtype=np.intp)])
X += np.random.normal(scale=0.15, size=X.shape)
x_train = X
y_train = y

plt.scatter(x_train[:,0], x_train[:,1], c=y_train)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/dff0b6ac-9364-4bfa-e586-a6cf4aa65044.png)


```python
def rbf(u, v):
        sigma = 0.8
        return np.exp(-0.5 * ((u - v)**2).sum() / sigma**2)
    
X_train = x_train
t = np.where(y_train == 1.0, 1.0, -1.0)

n_samples = len(X_train)
# RBFカーネル
K = np.zeros((n_samples, n_samples))
for i in range(n_samples):
    for j in range(n_samples):
        K[i, j] = rbf(X_train[i], X_train[j])

eta1 = 0.01
eta2 = 0.001
n_iter = 5000

H = np.outer(t, t) * K

a = np.ones(n_samples)
for _ in range(n_iter):
    grad = 1 - H.dot(a)
    a += eta1 * grad
    a -= eta2 * a.dot(t) * t
    a = np.where(a > 0, a, 0)

index = a > 1e-6
support_vectors = X_train[index]
support_vector_t = t[index]
support_vector_a = a[index]

term2 = K[index][:, index].dot(support_vector_a * support_vector_t)
b = (support_vector_t - term2).mean()
```

```python
xx0, xx1 = np.meshgrid(np.linspace(-1.5, 1.5, 100), np.linspace(-1.5, 1.5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_project = np.ones(len(X_test)) * b
for i in range(len(X_test)):
    for a, sv_t, sv in zip(support_vector_a, support_vector_t, support_vectors):
        y_project[i] += a * sv_t * rbf(X_test[i], sv)
y_pred = np.sign(y_project)

# 訓練データを可視化
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, y_project.reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/357b0096-e132-d951-80c4-61ec1db5aeae.png)


```python
svc = SVC(C=1, kernel='rbf', degree=2, probability=True) # Cはペナルティ
svc.fit(X_train, ys_train)

xx0, xx1 = np.meshgrid(np.linspace(-1.5, 1.5, 100), np.linspace(-1.5, 1.5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T

X_test = xx
y_pred = svc.predict(X_test)

support_vectors = svc.support_vectors_

# 訓練データを可視化
plt.scatter(X_train[:, 0], X_train[:, 1], c=ys_train)
# サポートベクトルを可視化
plt.scatter(support_vectors[:, 0], support_vectors[:, 1],
                    s=100, facecolors='none', edgecolors='k')
# 領域を可視化
#plt.contourf(xx0, xx1, y_pred.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
# マージンと決定境界を可視化
plt.contour(xx0, xx1, svc.decision_function(X_test).reshape(100, 100), colors='k',
                     levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/cfc08fb7-fe08-36be-69c8-53b039cddd7e.png)