你可以像这样在 Keras 中做到这一点:
import keras.backend as K
diff = K.constant([0, 1, 2, -2, 3, 0])
sum = K.constant([2, 4, 1, 0, 5, 0])
rel_dev = diff / sum
d0 = K.equal(diff, 0)
s0 = K.equal(sum, 0)
rel_dev = K.switch(d0 & s0, K.zeros_like(rel_dev), rel_dev)
rel_dev = K.switch(~d0 & s0, K.sign(diff), rel_dev)
print(K.eval(rel_dev))
# [ 0. 0.25 2. -1. 0.6 0. ]
编辑:上面的公式有一个隐蔽的问题,即即使结果是正确的,nan 值也会通过梯度传播回来(即,因为除以零得到inf 或nan,并且将inf 或nan 乘以零得到nan)。事实上,如果你检查渐变:
gd, gs = K.gradients(rel_dev, (diff, sum))
print(K.eval(gd))
# [0.5 0.25 1. nan 0.2 nan]
print(K.eval(gs))
# [-0. -0.0625 -2. nan -0.12 nan]
您可以用来避免这种情况的技巧是更改除法中的sum,但不会影响结果但会阻止nan 值,例如:
import keras.backend as K
diff = K.constant([0, 1, 2, -2, 3, 0])
sum = K.constant([2, 4, 1, 0, 5, 0])
d0 = K.equal(diff, 0)
s0 = K.equal(sum, 0)
# sum zeros are replaced by ones on division
rel_dev = diff / K.switch(s0, K.ones_like(sum), sum)
rel_dev = K.switch(d0 & s0, K.zeros_like(rel_dev), rel_dev)
rel_dev = K.switch(~d0 & s0, K.sign(diff), rel_dev)
print(K.eval(rel_dev))
# [ 0. 0.25 2. -1. 0.6 0. ]
gd, gs = K.gradients(rel_dev, (diff, sum))
print(K.eval(gd))
# [0.5 0.25 1. 0. 0.2 0. ]
print(K.eval(gs))
# [-0. -0.0625 -2. 0. -0.12 0. ]