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.py
のloss_W
がW
使ってない
全部一緒の結果返ってくるんじゃ?
他にも同じところで詰まってる人がいた。
ゼロから学ぶ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
も変わってる、ということ。
W
(f
に渡した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()
にしてて無限ループした。