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:
Yu-Yang 2017-04-08 02:41:59 +08:00 committed by François Chollet
parent 1a16857886
commit 65a215646c
3 changed files with 69 additions and 13 deletions

@ -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)