一応動くところまで。
https://pycall-mnist.herokuapp.com
GIFではうまく言ってるようにみえるけど思ったより認識精度よくない。
参考アプリでは結構入力の画像をいじってたのでその辺りの工夫が必要かも。
一応動くところまで。
https://pycall-mnist.herokuapp.com
GIFではうまく言ってるようにみえるけど思ったより認識精度よくない。
参考アプリでは結構入力の画像をいじってたのでその辺りの工夫が必要かも。
今までは100個のサンプルデータとかでやってたんだけど、概ね動いてきたので実データで。
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/train_neuralnet.py とほぼ同じ処理をしているはず。
$ ruby train.rb 0 10000 100 data/params.json
10000 / 10000
      user     system      total        real
351.320000  38.590000 389.910000 (183.712062)
3分ぐらいかかった。

結果はいい感じ。
$ ruby predict.rb 
7 == 7: o
2 == 2: o
1 == 1: o
0 == 0: o
4 == 4: o
1 == 1: o
4 == 4: o
9 == 9: o
6 == 5: x
9 == 9: o
      user     system      total        real
  0.040000   0.000000   0.040000 (  0.020253)
こちらは全然時間かからず。精度も大丈夫そう。
O'Reilly Japan - ゼロから作るDeep Learning
下のような感じで書き方が変わる。
第4章のバージョンについては説明がないので、誤差逆伝播法の理解を深めるためにもRuby化してみる。
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/two_layer_net.py
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}
        batch_num = x.shape[0]
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        da1 = np.dot(dy, W2.T)
        dz1 = sigmoid_grad(a1) * da1
        grads['W1'] = np.dot(x.T, dz1)
        grads['b1'] = np.sum(dz1, axis=0)
        return grads
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch05/two_layer_net.py
    def gradient(self, x, t):
        # forward
        self.loss(x, t)
        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        # 設定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
        return grads
dはデルタ(δ)のd。
f(x, y) = x + yという関数があったら、
xの偏微分はδf/δx、
yの偏微分はδf/δy、
と書く。
Ruby版はこんな感じになった。
dがdeltaの頭文字ということがわかっていれば、やっていることはレイヤー版と何ら変わりないことがわかるので、あとは本を読めば良い。
sigmoid_gradはSigmoidレイヤーのbackwardメソッドと同じ意味。(レイヤー版ではself.outに保持していたものを引数で渡すようにした)
  def gradient(x, t)
    grads = {}
    batch_num = x.shape[0]
    forward = predict(x)
    w2, a1, z1, a2, y = forward[:w2], forward[:a1], forward[:z1], forward[:a2], forward[:y]
    dy = (y - t) / batch_num
    grads[:W2] = np.dot.(z1.T, dy)
    grads[:b2] = np.sum.(dy, 0)
    da1 = np.dot.(dy, w2.T)
    dz1 = Util.sigmoid_grad(z1) * da1
    grads[:W1] = np.dot.(x.T, dz1)
    grads[:b1] = np.sum.(dz1, 0)
    return grads
  end
O'Reilly Japan - ゼロから作るDeep Learning
誤差逆伝播法は2を効率化するもの。
全然本書とは関係ない話。
積分も本書で扱ってる微分と同じ。
数値積分はわかりやすい。
誤差逆伝播法みたいに積分を効率的にやる手法もあるのかな?
https://github.com/redis-store/redis-rails#usage
には、
MyApplication::Application.config.session_store :redis_store, {
  servers: [
    {
      host: "localhost",
      port: 6379,
      db: 0,
      password: "mysecret",
      namespace: "session"
    },
  ],
  expires_in: 90.minutes
}
という例があり、ほぼこのまま書いていたアプリがあった。
しかし、iPhoneのSafariで頻繁にログアウトされるという報告があり、改めて確認するとPCでもブラウザを再起動するとCookieが消えていた。
これはCookieにExpiresが付いていない時の挙動である。
そこで、真っ先に疑われたのがこの設定。Rails標準のCookieStoreでは、expire_afterを設定するのでそちらに変更してみたら解消した。
http://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html 
とはいえREADMEに書いてあるので、中身を一応追ってみる。
redis-railsには実コードはないけどテストによると、session_storeの実体にはActionDispatch::Session::RedisStoreが使われる。
https://github.com/redis-store/redis-rails/blob/master/test/redis_rails_test.rb
ActionDispatch::Session::RedisStoreはredis-actionpackで定義され、Rack::Session::Redisを継承している。
https://github.com/redis-store/redis-actionpack/blob/master/lib/action_dispatch/middleware/session/redis_store.rb
Rack::Session::Redisはredis-rackで定義され、Rack::Session::Abstract::IDを継承している。
https://github.com/redis-store/redis-rack/blob/master/lib/rack/session/redis.rb
Rack::Session::Abstract::IDはrackで定義され、さらにRack::Session::Abstract::Persistedを継承している。
https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb
(Persistedも同一ファイルに定義)
ここにexpire_afterはあるが、expires_inは見当たらない。
ActionDispatch::Session::AbstractStoreを継承して、それがさらにRack::Session::Abstract::Persistedを継承している。
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
つまり結局Rack::Session::Abstract::Persistedが使われる。クラス階層の途中でexpires_inを見てそれらしき処理をしてるクラスがいない場合、READMEが間違っている、でよさそう。
ぷるり出してみようかな。
→ 出してみた https://github.com/redis-store/redis-rails/pull/64
→ 無事マージされた