はじめに
現在私は、学部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$平面にプロットとしてみてください。
誤差が最小になる直線はどのような直線でしょうか。
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()
上記のプログラム(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
$$
- リッジは傾きの絶対値を縮小します。
ここで、$α$は正則化パラメータであり、$α$を大きくすると、モデルの表現力を抑えることができます。
# リッジ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()
$α$が大きくなるほど、傾きが0に近づいていることがわかります。
傾きの絶対値を縮小しています。
ラッソ回帰(本題)
1次式の場合を考えます。
※多項式の場合は$Σ$を用いて表現します。
正則化項(ラッソ)
$$
α |θ_{1}|
$$
- ラッソは傾きを完全に0にします。
# ラッソ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()
$α=10000$の場合、傾きが完全に0になっていることがわかります。
傾きを完全に0にしています。
おわりに
今回は、リッジとラッソについてまとめてみました。
理解のために、やや強引な表現もあったと思いますが、他の文献と読み比べて修正していただけると幸いです。
また、内容の間違いや誤字等ありましたら、Twitter(@gobugobu__rin)のDMまで、教えていただけると幸いです。