Fix in_top_k() for Theano when identical values appear in predictions (#6133)
* Fix in_top_k() for Theano when identical values appear in predictions * Add test and update docstrings for in_top_k()
This commit is contained in:
parent
1a16857886
commit
65a215646c
@ -2730,13 +2730,14 @@ def in_top_k(predictions, targets, k):
|
||||
"""Returns whether the `targets` are in the top `k` `predictions`.
|
||||
|
||||
# Arguments
|
||||
predictions: A tensor of shape `batch_size` x classes and type `float32`.
|
||||
targets: A tensor of shape batch_size and type `int32` or `int64`.
|
||||
predictions: A tensor of shape `(batch_size, classes)` and type `float32`.
|
||||
targets: A 1D tensor of length `batch_size` and type `int32` or `int64`.
|
||||
k: An `int`, number of top elements to consider.
|
||||
|
||||
# Returns
|
||||
A tensor of shape `batch_size` and type `bool`. `output_i` is `True` if
|
||||
`targets_i` is within top-k values of `predictions_i`
|
||||
A 1D tensor of length `batch_size` and type `bool`.
|
||||
`output[i]` is `True` if `predictions[i, targets[i]]` is within top-`k`
|
||||
values of `predictions[i]`.
|
||||
"""
|
||||
return tf.nn.in_top_k(predictions, targets, k)
|
||||
|
||||
|
@ -1494,20 +1494,35 @@ def l2_normalize(x, axis):
|
||||
|
||||
|
||||
def in_top_k(predictions, targets, k):
|
||||
"""Returns whether the `targets` are in the top `k` `predictions`
|
||||
"""Returns whether the `targets` are in the top `k` `predictions`.
|
||||
|
||||
# Arguments
|
||||
predictions: A tensor of shape batch_size x classess and type float32.
|
||||
targets: A tensor of shape batch_size and type int32 or int64.
|
||||
k: An int, number of top elements to consider.
|
||||
predictions: A tensor of shape `(batch_size, classes)` and type `float32`.
|
||||
targets: A 1D tensor of length `batch_size` and type `int32` or `int64`.
|
||||
k: An `int`, number of top elements to consider.
|
||||
|
||||
# Returns
|
||||
A tensor of shape batch_size and type int. output_i is 1 if
|
||||
targets_i is within top-k values of predictions_i
|
||||
A 1D tensor of length `batch_size` and type `bool`.
|
||||
`output[i]` is `True` if `predictions[i, targets[i]]` is within top-`k`
|
||||
values of `predictions[i]`.
|
||||
"""
|
||||
predictions_top_k = T.argsort(predictions)[:, -k:]
|
||||
result, _ = theano.map(lambda prediction, target: any(equal(prediction, target)), sequences=[predictions_top_k, targets])
|
||||
return result
|
||||
# handle k < 1 and k >= predictions.shape[1] cases to match TF behavior
|
||||
if k < 1:
|
||||
# dtype='bool' is only available since Theano 0.9.0
|
||||
try:
|
||||
return T.zeros_like(targets, dtype='bool')
|
||||
except TypeError:
|
||||
return T.zeros_like(targets, dtype='int8')
|
||||
|
||||
if k >= int_shape(predictions)[1]:
|
||||
try:
|
||||
return T.ones_like(targets, dtype='bool')
|
||||
except TypeError:
|
||||
return T.ones_like(targets, dtype='int8')
|
||||
|
||||
predictions_k = T.sort(predictions)[:, -k]
|
||||
targets_values = predictions[T.arange(targets.shape[0]), targets]
|
||||
return T.ge(targets_values, predictions_k)
|
||||
|
||||
|
||||
# CONVOLUTIONS
|
||||
|
@ -618,6 +618,46 @@ class TestBackend(object):
|
||||
check_single_tensor_operation('l2_normalize', (4, 3), axis=-1)
|
||||
check_single_tensor_operation('l2_normalize', (4, 3), axis=1)
|
||||
|
||||
def test_in_top_k(self):
|
||||
batch_size = 20
|
||||
num_classes = 10
|
||||
|
||||
# Random prediction test case
|
||||
predictions = np.random.random((batch_size, num_classes)).astype('float32')
|
||||
targets = np.random.randint(num_classes, size=batch_size, dtype='int32')
|
||||
|
||||
predictions_th = KTH.variable(predictions, dtype='float32')
|
||||
targets_th = KTH.variable(targets, dtype='int32')
|
||||
predictions_tf = KTF.variable(predictions, dtype='float32')
|
||||
targets_tf = KTF.variable(targets, dtype='int32')
|
||||
|
||||
for k in range(1, num_classes + 1):
|
||||
res_th = KTH.eval(KTH.in_top_k(predictions_th, targets_th, k))
|
||||
res_tf = KTF.eval(KTF.in_top_k(predictions_tf, targets_tf, k))
|
||||
|
||||
assert res_th.shape == res_tf.shape
|
||||
assert_allclose(res_th, res_tf, atol=1e-05)
|
||||
|
||||
# Identical prediction test case:
|
||||
# randomly set half of the predictions to an identical value
|
||||
num_identical = num_classes // 2
|
||||
for i in range(batch_size):
|
||||
idx_identical = np.random.choice(num_classes, size=num_identical, replace=False)
|
||||
predictions[i, idx_identical] = predictions[i, 0]
|
||||
targets = np.zeros(batch_size, dtype='int32')
|
||||
|
||||
predictions_th = KTH.variable(predictions, dtype='float32')
|
||||
targets_th = KTH.variable(targets, dtype='int32')
|
||||
predictions_tf = KTF.variable(predictions, dtype='float32')
|
||||
targets_tf = KTF.variable(targets, dtype='int32')
|
||||
|
||||
for k in range(1, num_classes + 1):
|
||||
res_th = KTH.eval(KTH.in_top_k(predictions_th, targets_th, k))
|
||||
res_tf = KTF.eval(KTF.in_top_k(predictions_tf, targets_tf, k))
|
||||
|
||||
assert res_th.shape == res_tf.shape
|
||||
assert_allclose(res_th, res_tf, atol=1e-05)
|
||||
|
||||
def test_conv2d(self):
|
||||
# TF kernel shape: (rows, cols, input_depth, depth)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user