【Python】線形回帰(Ridge, Lasso)と多項式回帰の作り方

Python
Image by Gerd Altmann from Pixabay
スポンサーリンク

Pythonで線形回帰モデルを作成することができます。

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

線形回帰とは以下のようにデータにフィットした直線を引くモデルのことです。

シンプルですが、それゆえに過学習を抑えた予測が可能です。

load_boston

まずは予測したいデータを準備しましょう。

Pythonに標準搭載されている“load_boston”というデータを使います。

import pandas as pd
from sklearn.datasets import load_boston

data = load_boston()
df = pd.DataFrame(data["data"], columns = data["feature_names"])
df["target"] = data["target"]
df.head()

このように表として出力されたらOKです。

“CRIM”~”LSTAT”を使って”target”の値を予測しましょう。

データ分割

モデルを作成する前にデータ分割が必要です。

学習に使うデータとモデル性能を確かめるデータに分けましょう!

from sklearn.model_selection import KFold
df["fold"] = -1
kf = KFold(n_splits = 5, shuffle = True)
for fold, (train_idx, valid_idx) in enumerate(kf.split(df)):
    df.loc[valid_idx, "fold"] = fold
print(df["fold"].value_counts())

KFoldでデータを行方向に等分しました。

n_splits = 5にしているので5等分です。

fold = 0
train = df.loc[df["fold"] != fold].copy()
valid = df.loc[df["fold"] == fold].copy()
print(train.shape, valid.shape)

今回は番号が0のデータを評価用にしました。

本来は0~4番の5つのデータそれぞれを評価用にして、計5つのモデルを作ると良いですよ。

単回帰

単回帰は”1列の特徴量から目的変数を予測するモデル”です。

相関関係

“LSTAT”の列を予測に使うとしましょう。

import matplotlib.pyplot as plt
X_train = train[["LSTAT"]]
y_train = train["target"]
X_valid = valid[["LSTAT"]]
y_valid = valid["target"]

plt.scatter(X_train, y_train)
plt.title("train")
plt.show()

plt.scatter(X_valid, y_valid)
plt.title("valid")
plt.show()

“LSTAT”と”target”とで散布図を作るとこんな感じ。

負の相関がありそうですね。

モデル作成

さっき確認した関係性から単回帰モデルを作りましょう!

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

まず”LinearRegression”をインポートします。

変数”model”としてモデルを定義し、fitで学習データを渡してあげればOK。

予測結果

作成したモデルで予測をしてみましょう。

print(model.coef_, model.intercept_)
coef_:勾配(直線の傾き)
intercept_:切片(直線とy軸が交差する位置)

勾配をxにかけて切片を足せば予測結果ですね。

a = model.coef_[0]
b = model.intercept_
pred = X_valid * a + b
print(pred.values.ravel()[:10])
print(y_valid.values[:10])

上段が予測結果で下段が正解です。

ここで注意点が1つあります。

学習データ(train)はモデルに入れたデータなので、これで性能評価すると当然良い結果になります。

なので、モデルの性能を確認するときはモデルに入れていないデータ(valid)を使ってください!

誤差を評価するために、平均二乗誤差の平方根(RMSE)を計算しましょう。

from sklearn.metrics import mean_squared_error
import numpy as np

mse = mean_squared_error(y_valid, pred)
rmse = np.sqrt(mse)
print(rmse)

“mean_squared_error”をインポートします。

“numpy”は各種計算に使われるライブラリです。ほぼ必須!

“mean_squared_error”に正解データと予測結果を入れると平均二乗誤差(MSE)が計算されます。

RMSEはその平方根です。

実行すると5くらいになるかと思います。つまり誤差が5ということです。

最後に予測結果となる直線を可視化してみましょう。↓

plt.scatter(X_train, y_train)
y_line = (a * X_train + b).values.ravel()
plt.plot(X_train, y_line, color = "red")
plt.title("train")
plt.show()

plt.scatter(X_valid, y_valid)
y_line = (a * X_valid + b).values.ravel()
plt.plot(X_valid, y_line, color = "red")
plt.title("valid")
plt.show()

このようにそれっぽい直線ができました!

これで単回帰モデルは完成です。

重回帰

単回帰では1つの特徴から目的変数”target”を予測しました。

複数の特徴を使いたいなら重回帰モデルを作りましょう!

まずは予測に使う特徴量と予測したい目的変数に分けます。↓

feat_cols = train.drop(columns = ["fold", "target"]).columns.tolist()
print(feat_cols)

X_train = train[feat_cols]
X_valid = valid[feat_cols]
y_train = train["target"]
y_valid = valid["target"]

特徴量としてモデルに入れる列を”feat_cols”としました。

“fold”と”target”は特徴として使えません。

X_trainが学習データでの特徴量で、y_trainが学習データでの目的変数です。

では学習させてみましょう。↓

model = LinearRegression()
model.fit(X_train, y_train)
print(model.coef_, model.intercept_)

学習方法はさっきと同じです。

結果を見ると、coef_で出力される勾配の数が特徴量の数と同じ13個になっているはず。

モデルができたので評価データで予測してみます。↓

preds = []
for i in range(X_valid.shape[0]):
    row = X_valid.iloc[i].values
    pred = np.sum([row[j] * model.coef_[j] for j in range(len(row))]) + model.intercept_
    preds.append(pred)
print(preds[:10])
print(y_valid.values[:10])

ちょっと手間ですが各行の特徴量に対して勾配をかけて和をとり、切片を足しましょう。

mse = mean_squared_error(y_valid, preds)
rmse = np.sqrt(mse)
print(rmse)

RMSEが単回帰の予測よりも誤差が小さくなっているはずです。

これで重回帰モデルの作成は終わりです!

特徴として入れるデータを増やすだけですね。

Ridge回帰

Ridge回帰は線形回帰の仲間です。

特徴量がモデルに与える影響を抑え、過学習を抑制することができます。

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

“Ridge”をインポートしてこれまでと同じようにfitさせるだけです。

preds = []
for i in range(X_valid.shape[0]):
    row = X_valid.iloc[i].values
    pred = np.sum([row[j] * model.coef_[j] for j in range(len(row))]) + model.intercept_
    preds.append(pred)
mse = mean_squared_error(y_valid, preds)
rmse = np.sqrt(mse)
print(rmse)

RMSEを計算してみましょう。

さっきのLinearRegressionでの重回帰モデルよりもよくなっているかと思います。

このように過学習を抑える(学習データに過剰適合しないようにする)ことで評価データでのスコアを上げることができます。

Lasso回帰

Lasso回帰も過学習を抑える手法の1つです。

Ridge回帰と違って”関係ない特徴量は完全無視する”性質があります。

from sklearn.linear_model import Lasso
model = Lasso()
model.fit(X_train, y_train)
print(model.coef_, model.intercept_)

“Lasso”をインポートして使いましょう。

出力結果を見るといくつかの特徴量は勾配が0つまり採用されていないことがわかります。

このようにLasso回帰は特徴量選択の性質を持っているわけです。

preds = []
for i in range(X_valid.shape[0]):
    row = X_valid.iloc[i].values
    pred = np.sum([row[j] * model.coef_[j] for j in range(len(row))]) + model.intercept_
    preds.append(pred)
mse = mean_squared_error(y_valid, preds)
rmse = np.sqrt(mse)
print(rmse)

RMSEを計算してみましょう。

私の手元ではLinearRegressionでの結果とさほど変わりませんでした。

つまり関係なさそうな特徴量は元から予測に効いていなかったということですね。

多項式回帰

非線形データ

単回帰と重回帰は直線フィッティングなので非線形のデータを表現できません。

lin = np.linspace(0, 10, 100)
sin = np.sin(lin)
plt.scatter(lin, sin)
plt.show()

このようなSin(サイン)カーブを単回帰でフィッティングしましょう。

model = LinearRegression()
model.fit(lin.reshape(-1, 1), sin)
pred = model.predict(lin.reshape(-1, 1))
plt.scatter(lin, sin)
plt.plot(lin, pred, "red")
plt.show()

赤線がフィッティングした結果です。うまくいっていませんね。

こんなときは多項式回帰を使いましょう。

簡単に言うと、予測する式の次元を増やしまくって表現力を上げる方法です。

PolynomialFeatures

まずは”PolynomialFeatures”というライブラリをインポートします。

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree = 4)
lin_poly = poly.fit_transform(lin.reshape(-1, 1))
print(lin.shape)
print(lin_poly.shape)

“degree”は多項式回帰の次数を決める引数です。今回は4まで増やしてみました。

変換前(lin)のサイズが100行1列で、変換後(lin_poly)は100行5列となります。

つまりdegree = 4の分だけ列数が増えて表現力が上がっているわけです。

モデル作成

model = LinearRegression()
model.fit(lin_poly, sin)
pred = model.predict(lin_poly)
plt.scatter(lin, sin)
plt.plot(lin, pred, "red")
plt.show()

多項式にしたlin_polyで学習させました。

モデルはこれまでと同じLinearRegressionでOKです。

この通り最初の単回帰よりも近づいていますね。

適切な次数

degree = 4で表現力が上がっていたので、もっと次数を増やしてみましょう。

poly = PolynomialFeatures(degree = 8)
lin_poly = poly.fit_transform(lin.reshape(-1, 1))
model = LinearRegression()
model.fit(lin_poly, sin)
pred = model.predict(lin_poly)
plt.scatter(lin, sin)
plt.plot(lin, pred, "red")
plt.show()

degree = 8でピッタリ一致しました。

poly = PolynomialFeatures(degree = 20)
lin_poly = poly.fit_transform(lin.reshape(-1, 1))
model = LinearRegression()
model.fit(lin_poly, sin)
pred = model.predict(lin_poly)
plt.scatter(lin, sin)
plt.plot(lin, pred, "red")
plt.show()

degree = 20にすると1点ずつに過剰フィットするため歪な形になってしまいます。

なので、degreeの値はfor文を使うなどして探索するといいですね。

まとめ

今回は単回帰、重回帰、多項式回帰を解説しました。

直線関係であるとわかっているデータなら単回帰や重回帰がおススメです。

もうちょっと実践的なモデルを学びたい人はXGBoostLightGBMに挑戦しましょう。

コメント

タイトルとURLをコピーしました