概要
いま Chainer を学習中です。本来の Chainer の使い方ではないのですが、chainer.Link と chainer.Optimizer という一番基本的なクラスだけを用いて、ベタに単回帰を実装したらどうなるか?という実験を行いました(もちろん、実際に単回帰を行いたいときは別の方法でやったほうがいいです)。
単回帰
3点 $ (1, 1), (2, 3), (3, 4) $ のなるべく近くを通る直線 $ y_p = wx + b $ を考え、その傾き $ w $ と 切片 $ b $ を求めることを考えます。
$i$ 番目の点を$(x^{(i)},y^{(i)})$と表すと、平均二乗誤差 $loss$
loss = (1/3) \sum_{i=1}^{3} ((wx^{(i)} + b) - y^{(i)})^2
が最小になるように $w$ と $b$ を定めることにします。
実際に別のツールで計算してみると、
w = 1.5, b = -0.333333
となります。
この計算を以下で Chainer を使ってやってみます。
コード
import numpy as np
import chainer
class SimpleRegression(chainer.Link):
def __init__(self):
super().__init__(
w = (1),
b = (1),
)
self.w.data = np.array([2], dtype = np.float32)
self.b.data = np.array([0], dtype = np.float32)
def __call__(self, x):
return (self.w * x) + self.b
def lossfun(model, xs, ys):
loss = 0
for i in range(len(xs)):
yp = model(xs[i])
z = yp - ys[i]
loss += z * z
return loss / len(xs)
def main():
xs = [1, 2, 3]
ys = [1, 3, 4]
model = SimpleRegression()
optimizer = chainer.optimizers.SGD(lr = 0.1)
optimizer.setup(model)
for i in range(500):
model.zerograds()
print("=== Epoch %d ===" % (i + 1))
print("model.w.data = %f" % model.w.data)
print("model.w.grad = %f" % model.w.grad)
print("model.b.data = %f" % model.b.data)
print("model.b.grad = %f" % model.b.grad)
loss = lossfun(model, xs, ys)
print("loss = %f" % loss.data)
print("")
loss.backward()
optimizer.update()
main()
実行結果
=== Epoch 1 ===
model.w.data = 2.000000
model.w.grad = 0.000000
model.b.data = 0.000000
model.b.grad = 0.000000
loss = 2.000000
=== Epoch 2 ===
model.w.data = 1.400000
model.w.grad = 0.000000
model.b.data = -0.266667
model.b.grad = 0.000000
loss = 0.080000
=== Epoch 3 ===
model.w.data = 1.466667
model.w.grad = 0.000000
model.b.data = -0.240000
model.b.grad = 0.000000
loss = 0.057007
...
=== Epoch 499 ===
model.w.data = 1.500000
model.w.grad = 0.000000
model.b.data = -0.333333
model.b.grad = 0.000000
loss = 0.055556
=== Epoch 500 ===
model.w.data = 1.500000
model.w.grad = 0.000000
model.b.data = -0.333333
model.b.grad = 0.000000
loss = 0.055556
$ w $(model.w.data
) = 1.5, $b$(model.b.data
) = - 0.333333 という解に収束しているのがわかります。
解説
SimpleRegression
は、chainer.Link
クラスのサブクラスで、$ y_p = wx + b $ というモデルを表現しています(仮説関数(hypothesis function))。それに対して、lossfun
関数が、このモデル(仮説関数)を使った場合の平均二乗誤差を計算しています。この関数の戻り値が chainer.Variable
オブジェクトであることに注意してください。loss.backward()
で、$w$ と $b$ の傾き(model.w.grad
と model.b.grad
) を計算し、optimizer.update()
で $w$ と $b$ (model.w.data
と model.b.data
) を更新していきます。