nanoseeingの技術系ブログ

機械学習・競プロなど。アウトプットが目的。

リッジ回帰(L2ノルム)による正則化【機械学習アウトプット第3回】

正則化とは

前提として、重回帰分析がある。
重回帰分析については第2回記事を参照。

nanoseeing.hatenablog.com

重回帰分析における二乗和誤差による損失関数は、 下記で表せる。

 L=(\boldsymbol{y}-\boldsymbol{Xw})^{T}(\boldsymbol{y}-\boldsymbol{Xw})

この損失関数に、ある罰則項を与え、求めるべきパラメーター\boldsymbol{w}の値を小さくすることが、 正則化の目的である。

正則化を行うことで、モデルの複雑化を防ぎ、過学習を抑えることが期待される。

リッジ回帰(L2ノルム)とは

そもそもL_{p}ノルムとは、下記のこと。

‖\boldsymbol{x}‖_{p} = \sqrt[p]{\sum_{i=1}^{n} {x_{i}}^{p}}

p=2のときL2ノルムと呼ばれ、これはいわゆるユークリッド距離のことである。

このL2ノルムを罰則項として付与した損失関数、

 L=(\boldsymbol{y}-\boldsymbol{Xw})^{T}(\boldsymbol{y}-\boldsymbol{Xw}) + \alpha‖\boldsymbol{w}‖_{2}

を考える。 ここで、\alpha正則化の強さをコントロールするパラメータで、任意に決定してよい。

損失関数を偏微分してイコール0となる方程式を解くと、

\boldsymbol{w}=(\boldsymbol{X^{T}}\boldsymbol{X}+\alpha\boldsymbol{I})^{-1}\boldsymbol{X^{T}}\boldsymbol{y}

が求まる。

データ作成

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline

np.random.seed(seed=32)
plot_num = 50

x1 = np.random.rand(plot_num) * 10 - 5 
x2 = np.random.rand(plot_num) * 10 - 5 
y = 3 * x1 - 2 * x2 + 7
y += 5 * np.random.randn(plot_num)

fig = plt.figure(figsize=(6, 6))
ax = Axes3D(fig)
ax.plot(x1, x2, y, marker="o", linestyle='None')
plt.show()

f:id:nanoseeing:20210202165048p:plain

データは記事第2回の重回帰分析のときから変えていない。 切片7、回帰係数[3, -2]が予想される

自作実装

x0 = np.ones(plot_num).reshape(plot_num,1)
x1 = x1.reshape(plot_num,1)
x2 = x2.reshape(plot_num,1)
y = y.reshape(plot_num,1)

alpha = 5

X = np.concatenate([x0, x1, x2], 1)
I = np.eye(X.shape[1])
W = np.linalg.inv(X.T @ X + alpha * I) @ X.T @ y
print(W[1:])
print(W[0])
>>
[[ 2.8152892 ]
 [-2.36886758]]
[6.5056526]

sklearnで実装

from sklearn.linear_model import Ridge

X = np.concatenate([x1, x2], 1)
clf = Ridge(alpha=5)
clf.fit(X, y)
print(clf.coef_)
print(clf.intercept_)
>>
[[ 2.79545591 -2.3170463 ]]
[7.18487782]

自作実装と違う値が求まってしまった。 何が間違っているのかわからないが、理論はあっているはず。

参考サイト