動機
「とりあえず今ここで3層NNの学習器フルスクラッチしてね効率気にしないでいいから」と言われて15分くらいで出来たら上出来だと思う。NumPyとかはなし
— Yusuke Oda (@odashi_t) 2019年5月3日
という@odashi_tさんのツイートを真に受けたので実際にやってみました。15分で出来たら凄いということを達成したかったけど、結果的に3時間くらいかかったので私はニューラルネットの理解が足りないなとつくづく感じました。
やったこと
3層NNを作るために必要なクラスを一通り実装し、それに基づいて学習ループを書いた。Chainerしか知らないのでどうしてもChainer風になってしまった。
タスクは「3入力1出力の足し算」です。訓練データは10個を使って学習し、最後にテストデータ4個で結果を見ている。どのテストも正解から0.2くらいズレている。
出力結果
epoch 0: y = 6.944188825159266
epoch 1: y = 6.838310105649193
epoch 2: y = 6.743423697756349
...
epoch 96: y = 5.853637753722643
epoch 97: y = 5.853626668028286
epoch 98: y = 5.853616573368178
epoch 99: y = 5.853607381144707
test:
8 7.896960108841725
3 2.7903926894351105
21 21.174035399298916
22 22.195348883180234
所感
データ少ないし精度は微妙だが、まあまあそれっぽい結果が出ている。
ニューラルネット楽しいよの気持ちを再認識できたので良かった。
以上。
コード
3LNN.py
import random
napier = 2.71828
lrate = 0.0001
epoch = 100
class node():
def __init__(self, edge):
self.b = 0
self.edge = edge
def forward(self, x):
if self.edge == None:
return self.b
return self.edge.w * x + self.b
def backward(self, dx):
self.b += lrate * dx
if self.edge == None:
return
self.edge.w += lrate * dx
class edge():
def __init__(self):
self.w = random.random()
class layer():
def __init__(self, node_num):
self.nodes = []
for _ in range(node_num):
self.nodes.append(node(edge()))
def __call__(self, x):
self.x = x
ans = []
for n in self.nodes:
a = 0
for e in x:
a += n.forward(e)
ans.append(a)
return ans
def update(self, dx):
for n in self.nodes:
n.backward(dx)
def ewise(x, func):
for e in x:
e = func(e)
return x
def sigmoid(x):
return 1 / (1 + napier**(-x))
class LNN():
def __init__(self, inputs, hidden, outputs):
self.l1 = layer(inputs)
self.l2 = layer(hidden)
self.l3 = layer(outputs)
def forward(self, x):
self.x = x
h = ewise(self.l1(x), sigmoid)
h = ewise(self.l2(h), sigmoid)
h = ewise(self.l3(h), sigmoid)
return h[0]
def backward(self, y, t):
dx = t-y
self.l3.update(dx)
self.l2.update(dx)
self.l1.update(dx)
# main #
data = [[1, 2, 3], [4, 4, 5], [5, 1, 2], [3, 5, 7], [2, 8, 9], # add 3 elements
[2, 3, 6], [3, 4, 5], [6, 7, 8], [1, 9, 7], [2, 2, 2]]
ans = [6, 13, 8, 15, 19, 11, 12, 21, 17, 6]
def train(data, ans, model):
for x, t in zip(data, ans):
y = model.forward(x)
model.backward(y, t)
return model, y
def trainer(epoch):
model = LNN(3, 3, 1)
for i in range(epoch):
model, y = train(data, ans, model)
print('epoch {}: y = {}'.format(i, y))
return model
model = trainer(epoch)
print('test:')
test = [[[3, 4, 1], 8], [[1, 1, 1], 3], [[5, 8, 8], 21], [[10, 4, 8], 22]]
for x in test:
print(x[1], model.forward(x[0]))