半歩ずつ進める機械学習　～scikit-learn 主な線形回帰クラス～

##前回まで
ここまでscikit-learnの`LinearRegression`で作ったモデルに対して
交差検証を行う為に、検証に関わる諸々を学んできました（分割方法とか評価指標）
ただ、当の回帰モデルに関しては`LinearRegression`のみだったの
今回はscikit-learnに実装されている、その他のメジャーな回帰モデルを調べて
`LinearRegression`との違いを調べてみたいと思います。

##scikit-learnチートシート
見たことある人も多いかと思いますが、以下の様なチートシートがあります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/316551/8027b001-52ba-b587-5754-887497a664dd.png)

この中で、回帰計算は以下の部分です

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316551/c9a85b7e-e8d7-711c-ebf2-733124ef5ca0.png)

+ **SGD Regressor**
+ **Lasso**
+ **ElasticNet**
+ **RidgeRegression**
+ **SVR**
+ **EnsembleRegressor**

今まで使っていた`LinearRegression`がありません
scikit-learnには上記以外にも`LinearRegression`含め回帰計算クラスが沢山ありますが
良く使いそうな物だけ、まず勉強したいと思います

SGD/Lasso/Elastic/RidgeとSVRとEnsembleはそれぞれジャンルがちょっと違う感じなんで
今回はまず上の4個を学びます。

##数式の定義
今まで同様に、ここからは式の中の記号を以下のように定義します

```math
x = 説明変数\\
y = 教師データ（正解値)\\
\hat{y} = 予測値\\
θ = 回帰係数\\
n = サンプル数\\
```

##[SGD Regressor][1]
確率的勾配降下法を用いた回帰計算です
通常の勾配降下法だとパラメータの更新を行う時に
以下の様にトレーニングデータのサイズに比例した計算量が必要になります
※評価関数が二乗誤差の場合

```math
\sum_{i=1}^{n}(\hat{y_i} - y_i)x_i
```

確率的勾配降下法の場合は、トレーニングデータからランダムで選んだデータを使って
パラメータ更新の計算を行うので、データが膨大な場合に計算量が少なく済む場合があります
なので100,000以上のデータの場合　->　SGD Regressorとチートシートにあるんだと思います。

また、確率的勾配降下法の特徴として、**局所解**に陥りにくいというのもあります。
局所解は、最小化したい評価関数の出力に対して複雑なカーブを持つ説明変数がある時
その説明変数に付く回帰係数の値が最適でない点で収束してしまう現象だと理解しています。
分かり難い説明ですが、要約すると最適でないモデルに落ち着く現象です。

##[Ridge][2]
以下の様に通常の評価関数に、L2ノルムという値を加えてモデルを作ります。

```math
\sum_{i=1}^{n}(\hat{y_i} - y_i)x_i + \alpha\frac{1}{2}\sum{θ_k^2}
```

この後のLassoとElasticNetでも、基本的に考え方は同じで
後半部分の

```math
\alpha\frac{1}{2}\sum{θ_k^2}
```

これが変わるだけです。
※L2ノルムの定義はネット検索する限り以下の物も見受けられます

```math
\alpha\sqrt{\sum{θ_k^2}}
```

どちらが正しいのか、または複数の定義があるのかは分かりませんが、scikit-learnでの実装は前者の様になっている模様です。

通常の線形回帰は、モデルの予測値と正解値の誤差が最も小さくなる回帰係数を求めますが
プラス回帰係数も加える事で、回帰係数にも最小化しようという力が働きます
ルートの前のアルファは自分で決める必要があります
alpha値と呼ばれてるみたいで、これがを大きくすれば回帰係数を小さくしようという力がより強く働きます。

![sphx_glr_plot_ridge_path_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316551/c5758a8d-d6d3-f472-df19-9d0827948ca0.png)

Ridge回帰のalpha値と回帰係数のグラフです
alpha値を強くすればするほど（左に行く程）、各回帰係数の値が小さくなって行ってるのがわかります。

##[Lasso][3]
Ridgeの時と似ています、後半の式が少し違うだけです

```math
\sum_{i=1}^{n}(\hat{y_i} - y_i)x_i + \alpha\sum{|θ_k|}
```

![sphx_glr_plot_lasso_coordinate_descent_path_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316551/0a166c88-36f3-c238-9dd8-20891ace1217.png)
Ridgeと比較すると急激な変化に見えます

##[ElasticNet][4]
RidgeとLassoの間を取った用な形です

```math
\beta = L1 ratio\\
\sum_{i=1}^{n}(\hat{y_i} - y_i)x_i + \sum{(\alpha\beta{|θ_k|} + (1-\beta)\alpha\frac{1}{2}θ_k^2})
```

少しややこしいですが、RidgeとLasso同様に、係数圧縮の強さを決めるalpha値に加えて
L1とL2の比率を変化させるL1 ratioというパラメータが必要になります。
1にすれば完全なLassoに、0なら完全なRidgeと同じになる事になります。

![sphx_glr_plot_lasso_coordinate_descent_path_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316551/d422ad95-ece6-b885-a3e0-a1da1ff1391c.png)

Lassoよりは緩やかで、しかしRidgeよりハッキリと0に向かっているように見えます。

##Ridge/Lasso/ElasticNetの違い
まずは試しにRidge/Lasso/ElasticNetでボストン住宅価格データセットを使い回帰係数を求めてみます。
alpha・L1ratioは全てデフォルトのままです。

```python:
#Ridge
[-0.91215884  1.0658758   0.11942614  0.68558782 -2.03231631  2.67922108
  0.01477326 -3.0777265   2.58814315 -2.00973173 -2.05390717  0.85614763
 -3.73565106]
#Lasso
[-0.          0.         -0.          0.         -0.          2.71335334
 -0.         -0.         -0.         -0.         -1.3435484   0.18095537
 -3.54338288]
#ElasticNet
[-0.33381113  0.07901376 -0.26256326  0.40280224 -0.2408203   2.3608474
 -0.         -0.         -0.         -0.31392604 -1.26823446  0.4647664
 -2.33807266]

```
以上の様な結果が得られました
Lassoの回帰係数が殆どゼロになっていますね

おおまかに以下のような特徴があるようです
**Ridge**
全ての回帰係数を残しつつ圧縮してくれる

**Lasso**
回帰係数をゼロにする力が強い
相関の強い説明変数があった場合に、どちらかがいきなりゼロになってしまう弱点がある。

**ElasticNet**
相関の強い説明変数があっても、どちらの回帰係数もいきなりゼロになったりしない

scikit-learnのチートシートにもあるように、用途別で考えると

**few features should be important**
**一部の説明変数のみが重要?**

**→YES**（重要な説明変数は少ない）
LassoやElasticNetを使ってムダな説明変数を除外したモデルを作る
説明変数の数が減るので、**EDA**と呼ばれる説明変数のデータを読み込んで、相関や法則を探したりする行為がラクになる。
自分はまだ挑戦出来てませんがKaggle等のコンペでは、このEDAが重要なポイントでもあるようで
何十何百という説明変数のデータを見たり出来ないので、Lasso等を使って変数の絞り込みを行うのは有効だと考えられます。

**→NO**(説明変数は全て重要or重要性が分からない)
Ridge回帰を使って過学習が発生しないようにモデルを作る。

といった使い方になるのかなと思います。

#**Ridge/Lasso/ElasticNet使用時の注意点**
上記3種は正則化付き回帰で、回帰係数を圧縮する効果があります。
この時、事前に**説明変数の標準化**を行ってからモデルを作らないと
回帰係数の大きさにバラつきが出て、重要な回帰係数が過剰に圧縮される可能性があります。

##次回へ
次回はSVRについて纏めたいと思います。

##お願い
機械学習の初心者が、学んだ知識の確認と備忘用に投稿しています。
間違っている部分や、何かお気づきの事がありましたらご指摘頂けますと幸いです
あと、独学者なので「私も今勉強中なんです！」みたいな人がいればコメント貰えると喜びます。



[1]:https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDRegressor.html
[2]:https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html
[3]:https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Lasso.html
[4]:https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.ElasticNet.html