これまで第2回・第4回で、線形回帰について学んできました。
※第3回では最小二乗法を紹介しました。
≫参考 : 【機械学習入門③】最小二乗法とは?手計算とPythonを使った実装を解説【初心者向け】
今回は、さらに発展したモデルである「非線形回帰・多項式回帰モデル」について学んだいきます。
この記事で学べる内容は、以下のとおりです。
- 多項式回帰モデルについて
- 多項式回帰モデルの実装方法
今まで学習していた線形回帰モデルは、その性質上、現実問題に適応するのが難しかったです。
でも、これから学ぶ「多項式回帰モデル」を含む非線形モデルは、線形回帰で実現できなかったことを可能にしてくれます。
多項式回帰モデルは、非線形回帰の中でもっとも基本的なモデルになるので、しっかりおさえておきましょう。
この記事は、機械学習を体系的に学べる「機械学習入門講座」の1つです。
機械学習を勉強しようと思ったとき、以下のような悩みを持ったことはないですか?
- 機械学習を学んでみたいけど、何から勉強したら良いのか分からない
- 機械学習の本を買ってみたけど、よく分からなくて挫折した
- そもそも本を買うのではなく、まずは「無料」で学習してみたい
上記のような、「機械学習を勉強し始める前の悩み」を解決するために、この機械学習講座を作成しました。
「プログラミングスクールは高いし、参考書は途中で飽きちゃうし…」といった方は、ぜひ第1回から学習して頂けたらと思います(*’ω’*)
※ブログ記事なので、無料です。でも、作成に手は抜いていません。
Contents
非線形回帰・多項式回帰モデルとは?
線形回帰・単回帰モデルは、直線で表されるような機械学習モデルでした。

少し言い換えると、データが直線上に並んでいなくても、直線で予測するしかありませんでした。
ただ、現実問題を考えると、すべてのことが直線で表せるとは限りません。
むしろ、直線では説明がつかない場合が多いです。
では、「より正確に予測をおこなうには、どうすれば良いのか」というと、それは直線でなく曲線を使って予測すれば解決できます。
今ままで使っていた線形モデルが”直線”だったのに対し、多項式モデルは”曲線”を使って予測します。
- 線形モデル : 直線(平面)
- 非線形モデル : 曲線(曲面)
正しい定義は異なりますが、直感的には上記のとおりです。
そして、非線形モデルの中で、もっとも基本的で理解しやすいのが、今回学習する「多項式モデル」になります。
多項式モデルは、以下の式で表されます。
$$y = w_0 + w_1 x + w_2 x^2 + \cdots + w_N x^N$$
線形回帰・重回帰モデルに似ていますね。
≫参考 : 【機械学習入門③】線形回帰・重回帰モデルをPythonの実装付きで理解!
具体例で考える多項式回帰モデル
Pythonの機械学習ライブラリscikit-learn
に入っている『ボストン住宅価格データセット』を使って、具体的に考えてみます。
ボストン住宅価格データセットの中身
『ボストン住宅価格データセット』には、以下のデータが入っています。
変数名 | データの説明 |
CRIM | 犯罪発生率 |
ZN | 住居区画の密集度 |
INDUS | 非小売業の土地割合 |
CHAS | チャールズ川の周辺か否か |
NOX | NOx濃度 |
RM | 住居に含まれる平均部屋数 |
AGE | 1940年より前に建てられた物件の割合 |
DIS | ボストン市の5つの雇用施設からの重み付き距離 |
RAD | 大きな道路へのアクセスのしやすさ |
TAX | 1万ドルあたりの不動産税率の総計 |
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 | チャールズ川の周辺か否か |
NOX | NOx濃度 |
RM | 住居に含まれる平均部屋数 |
AGE | 1940年より前に建てられた物件の割合 |
DIS | ボストン市の5つの雇用施設からの重み付き距離 |
RAD | 大きな道路へのアクセスのしやすさ |
TAX | 1万ドルあたりの不動産税率の総計 |
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()
で確認すると、以下のようになっています。

新しく追加した部分は、下式の\({\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 Score | Test Score | |
単回帰モデル | 0.50 | 0.42 |
多項式モデル | 0.56 | 0.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 Score | Test Score | |
二次元モデル | 0.56 | 0.51 |
五次元モデル | 0.61 | 0.50 |
十次元モデル | 0.61 | 0.45 |
上記のように、モデルの次元を上げれば上げるほど、テストデータに対する当てはまりが悪くなってしまいました。
つまり、訓練用データに対して過剰に適合してしまっている状態で、これを過学習(Over Fitting)と呼びます。
モデルを複雑にしすぎると、未知のデータに対応できなくなるんですね。
【機械学習入門⑤】多項式モデルまとめ
というわけで、今回は以上です。
非線形回帰・多項式モデルについて、理解できたのではないでしょうか。
今後は、非線形モデルを扱う機会が増えていきます。
多項式モデルは、もっとも基本的な非線形モデルになるので、しっかりとおさえておきましょう。
次回は、多項式モデルの次元を上げたときに生じた「過学習」について紹介していきます。

過学習は、機械学習を学ぶ上で、切っても切り離せない内容です。
この記事が良いと思ったら、ぜひ次の『【機械学習入門⑥】過学習とは?回避・対策方法まで』も学んで頂けたらと思います(*’ω’*)
なお、『機械学習入門シリーズ』は長いので、休みながら学習を進めてみてくださいね。
休憩するときは、記事をツイートしておくと便利ですよ!
※メンションに気が付いたら、拡散する可能性高いです(`・ω・´)!
おすすめの記事
