ギークなエンジニアを目指す男

機械学習系の知識を蓄えようとするブログ

線形回帰とリッジ回帰をPythonで比べてみた

本日は回帰で頻出の線形回帰とリッジ回帰についてです。

線形回帰とは

訓練データにおいて、予測と真の回帰ターゲットyとの平均二乗誤差が最小になるように、パラメータwとバイアスbを求めるアルゴリズムです。
単回帰分析や重回帰分析などが該当します。

import mglearn
from sklearn.linear_model import LinearRegression # 線形回帰
%matplotlib inline

X, y = mglearn.datasets.load_extended_boston()
X.shape
# -> (506, 104)

# モデルの定義
model = LinearRegression(fit_intercept=False)
# 学習
model.fit(X_train, y_train)

print('Training ser score: {:.2f}'.format(model.score(X_train, y_train)))
print('Test set score: {:.2f}'.format(model.score(X_test, y_test)))

# -> Training ser score: 0.95
# -> Test set score: 0.61

リッジ回帰

直線回帰に正則化項の概念を加えた回帰分析です。
最小二乗法の式に正則化項(L2ノルム)を加え、その最小を求めることでモデル関数を発見します。
係数の絶対値の大きさを可能な限り小さくするのがモチベーション(w ≒ 0)であり、線形回帰に比べ、稼業適合(オーバーフィッティング)の危険が少ないのが特徴と言えます。

from sklearn.linear_model import Ridge
model = Ridge()
model.fit(X_train, y_train)

print('Training ser score: {:.2f}'.format(model.score(X_train, y_train)))
print('Test set score: {:.2f}'.format(model.score(X_test, y_test)))
# -> Training ser score: 0.89
# -> Test set score: 0.75

L1正規化とか、L2正規化ってなんだよ

下記のようなものらしい

機械学習で一般的に使用されるのは、L1正則化とL2正則化です。L1正則化、L2正則化というのは、それぞれ上述のペナルティとして、学習モデルのパラメータのL1ノルム、L2ノルムを用いるものです。別の言い方をすると、L1正則化は、ペナルティとして学習モデルのパラメータの絶対値の総和を用いるものであり、L2正則化は、ペナルティとして学習モデルのパラメータの二乗の総和を用いるもので、それぞれ下記の特徴をもちます。
・L1正則化 特定のデータの重みを0にする事で、不要なデータを削除する
・L2正則化 データの大きさに応じて0に近づけて、滑らかなモデルとする
つまり、L1正則化もL2正則化も、モデルの次元が低い方がペナルティが小さくなる正則化のモデルです。従って、学習データの中に例外的なデータが含まれていた場合に、その例外的なデータに対応するために学習モデルの次元をいくつも増やす必要がある場合には、その例外データには対応しないようなモデルが選択されます。
L2正則化は微分可能なモデルであり解析的な解が存在しますが、L1正則化は解析的には解けないために数値計算的な手法により求めることになります。いずれにしても、さまざまなライブラリ・ソフトウェア・サービスにより実装は提供されていますので、データの内容を検討して適切な正則化を設定することにより、機械学習をより有効に行う事が出来ます。

このスライドも分かりやすいかと。 (24P〜)

www.slideshare.net

スコアを比較してみる

上記2つのアルゴリズムのスコアを見てみると、線形回帰よりもリッジ回帰の方が、モデルに対するスコアは低いが、テストデータに対するスコアは向上していることがわかると思います。
これはリッジ回帰に汎化性能があることを示していると言えそうですね。(未来のデータに対してのスコアが高いため)

リッジ回帰のalpha値をいろいろ変更してみる

リッジ回帰では、モデルの簡潔さと、訓練セットに対する性能がトレードオフの関係になります。
このどちらに重きを置くかはalphaパラメータによって指定することができます。
上記の例ではデフォルトのパラメータの値(省略時は1)を用いていましたが、この値を変化させると、スコアにどのような影響がでるのか、確認してみます。

alpha = 10のとき

model10 = Ridge(alpha=10)
model10.fit(X_train, y_train)
print('Training ser score: {:.2f}'.format(model10.score(X_train, y_train)))
print('Test set score: {:.2f}'.format(model10.score(X_test, y_test)))
# -> Training ser score: 0.79
# -> Test set score: 0.64

alpha = 0.1のとき

model01 = Ridge(alpha=0.1)
model01.fit(X_train, y_train)
print('Training ser score: {:.2f}'.format(model01.score(X_train, y_train)))
print('Test set score: {:.2f}'.format(model01.score(X_test, y_test)))
# -> Training ser score: 0.93
# -> Test set score: 0.77

今回のケースでは、alpha = 0.1がうまく行えているようにみえますね。

線形回帰とリッジ回帰の係数の大きさをプロットしてみる

plt.plot(model.coef_, 's', label = 'Ridge alpha=1')
plt.plot(model10.coef_, '^', label = 'Ridge alpha=10')
plt.plot(model01.coef_, 'v', label = 'Ridge alpha=01')
plt.plot(lr.coef_, 'o', label = 'LinearRegression')

plt.xlabel('Coefficient index')
plt.ylabel('Coefficient magnitude')

plt.hlines(0, 0, len(lr.coef_))
plt.ylim(-25, 25)
plt.legend()

f:id:taxa_program:20180701193547p:plain x軸はcoef_の要素を表している。x=0は最初の特徴量に対する係数、x=1は2番目の特徴に対する係数、というようにx=104まで続いている。
この図を見てわかるように、alpha=10では、ほとんどの係数が-3から3の範囲に収まっている。
alpha=1では、もう少し広い範囲になっている。alpha=0.1ではさらに広い範囲になっている。そして、正規化していない線形回帰の場合、さらに広い範囲になっていることがわかる。

本日学んだことはここまでです。