[読書メモ] ゼロから作るDeep Learning: 第6章

O'Reilly Japan - ゼロから作るDeep Learning

パラメータ更新手法

  • SGD
    • 確率的勾配降下法
    • 単純
    • やみくもにパラメータを探すよりは賢い
  • optimizer
    • SGDというクラスを作り、パラメータの更新はそこでやる
    • 入れ替え可能に
  • SGDの欠点
    • 関数によっては勾配方向に進むのがかなり非効率な場合がある
    • 等方的でない関数
  • Momentum
    • 運動量
    • 速度という概念を使い、物理法則に準じる動き
    • 等方的でない関数も効率的に
  • AdaGrad
    • パラメータの減衰
    • Adaptive、適応的
    • 学習係数をパラメータごとに調整
    • よく変化した要素は学習係数が小さくなっていく
  • Adam
    • Momentum x AdaGrad
  • どの更新手法がいい?
    • 得意な問題が違う
    • SGDはよく使われる
    • 最近はAdamが人気
    • MNISTの学習はAdaGradが得意

重みの初期値

  • 重みの初期値で学習の成否が分かれる
  • 小さい方がいい
    • 0にすればいい?
    • 重みが均一だと意味がない
  • 標準偏差1のガウス分布
    • 勾配消失
  • 標準偏差 0.01のガウス分布
    • 勾配消失は起きないが、偏りがある
    • シグモイド関数の表現力に制限
    • 複数のニューロンが存在する意味が薄れる
  • Xavierの初期値
    • 適度な広がり
    • 線形関数向け
  • Heの初期値
    • 非線形なReLU向け

Batch Norm

  • Batch Normalization
  • 各層のアクティベーションの分布を強制的に広がりを持つように焼成する
  • メリット
    • 学習が速い(学習係数を大きくできる)
    • 初期値にあまり依存しない
    • 過学習を抑制できる

正則化

  • 過学習の原因
    • パラメータが大量で、表現力の高いモデル
    • 訓練データが少ない
  • Weight decay
    • 荷重減衰
    • 大きな重みにペナルティを課すことで過学習を抑制
  • Dropout
    • Weight decayは実装簡単だが、複雑なモデルへの対応は困難
    • 訓練時にランダムにニューロンを消去
    • テスト次じ消去した割合を乗算

ハイパーパラメータの検証

  • テストデータで調整してはダメ
    • テストデータで過学習してしまう
  • 検証データ
    • ハイパーパラメータの良さを評価するためのデータ
    • MNISTの場合は訓練データから20%ぐらい先に分離しておく
    • データに偏りが出ないようにシャッフルしておく

TODO

  • SGDを別クラス化 → 済
  • ハイパーパラメータの検証

[MySQL] rootなのにGRANTできない

MySQLの5.1を間違えて入れたあと、すぐ5.5にして、mysql_upgradeを叩いていなかったという凡ミス…。

参考:mysqlで適切な権限があるにも関わらずgrant文でエラーが出た場合の対処法 | Skyarch Broadcasting

PyCallで手書き数字認識のWebアプリ

一応動くところまで。
https://pycall-mnist.herokuapp.com

GIFではうまく言ってるようにみえるけど思ったより認識精度よくない。
参考アプリでは結構入力の画像をいじってたのでその辺りの工夫が必要かも。

参考

PyCallでMNISTの実データを使った学習・推論

今までは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)

こちらは全然時間かからず。精度も大丈夫そう。

ゼロから作るDeep Learning: 第4章のgradientをRuby化

O'Reilly Japan - ゼロから作るDeep Learning

下のような感じで書き方が変わる。
第4章のバージョンについては説明がないので、誤差逆伝播法の理解を深めるためにもRuby化してみる。

ch04/two_layer_net.py

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

ch05/two_layer_net.py

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化

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

参考