Ridge回帰

Kaggleのテーブルデータコンペなどで役に立ちそうなモデルの一つとして、リッジ回帰についてまとめていこうと思います。リッジ回帰の特徴は、独立変数の係数の大きさに制約を設けることです。そうすることで正則化の効果を得ることができます。

Ridge回帰は何をやっているのか?

回帰分析をする際、OLS推定ができるときの条件が存在します。

Ⅰ.回帰モデルは線形で、正しく記述されており、加法的な誤差項をもつ。
Ⅱ.誤差項の母平均は0である。
Ⅲ.説明変数は誤差項と無相関である。
Ⅳ.誤差項の観察値は互いに無相関である。(系列相関なし)
Ⅴ.誤差項の分散は均一である。(不均一分散なし)
Ⅵ.すべての説明変数は、他の一連の説明変数の完全な線形関数ではない。(完全な多重共線性なし)
Ⅶ.誤差項は正規分布に従う。(この仮定は付属的なものだが、通常は用いられる)

計量経済学の使い方上[基礎編]p102

 上記の条件を満たしていないときにOLSを用いてしまうと、信頼度の低い結果しか得られなくなってしまうのです。その場合、OLS以外の推定方法が用いられます。リッジ回帰は上の条件の中の多重共線性の条件が満たされないときに効果を発揮します。
 仮に、多重共線性の問題がクリアされていない場合にOLSを用いたとします。そうすると、推定された係数の分散が大きくなってしまいます。分散が大きくなると係数の取りうる値の範囲が広くなります。これが、「信頼度の低い結果しか得られなくなる」という意味です。
 この問題を克服するために、Ridge回帰では各独立変数の係数の二乗の和が大きくならないように制約を課します。直感的には以下の式がわかりやすいです。

\displaystyle{
\hat{\beta}_{ridge} =
 argmin _{\beta} \{ \sum_{n=1}^{N}(y_i - \beta_0 + \sum_{j=1}^{p}x_{ij}\beta_{ij})^2 + \lambda\sum_{j=1}^{p} \beta_j^{2} \}
}

式の第一項目は通常のOLSで用いられているものです。第二項目は、係数の推定値の二乗和にパラメータλの積です。λの値が大きくなるほど、モデルの係数推定値は大きい値をとりにくくなります。また、係数の二乗和の値の最大値を明示的に提示する場合には、以下のような書き方もできます。

\displaystyle{
\hat{\beta}_{ridge} = argmin_{\beta} ( \sum_{n=1}^{N}(y_i - \beta_0 + \sum_{j=1}^{p}x_{ij}\beta_{ij})^2 \\ subject\ \  to\ \  \sum_{j=1}^{p} \beta_j^{2} \leq t 
}

この場合係数の推定値の二乗和はtの値より大きくなることはありません。

どんな時に役に立つのか?

では係数の二乗和を小さくすることに何のメリットがあるのでしょうか?
その答えとして変数選択が考えられると思います。いま、30個の独立変数を用いて、一つの従属変数を予測するという課題が与えられたとします。この様な場合、背景にある理論や原理に基づいて変数を選択することが最初のアプローチですが、それだけでは完全に決めきれないことが一般的です。そのようなときに変数選択の基準となる指標となるものが欲しくなります。その役割を果たしてくれるのがRidge回帰です。Ridge回帰の制約の強さを決めるλの値をいろいろ変えながら本当に「重要な変数」を選ぶことができます。そして多くの場合、Ridge回帰で推定された係数を用いたモデルは、OLSを用いて推定したモデルよりも「予測において」精度が高くなります。

Boston House Price で検証

scikit learnで提供されているデータを用いて実際にRidg回帰適応してみます。
以下にコードを載せます。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

from sklearn.datasets import load_boston
# scaling and dataset split
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.metrics import r2_score, mean_squared_error
house_price = load_boston()
df = pd.DataFrame(house_price.data, columns=house_price.feature_names)
df["PRICE"] = house_price.target

house_price.data = preprocessing.scale(house_price.data)
X_train, X_test, y_train, y_test = train_test_split(
    house_price.data, house_price.target, test_size=0.3, random_state=10)
ridge_reg = Ridge(alpha=0)
ridge_reg.fit(X_train, y_train)
ridge_df = pd.DataFrame({"variable":house_price.feature_names, "estimated":ridge_reg.coef_})
ridge_train_pred = []
ridge_test_pred = []
for alpha in range(0, 200, 1):
    ridge_reg = Ridge(alpha=alpha)
    ridge_reg.fit(X_train, y_train)
    var_name ="estimate" + str(alpha)
    ridge_df[var_name] = ridge_reg.coef_

    ridge_train_pred.append(ridge_reg.predict(X_train))
    ridge_test_pred.append(ridge_reg.predict(X_test))

ridge_df = ridge_df.set_index("variable").T
ridge_df.index = range(0, 201)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(ridge_df.RM, "r", ridge_df.ZN, "g", ridge_df.RAD, "b", ridge_df.CRIM, "c", ridge_df.TAX, "y")
ax.axhline(y=0, color="black", linestyle="--")
ax.set_xlabel("Lambda")
ax.set_ylabel("Beta Estimation")
ax.set_title("Ridge Regression Trace", fontsize=16)
ax.legend(labels=["Room", "Residential Zone", "Highway Access",'Crime Rate','Tax'])
ax.grid = True

f:id:memo_dl:20191124160349p:plain
いくつかの変数を抽出して、λの値(制約の強さ)と係数の大きさの関係を調べてみます。
最も変化の大きい変数は「Highway access」です。制約が弱いときには一番係数の絶対値が大きかったのですが、制約が強くなるにつれて小さくなっていき、最終的には最も0に近くなります。対照的に変数Room(居住施設の平均部屋数)は制約が強くなっても係数が比較的大きいままです。

ridge_mse_test = [mean_squared_error(y_test, p) for p in ridge_test_pred]
# ols_mse = mean_squared_error(y_test, ols_pred)

# plot mse
plt.plot(ridge_mse_test[:50], 'ro')
# plt.axhline(y=ols_mse, color='g', linestyle='--')
plt.title("Ridge Test Set MSE", fontsize=16)
plt.xlabel("Model Simplicity$\longrightarrow$")
plt.ylabel("MSE")

f:id:memo_dl:20191124160339p:plain

最後にλの値とテストセットにおける予測精度の関係を示します。λ=0のとき、つまりOLS推定の時よりも少し制約をかけた時の方が精度がよいことがわかります。一番予測精度がよくなっているのは、λの値が7、8くらいの時です。