線形回帰　用語集

# はじめに

オライリージャパン様の***scikit-learn, Keras,TensorFlowによる実践機械学習***の４章、モデルの訓練について学んだことをメモするための記事になります。

# 線形回帰
## 正規方程式

$$\theta = (X^TX)^{-1}X^Ty$$
で与えられる式のこと。
$$目的変数 = (X^TX\theta)-X^Ty$$
$X\theta$は仮定関数のこと。  
これを目的変数 = 0で解くと正規方程式の形になる。

```python
import numpy as np
import matplotlib.pyplot as plt

X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
plt.scatter(X, y);
```
![Imgur](https://i.imgur.com/gYDz5cZl.png)

```python
# 切片の追加
X_b = np.c_[np.ones((100, 1)), X]
# linalg.inv は逆行列を返す
theta_best = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
display(theta_best)

# 出力----------------
array([[3.9500543 ],
       [2.87523365]])
```

```python
plt.plot(X_new, y_pred, 'r')
plt.scatter(X, y);
```
![Imgur](https://i.imgur.com/yKtAGf7l.png)

最急降下法を使用しなくとも、正規方程式を用いることで近似曲線を求めることができた。
逆行列を求める必要があるが、存在しない場合は疑似逆行列を使用して解くことが可能。
その場合特異値分解を使用する。

## 固有値分解

$$A\vec{v} = \lambda\vec{v}$$

これを成し遂げたい。Aは正方行列、$\lambda$はスカラー値になる  

```math
\begin{pmatrix}
2 & 5 \\\
3 & 4
\end{pmatrix}
*
\begin{pmatrix}
1\\\
1
\end{pmatrix}
=
7
*
\begin{pmatrix}
1 \\\
1
\end{pmatrix}
```

目的はこれを行いたい。
$\lambda, \vec{v}$は一つではないので行列で表す.
$$AV = V\Lambda$$
の式で表す

```math 
\begin{pmatrix}
2 & 5 \\\
3 & 4
\end{pmatrix}
*
\begin{pmatrix}
5 & 1\\\
-3 & 1
\end{pmatrix}
=
\begin{pmatrix}
5 & 1\\\
-3 & 1
\end{pmatrix}
*
\begin{pmatrix}
-1 & 0\\\
0 & 7
\end{pmatrix}
```

その後
$$A = V\Lambda V^{-1}$$
の形に変えたもの

```math 
\begin{pmatrix}
2 & 5 \\\
3 & 4
\end{pmatrix}
=
\begin{pmatrix}
5 & 1\\\
-3 & 1
\end{pmatrix}
*
\begin{pmatrix}
-1 & 0\\\
0 & 7
\end{pmatrix}
*
\begin{pmatrix}
5 & 1\\\
-3 & 1
\end{pmatrix}^{-1}
```
の形に直した物になる。
ベクトルの掛け算等が楽になる。

## 特異値分解
固有値分解は、正方行列限定のものであり逆行列が存在しない直方行列には使用できない。
その場合は特異値分解を使用する。

ものすごく詳しく教えてくれる  
https://www.youtube.com/watch?v=CUtT2Pi3ITQ

正方行列ではない行列（直行行列）に対しても固有値分解を行う手段のこと  
Uを右得意ベクトル、Vを左特異ベクトル、シグマを特異値と呼ぶ  
次元削減に使える、RANKにも
$$A = U\sum V^T$$

## 確率的勾配効果法

```python
n_epochs = 50 # m回のイテレーションを何セット繰り返すか
m = 100 # サンプル数

# 学習スケジュール、繰り返しごとにだんだん小さくなっていく。
t0, t1 = 5, 50
def learning_schedule(t):
    return t0 / (t + t1)
`

theta = np.random.randn(2, 1) # ランダムに初期化

for epoch in range(n_epochs):
    for i in range (m): # 回数はサンプル数回
        random_index = np.random.randint(m) # サンプル数mの中からランダムに一つの値を取り出す
        xi = X_b[random_index : random_index + 1]
        yi = y[random_index : random_index + 1]
        gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(epoch + m + i)
        theta = theta - eta * gradients

display(theta)

# 出力
array([[4.01986671],
       [2.85402954]])


from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, eta0=0.1) # tolは誤差率
sgd_reg.fit(X, y.ravel()) # 一次元化
display(sgd_reg.intercept_, sgd_reg.coef_)

# 出力
(array([3.96178765]), array([2.88206974]))
```

### 注意事項

```python
test = np.arange(12).reshape(6, 2)
# 出力
array([[ 0,  1],
       [ 2,  3],
       [ 4,  5],
       [ 6,  7],
       [ 8,  9],
       [10, 11]])

display(test[1])
display(test[1].shape)
#　　出力
array([2, 3])
(2,)

display(test[1:2])
display(test[1:2].shape)
# 出力
array([[2, 3]])
(1, 2)
```
場合によって使い分ける必要がある。

## 多項式回帰

``` python
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X ** 2 + X + 2 + np.random.randn(m, 1) 
plt.scatter(X, y, c='r')
plt.xlabel('X')
plt.ylabel('y');
```
![Imgur](https://i.imgur.com/GPB9X05l.png)

このグラフの回帰直線は以下の通り

```python
from sklearn.linear_model import LinearRegression
sk_model = LinearRegression()
sk_model.fit(X, y)
plt.plot(X, sk_model.intercept_ + sk_model.coef_ * X)
plt.scatter(X, y, color='r');
```

![Imgur](https://i.imgur.com/NPs14grl.png)

特徴量の2乗を新しい特徴量として説明変数に加える。

```python
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(2, include_bias=False)
X_poly = poly.fit_transform(X)
sk_model_3 = LinearRegression()
sk_model_3.fit(X_poly, y)
plt.plot(np.sort(X, axis=0), sk_model_3.intercept_ + sk_model_3.coef_[:, 0] *np.sort(X, axis=0)+ sk_model_3.coef_[:, 1] * np.sort(X, axis=0) **2)
plt.scatter(X, y, color='r');
```
![Imgur](https://i.imgur.com/CCIswtHl.png)

## 学習曲線
横軸にイテレーション数、縦軸に評価指標をプロットしたもの

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

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
    
def plot_learning_curves(model, X, y):
    train_error, val_error = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_pred = model.predict(X_train[:m])
        y_val_pred = model.predict(X_val)
        train_error.append(mean_squared_error(y_train[:m], y_train_pred[:m]))
        val_error.append(mean_squared_error(y_val, y_val_pred))
    plt.plot(train_error, label='train')
    plt.plot(val_error, label='val')
    plt.xlabel('train set size')
    plt.ylabel('MSE')
    plt.ylim(0, 7)
    plt.legend()
```

### 過小適合
MSEが全体的に高い

```python
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
```

![Imgur](https://i.imgur.com/vJTRjqml.png)

### 過適合

trainにたいして、testがさがっていない
回数が増えると同じ

```python
from sklearn.pipeline import Pipeline

poly = Pipeline([
    ('poly', PolynomialFeatures(degree=10, include_bias=False)),
    ('lin_reg', LinearRegression())
])
plot_learning_curves(poly, X ,y)
```
![Imgur](https://i.imgur.com/QMA8pZil.png)

## Ridge

$$J(\theta)=MSE(\theta)+\alpha \frac{1}{2}\sum_{i=1}^n\theta_i^2$$


$$\theta_i = \theta_0 + \theta_1 +\theta_2 +\theta_3 +...$$
を目的変数とする。  
2乗を取るので、ベクトルは合成ベクトルとなりその距離で表される  
L2ノルムの上を減らす方向に進んでいく。  
合成ベクトル上なので、どちらかのベクトルが0になることはない。
収束値に近づくにつれ、勾配が穏やかになるが完全に収束はしない
![Imgur](https://i.imgur.com/zhudNF6l.jpg)

## Lasso

$$J(\theta)=MSE(\theta)+\alpha \frac{1}{2}\sum_{i=1}^n|\theta_i|$$
を目的変数とする。  
L1ノルム上を減らす方向に進んでいく。
合成ベクトルではないので、両方の重みは均等に減っていき小さい重みは先に0になる。  
![Imgur](https://i.imgur.com/WgFeTkOl.jpg)

## Elastic Net
$$J(\theta)=MSE(\theta)+r\alpha \frac{1}{2}\sum_{i=1}^n|\theta_i| + \frac{1-r}{2}\alpha\sum_{i=1}^n\theta_i^2$$
r=0でRidge、r=1でLasso  
基本的にはRidgeを使うのが一般(データを落としてしまうから)  
ただ、必要のなさそうな特徴量や次元削減を行う場合一般的にはLassoよりかはElastic Net

## 早期打ち切り(early stopping)
過学習を抑えるために、検証データでの損失関数が学習回数と反比例しなくなったタイミングで学習を止めてしまう方法

```python
from sklearn.base import clone
from sklearn.preprocessing import StandardScaler

#データの準備
poly_scaler = Pipeline([
    ('poly', PolynomialFeatures(degree=90, include_bias=False)),
    ('std_scaler', StandardScaler())
])

X_train_poly_scaler = poly_scaler.fit_transform(X_train)
X_val_poly_scaler = poly_scaler.fit_transform(X_val)

from sklearn.linear_model import SGDRegressor

#warm_startは学習経過を保存し、続きから行うもの。そのためiterは1
#learning_rateがconstantの場合、eta=eta0になる
#通常は学習スケジュール(回数を重ねるごとに減る)

sgd_reg = SGDRegressor(max_iter=1, tol=np.infty, warm_start=True, penalty=None, learning_rate='constant', eta0=0.0005)
minimum_val_error = float('inf')
best_epoch = None
best_model = None
for epoch in range(1000):
    sgd_reg.fit(X_train_poly_scaler, y_train.ravel())
    y_val_pred = sgd_reg.predict(X_val_poly_scaler)
    val_error = mean_squared_error(y_val, y_val_pred)
    if val_error < minimum_val_error:
        minimum_val_error = val_error
        best_epoch = epoch
        best_model = clone(sgd_reg)

display(minimum_val_error, best_epoch, best_model)

# 出力
(13.079674530111143,
 0,
 SGDRegressor(eta0=0.0005, learning_rate='constant', max_iter=1, penalty=None,
              tol=inf, warm_start=True))
```

早期で打切るっていうよりかは、一番良かったのを保存するイメージ

# ロジスティック回帰

```python
from sklearn import datasets
iris = datasets.load_iris()
X = iris['data'][:, 3:]
y = (iris['target'] == 2).astype(int)

from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)

X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
plt.plot(X_new, y_proba[:, 1], c='g', label='iris virginica')
plt.plot(X_new, y_proba[:, 0], c='b', label='not iris virginica')
plt.legend();

```
![Imgur](https://i.imgur.com/xNeyMFEl.png)

```python
log_reg.predict([[1.7], [1.5]])
# 大体1.6が判断の境目

# 出力
array([1, 0])
```

## ソフトマックス関数
2値以上の分類で使われる活性化関数  
上のグラフに線を加えていくイメージ、足して1になるのは変わらず

```python
X = iris['data'][:, (2, 3)]
y = iris['target']
softmax_leg = LogisticRegression(multi_class='multinomial', solver='lbfgs', C=10)
softmax_leg.fit(X, y)
display(softmax_leg.predict([[5, 2]]))

# 出力
array([2])

display(softmax_leg.predict_proba([[5, 2]]))

# 出力
array([[6.38014896e-07, 5.74929995e-02, 9.42506362e-01]])
```
94%で2, 5.7%で1を予測している

# 多重共線性
重回帰分析で解析を行う際、説明変数同士に相関関係が見られてしまうこと。  
片方のデータから得られた情報(残差)が少なく、目的変数を予測できなくなる

## VIF（分散拡大要因）
多重共線性があるかどうか判断する指標の一つ。
$$\frac{1}{1-r^2}$$
rは決定係数
相関行列と近いが、相関行列の場合AとBの相関は観れるが、ABとCといった多変量の相関は見れないためこちらが使われることが多い

``` python
X = pd.DataFrame(iris.data, columns=iris.feature_names)
y = iris['target']
display(X)
```

|   |sepal length (cm)|sepal width (cm)|petal length (cm)|petal width (cm)|
|--:|----------------:|---------------:|----------------:|---------------:|
|  0|              5.1|             3.5|              1.4|             0.2|
|  1|              4.9|             3.0|              1.4|             0.2|
|  2|              4.7|             3.2|              1.3|             0.2|
|  3|              4.6|             3.1|              1.5|             0.2|
|  4|              5.0|             3.6|              1.4|             0.2|
|  5|              5.4|             3.9|              1.7|             0.4|

150 rows × 4 columns

```python
from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd
vif = pd.DataFrame()
vif['VIF Factor'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
vif['features'] = X.columns
display(vif)
```
|   |    VIF Factor    |    features     |
|--:|-----------------:|-----------------|
|  0|262.96934824146770|sepal length (cm)|
|  1| 96.35329172369063|sepal width (cm) |
|  2|172.96096155387588|petal length (cm)|
|  3| 55.50205979323787|petal width (cm) |

```python
plt.bar(vif['features'], vif["VIF Factor"], color='red')
plt.ylabel('VIF values', fontsize = 16)
plt.xticks(rotation=45);
```
![Imgur](https://i.imgur.com/IM11tpSl.png)

10以上あると多重共線性が疑われる。
今回の場合かなり、多重共感性の影響はありそう。

#終わりに

自分なりのメモになる。
知っていることが増えたらまた記載する。