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

交差エントロピー誤差

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

バッチ非対応版

    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メソッドの方を使ったけど…。

その他

参考