From 1f4084870bb81e939ac5e0c646f0dd6f0cae19d9 Mon Sep 17 00:00:00 2001 From: fchollet Date: Sun, 24 Apr 2016 12:10:47 -0700 Subject: [PATCH] Add new metrics and metrics tests --- keras/metrics.py | 66 ++++++++++++++++++++++++++++++++++++- tests/keras/test_metrics.py | 44 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/keras/test_metrics.py diff --git a/keras/metrics.py b/keras/metrics.py index 44d3485dd..222ec1efc 100644 --- a/keras/metrics.py +++ b/keras/metrics.py @@ -1,3 +1,4 @@ +import numpy as np from . import backend as K @@ -12,7 +13,70 @@ def categorical_accuracy(y_true, y_pred): def sparse_categorical_accuracy(y_true, y_pred): return K.mean(K.equal(K.max(y_true, axis=-1), - K.argmax(y_pred, axis=-1))) + K.cast(K.argmax(y_pred, axis=-1), K.floatx()))) + + +def mean_squared_error(y_true, y_pred): + return K.mean(K.square(y_pred - y_true)) + + +def mean_absolute_error(y_true, y_pred): + return K.mean(K.abs(y_pred - y_true)) + + +def mean_absolute_percentage_error(y_true, y_pred): + diff = K.abs((y_true - y_pred) / K.clip(K.abs(y_true), K.epsilon(), np.inf)) + return 100. * K.mean(diff) + + +def mean_squared_logarithmic_error(y_true, y_pred): + first_log = K.log(K.clip(y_pred, K.epsilon(), np.inf) + 1.) + second_log = K.log(K.clip(y_true, K.epsilon(), np.inf) + 1.) + return K.mean(K.square(first_log - second_log)) + + +def squared_hinge(y_true, y_pred): + return K.mean(K.square(K.maximum(1. - y_true * y_pred, 0.))) + + +def hinge(y_true, y_pred): + return K.mean(K.maximum(1. - y_true * y_pred, 0.)) + + +def categorical_crossentropy(y_true, y_pred): + '''Expects a binary class matrix instead of a vector of scalar classes. + ''' + return K.mean(K.categorical_crossentropy(y_pred, y_true)) + + +def sparse_categorical_crossentropy(y_true, y_pred): + '''expects an array of integer classes. + Note: labels shape must have the same number of dimensions as output shape. + If you get a shape error, add a length-1 dimension to labels. + ''' + return K.mean(K.sparse_categorical_crossentropy(y_pred, y_true)) + + +def binary_crossentropy(y_true, y_pred): + return K.mean(K.binary_crossentropy(y_pred, y_true)) + + +def poisson(y_true, y_pred): + return K.mean(y_pred - y_true * K.log(y_pred + K.epsilon())) + + +def cosine_proximity(y_true, y_pred): + y_true = K.l2_normalize(y_true, axis=-1) + y_pred = K.l2_normalize(y_pred, axis=-1) + return -K.mean(y_true * y_pred) + + +# aliases +mse = MSE = mean_squared_error +mae = MAE = mean_absolute_error +mape = MAPE = mean_absolute_percentage_error +msle = MSLE = mean_squared_logarithmic_error +cosine = cosine_proximity from .utils.generic_utils import get_from_module diff --git a/tests/keras/test_metrics.py b/tests/keras/test_metrics.py new file mode 100644 index 000000000..32dbec827 --- /dev/null +++ b/tests/keras/test_metrics.py @@ -0,0 +1,44 @@ +import pytest +import numpy as np + +from keras import metrics +from keras import backend as K + +all_metrics = [ + metrics.binary_accuracy, + metrics.categorical_accuracy, + metrics.mean_squared_error, + metrics.mean_absolute_error, + metrics.mean_absolute_percentage_error, + metrics.mean_squared_logarithmic_error, + metrics.squared_hinge, + metrics.hinge, + metrics.categorical_crossentropy, + metrics.binary_crossentropy, + metrics.poisson, + metrics.cosine_proximity, +] + +all_sparse_metrics = [ + metrics.sparse_categorical_accuracy, + metrics.sparse_categorical_crossentropy, +] + + +def test_metrics(): + y_a = K.variable(np.random.random((6, 7))) + y_b = K.variable(np.random.random((6, 7))) + for metric in all_metrics: + output = metric(y_a, y_b) + assert K.eval(output).shape == () + + +def test_sparse_metrics(): + for metric in all_sparse_metrics: + y_a = K.variable(np.random.randint(0, 7, (6,)), dtype=K.floatx()) + y_b = K.variable(np.random.random((6, 7)), dtype=K.floatx()) + assert K.eval(metric(y_a, y_b)).shape == () + + +if __name__ == "__main__": + pytest.main([__file__])