O'Reilly Japan - ゼロから作るDeep Learning P.112〜を参考に。

交差エントロピー誤差

P.94-95のバッチ対応版の理解に少し時間がかかった。

  • tが0の場合はlogは0になるので無視できる
  • 正解ラベルのインデックスに対してどういう出力をしたかだけが問題
  • それのlogを取りたい
  • 正解ラベルは数字なのでそのままインデックスとして使える
  • y[0, 正解ラベル], y[1, 正解ラベル]とすることで各バッチの正解ラベルに対応した出力が取れる

バッチ非対応版

    def cross_entropy_error(y, t)
      delta = 1e-7
      -np.sum.(t * np.log.(y + delta))
    end 
> t = [0,0,1,0,0,0,0,0,0,0] 
> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
> Util.cross_entropy_error(np.array.(y), np.array.(t))
=> 0.510825457099338

バッチ対応版

    def cross_entropy_error(y, t)
      not_batch = y.ndim == 1 
      if not_batch
        t = t.reshape.(1, t.size)
        y = y.reshape.(1, y.size)
      end 

      one_hot_vector = t.size == y.size
      t = t.argmax.(1) if one_hot_vector

      batch_size = y.shape[0]
      output_for_answer = y[np.arange.(batch_size), t]
      sum = -np.sum.(np.log.(output_for_answer))
      return sum / batch_size
    end
> Util.cross_entropy_error(np.array.(y), np.array.(t))
=> 0.5108256237659907

結果が変わったので気になったけど、0がないyを使って、deltaをなくしたら同じ値になったので、deltaのせい。問題なさそう。

loss_Wの罠

two_layer_net.pyloss_WW使ってない
全部一緒の結果返ってくるんじゃ?

他にも同じところで詰まってる人がいた。
ゼロから学ぶDeep Learningの4章で詰まった – bitter chains

今の版だと説明が変わって少しわかりやすくなってるっぽい。
https://github.com/oreilly-japan/deep-learning-from-scratch/wiki/errata#%E7%AC%AC4%E5%88%B7%E3%81%BE%E3%81%A7

two_layer_net.py

    # x:入力データ, t:教師データ
    def numerical_gradient(self, x, t): 
        loss_W = lambda W: self.loss(x, t)

        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

        return grads

functions.py

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)

        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)

        x[idx] = tmp_val # 値を元に戻す
        it.iternext()   

    return grad

ここでxに渡されたものを、直接いじってるからもとのnetも変わってる、ということ。
Wfに渡したx)はやはり使ってない。

Ruby化する時に直接書き換えないコードにした。(Util側はxをnp.copyして使っている)

  def numerical_gradient(x, t)
    {
      W1: Util.numerical_gradient(loss_w(:W1, x, t), params[:W1]),
      b1: Util.numerical_gradient(loss_w(:b1, x, t), params[:b1]),
      W2: Util.numerical_gradient(loss_w(:W2, x, t), params[:W2]),
      b2: Util.numerical_gradient(loss_w(:b2, x, t), params[:b2]),
    }
  end  

  def loss_w(key, x, t)
    -> w {
      tmp_w = params[key]
      params[key] = w
      l = loss(x, t)
      params[key] = tmp_w
      l
    }
  end

結局numerical_gradientだと速度的に実用できなそうだったので、gradientメソッドの方を使ったけど…。

その他

  • .call()[].()でもいいけど結局callが冗長ながらもわかりやすい。numerical_gradientに渡したfの話。
  • it.iternext.()it.iternext()にしてて無限ループした。

参考