リッジとラッソをざっくりまとめてみた

# はじめに
現在私は、学部4年生で卒業研究の真っ最中です。
そんな中、リッジとラッソについて、上手くゼミで説明できませんでした。（反省………汗汗）
もう自分なりに再構成してまとめてやろうと思い、今回記事を書く決意をしました。

完全に自分用なのでご容赦ください。

# リッジとラッソの目的
まずは「どのような目的でリッジやラッソを行うのか」というところです。
ここをはっきりさせます。

キーワードとしては「正則化」です。
正則化とは、過学習（過剰適合）を防ぐことです。
つまり、リッジとラッソを行うことで、過学習（過剰適合）を防ぐことができます。

では、「過学習」とは何でしょうか。
過学習（過剰適合）とは言い換えると「過度に複雑なモデル」の状態のことです。
例えば、以下の関数同士を比べると複雑さは以下の通りです。

- 1次関数（直線）←単純
- 2次関数（放物線）←複雑

そして言い換えると、過学習（過剰適合）を防ぐことは「モデルの表現を制約」することと同義になります。

まとめると、リッジやラッソを行うことで「モデルの表現を制約」できます。

次に、よく見られる具体例として、線形モデルでのリッジ回帰とラッソ回帰を見ていきます。

# リッジ回帰とラッソ回帰

### ①線形モデル
教師あり学習ですので、入力ベクトル$\boldsymbol{x} \in \mathbb{R}^d$と正解値$y \in \mathbb{R}$のペアのデータが観測された場合を考えます。
今回は例ですので、5次元$(d=5)$のベクトル$\boldsymbol{x}=(x_1, x_2, x_3, x_4, x_5)$としましょう。
つまり、今回の観測されたデータセット集合$S$は以下になります。
$$
    S=\\{(x_1, y_1), (x_2, y_2), (x_3, y_3), (x_4, y_4), (x_5, y_5)\\}
$$
このとき、予測値$\hat{y}$を以下のように表現するのが線形モデルです。
$$
    \hat{y} = a\boldsymbol{x}+b
$$
※回帰係数：①傾き$a$②切片$b$
回帰係数を定めることで予測値が決まります。
つまり、回帰係数を求めていくわけですが、多くの場合で「パラメータ」と表現しています。
したがって、回帰係数をパラメータ$θ$を用いた表現に変えます。
$$
    \hat{y} = θ_1\boldsymbol{x}+θ_0
$$
パラメータを求めることが目的です。

### ②線形回帰（最小二乗法）
パラメータを求めていくわけですが、判断基準として正解値$y$と予測値$\hat{y}$の誤差を最小にするパラメータを求めるのが最小二乗法です。

#### 具体的な数値での最小二乗法
線形モデルでの設定を具体的な数値にします。
データセット集合$S$を以下のように定めます。
$$
    S=\\{(1, 2), (2, 2), (3, 4), (4, 4), (5, 5)\\}
$$
$xy$平面にプロットとしてみてください。
誤差が最小になる直線はどのような直線でしょうか。

```python:sample1.py
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
#OLSモジュール
import statsmodels.api as sm
S = np.array([[1,2],[2,2],[3,4],[4,4],[5,5]])#値を変更させてみましょう
print("データセット集合S: {}".format(S.shape))
X = S[:,0]#変数Xに格納
print("入力データ: {}".format(X))
y = S[:,1]#変数yに格納
print("出力データ(正解値): {}".format(y))
XC = sm.add_constant(X)#切片
#正解値のプロット
plt.scatter(S[:,0], S[:,1])#データセット集合
plt.xlabel("input")
plt.ylabel("output")

#線形回帰（最小二乗法）
lr = sm.OLS(y, XC).fit()#フィッティング
print("パラメータ(切片,　傾き): {}".format(lr.params))#パラメータ推定

n=100
tx = np.linspace(X.min(), X.max(), n)#xの予測点生成
txc = sm.add_constant(tx)#切片
y_lr_pred = lr.predict(txc)#予測
plt.plot(tx, y_lr_pred, "r--")#プロット
plt.show()
```
![qiita_sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637147/0a7b68c1-bcf5-dd10-7210-4b8e335e90ab.png)



上記のプログラム(Python)を実行した場合、パラメータは以下のように求められます。

- $θ_0$(切片)：1.0　　$θ_1$(傾き)：0.8

$$
    \hat{y}=0.8x_i+1.0　（i=1,2,3,4,5）
$$

例えば$x_1=1$を入力した場合、正解値$y_1=2$に対して予測値$\hat{y}=1.8$となります。
この誤差はどのくらい許容できるでしょうか。
これはモデルの評価です。
今回は詳細を省きますが、非常に重要です。

1次式の場合で、リッジ回帰とラッソ回帰を見ていきます。

## リッジ回帰（本題）
さあいよいよ本題です。
1次式（単純）では、パラメータが求まるとモデルが定まります。
これは、2次式（複雑）でも同様です。

つまり、パラメータに制約を加えることで、モデルを制約できるわけです。
具体的な制約の仕方は、正則化項を追加します。

正則化項を追加するとモデルが単純になるためです。
単純になる理由を述べます。
2次式（複雑）
$$
    θ_2x_i^2+θ_1x_i+θ_0
$$

1次式（単純）
$$
    θ_1x_i+θ_0
$$

⚠モデルを単純にするということは、制約をつけてパラメータを0に近づけているということです。
例えば、極端な話ですが、2次式（複雑）で$θ_2=0$となった場合、モデルは1次式（単純）になります。
正則化項を加えることで、「大きなパラメータの値が取りにくくなる」ということです。
また、通常は切片$θ_0$を正則化項に含めません。

1次式の場合を考えます。
※多項式の場合は$Σ$を用いて表現します。

正則化項（リッジ）
$$
    α θ_{1}^2
$$

- リッジは傾きの絶対値を縮小します。

ここで、$α$は正則化パラメータであり、$α$を大きくすると、モデルの表現力を抑えることができます。

```python:sample2.py
#リッジ0.01
alpha=0.01; rp=np.r_[0, np.repeat(alpha/n, 1)]
ri001 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=0)
print("正則化パラメータ0.01 パラメータ(切片,　傾き): {}".format(ri001.params))#パラメータ推定
y_ri001_pred = ri001.predict(txc)#予測
plt.plot(tx, y_ri001_pred, "g--")#プロット
#リッジ10
alpha=10; rp=np.r_[0, np.repeat(alpha/n, 1)]
ri10 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=0)
print("正則化パラメータ10 パラメータ(切片,　傾き): {}".format(ri10.params))#パラメータ推定
y_ri10_pred = ri10.predict(txc)#予測
plt.plot(tx, y_ri10_pred, "b--")#プロット
#リッジ100
alpha=100; rp=np.r_[0, np.repeat(alpha/n, 1)]
ri100 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=0)
print("正則化パラメータ100 パラメータ(切片,　傾き): {}".format(ri100.params))#パラメータ推定
y_ri100_pred = ri100.predict(txc)#予測
plt.plot(tx, y_ri100_pred, "b--")#プロット
#リッジ10000
alpha=10000; rp=np.r_[0, np.repeat(alpha/n, 1)]
ri10000 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=0)
print("正則化パラメータ10000 パラメータ(切片,　傾き): {}".format(ri10000.params))#パラメータ推定
y_ri10000_pred = ri10000.predict(txc)#予測
plt.plot(tx, y_ri10000_pred, "y--")#プロット
plt.show()
```
![qiita_sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637147/8d3c2541-4767-50a0-3cc2-12479b00a8d2.png)


$α$が大きくなるほど、傾きが0に近づいていることがわかります。
傾きの絶対値を縮小しています。

## ラッソ回帰（本題）

1次式の場合を考えます。
※多項式の場合は$Σ$を用いて表現します。

正則化項（ラッソ）
$$
    α |θ_{1}|
$$

- ラッソは傾きを完全に0にします。

```python:sample3.py
#ラッソ0.01
alpha=0.01; rp=np.r_[0, np.repeat(alpha/n, 1)]
la001 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=1)
print("正則化パラメータ0.01 パラメータ(切片,　傾き): {}".format(la001.params))#パラメータ推定
y_la001_pred = la001.predict(txc)#予測
plt.plot(tx, y_la001_pred, "g--")#プロット
#ラッソ10
alpha=10; rp=np.r_[0, np.repeat(alpha/n, 1)]
la10 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=1)
print("正則化パラメータ10 パラメータ(切片,　傾き): {}".format(la10.params))#パラメータ推定
y_la10_pred = la10.predict(txc)#予測
plt.plot(tx, y_la10_pred, "b--")#プロット
#ラッソ100
alpha=100; rp=np.r_[0, np.repeat(alpha/n, 1)]
la100 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=1)
print("正則化パラメータ100 パラメータ(切片,　傾き): {}".format(la100.params))#パラメータ推定
y_la100_pred = la100.predict(txc)#予測
plt.plot(tx, y_la100_pred, "b--")#プロット
#ラッソ10000
alpha=10000; rp=np.r_[0, np.repeat(alpha/n, 1)]
la10000 = sm.OLS(y, XC).fit_regularized(alpha=rp, L1_wt=1)
print("正則化パラメータ10000 パラメータ(切片,　傾き): {}".format(la10000.params))#パラメータ推定
y_la10000_pred = la10000.predict(txc)#予測
plt.plot(tx, y_la10000_pred, "y--")#プロット
plt.show()
```
![qiita_sample3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637147/0eda76d1-7a83-fc69-5b63-ba34ef24b89e.png)


$α=10000$の場合、傾きが完全に0になっていることがわかります。
傾きを完全に0にしています。

# おわりに
今回は、リッジとラッソについてまとめてみました。
理解のために、やや強引な表現もあったと思いますが、他の文献と読み比べて修正していただけると幸いです。
また、内容の間違いや誤字等ありましたら、Twitter(@gobugobu__rin)のDMまで、教えていただけると幸いです。