機械学習基礎の基礎: 確率的勾配降下法の実装

前回のデータとコードを改造してStochastic gradient descent method(SGD)を実装してみました。
と言っても、大した改造ではなくパラメータの更新を1つの訓練サンプルごとに行うようにしただけです。
np.random.permutation(X.shape[0])
で、いったんランダムにデータを並べなおして、そこから一つずつデータを取り出してデータの数の回数だけパラメータを更新します。
最後に、平均誤差をプロットして学習が進むごとに誤差が小さくなっていることを確認してみました。

###############
# SGDの実装
###############
import numpy as np
import matplotlib.pyplot as plt

### (1) データ読み込み ###
trainData = np.loadtxt('kodomoShinchouTaijyu.txt', delimiter=',', skiprows=0)
trainDataX = trainData[:, 0]
trainDataY = trainData[:, 1]

### (2) データの標準化
mu = trainDataX.mean() # 平均
sigma = trainDataX.std() # 標準偏差

def standaradize(x):
	return (x - mu)/sigma

trainDataXSTD = standaradize(trainDataX)

# fitting 関数を2次関数にする
# y = theta0 + theta1 * x + theta2 * x^2

# 学習データをマトリクスとしてまとめる
# 2次関数でfittingするので2次まで
def toMatrix(x):
	return np.vstack([np.ones(x.shape[0]), x, x**2]).T

X = toMatrix(trainDataXSTD)

# パラメータの初期値
theta = np.random.rand(3)

### 線形fitting ###
# fitting関数 (線形回帰)
def f(x):
	return np.dot(x, theta)

# MSE関数(最小2乗の損失関数)
def E(x, y):
	return 0.5 * np.sum( (y - f(x)) ** 2)

# 平均2乗誤差
def MSE(x, y):
	return (1.0/x.shape[0] * np.sum((y - f(x))**2 ))

### 学習 ###
eta = 1e-2 #学習率
diff = 1 # 誤差

# 平均誤差を記録する
errors = []

# 学習する
errors.append(MSE(X, trainDataY))

while diff > 1e-3: # 損失関数の変化が小さくなるまでループする
	# 学習データをランダムにシャッフルする
	n = np.random.permutation(X.shape[0])
	# ランダムにシャッフルした学習データの中から一つ抜き出してパラメータを更新していく(確率的勾配降下法)
	for x, y in zip(X[n, :], trainDataY[n]):
		theta = theta - eta * (f(x) - y) * x
	# 平均誤差を配列に追加
	errors.append(MSE(X, trainDataY))
		# 1つ前のステップの誤差との差分
	diff = errors[-2] - errors[-1]

### 結果をグラフにする
x = np.linspace(-2, 2, 100)
plt.plot(trainDataXSTD, trainDataY, 'o')
plt.plot(x, f(toMatrix(x)))
plt.show()

# 平均誤差をグラフにする
x = np.arange(len(errors))
plt.plot(x, errors)
plt.show()

平均誤差の推移は以下のようになりました。20回ほどのイテレーションでほぼ収束していることがわかります。
f:id:wshinya:20180325183703p:plain