機械学習入門

【機械学習入門⑤】非線形回帰・多項式回帰モデルをPythonの実装付きで理解!

機械学習入門⑤ 多項式モデル

 

これまで第2回・第4回で、線形回帰について学んできました。

※第3回では最小二乗法を紹介しました。

≫参考 : 【機械学習入門③】最小二乗法とは?手計算とPythonを使った実装を解説【初心者向け】

 

今回は、さらに発展したモデルである「非線形回帰・多項式回帰モデル」について学んだいきます。

この記事で学べる内容は、以下のとおりです。

  • 多項式回帰モデルについて
  • 多項式回帰モデルの実装方法

 

今まで学習していた線形回帰モデルは、その性質上、現実問題に適応するのが難しかったです。

でも、これから学ぶ「多項式回帰モデル」を含む非線形モデルは、線形回帰で実現できなかったことを可能にしてくれます。

 

多項式回帰モデルは、非線形回帰の中でもっとも基本的なモデルになるので、しっかりおさえておきましょう。

 

この記事について

この記事は、機械学習を体系的に学べる「機械学習入門講座」の1つです。

 

機械学習を勉強しようと思ったとき、以下のような悩みを持ったことはないですか?

  • 機械学習を学んでみたいけど、何から勉強したら良いのか分からない
  • 機械学習の本を買ってみたけど、よく分からなくて挫折した
  • そもそも本を買うのではなく、まずは「無料」で学習してみたい

上記のような、「機械学習を勉強し始める前の悩み」を解決するために、この機械学習講座を作成しました。

 

「プログラミングスクールは高いし、参考書は途中で飽きちゃうし…」といった方は、ぜひ第1回から学習して頂けたらと思います(*’ω’*)

一覧ページへ≫

※ブログ記事なので、無料です。でも、作成に手は抜いていません。

非線形回帰・多項式回帰モデルとは?

 

線形回帰・単回帰モデルは、直線で表されるような機械学習モデルでした。

単回帰モデル イメージ

少し言い換えると、データが直線上に並んでいなくても、直線で予測するしかありませんでした。

 

ただ、現実問題を考えると、すべてのことが直線で表せるとは限りません。

むしろ、直線では説明がつかない場合が多いです。

 

では、「より正確に予測をおこなうには、どうすれば良いのか」というと、それは直線でなく曲線を使って予測すれば解決できます。

 

今ままで使っていた線形モデルが”直線”だったのに対し、多項式モデルは”曲線”を使って予測します。

  • 線形モデル : 直線(平面)
  • 非線形モデル : 曲線(曲面)

正しい定義は異なりますが、直感的には上記のとおりです。

 

そして、非線形モデルの中で、もっとも基本的で理解しやすいのが、今回学習する「多項式モデル」になります。

多項式モデルは、以下の式で表されます。

$$y = w_0 +  w_1 x + w_2 x^2 + \cdots + w_N x^N$$

線形回帰・重回帰モデルに似ていますね。

≫参考 : 【機械学習入門③】線形回帰・重回帰モデルをPythonの実装付きで理解!

 

具体例で考える多項式回帰モデル

 

Pythonの機械学習ライブラリscikit-learnに入っている『ボストン住宅価格データセット』を使って、具体的に考えてみます。

 

ボストン住宅価格データセットの中身

『ボストン住宅価格データセット』には、以下のデータが入っています。

変数名データの説明
CRIM犯罪発生率
ZN住居区画の密集度
INDUS非小売業の土地割合
CHASチャールズ川の周辺か否か
NOXNOx濃度
RM住居に含まれる平均部屋数
AGE1940年より前に建てられた物件の割合
DISボストン市の5つの雇用施設からの重み付き距離
RAD大きな道路へのアクセスのしやすさ
TAX1万ドルあたりの不動産税率の総計
PTRATIO教師あたりの生徒数
B対象地域に住む黒人の比率
LSTAT低所得者の割合

 

たとえば、『住居に含まれる平均部屋数=RM』と、予測対象の『住宅価格=MEDV』を、散布図で確認してみると、以下のようになっています。

ボストン住宅価格 散布図

「直線で予測できなくはないけど、曲線を使った方がさらに良い精度が出せそうだ」といった感じですね。

 

このときの多項式モデルは、二次であれば以下のようになります。

$${\rm MEDV} = w_0 +  w_1 {\rm RM} + w_2 {\rm RM}^2$$

 

三次の場合は以下です。

$${\rm MEDV} = w_0 +  w_1 {\rm RM} + w_2 {\rm RM}^2 + w_3 {\rm RM}^3$$

 

いずれにせよ、線形回帰のときと同様に、\(w_0\)〜\(w_2\)を求める必要があります。

 

\(w_0\)〜\(w_2\)が分かれば、式が完成するので、最終的に以下のような予測曲線ができあがります。

※二次元の場合

多項式モデル 予測曲線

 

非線形回帰の「非線形」とは?

 

単回帰モデルや重回帰モデルは「線形」でした。

それに対して、今回学習する多項式モデルは、「非線形」になります。

 

それぞれの違いを「直感的に」解説すると、以下のようになります。

  • 線形 : 数式が直線で表せる
  • 非線形 : 数式が曲線で表せる

本当にざっくりですが、上記のとおり。

※僕は数学が専攻だったので、こんな説明したら教授にボコボコにされることは承知の上です。笑

 

ただ、直感的には正しいので、「非線形は数式が曲線になるんだ〜」と認識しておいて頂ければと。

たとえば、先ほど見ていた\({\rm MEDV} = w_0 +  w_1 {\rm RM} + w_2 {\rm RM}^2\)は、よく見てみると中学校で習った二次関数の形をしています。

多項式モデル 予測曲線

二次関数は、いうまでもなく”曲線”なので、非線形であることが分かります。

 

※線形の正しい定義については、【機械学習入門②】線形回帰・単回帰モデルをPythonの実装付きで理解!をどうぞ。線形であることの証明まで丁寧に解説しています。

 

Pythonを使った多項式モデルの実装方法【Scikit-learn】

 

多項式モデルの概要が分かったところで、Pythonを使った実装をしていきましょう。

 

今回はカンタンに二次元の多項式モデルを扱っていきます。

数式で書くと、以下のようになりますね。

$$y = w_0 +  w_1 x + w_2 x^2$$

※ちなみに、これは中学生のときに習った二次関数の形をしています。二次元だから、二次関数の形になるんですね。

 

先にコードを見せてしまうと、以下のようになっています。

# ### 2.1 ライブラリのインポート
import numpy as np
import pandas as pd

from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures

import matplotlib.pyplot as plt
import seaborn as sns

sns.set()
sns.set_style("ticks")

# ### 2.2 データの読み込み
boston = load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names)
df['MEDV'] = boston.target

print(df.head())

# 散布図を使った可視化
plt.figure(figsize=(10, 6))
sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7)
plt.show()

# 線形回帰の直線も合わせて確認
plt.figure(figsize=(10, 6))
sns.regplot(x=df.RM, y=df.MEDV, scatter_kws={'alpha': 0.4})
plt.show()

# ### 2.3 データの準備
x = df['RM']
X = np.array(x).reshape(-1, 1)
y = df['MEDV']

print('-'*10 + '特徴量とターゲットに分割' + '-'*10)
print('X:', X.shape)
print('y:', y.shape)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# ### 2.4 多項式モデルの作成
pf = PolynomialFeatures(degree=2, include_bias=False)
X_train_2 = pf.fit_transform(X_train)
X_test_2 = pf.fit_transform(X_test)

print('元のRM:')
print(X_train[:5])
print('二乗したRM:')
print(X_train_2[:5])

# モデルの作成
lm = LinearRegression()
lm.fit(X_train_2,y_train)

# パラメータを表示
print('バイアス', lm.intercept_)
print('重み', lm.coef_)

#精度を表示
print('Train Score:{:.2f}' .format(lm.score(X_train_2, y_train)))
print('Test Score:{:.2f}' .format(lm.score(X_test_2, y_test)))

# モデルの作成
tmp = LinearRegression()
tmp.fit(X_train,y_train)

y_pred_tmp = tmp.predict(X_test)

#精度を表示
print('Train Score:{:.2f}' .format(tmp.score(X_train, y_train)))
print('Test Score:{:.2f}' .format(tmp.score(X_test, y_test)))

# ### 2.5 学習結果の可視化
X_test_a = np.array([np.sort(X_test[:,0])]).reshape(len(X_test),1)
X_test_2 = pf.fit_transform(X_test_a)

y_pred = lm.predict(X_test_2)

plt.figure(figsize=(10, 6))
sns.scatterplot(X.reshape(len(X), ), y, alpha=0.7)
sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred, linewidth=2.5, label='poly')
sns.lineplot(x=X_test.reshape(len(X_test), ), y=y_pred_tmp, linewidth=2.5, label='linear')
plt.xlabel('RM')
plt.ylabel('MEDV')
plt.show()

順番にコードの中身を確認していきましょう。

 

STEP① : ライブラリの読み込み

import numpy as np
import pandas as pd

from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures

import matplotlib.pyplot as plt
import seaborn as sns

sns.set()
sns.set_style("ticks")

今回使うライブラリは上記です。

 

  • Numpy : 配列計算用ライブラリ
  • Pandas : データ操作用ライブラリ
  • Scikit-learn(sklearn) : データの準備・機械学習モデルの作成用ライブラリ
  • Matplotlib : 作図用ライブラリ①
  • Seaborn : 作図用ライブラリ②

主にScikit-learnを使って、多項式モデルを組んでいきます。

 

STEP② : データの読み込み

boston = load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names)
df['MEDV'] = boston.target

print(df.head())

# 散布図を使った可視化
plt.figure(figsize=(10, 6))
sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7)
plt.show()

# 線形回帰の直線も合わせて確認
plt.figure(figsize=(10, 6))
sns.regplot(x=df.RM, y=df.MEDV, scatter_kws={'alpha': 0.4})
plt.show()

 

次に、多項式モデルで使うデータを読み込みます。

使うデータは、今まで同様に『ボストンの住宅価格データセット』です。

 

再掲 : ボストン住宅価格データセットの中身

変数名データの説明
CRIM犯罪発生率
ZN住居区画の密集度
INDUS非小売業の土地割合
CHASチャールズ川の周辺か否か
NOXNOx濃度
RM住居に含まれる平均部屋数
AGE1940年より前に建てられた物件の割合
DISボストン市の5つの雇用施設からの重み付き距離
RAD大きな道路へのアクセスのしやすさ
TAX1万ドルあたりの不動産税率の総計
PTRATIO教師あたりの生徒数
B対象地域に住む黒人の比率
LSTAT低所得者の割合

 

df.head()を使うことで、これから使うデータの上から5行を、Excelのようにキレイな形で確認できます。

ボストン住宅価格 データフレーム

※Jupyter notebookの場合です。基本的にJupyter notebookの使用をおすすめします。

 

また、散布図と単回帰モデルでの予測も可視化しておきましょう。

# 散布図を使った可視化
plt.figure(figsize=(10, 6))
sns.scatterplot(x=df.RM, y=df.MEDV, alpha=0.7)
plt.show()

# 線形回帰の直線も合わせて確認
plt.figure(figsize=(10, 6))
sns.regplot(x=df.RM, y=df.MEDV, scatter_kws={'alpha': 0.4})
plt.show()
単回帰モデル イメージ

こんな感じですね。

直線でもうまく予測できていそうですが、「若干捉えきれていないかな」といった印象です。

 

STEP③ : データの準備

x = df['RM']
X = np.array(x).reshape(-1, 1)
y = df['MEDV']

print('-'*10 + '特徴量とターゲットに分割' + '-'*10)
print('X:', X.shape)
print('y:', y.shape)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

 

多項式モデルを作成するために、今回使うデータを分割します。

今回は、「住居に含まれる平均部屋数=RM」を特徴量に使い、住宅価格MEDVの予測します。

 

train_test_split()で、訓練用データとテスト用データに、データを分割しています。

この分割をしないと、多項式モデルを”作りっぱなし”になってしまい、作成されたモデルの良し悪しが分かりません。

 

テストデータを残しておくことで、後からモデルの性能を評価します。

 

STEP④ : 多項式モデルの作成と評価

pf = PolynomialFeatures(degree=2, include_bias=False)
X_train_2 = pf.fit_transform(X_train)
X_test_2 = pf.fit_transform(X_test)

print('元のRM:')
print(X_train[:5])
print('二乗したRM:')
print(X_train_2[:5])

# モデルの作成
lm = LinearRegression()
lm.fit(X_train_2,y_train)

# パラメータを表示
print('バイアス', lm.intercept_)
print('重み', lm.coef_)

#精度を表示
print('Train Score:{:.2f}' .format(lm.score(X_train_2, y_train)))
print('Test Score:{:.2f}' .format(lm.score(X_test_2, y_test)))

 

多項式モデルを作成するには、以下の手順が必要です。

  • ①二次元の特徴量を作成する
  • ②線形回帰と同じ手順でモデルを作る

今回ポイントになるのが、①二次元の特徴量を作成するですね。

 

これはなんのことを言っているのかというと、\(y = w_0 +  w_1 x + w_2 x^2\)の\(x^2\)に当たる部分を作成するということです。

つまり、RMを二乗した値を求めてから、多項式モデルを作成することになります。

 

STEP① : 二次元の特徴量を作成する

pf = PolynomialFeatures(degree=2, include_bias=False)
X_train_2 = pf.fit_transform(X_train)
X_test_2 = pf.fit_transform(X_test)

print('元のRM:')
print(X_train[:5])
print('二乗したRM:')
print(X_train_2[:5])

上記のように、scikit-learnに入っているPolynomialFeatures()を使って、二次元の特徴量を作成します。

 

print()で確認すると、以下のようになっています。

多項式モデル 実装 PolynomialFeatures

新しく追加した部分は、下式の\({\rm RM}^2\)の部分を求めたことになります。

$${\rm MEDV} = w_0 +  w_1 {\rm RM} + w_2 {\rm RM}^2$$

 

STEP② : 線形回帰と同じ手順でモデルを作る

# モデルの作成
lm = LinearRegression()
lm.fit(X_train_2,y_train)

# パラメータを表示
print('バイアス', lm.intercept_)
print('重み', lm.coef_)

#精度を表示
print('Train Score:{:.2f}' .format(lm.score(X_train_2, y_train)))
print('Test Score:{:.2f}' .format(lm.score(X_test_2, y_test)))

 

あとは、線形回帰と同様に、LinearRegression()を使えばOKです。

モデルの学習で使うのは、二乗したデータが入っているX_train_2と正解が入っているy_trainです。

 

ボストン住宅価格のデータ量は、そこまで多くないので、実行するとすぐに結果が出ます。

 

学習結果を見てみましょう。

lm.score()を使えばモデルの決定係数を確認できます。

print('Train Score:{:.2f}' .format(lm.score(X_train_2, y_train)))
print('Test Score:{:.2f}' .format(lm.score(X_test_2, y_test)))
  • Train Score:0.56
  • Test Score:0.51

決定係数は、0〜1の値を取り、1に近い方が精度が良いという解釈になります。

なので、今回作成したモデルは、そこまで良いモデルだとは言えないですね。

 

ちなみに、「良い多項式モデルを作成する」とは、\([latex]に含まれる、[latex]w_0\)〜\(w_2\)で最適な値を求めることになります。

 

つまり、Scikit-learnを使って多項式モデルを作成すると、\(w_0\)〜\(w_2\)がすでに求まっている状態です。

※ライブラリを使って計算しているので中身は見えませんが、実は色々なロジックに基づいて最適な\(w_0\)〜\(w_2\)を計算しています。

print('バイアス', lm.intercept_)
print('重み', lm.coef_)
  • バイアス 68.20229885775146
  • 重み [-23.56509374 2.56037958]

バイアスと呼んでいるのが\(w_0\)、重みと呼んでいるのが\(w_1, w_2\)です。

 

数式で表すと、以下のようなモデルが完成したことになります。

$$y = 68.202 – 23.565 x + 2.5604 x^2$$

 

ちなみに、単回帰モデルで精度を確認すると、以下のようになっています。

# モデルの作成
tmp = LinearRegression()
tmp.fit(X_train,y_train)

y_pred_tmp = tmp.predict(X_test)

#精度を表示
print('Train Score:{:.2f}' .format(tmp.score(X_train, y_train)))
print('Test Score:{:.2f}' .format(tmp.score(X_test, y_test)))
  • Train Score:0.50
  • Test Score:0.42

 

多項式モデルの方が、学習結果と予測結果ともに精度が良くなっていますね。

Train ScoreTest Score
単回帰モデル0.500.42
多項式モデル0.560.51

 

STEP⑤ : 学習結果の可視化

X_test_a = np.array([np.sort(X_test[:,0])]).reshape(len(X_test),1)
X_test_2 = pf.fit_transform(X_test_a)

y_pred = lm.predict(X_test_2)

plt.figure(figsize=(10, 6))
sns.scatterplot(X.reshape(len(X), ), y, alpha=0.7)
sns.lineplot(x=X_test_a.reshape(len(X_test_a), ), y=y_pred, linewidth=2.5, label='poly')
sns.lineplot(x=X_test.reshape(len(X_test), ), y=y_pred_tmp, linewidth=2.5, label='linear')
plt.xlabel('RM')
plt.ylabel('MEDV')
plt.show()

最後に、元データと予測結果を確認してみましょう。

 

多項式モデルが青の線、単回帰モデルがオレンジの線です。

多項式モデル 結果

目で確認しても、多項式モデルの方が正確に予測できている気がしますね。

 

おまけ : 多項式モデルの次元を上げる

 

今回は、単回帰モデルから二次元にすることで、モデルの精度が向上しました。

 

それでは、次元を上げれば上げるほど、モデルの精度は良くなるのでしょうか?

以下おまけで、次元を上げたときの多項式モデル精度を確認しましょう。

def get_polynomial_features(degree:int, data):
    pf = PolynomialFeatures(degree=degree, include_bias=False)
    return pf.fit_transform(data)

# 二次元(今回取り扱ったモデル)
X_train_2 = get_polynomial_features(2, X_train)
X_test_2 = get_polynomial_features(2, X_test)

# 五次元
X_train_5 = get_polynomial_features(5, X_train)
X_test_5 = get_polynomial_features(5, X_test)

# 十次元
X_train_10 = get_polynomial_features(10, X_train)
X_test_10 = get_polynomial_features(10, X_test)

# モデルの作成
lm_2deg = LinearRegression()
lm_2deg.fit(X_train_2,y_train)

# モデルの作成
lm_5deg = LinearRegression()
lm_5deg.fit(X_train_5,y_train)

# モデルの作成
lm_10deg = LinearRegression()
lm_10deg.fit(X_train_10,y_train)

#精度を表示
print('-'*10 + '二次元モデルの精度' + '-'*10)
print('Train Score:{:.2f}' .format(lm_2deg.score(X_train_2, y_train)))
print('Test Score:{:.2f}' .format(lm_2deg.score(X_test_2, y_test)))

#精度を表示
print('-'*10 + '五次元モデルの精度' + '-'*10)
print('Train Score:{:.2f}' .format(lm_5deg.score(X_train_5, y_train)))
print('Test Score:{:.2f}' .format(lm_5deg.score(X_test_5, y_test)))

#精度を表示
print('-'*10 + '十次元モデルの精度' + '-'*10)
print('Train Score:{:.2f}' .format(lm_10deg.score(X_train_10, y_train)))
print('Test Score:{:.2f}' .format(lm_10deg.score(X_test_10, y_test)))

 

最終的な結果が、以下のようになります。

Train ScoreTest Score
二次元モデル0.560.51
五次元モデル0.610.50
十次元モデル0.610.45

 

上記のように、モデルの次元を上げれば上げるほど、テストデータに対する当てはまりが悪くなってしまいました。

つまり、訓練用データに対して過剰に適合してしまっている状態で、これを過学習(Over Fitting)と呼びます。

 

モデルを複雑にしすぎると、未知のデータに対応できなくなるんですね。

 

【機械学習入門⑤】多項式モデルまとめ

 

というわけで、今回は以上です。

非線形回帰・多項式モデルについて、理解できたのではないでしょうか。

 

今後は、非線形モデルを扱う機会が増えていきます。

多項式モデルは、もっとも基本的な非線形モデルになるので、しっかりとおさえておきましょう。

 

次回は、多項式モデルの次元を上げたときに生じた「過学習」について紹介していきます。

機械学習入門⑥ 過学習 対策 回避
【機械学習入門⑥】過学習とは?回避・対策する3つの方法も解説 前回の記事『【機械学習入門⑤】非線形回帰・多項式回帰モデルをPythonの実装付きで理解!』では、非線形回帰・多項式モデルを学習...

 

過学習は、機械学習を学ぶ上で、切っても切り離せない内容です。

 

この記事が良いと思ったら、ぜひ次の『【機械学習入門⑥】過学習とは?回避・対策方法まで』も学んで頂けたらと思います(*’ω’*)

 

なお、『機械学習入門シリーズ』は長いので、休みながら学習を進めてみてくださいね。

休憩するときは、記事をツイートしておくと便利ですよ!

※メンションに気が付いたら、拡散する可能性高いです(`・ω・´)!

 

おすすめの記事

機械学習入門⑥ 過学習 対策 回避
【機械学習入門⑥】過学習とは?回避・対策する3つの方法も解説 前回の記事『【機械学習入門⑤】非線形回帰・多項式回帰モデルをPythonの実装付きで理解!』では、非線形回帰・多項式モデルを学習...