
本記事では線形回帰の実装方法を解説します。
機械学習を始めたばかりの人でも簡単に作れますよ!
今回の記事は以下のような人におススメです。

・機械学習を始めてみたい
・簡単なモデルを知りたい
・Pythonを勉強するきっかけをつくりたい
線形回帰とは以下のようにデータにフィットした直線を引くモデルのこと。

シンプルですが、それゆえに過学習を抑えた予測が可能です。
今回は単純な直線を描くモデルや曲線にもフィットするモデルの作り方を解説します!
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_)
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)が計算されます。
しかしMSEは二乗されているためスケールが大きいです。
なのでnumpyで平方根をとりました。(これがRMSE)
実行すると5くらいになるかと思います。つまり誤差が5ということ。

“target”の分散が9程度なので、まぁまぁ良いですね。
最後に予測結果となる直線を可視化してみましょう。↓
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
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)
まずは“PolynomialFeatures”というライブラリをインポートします。
“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文を使うなどして探索するといいですね。
まとめ
今回は単回帰、重回帰、多項式回帰を解説しました。
直線関係であるとわかっているデータなら単回帰や重回帰がおススメです。
もうちょっと実践的なモデルを学びたい人はXGBoostかLightGBMに挑戦しましょう。


おススメ書籍
モデル構築方法について広く浅く学びたい人向け。
Pythonの文法についての解説はありませんが、ネットで調べながらで十分読めます。
さっさとkaggleに登録して簡単なコンペに参加することをおススメします。
この本を読めば最低限の知識を補完しながらkaggleに取り組めるはずです。
後は他のユーザーが上げているコードを読んで勉強すると効率がいいですよ!
コメント