線形回帰のまとめ(その3、Pythonで実践してみる)
前回は、線形回帰の手法として用いられる、最急降下法について説明しました。
今回は、これまで導いてきた式を使って、実際に最急降下法を試してみたいと思います。
私の環境は、Python3で作った、Jupyter Notebook上で実行しています。
これで特徴量1、それに相関するデータ1、いくらかのノイズを乗せたデータセットがファイルに出力されました。
グラフにプロットしてみましょう。
解は
w0=3.40246005
w1=71.96840595
となりました。
とりあえずα=0.1として、1000回ループしています。
同様の解が出力されました。
同じくα=0.1として、1000回ループしています。
また同じ解が出力されましたね。
αもループ回数も指定する必要がありません…
(上で説明したやつは何だったんだ、って思っちゃいますが…)
LinearRegressionをインスタンス化して、fitの一言で終わってしまいます。最強です。
もちろん解は同じですね。
これで線形回帰はマスターしました。次は、4通りの方法でのパフォーマンスを比較してみましょうか。
今回は、これまで導いてきた式を使って、実際に最急降下法を試してみたいと思います。
私の環境は、Python3で作った、Jupyter Notebook上で実行しています。
テストデータの生成
ライブラリScikitLearnを使えば、相関のあるランダムなデータセットを作ることができます。# 必要なライブラリをロード
%matplotlib inline
import numpy as np
import time
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
from sklearn import datasets
import os
font = {'family':'IPAexGothic'}
plt.rc('font', **font)
# テストデータを作成
if not os.path.isdir("./data"):
os.makedirs("./data")
if os.path.isfile("./data/linear1.dat") == True:
data = np.loadtxt("./data/linear1.dat", delimiter = ",")
X = data[:,0][:,np.newaxis]
Y = data[:,1][:,np.newaxis]
else:
X, Y = datasets.make_regression(n_samples=100, n_features=1, n_informative=1, noise=50)
np.savetxt("./data/linear1.dat", np.c_[X, Y], delimiter=",")
Y = Y[:,np.newaxis]
これで特徴量1、それに相関するデータ1、いくらかのノイズを乗せたデータセットがファイルに出力されました。
グラフにプロットしてみましょう。
# (x, y)データをプロットする関数
def plotData(X, Y):
plt.xlim([-4,4])
plt.ylim([-250,250])
plt.scatter(X,Y)
plt.xlabel(u'X')
plt.ylabel(u'Y')
plt.draw()
plotData(X, Y)
数式で解く
まずは数式にあてはめれば解が出ますので、当てはめてみましょう。シグマだらけの式でしたが、sumは関数でサクッと計算してくれます。# 数式で解を求める m = Y.size w0 = (sum(X**2) * sum(Y) - sum(X) * sum(X*Y)) / (m * sum(X**2) - sum(X)**2) w1 = (sum(X) * sum(Y) - m * sum(X*Y)) / (sum(X)**2 - m * sum(X**2)) w0,w1(array([3.40246005]), array([71.96840595]))
解は
w0=3.40246005
w1=71.96840595
となりました。
行列を使わない力技で最急降下させる
次はw0, w1の変位量をそれぞれ計算し移動させていく方法です。とりあえずα=0.1として、1000回ループしています。
# 目的関数
def computeCost(X, Y, w0, w1):
m = Y.size
L = 0
for i in range(m):
L = L + (w0 + w1 * X[i] - Y[i])**2 / (2 * m)
return L
# w0, w1を低い方向へ移動させる関数
def doDescent(X, Y, w0, w1, alpha):
m = Y.size
w0 = w0 - sum(w0 + w1 * X -Y) * alpha / m
w1 = w1 - sum((w0 + w1 * X - Y) * X) * alpha / m
return w0, w1
alpha = 0.1
for i in range(1000):
w0, w1 = doDescent(X, Y, w0, w1, alpha)
w0,w1
(array([3.40246005]), array([71.96840595]))同様の解が出力されました。
行列を使った方法で最急降下させる
続いて行列を使った魔法のような式を使ってベクトルWを移動させていく方法です。同じくα=0.1として、1000回ループしています。
# 行列を使った最急降下法 # Xはデータ列のみなので、1列目に1の列を追加してやる # (1, x^(i))の形の行列を作る Xb=np.c_[np.ones([X.size,1]),X] # Wの初期値(0,0)を作る W=np.zeros([2,1])
# 行列版の目的関数
def computeCost(Xb, Y, W):
m = Y.size
L = ((Xb.dot(W) - Y)**2).sum() / (2 * m)
return L
# 下りのベクトル方向へWを動かす関数
def doDescent(Xb, Y, W, alpha):
m = Y.size
W = W - (alpha / m) * Xb.T.dot(Xb.dot(W)-Y)
return W
alpha = 0.1
for i in range(1000):
W = doDescent(Xb, Y, W, alpha)
(array([3.40246005]), array([[71.96840595]]))また同じ解が出力されましたね。
ScikitLearnのライブラリを使う
ScikitLearnの中には、線形回帰のライブラリがあります。αもループ回数も指定する必要がありません…
(上で説明したやつは何だったんだ、って思っちゃいますが…)
LinearRegressionをインスタンス化して、fitの一言で終わってしまいます。最強です。
from sklearn.linear_model import LinearRegression linear = LinearRegression()
linear.fit(X, Y) linear.intercept_, linear.coef_(array([3.40246005]), array([[71.96840595]]))
もちろん解は同じですね。
最後に、プロットして確認
y=w0 + w1xをグラフにプロットして、正しく計算されたか見てみましょう。def plotDataWithLine(X, Y, w0, w1):
plotData(X, Y)
plt.hold(True)
plt.plot([-4, 4], [(-3)*w1+w0, 3*w1+w0])
plt.show()
ばっちりですね。これで線形回帰はマスターしました。次は、4通りの方法でのパフォーマンスを比較してみましょうか。