-Python- 最小二乗法と評価方法

回帰を行ううえで,最小二乗法がよく用いられます.最小二乗法についてのメモを以下に示します.
対象を何らかの観測によって得られた平面上の点の関係として,その関係を最も的確に表す直線を考えます.一般的に全ての点を通る直線が得られることは,ほぼありません.また,観測結果には観測誤差も含まれます.なので,真実を表す直線からも観測された点が外れているということが発生します.
例として,観測によって得られた点を
    {(x1, y1),(x2, y2),(x3, y3), (x4, y4)}
として,直線を
    y = f(x)
で表すものとします.このとき観測された値 y1, y2, y3, y4と予測値f(x1), f(x2), f(x3), f(x4)のさである yi - f(xi) の総和が最も小さくなる直線を考えます.
総和を考えるにあたって,差にはプラス/マイナスの両方があるため,単に足し合わせるだけでは打ち消しあってしまう可能性があります.また,絶対値の足し合わせは数式内で扱って行くうえで面倒が生じます.そこで,差を二乗することを考えます.そうすれば,符号は全てプラスになり,総和の最小値となる組み合わせを考えれば,絶対値の総和の最小値が得られます.すなわち,以下の式が最小になるようなf(x)を求めればよいことになります.
    (y1 - f(x))^2 + (y2 - f(x))^2 + (y3 - f(x))^2 + (y4 - f(x))^2

上記が最小二乗法のアイデアになります.なお,f(x)は線形一次式の直線を表す式である必要はありません.

scikit-learn には最小二乗法を実装する sklearn.linear_model.LinearRegressionがあります.以下に実装例を示します.最も簡単な例として線形単回帰の例を示します.

単回帰が1変数で成り立つ式を想定する時の変数とは,あるデータセットにおける一つの属性値を指します.以下の実装例では,明らかに y = ax + b の関係になるようなデータを作り,回帰問題を解いて見ます.例として
    y = 3x - 2
としてデータを作成します.

 
import matplotlib.pyplot as plt
import numpy as np
 
x = np.random.rand(100, 1# Make 100 random numbers up to 0-1
x = x * 4 - 2               # Change the value range to -2 - 2
 
y = 3 * x - 2
 
plt.scatter(x, y, marker = '+')

 

plt.show()
 
上記のスクリプトを実行すると,以下のような図が描画されます.

f:id:HidehikoMURAO:20181012124200p:plain

このxとyの関係について最小二乗法で回帰直線を求めます.最小二乗法は以下のように計算します.
 
from sklearn import linear_model
 
model = linear_model.LinearRegression()
model.fit(x, y)
 
print(model.coef_)
print(model.intercept_)
 
実行結果は以下のようになります.
[-2.]
 
当然ですが,a = 3, b = -2がも止まりました.実際の観測結果では,このように予測される直線に全ての点が乗っていることはなく,バラつきが生じます.一般に誤差は正規分布に従うことが多いので,そのような乱数を加えた例を以下に示します.
 
# import modules
import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model
 
# data of y = 3 x -2
x = np.random.rand(100, 1# Make 100 random numbers up to 0-1
x = x * 4 - 2               # Change the value range to -2 - 2
 
y = 3 * x - 2
 
y += np.random.randn(100, 1)      # Add a random number with SND (avg. 0, sd. 1)
 
# Learning
model = linear_model.LinearRegression()
model.fit(x, y)
 
# Display
print(model.coef_)          # coefficient
print(model.intercept_)     # intercept
 
# Graph display
plt.scatter(x, y, marker = '+')
plt.scatter(x,model.predict(x), marker='o')
plt.show()
 
上記のスクリプトを実行すると,以下のような結果とグラフが表示されます.
[-1.85888128]

f:id:HidehikoMURAO:20181012124249p:plain

上記の例では,y = 3.08782978 x - 1.85888128 という結果が得られました.求めた係数と切片は y = 3x -2 からずれていますが,こればバラつきが原因です.
 

続いて,2次方程式 y = ax^2 + b を想定したデータについて求めて見ます.ここでは y = 3x^2 - 2 としてデータを作成します.

# coding: UTF-8

# line feed code: LF(\n)

 

# import modules

#---+--10|----+--20|----+--30|----+--40|----+--50|----+--60|----+--70|----+--80|

import matplotlib.pyplot as plt

import numpy as np

from sklearn import linear_model

 

# data of y = 3 * x^2 -2

# ---+--10|----+--20|----+--30|----+--40|----+--50|----+--60|----+--70|----+--80|

x = np.random.rand(100, 1)   # Make 100 random numbers up to 0-1

x = x * 4 - 2           # Change the value range to -2 - 2

 

y = 3 * x**2 - 2

 

y += np.random.randn(100, 1)    # Add a random number with SND (avg. 0, sd. 1)

 

# Learning

# ---+--10|----+--20|----+--30|----+--40|----+--50|----+--60|----+--70|----+--80|

model = linear_model.LinearRegression()

model.fit(x**2, y)  # Pass x by squaring

 

# Display

# ---+--10|----+--20|----+--30|----+--40|----+--50|----+--60|----+--70|----+--80|

print(model.coef_)          # coefficient

print(model.intercept_)     # intercept

 

print(model.score(x**2, y)) # Pass x by squaring

 

# Graph display

# ---+--10|----+--20|----+--30|----+--40|----+--50|----+--60|----+--70|----+--80|

plt.scatter(x, y, marker = '+')

plt.scatter(x,model.predict(x**2), marker='o') # Send squared x to predict

 

plt.show()

 

y = ax^2 + b は2次式ですが a, b については1次式なので,線形回帰で解くことができます.今回はxを二乗しているので,LinearRegression.fit にもxを二乗した値を渡します.

実行結果は以下のようになります.

[-2.26010073]

0.9474711888019949

f:id:HidehikoMURAO:20181012213117p:plain