目次
- 機械学習の線形回帰問題
- まずは学習用のサンプルデータを作成
- scikit-learnのLinearRegressionクラスで学習・評価
- 共通の補足事項:決定係数
- 共通の補足事項:ありがちなエラー
- 最後に全ソースです
機械学習の線形回帰問題
線形回帰問題をPythonで解く「scikit-learn」の「LinearRegression」クラスの使い方と決定係数での評価について、基本的なところを、おさらいしておこうかと思います。
入力データと出力データの組から対応する規則を学んで、未知の入力データに対し適切な出力をえる(つまり予測)できるようにするのが「回帰問題」です。
回帰問題には「線形回帰」と「非線形回帰」があります。
線形回帰と非線形回帰の基本的な違いは、モデルが取り得る関数の形式です。
このへんは線形・非線形の名前でなんとなくはわかります。
線形回帰では線形パラメータが必要で、非線形回帰では必要ないとか理論的な話はありますが、とりあえず趣味でやってるので、なんとなくイメージできたら、とりあえずプログラムを動かして理解してみよう・・というスタンスでやってみます。
まずは学習用のサンプルデータを作成
ノイズの入ったランダムなデータを作成します。
それを学習して、ノイズが加わる前の「正解」を線形回帰で予測する形を3パターンほどやってみることにします。
サンプルデータ1:単回帰(変数が1つ)の一次式用
$$y=3x-1$$ の式で「-2 から 2」までの乱数をXとして計算したものです。
単回帰(変数が1つ)の一次式の場合の例です。
その結果にノイズとして「-5 から 5」の間の乱数を加算してばらけさせてます。
これを学習して、正解にどれくらい近い式を導けるかを試します。
sk_linear_sample01.py
乱数を発生させる部分のソースです。
x = np.random.rand(200, 1) * 4 -2 noise = np.random.rand(200, 1) * 10 -5 true_y = x * 3 - 1 y = true_y + noise
ちなみに 「np.random.rand()」が、0.0から1.0の乱数を返すので、それい4をかけて、2を引くことで、無理やり「-2から2」の乱数にするという方法をつかってます。
サンプルデータ2 :単回帰(変数がひとつ)の二次式用
$$y = 3x^2 + 1$$の式で「-2 から 2」までの乱数をXに計算したものです。
単回帰(変数がひとつ)の二次式の場合の式になります。
その結果にノイズとして「-5 から 5」の間の乱数を加算してばらけさせてます。
これを学習して、正解にどれくらい近い式を導けるかを試します
sk_linear_sample02.py
上記の元データを発生させる部分のソースです。
x = np.random.rand(300, 1) * 4 -2 noise = np.random.rand(300, 1) * 10 -5 true_y = 3 * x**2 + 1 y = true_y + noise
サンプルデータ3:重回帰(変数が複数・・今回は2つ)用
$$y=3x -2z -3$$ の式で「-2 から 2」までの乱数をXとして計算したものです。
重回帰(変数が複数・・今回は2つ)の例です。
その結果にノイズとして「-5 から 5」の間の乱数を加算してばらけさせてます。
これを学習して、正解にどれくらい近い式を導けるかを試します。
なお、グラフは変数2つだと3Dにした方が良いのですが、平面のグラフで表現するために、変数「x」と[z」それぞれのグラフで表現しています。
sk_linear_sample03.py
上記の元データを発生させるソースです。
x = np.random.rand(300, 1) * 4 -2 z = np.random.rand(300, 1) * 4 -2 noise = np.random.rand(300, 1) * 10 -5 true_y = 3 * x - 2 * z - 3 y = true_y + noise
scikit-learnのLinearRegressionクラスで学習・評価
学習と評価には、scikit-learnのLinearRegressionクラスを使ってみます。
LinearRegressionの和訳は「線形回帰」です。
まさにどんぴしゃですので。
結果はグラフで表示します。
元データに重ねて、予測値を赤色で表示します。
ソースは、データ生成・学習・評価・R2スコアの計算というメインの部分のみを記載しています。(グラフに表示する部分は最後にまとめて掲載します。 )
なお、今回はやり方の確認だけを目的として、横着して、学習データと予測元データに同じものを使っています。
かなり手抜きですが・・すいません。
サンプル1(単回帰一次式)
結果のグラフはこんな感じです。
ソースはこんな感じ。
sk_linear_sample01.py
import matplotlib.pyplot as plt import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score # -2から2の範囲で乱数を生成する x = np.random.rand(200, 1) * 4 - 2 # ノイズは-5から5の範囲で乱数を生成する noise = np.random.rand(200, 1) * 10 - 5 # 正解のyを計算する y=3x-1 true_y = x * 3 - 1 # ノイズを加える y = true_y + noise # クラスオブジェクトの生成 model = linear_model.LinearRegression() # 学習する model.fit(x, y) # 予測する predict_y = model.predict(x) plt.scatter(x, y, marker="+", color="b") plt.scatter(x, predict_y, color="r") # 決定係数を計算する方法1 r2 = model.score(x, true_y) print(r2) # 決定係数を計算する方法2 r2_score = r2_score(true_y, predict_y) print(r2_score) plt.show()
サンプル2(単回帰二次式)
結果のグラフはこんな感じです。
ソースはこんな感じ。
sk_linear_sample02.py
import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score #-2から2の範囲で乱数を生成する _x = np.random.rand(300) * 4 -2 #ノイズは-5から5の範囲で乱数を生成する noise = np.random.rand(300) * 10 -5 #正解を求める y=3x**2+1 _true_y = 3 * _x**2 + 1 #ノイズを加える _y = _true_y + noise #エラー回避のため、reshapeする x = np.reshape(_x,(-1, 1)) y = np.reshape(_y,(-1, 1)) true_y = np.reshape(_true_y,(-1, 1)) #クラスインスタンスを生成 model = linear_model.LinearRegression() #学習する(xは二乗であたえる) model.fit(x**2, y) #予測する(xは二乗であたえる) predict_y = model.predict(x**2) #決定係数を求める方法1 r2 = model.score(x**2,true_y) print(r2) #決定係数を求める方法2 r2_score = r2_score(true_y,predict_y) print(r2_score)
サンプル3(重回帰2変数)
結果のグラフです。
うーん。
視覚的にはわかりづらいです。
ソースはこんな感じ。
sk_linear_sample03.py
import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score #変数2つを、-2から2の範囲の乱数で初期化 _x = np.random.rand(300) * 4 -2 _z = np.random.rand(300) * 4 -2 #ノイズはー5から5の範囲の乱数で生成する noise = np.random.rand(300) * 10 -5 #正解をもとめる y=3x - 2z -3 _true_y = 3 * _x - 2 * _z - 3 #ノイズを加える _y = _true_y + noise #引数に使うため、2変数をまとめる x_z = np.c_[_x,_z] #エラー回避のためreshapeする y = np.reshape(_y,(-1, 1)) true_y = np.reshape(_true_y,(-1, 1)) #クラスオブジェクトを生成 model = linear_model.LinearRegression() #学習する model.fit(x_z, y) #予測する predict_y = model.predict(x_z) r2 = model.score(x_z,true_y) print(r2) r2_score = r2_score(true_y,predict_y) print(r2_score) plt.tight_layout()
ソースをちょっとだけ補足します。
x_z = np.c_[_x,_z]
の部分です。
重回帰(複数変数)の場合、変数が2つ以上あるのに、fitやpredictの引数はひとつしか与えられないので、変数を一本化する必要があるということです。
例えば
- X=[0.72798292 0.40134744]
- Z=[0.4321639 0.12507638]
だった場合、np.c_[X,Z] をすると、以下のように変換されます。
[
[0.72798292 0.4321639 ]
[0.40134744 0.12507638]]
これは便利なんですよね。
共通の補足事項:決定係数
各サンプルでは決定係数を計算しています。
決定係数というのは、予測精度の客観的な指標です。
決定係数の求め方ですが、使われるやり方が2通りあります。
linear_model.LinearRegressionクラスのscoreメソッドを使う方法がひとつ。
r2 = model.score(x,true_y)
もうひとつは、sklearn.metrics.r2_scoreを使う方法です。
from sklearn.metrics import r2_score
r2_score = r2_score(true_y,predict_y)
どちらも得られる結果は同じです。
ちなみに「true_y」は正解、「predict_y」は予測結果となりますが、学習データと予測元データに同じものを使っているので上記の結果は0.99を超えてます(笑)。
普通は、0.5を超えたらまあまあ・・くらいの目安らしいのですけどね。
共通の補足事項:ありがちなエラー
変数「x」「y」で学習評価する場合の基本形はこんな感じです。
from sklearn import linear_model #クラスオブジェクト生成 model = linear_model.LinearRegression() #学習の実行 model.fit(x, y) #評価の実行 predict_y = model.predict(x)
model.fitやmodel.predictに渡す時に、以下のようなエラーがでるときがあります。
ValueError: Expected 2D array, got 1D array instead:
これは、たとえば以下のように次元を明示しないで生成したようなデータとかがはいってきた時に発生します。
x = np.random.rand(200) * 4 -2
この例だと、本来以下のように、shapeを指定してなければいけないところ、されていないので次元があっていないわけです。
x = np.random.rand(200,1) * 4 -2
まあ、この例のように自分でデータを生成しているのなら、発生元を直せばいいのですが、そうでなくて外部からデータを受け取る場合もあるので、その場合は、model.fit等に渡す前にreshapeしてやる必要があります。
たとえば、上記の例だと。
_x = np.random.rand(200) * 4 -2 noise = np.random.rand(200) * 10 -5 _y = _x * 3 - 1 + noise x = np.reshape(_x,(-1, 1)) y = np.reshape(_y,(-1, 1)) model = linear_model.LinearRegression() model.fit(x, y) predict_y = model.predict(x)
こんな感じです。
最後に全ソースです
上記ではグラフを描く部分をはしょったので、最後に全ソースを掲載しておきます。
サンプル1
import matplotlib.pyplot as plt import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score #-2から2の範囲で乱数を生成する x = np.random.rand(200,1) * 4 -2 #ノイズは-5から5の範囲で乱数を生成する noise = np.random.rand(200,1) * 10 -5 #正解のyを計算する y=3x-1 true_y = x * 3 - 1 #ノイズを加える y = true_y + noise #クラスオブジェクトの生成 model = linear_model.LinearRegression() #学習する model.fit(x, y) #予測する predict_y = model.predict(x) plt.scatter(x,y,marker="+",color="b") plt.scatter(x,predict_y,color="r") #決定係数を計算する方法1 r2 = model.score(x,true_y) print(r2) #決定係数を計算する方法2 r2_score = r2_score(true_y,predict_y) print(r2_score) plt.show()
サンプル2
import matplotlib.pyplot as plt import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score # -2から2の範囲で乱数を生成する _x = np.random.rand(300) * 4 - 2 # ノイズは-5から5の範囲で乱数を生成する noise = np.random.rand(300) * 10 - 5 # 正解を求める y=3x**2+1 _true_y = 3 * _x**2 + 1 # ノイズを加える _y = _true_y + noise # エラー回避のため、reshapeする x = np.reshape(_x, (-1, 1)) y = np.reshape(_y, (-1, 1)) true_y = np.reshape(_true_y, (-1, 1)) # クラスインスタンスを生成 model = linear_model.LinearRegression() # 学習する(xは二乗であたえる) model.fit(x**2, y) # 予測する(xは二乗であたえる) predict_y = model.predict(x**2) plt.scatter(x, y, marker="+", color="b") plt.scatter(x, predict_y, color="r") # 決定係数を求める方法1 r2 = model.score(x**2, true_y) print(r2) # 決定係数を求める方法2 r2_score = r2_score(true_y, predict_y) print(r2_score) plt.show()
サンプル3
import matplotlib.pyplot as plt import numpy as np from sklearn import linear_model from sklearn.metrics import r2_score # 変数2つを、-2から2の範囲の乱数で初期化 _x = np.random.rand(300) * 4 - 2 _z = np.random.rand(300) * 4 - 2 # ノイズはー5から5の範囲の乱数で生成する noise = np.random.rand(300) * 10 - 5 # 正解をもとめる y=3x - 2z -3 _true_y = 3 * _x - 2 * _z - 3 # ノイズを加える _y = _true_y + noise # 引数に使うため、2変数をまとめる x_z = np.c_[_x, _z] # エラー回避のためreshapeする y = np.reshape(_y, (-1, 1)) true_y = np.reshape(_true_y, (-1, 1)) # クラスオブジェクトを生成 model = linear_model.LinearRegression() # 学習する model.fit(x_z, y) # 予測する predict_y = model.predict(x_z) plt.subplot(1, 2, 1) plt.scatter(_x, y, marker="+", color="b") plt.scatter(_x, predict_y, color="r") plt.xlabel('x') plt.subplot(1, 2, 2) plt.scatter(_z, y, marker="+", color="b") plt.scatter(_z, predict_y, color="r") plt.xlabel('z') r2 = model.score(x_z, true_y) print(r2) r2_score = r2_score(true_y, predict_y) print(r2_score) plt.tight_layout() plt.show()
今回はこのへんで。
ではでは。