【Python】scikit-learn ~ 重回帰 / リッジ回帰 ~

■ はじめに

https://dk521123.hatenablog.com/entry/2020/03/02/233902
https://dk521123.hatenablog.com/entry/2020/03/08/113356
https://dk521123.hatenablog.com/entry/2020/04/04/021413
https://dk521123.hatenablog.com/entry/2020/07/04/000000

の続き。
今回は、リッジ回帰について扱う。
scikit-learn を使えば、簡単に実装できる。

目次

【1】リッジ回帰 (Ridge Regression; RR)
 補足:関連用語
  a) 正則化 (Regularization)
  b) 線形回帰(Linear Regression)
  c) 単回帰分析 / 重回帰分析
【2】サンプル
 例1:Hello world
 例2:株価予測

【1】リッジ回帰 (Ridge Regression; RR)

* 線形回帰のコスト関数に学習した重みの二乗(L2正則化項)を加えたもの

 => 他にも「ラッソ回帰(Lasso Regression)」や
  「エラスティックネット(Elastic Net)」などがある
  詳細は、以下の関連記事を参照のこと。

https://dk521123.hatenablog.com/entry/2020/11/29/193247

特徴

* 最小二乗法による線形回帰(※補足)よりもノイズの影響を受けにくい
* リッジ回帰の正規化を強くし過ぎると平均値になる

補足:関連用語

a) 正則化 (Regularization)

* 過学習(Over-fitting)を防ぐために、モデルが複雑になりすぎないように制約する
 ⇒ モデルに対して、ノイズが過度に影響しないようにするためのパラメータ
* L2正則化項による正則化では、0にはならない性質がある

b) 線形回帰(Linear Regression)

* ある変数 x (説明変数と呼ぶ)が与えられた時、
 相関のある y (目的変数と呼ぶ)を予測すること
* 教師あり学習
* Schoo の以下から勉強できる

https://schoo.jp/class/6707

c) 単回帰分析 / 重回帰分析

* 説明変数 x が1つ(1次元)の場合、
 「単回帰分析(Simple linear regression analysis)」、
 説明変数 x が複数の場合、
 「重回帰分析(Multiple-linear regression analysis)」
 => 単回帰分析: y=ax+b、重回帰分析: y=w1x1+w2x2+w3x3+w0
モデル 予測モデル式 パラメータ 特徴量
単回帰 y = θ0 + θ1*X1 線形 線形
重回帰 y = θ0 + θ1X1 + θ2X2 + ... + θn*Xn 線形 線形
多項式回帰 y = θ0 + θ1X + θ2X2 + ... + θn*Xn 線形 非線形

【2】サンプル

例1:Hello world

from sklearn import linear_model
import matplotlib.pyplot as plt

x = [[1], [3], [2], [4], [5], [7], [6], [8], [10]]
y = [3, 2, 4, 5, 7, 6, 8, 10, 9]

# リッジ回帰インスタンス作成
# alpha(α)は、ハイパーパラメータで大きいと正則化が強くなる
ridge_model = linear_model.Ridge(alpha=0, fit_intercept=True)

ridge_model.fit(x, y)
predicted_y = ridge_model.predict(x)
plt.plot(x, predicted_y)
plt.plot(x, y, 'o')
print("傾き = {}".format(ridge_model.coef_))
print("切片 = {}".format(ridge_model.intercept_))

ridge_model.set_params(alpha=100)
predicted_y = ridge_model.predict(x)
plt.plot(x, predicted_y)
plt.plot(x, y, 'o')
print("傾き = {}".format(ridge_model.coef_))
print("切片 = {}".format(ridge_model.intercept_))

ridge_model.set_params(alpha=10000)
predicted_y = ridge_model.predict(x)
plt.plot(x, predicted_y)
plt.plot(x, y, 'o')
print("傾き = {}".format(ridge_model.coef_))
print("切片 = {}".format(ridge_model.intercept_))

plt.show()

出力結果

傾き = [0.81290323]
切片 = 1.845161290322582
傾き = [0.33157895]
切片 = 4.305263157894737
傾き = [0.00556169] <= 傾きがほとんどなくなる(0に近づく)
切片 = 5.9715736040609135

例2:株価予測

株価の過去データ(今回は、10日分)を使い、
1日先のデータを予測するプログラムを作成する。

データについて

* セントルイス連邦準備銀行のFRED(Federal Reserve Economic Data)の
 NSDAQ の株価データを使って、リッジ回帰を行う。

https://fred.stlouisfed.org/
事前準備

conda install pandas-datareader

プログラム

import numpy as np
import matplotlib.pyplot as plt
import pandas_datareader.data as web
from sklearn.metrics import mean_squared_error
from sklearn import linear_model

def reshape(data_frame, input_day, period):
  x = []
  y = []
  for i in range(input_day, len(data_frame) - period):
    x.append(data_frame[i-input_day: i])
    y.append(data_frame[i+period][-1])
  x = (np.array(x)).reshape(len(x), input_day)
  y = np.array(y)
  return x, y

# リッジ回帰でモデルを作り予測する
def get_predicted_y_by_ridge(x, y, input_day, alpha=1):
  ridge_model = linear_model.Ridge(
    alpha=alpha, fit_intercept=True)
  predicted_y = []
  for i in range(input_day + 1, len(x)):
    input_x = x[:i-input_day]
    input_y = y[:i-input_day]
    temp_x = x[i]
    ridge_model.fit(input_x, input_y)
    predict = ridge_model.predict([temp_x])[0]
    predicted_y.append(predict)
  return np.array(predicted_y)

# NASDAQデータダウンロード
data_frame = web.DataReader(
  "NASDAQCOM", "fred", "1971/1/1", "2020/4/1").dropna()
# data_frame.columns = ['Close']
# data_frame.plot()

clone_data_frame = np.log(data_frame).values.copy()
# 予想に使うデータの日数
input_day = 10
# 予測の期間
period = 1
x, y = reshape(clone_data_frame, input_day, period)
predict_y = get_predicted_y_by_ridge(x, y, input_day)
# 実際の実測値をグラフ化
plt.plot(y[1:], label="Close")
# リッジ回帰のモデルで予測した値をグラフ化
plt.plot(predict_y[1:], label="Prediction")
plt.legend()

# 予測にどれくらい誤差があるのかを計算する
prediction_error = mean_squared_error(
  y[input_day+1:], predict_y)
benchmark = mean_squared_error(
  y[period:], y[:-period])

print(
  "予測誤差", prediction_error,
  "ベンチマーク", benchmark)

# グラフに表示
plt.show()

出力結果

予想誤差 0.00039215988425432406 ベンチマーク 0.0001556225921818145

関連記事

scikit-learn ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/03/02/233902
scikit-learn ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2020/03/08/113356
scikit-learn ~ 線形回帰 ~
https://dk521123.hatenablog.com/entry/2020/07/04/000000
scikit-learn ~ 重回帰 / ロッソ回帰・エラスティックネット ~
https://dk521123.hatenablog.com/entry/2020/11/29/193247
scikit-learn ~ 決定木 / ランダムフォレスト ~
https://dk521123.hatenablog.com/entry/2020/04/04/021413
Matplotlib ~ グラフ描画ライブラリ ~
https://dk521123.hatenablog.com/entry/2020/03/01/000000
パターン認識について
https://dk521123.hatenablog.com/entry/2013/12/16/225948
機械学習を勉強する際のデータセットについて
https://dk521123.hatenablog.com/entry/2020/03/28/131839