1.はじめに
深層学習の勉強を始めました。今回は、正則化について、簡単にまとめてみます。
2.データの作成
方程式 $y=-x^3+x^2+x$ を元に、x は -10〜10を50分割した値、y は方程式にそのxを代入した結果に 0〜0.05の乱数を加えた値としてデータを作成します。
from sklearn.preprocessing import PolynomialFeatures
from sklearn import linear_model
import numpy as np
import matplotlib.pyplot as plt
# データの生成
np.random.seed(0)
X = np.linspace(-10, 10, 50)
Y_truth = 0.001 * (-X **3 + X**2 + X)
Y = Y_truth + np.random.normal(0, 0.05, len(X))
plt.figure(figsize=(5, 5))
plt.plot(X, Y_truth, color='gray')
plt.plot(X, Y, '.', color='k')
plt.show()
作成したデータです。実線が真の値(方程式の値)、点が実際に観測する値(yにノイズを加えた値)という想定です。
3.多項式回帰の導入
過学習は自由度が高いほど起こりやすいので、あえて30次元の多項式回帰を導入します。
# グラフ表示
def graph(Y_lr, name):
plt.figure(figsize=(6, 6))
plt.plot(X, Y_truth, color='gray', label='truth')
plt.plot(xs, Y_lr, color='r', markersize=2, label=name)
plt.plot(X, Y, '.', color='k')
plt.legend()
plt.ylim(-1, 1)
plt.show()
# 表示設定
xs = np.linspace(-10, 10, 200)
# 多項式回帰の導入
poly = PolynomialFeatures(degree=30, include_bias=False)
X_poly = poly.fit_transform(X[:, np.newaxis])
グラフ表示や表示の設定をした後、PolynomialFeaturesをインスタンス化し、フィットしています。次元は30次元(degree=30)です。
4.正則化なし
まず、正則化なしで多項式回帰を行います。
# 正則化なし
lr0 = linear_model.LinearRegression(normalize=True)
lr0.fit(X_poly, Y)
Y_lr0 = lr0.predict(poly.fit_transform(xs[:, np.newaxis]))
graph(Y_lr0, 'No Regularization')
30次元の多項式と自由度が高いので、数多くの点を器用に通過することが出来てしまい、典型的な過学習に陥っています。真の値とは掛け離れてしまって、これでは汎化性能は望めません。
5.L2正則化
L2正則化とは、Ridge(リッジ)回帰で知られた手法で、係数が大きくなり過ぎない様に制限を加えるもので、パラメータのL2ノルムを損失に加えます(Cは定数)。
$L(W)+c|w|^2$
# L2正則化
lr2 = linear_model.Ridge(normalize=True, alpha=0.5)
lr2.fit(X_poly, Y)
Y_lr2 = lr2.predict(poly.fit_transform(xs[:, np.newaxis]))
graph(Y_lr2, 'L2')
6.L1正則化
L1正則化とは、Lasso(ラッソ)回帰で知られた手法で、同様に係数が大きくなり過ぎない様に制限を加えるもので、パラメータのL1ノルムを損失に加えます(Cは定数)。
$L(W)+c|w|$
# L1正則化
lr1 = linear_model.LassoLars(normalize=True, alpha=0.001)
lr1.fit(X_poly, Y)
Y_lr1 = lr1.predict(poly.fit_transform(xs[:, np.newaxis]))
graph(Y_lr1, 'L1')
ジャストフィットにかなり近い形です。L1正則化と比較すると、L2正則化の方がより上手く回帰出来ている様です。
7.次元係数の比較
正則化なし、L2正則化、L1正則化それぞれ30個の次元係数を比較します(次元が低い方から並べています)。
import pandas as pd
result = []
for i in range(len(lr0.coef_)):
tmp = lr0.coef_[i], lr2.coef_[i], lr1.coef_[i]
result.append(tmp)
df = pd.DataFrame(result)
df.columns = ['No Regularization', 'L2', 'L1']
print(df)
L2はNo Regularizationと比べて係数が小さくなっているのが分かると思います。L1はさらに、完全に0のところが多い表現(スパースな表現)になっています。
L1正則化は、過学習抑制+次元削減も出来るのは、嬉しいですね。