| @@ -39,6 +39,17 @@ namespace Tensorflow | |||||
| public Tensor sum(Tensor x, Axis? axis = null, string name = null) | public Tensor sum(Tensor x, Axis? axis = null, string name = null) | ||||
| => math_ops.reduce_sum(x, axis: axis, name: name); | => math_ops.reduce_sum(x, axis: axis, name: name); | ||||
| /// <summary> | |||||
| /// Finds values and indices of the `k` largest entries for the last dimension. | |||||
| /// </summary> | |||||
| /// <param name="input"></param> | |||||
| /// <param name="k"></param> | |||||
| /// <param name="sorted"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public Tensors top_k(Tensor input, int k, bool sorted = true, string name = null) | |||||
| => nn_ops.top_kv2(input, k, sorted: sorted, name: name); | |||||
| public Tensor in_top_k(Tensor predictions, Tensor targets, int k, string name = "InTopK") | public Tensor in_top_k(Tensor predictions, Tensor targets, int k, string name = "InTopK") | ||||
| => nn_ops.in_top_k(predictions, targets, k, name); | => nn_ops.in_top_k(predictions, targets, k, name); | ||||
| @@ -36,6 +36,17 @@ public interface IMetricsApi | |||||
| /// <returns></returns> | /// <returns></returns> | ||||
| IMetricFunc TopKCategoricalAccuracy(int k = 5, string name = "top_k_categorical_accuracy", TF_DataType dtype = TF_DataType.TF_FLOAT); | IMetricFunc TopKCategoricalAccuracy(int k = 5, string name = "top_k_categorical_accuracy", TF_DataType dtype = TF_DataType.TF_FLOAT); | ||||
| /// <summary> | |||||
| /// Computes the precision of the predictions with respect to the labels. | |||||
| /// </summary> | |||||
| /// <param name="thresholds"></param> | |||||
| /// <param name="top_k"></param> | |||||
| /// <param name="class_id"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <param name="dtype"></param> | |||||
| /// <returns></returns> | |||||
| IMetricFunc Precision(float thresholds = 0.5f, int top_k = 0, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT); | |||||
| /// <summary> | /// <summary> | ||||
| /// Computes the recall of the predictions with respect to the labels. | /// Computes the recall of the predictions with respect to the labels. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -45,5 +56,5 @@ public interface IMetricsApi | |||||
| /// <param name="name"></param> | /// <param name="name"></param> | ||||
| /// <param name="dtype"></param> | /// <param name="dtype"></param> | ||||
| /// <returns></returns> | /// <returns></returns> | ||||
| IMetricFunc Recall(float thresholds = 0.5f, int top_k = 1, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT); | |||||
| IMetricFunc Recall(float thresholds = 0.5f, int top_k = 0, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT); | |||||
| } | } | ||||
| @@ -109,6 +109,10 @@ namespace Tensorflow | |||||
| return noise_shape; | return noise_shape; | ||||
| } | } | ||||
| public static Tensors top_kv2(Tensor input, int k, bool sorted = true, string name = null) | |||||
| => tf.Context.ExecuteOp("TopKV2", name, new ExecuteOpArgs(input, k) | |||||
| .SetAttributes(new { sorted })); | |||||
| public static Tensor in_top_k(Tensor predictions, Tensor targets, int k, string name = null) | public static Tensor in_top_k(Tensor predictions, Tensor targets, int k, string name = null) | ||||
| { | { | ||||
| return tf_with(ops.name_scope(name, "in_top_k"), delegate | return tf_with(ops.name_scope(name, "in_top_k"), delegate | ||||
| @@ -62,7 +62,10 @@ | |||||
| public IMetricFunc TopKCategoricalAccuracy(int k = 5, string name = "top_k_categorical_accuracy", TF_DataType dtype = TF_DataType.TF_FLOAT) | public IMetricFunc TopKCategoricalAccuracy(int k = 5, string name = "top_k_categorical_accuracy", TF_DataType dtype = TF_DataType.TF_FLOAT) | ||||
| => new TopKCategoricalAccuracy(k: k, name: name, dtype: dtype); | => new TopKCategoricalAccuracy(k: k, name: name, dtype: dtype); | ||||
| public IMetricFunc Recall(float thresholds = 0.5f, int top_k = 1, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT) | |||||
| public IMetricFunc Precision(float thresholds = 0.5f, int top_k = 0, int class_id = 0, string name = "precision", TF_DataType dtype = TF_DataType.TF_FLOAT) | |||||
| => new Precision(thresholds: thresholds, top_k: top_k, class_id: class_id, name: name, dtype: dtype); | |||||
| public IMetricFunc Recall(float thresholds = 0.5f, int top_k = 0, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT) | |||||
| => new Recall(thresholds: thresholds, top_k: top_k, class_id: class_id, name: name, dtype: dtype); | => new Recall(thresholds: thresholds, top_k: top_k, class_id: class_id, name: name, dtype: dtype); | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,55 @@ | |||||
| namespace Tensorflow.Keras.Metrics; | |||||
| public class Precision : Metric | |||||
| { | |||||
| Tensor _thresholds; | |||||
| int _top_k; | |||||
| int _class_id; | |||||
| IVariableV1 true_positives; | |||||
| IVariableV1 false_positives; | |||||
| bool _thresholds_distributed_evenly; | |||||
| public Precision(float thresholds = 0.5f, int top_k = 0, int class_id = 0, string name = "recall", TF_DataType dtype = TF_DataType.TF_FLOAT) | |||||
| : base(name: name, dtype: dtype) | |||||
| { | |||||
| _thresholds = constant_op.constant(new float[] { thresholds }); | |||||
| _top_k = top_k; | |||||
| _class_id = class_id; | |||||
| true_positives = add_weight("true_positives", shape: 1, initializer: tf.initializers.zeros_initializer()); | |||||
| false_positives = add_weight("false_positives", shape: 1, initializer: tf.initializers.zeros_initializer()); | |||||
| } | |||||
| public override Tensor update_state(Tensor y_true, Tensor y_pred, Tensor sample_weight = null) | |||||
| { | |||||
| return metrics_utils.update_confusion_matrix_variables( | |||||
| new Dictionary<string, IVariableV1> | |||||
| { | |||||
| { "tp", true_positives }, | |||||
| { "fp", false_positives }, | |||||
| }, | |||||
| y_true, | |||||
| y_pred, | |||||
| thresholds: _thresholds, | |||||
| thresholds_distributed_evenly: _thresholds_distributed_evenly, | |||||
| top_k: _top_k, | |||||
| class_id: _class_id, | |||||
| sample_weight: sample_weight); | |||||
| } | |||||
| public override Tensor result() | |||||
| { | |||||
| var result = tf.divide(true_positives.AsTensor(), tf.add(true_positives, false_positives)); | |||||
| return _thresholds.size == 1 ? result[0] : result; | |||||
| } | |||||
| public override void reset_states() | |||||
| { | |||||
| var num_thresholds = (int)_thresholds.size; | |||||
| keras.backend.batch_set_value( | |||||
| new List<(IVariableV1, NDArray)> | |||||
| { | |||||
| (true_positives, np.zeros(num_thresholds)), | |||||
| (false_positives, np.zeros(num_thresholds)) | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -78,6 +78,17 @@ public class metrics_utils | |||||
| sample_weight: sample_weight); | sample_weight: sample_weight); | ||||
| } | } | ||||
| if (top_k > 0) | |||||
| { | |||||
| y_pred = _filter_top_k(y_pred, top_k); | |||||
| } | |||||
| if (class_id > 0) | |||||
| { | |||||
| y_true = y_true[Slice.All, class_id]; | |||||
| y_pred = y_pred[Slice.All, class_id]; | |||||
| } | |||||
| if (thresholds_distributed_evenly) | if (thresholds_distributed_evenly) | ||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| @@ -204,5 +215,14 @@ public class metrics_utils | |||||
| tf.group(update_ops.ToArray()); | tf.group(update_ops.ToArray()); | ||||
| return null; | return null; | ||||
| } | |||||
| } | |||||
| private static Tensor _filter_top_k(Tensor x, int k) | |||||
| { | |||||
| var NEG_INF = -1e10; | |||||
| var (_, top_k_idx) = tf.math.top_k(x, k, sorted: false); | |||||
| var top_k_mask = tf.reduce_sum( | |||||
| tf.one_hot(top_k_idx, (int)x.shape[-1], axis: -1), axis: -2); | |||||
| return x * top_k_mask + NEG_INF * (1 - top_k_mask); | |||||
| } | |||||
| } | } | ||||
| @@ -46,6 +46,40 @@ public class MetricsTest : EagerModeTestBase | |||||
| Assert.AreEqual(m.numpy(), new[] { 1f, 1f }); | Assert.AreEqual(m.numpy(), new[] { 1f, 1f }); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// https://www.tensorflow.org/api_docs/python/tf/keras/metrics/Precision | |||||
| /// </summary> | |||||
| [TestMethod] | |||||
| public void Precision() | |||||
| { | |||||
| var y_true = np.array(new[] { 0, 1, 1, 1 }); | |||||
| var y_pred = np.array(new[] { 1, 0, 1, 1 }); | |||||
| var m = tf.keras.metrics.Precision(); | |||||
| m.update_state(y_true, y_pred); | |||||
| var r = m.result().numpy(); | |||||
| Assert.AreEqual(r, 0.6666667f); | |||||
| m.reset_states(); | |||||
| var weights = np.array(new[] { 0f, 0f, 1f, 0f }); | |||||
| m.update_state(y_true, y_pred, sample_weight: weights); | |||||
| r = m.result().numpy(); | |||||
| Assert.AreEqual(r, 1f); | |||||
| // With top_k=2, it will calculate precision over y_true[:2] | |||||
| // and y_pred[:2] | |||||
| m = tf.keras.metrics.Precision(top_k: 2); | |||||
| m.update_state(np.array(new[] { 0, 0, 1, 1 }), np.array(new[] { 1, 1, 1, 1 })); | |||||
| r = m.result().numpy(); | |||||
| Assert.AreEqual(r, 0f); | |||||
| // With top_k=4, it will calculate precision over y_true[:4] | |||||
| // and y_pred[:4] | |||||
| m = tf.keras.metrics.Precision(top_k: 4); | |||||
| m.update_state(np.array(new[] { 0, 0, 1, 1 }), np.array(new[] { 1, 1, 1, 1 })); | |||||
| r = m.result().numpy(); | |||||
| Assert.AreEqual(r, 0.5f); | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// https://www.tensorflow.org/api_docs/python/tf/keras/metrics/Recall | /// https://www.tensorflow.org/api_docs/python/tf/keras/metrics/Recall | ||||
| /// </summary> | /// </summary> | ||||