強まっていこう

あっちゃこっちゃへ強まっていくためのブログです。

ディープラーニングで実際簡単なものを学習させてみる

wolfbash.hateblo.jp

上の記事では、学習なしで AND回路を作ってみました。今回は前回のコードに対して学習のコードを加えます。

#!/usr/bin/env python

import os
import numpy as np

class NeuralNetwork():

  saveFile = 'train.npy'

  def __init__(self, inSize, outSize, hidSize):
    if os.path.isfile(NeuralNetwork.saveFile):
      w = np.load(NeuralNetwork.saveFile)
      self.w1 = w[0]
      self.w2 = w[1]
    else:
      self.w1 = np.random.randn(inSize, hidSize)
      self.w2 = np.random.randn(hidSize, outSize)

  def forward(self, x):
    self.z = np.dot(x, self.w1)
    self.z2 = self.sigmoid(self.z)
    self.z3 = np.dot(self.z2, self.w2)
    return self.sigmoid(self.z3)

  def backward(self, data, result, check):
    delta = (result - check) * self.sigmoidPrime(check)
    error = delta.dot(self.w2.T)
    delta2 = error * self.sigmoidPrime(self.z2)
    self.w1 += data.T.dot(delta2)
    self.w2 += self.z2.T.dot(delta)

  def train(self, data, result):
    self.backward(data, result, self.forward(data))

  def save(self):
    np.save(NeuralNetwork.saveFile, np.array([ self.w1, self.w2 ]))

  def predict(self, x):
    print("""
[Prediction]
Data: {}
Result: {}
    """.format(x, self.forward(x)))

  def sigmoid(self, x): return 1 / (1 + np.exp(-x))

  def sigmoidPrime(self, x): return x * (1 - x)

if __name__ == '__main__':
  sample = np.array(([ 0, 0 ], [ 0, 1 ], [ 1, 0 ], [ 1, 1 ]))
  sampleResult = np.array(([ 0 ], [ 1 ], [ 1 ], [ 1 ]))

  nn = NeuralNetwork(2, 1, 3)
  for i in range(1000):
    print("""
# {}
Data: {}
Result: {}
Prediction: {}
Diff: {}
    """.format(i, sample, sampleResult, nn.forward(sample), np.mean(np.square(sampleResult - nn.forward(sample)))))
    nn.train(sample, sampleResult)
  nn.save()

  challenge = np.array([ 1, 1 ])
  nn.predict(challenge)

このコードでは、学習が終了次第 train.npy に対して学習結果を保存します。
np は save と load の便利なメソッドを持っているのでそれを利用して、学習結果である重みを保存しています。
一度学習したら次回はそのセーブデータからデータをロードして初期値として使います。叩けば叩くほどどんどん賢くなっていくと言うわけです。

前回の NeuralNetwork と比べ増えているメソッドとして、backward、train、save、predict、sigmoidPrime があります。

forward から学習結果が吐き出されそれを、backward に叩き込み、実際の正解を比べてみて、重みを微調整して行きます。

(この例ではかなり簡易化しているのですが、通常は forward の処理が何個もあるので、backward の処理は forward と逆の順番で次々と処理を流していくことになります。)

backwardの詳しい処理に関しては数学の偏微分、全微分あたりがもりもり出てくるので、追わない方が良いです。幸せになれません。
sigmoidPrime をかましてふんふんすると良い感じになるんだな、ぐらい思っておいて OK です。ディープラーニングで大事なのって数学の知識ではないので。
そこいらは、数学好きな変態達が便利なものを作ってくれるのでそれを使うだけで良い、ぐらいに思っておいてください。

エフェクターの作りは知らんが、そいつらを使って音は作れる、そんなノリがマジで大事ですんで。そうしないと心がブチ折られ続けます。

世のドキュメントはまじで数学知識を要求しすぎですって。普通に使う分には要らないでしょ・・・。
実は簡単すぎるからってそこで敷居を上げたがっているとしか思えない。

で、結局深いことする時は数学知識がトップランクの連中がよってたかっても解らず、なんとなくいろいろ数値いじって関数かまして、なんとなくうまく行ったヨ!とかやってんでしょ?
秘伝のレシピを作るノリで。じゃぁ最初からなんとなくうまく行ったよ!をみんなにやらせるべきだと思いますわ。

今回こそボヤくまい、と思ったのに結局ボヤくと言うね。

話戻します。

train は実際の学習処理になります。サンプルデータとその回答データを食わせて学習させます。

学習は、1000 回ループで回しています。やればやるほど正解率が上がっていきますが、物によってはやりすぎも良くなかったりします。過学習とか言う状態です。そういう話もまたおいおい。

学習させた NeuralNetwork に値を与えて予想させるのが、predict になります。

ある程度学習を回した回路に 1, 1 を与えると

[Prediction]
Data: [1 1]
Result: [0.98882596]

が返ってきます。0.98 ですが、まぁ 1 に近い値が出てますね。なんじゃこのポンコツマシーンはと思いましたか?こんなもんなんす。

で、これは回帰って言います。ディープラーニングには複数種類があります。回帰は株価予想や業績予想など任意のデータから結果の数字を予想するものです。
一方分類ってのがあります。これは、グループ分けになります。その他諸々ありますが、まぁそんなのもおいおい。

次回は、今回みたいな糞の役にも立たないポンコツマシーンではなくて、もうちょっとマシな代物を作って行きます。