Browse Source

Add metrics of Precision.

tags/v0.100.4-load-saved-model
Haiping Chen 2 years ago
parent
commit
98919983b1
7 changed files with 141 additions and 3 deletions
  1. +11
    -0
      src/TensorFlowNET.Core/APIs/tf.math.cs
  2. +12
    -1
      src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs
  3. +4
    -0
      src/TensorFlowNET.Core/Operations/nn_ops.cs
  4. +4
    -1
      src/TensorFlowNET.Keras/Metrics/MetricsApi.cs
  5. +55
    -0
      src/TensorFlowNET.Keras/Metrics/Precision.cs
  6. +21
    -1
      src/TensorFlowNET.Keras/Metrics/metrics_utils.cs
  7. +34
    -0
      test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs

+ 11
- 0
src/TensorFlowNET.Core/APIs/tf.math.cs View File

@@ -39,6 +39,17 @@ namespace Tensorflow
public Tensor sum(Tensor x, Axis? axis = null, string name = null)
=> 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")
=> nn_ops.in_top_k(predictions, targets, k, name);



+ 12
- 1
src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs View File

@@ -36,6 +36,17 @@ public interface IMetricsApi
/// <returns></returns>
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>
/// Computes the recall of the predictions with respect to the labels.
/// </summary>
@@ -45,5 +56,5 @@ public interface IMetricsApi
/// <param name="name"></param>
/// <param name="dtype"></param>
/// <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);
}

+ 4
- 0
src/TensorFlowNET.Core/Operations/nn_ops.cs View File

@@ -109,6 +109,10 @@ namespace Tensorflow
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)
{
return tf_with(ops.name_scope(name, "in_top_k"), delegate


+ 4
- 1
src/TensorFlowNET.Keras/Metrics/MetricsApi.cs View File

@@ -62,7 +62,10 @@
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);

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);
}
}

+ 55
- 0
src/TensorFlowNET.Keras/Metrics/Precision.cs View File

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

+ 21
- 1
src/TensorFlowNET.Keras/Metrics/metrics_utils.cs View File

@@ -78,6 +78,17 @@ public class metrics_utils
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)
{
throw new NotImplementedException();
@@ -204,5 +215,14 @@ public class metrics_utils

tf.group(update_ops.ToArray());
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);
}
}

+ 34
- 0
test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs View File

@@ -46,6 +46,40 @@ public class MetricsTest : EagerModeTestBase
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>
/// https://www.tensorflow.org/api_docs/python/tf/keras/metrics/Recall
/// </summary>


Loading…
Cancel
Save