diff --git a/src/TensorFlowNET.Core/APIs/tf.math.cs b/src/TensorFlowNET.Core/APIs/tf.math.cs
index dabdf126..61b7caf4 100644
--- a/src/TensorFlowNET.Core/APIs/tf.math.cs
+++ b/src/TensorFlowNET.Core/APIs/tf.math.cs
@@ -24,6 +24,8 @@ namespace Tensorflow
public Tensor argmax(Tensor input, Axis axis = null, string name = null, int? dimension = null, TF_DataType output_type = TF_DataType.TF_INT64)
=> gen_math_ops.arg_max(input, axis, name: name, output_type: output_type);
+ public Tensor count_nonzero(Tensor input, Axis? axis = null, bool? keepdims = null, TF_DataType dtype = TF_DataType.TF_INT64, string name = null)
+ => math_ops.count_nonzero_v2(input, axis: axis, keepdims: keepdims ?? false, dtype: dtype);
public Tensor log(Tensor x, string name = null)
=> gen_math_ops.log(x, name);
diff --git a/src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs b/src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs
index 64c2c14f..5d08cc78 100644
--- a/src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs
+++ b/src/TensorFlowNET.Core/Keras/Metrics/IMetricsApi.cs
@@ -91,7 +91,20 @@ public interface IMetricsApi
float? threshold = null,
string name = "fbeta_score",
TF_DataType dtype = TF_DataType.TF_FLOAT);
-
+
+ ///
+ /// Computes hamming loss.
+ ///
+ /// multiclass or multilabel
+ ///
+ ///
+ ///
+ ///
+ IMetricFunc HammingLoss(string mode,
+ float? threshold = null,
+ string name = "hamming_loss",
+ TF_DataType dtype = TF_DataType.TF_FLOAT);
+
///
/// Computes how often targets are in the top K predictions.
///
diff --git a/src/TensorFlowNET.Core/Operations/math_ops.cs b/src/TensorFlowNET.Core/Operations/math_ops.cs
index 9542f643..36f7db79 100644
--- a/src/TensorFlowNET.Core/Operations/math_ops.cs
+++ b/src/TensorFlowNET.Core/Operations/math_ops.cs
@@ -821,6 +821,18 @@ namespace Tensorflow
.SetAttributes(new { adj_x, adj_y }));
});
+ public static Tensor count_nonzero_v2(Tensor input,
+ Axis? axis,
+ bool keepdims = false,
+ string name = null,
+ TF_DataType dtype = TF_DataType.TF_INT64)
+ => tf_with(ops.name_scope(name, "count_nonzero", input), scope =>
+ {
+ name = scope;
+ var zero = array_ops.zeros(Shape.Scalar, dtype: input.dtype);
+ return reduce_sum(cast(gen_math_ops.not_equal(input, zero), dtype), axis: axis, keepdims: keepdims);
+ });
+
public static Tensor bincount(Tensor arr, Tensor weights = null,
Tensor minlength = null,
Tensor maxlength = null,
diff --git a/src/TensorFlowNET.Core/Tensorflow.Binding.csproj b/src/TensorFlowNET.Core/Tensorflow.Binding.csproj
index ecb63a7b..8925228d 100644
--- a/src/TensorFlowNET.Core/Tensorflow.Binding.csproj
+++ b/src/TensorFlowNET.Core/Tensorflow.Binding.csproj
@@ -109,7 +109,7 @@ https://tensorflownet.readthedocs.io
-
+
diff --git a/src/TensorFlowNET.Keras/Metrics/HammingLoss.cs b/src/TensorFlowNET.Keras/Metrics/HammingLoss.cs
new file mode 100644
index 00000000..2b65424e
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Metrics/HammingLoss.cs
@@ -0,0 +1,15 @@
+namespace Tensorflow.Keras.Metrics;
+
+public class HammingLoss : MeanMetricWrapper
+{
+ public HammingLoss(string mode,
+ NDArray threshold = null,
+ string name = "hamming_loss",
+ TF_DataType dtype = TF_DataType.TF_FLOAT)
+ : base((yt, yp) => metrics_utils.hamming_loss_fn(yt, yp, threshold, mode),
+ name: name,
+ dtype: dtype)
+ {
+ _dtype = dtype;
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Metrics/MetricsApi.cs b/src/TensorFlowNET.Keras/Metrics/MetricsApi.cs
index bd12f82a..585fefae 100644
--- a/src/TensorFlowNET.Keras/Metrics/MetricsApi.cs
+++ b/src/TensorFlowNET.Keras/Metrics/MetricsApi.cs
@@ -92,6 +92,9 @@
public IMetricFunc FBetaScore(int num_classes, string? average = null, float beta = 0.1F, float? threshold = null, string name = "fbeta_score", TF_DataType dtype = TF_DataType.TF_FLOAT)
=> new FBetaScore(num_classes, average: average,beta: beta, threshold: threshold, name: name, dtype: dtype);
+ public IMetricFunc HammingLoss(string mode, float? threshold = null, string name = "hamming_loss", TF_DataType dtype = TF_DataType.TF_FLOAT)
+ => new HammingLoss(mode, threshold: threshold, name: name, dtype: dtype);
+
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);
diff --git a/src/TensorFlowNET.Keras/Metrics/metrics_utils.cs b/src/TensorFlowNET.Keras/Metrics/metrics_utils.cs
index f4bfc3da..69cc789e 100644
--- a/src/TensorFlowNET.Keras/Metrics/metrics_utils.cs
+++ b/src/TensorFlowNET.Keras/Metrics/metrics_utils.cs
@@ -24,6 +24,36 @@ public class metrics_utils
return tf.reduce_sum(y_true * y_pred, axis: axis ?? -1);
}
+ public static Tensor hamming_loss_fn(Tensor y_true, Tensor y_pred, Tensor threshold, string mode)
+ {
+ if (threshold == null)
+ {
+ threshold = tf.reduce_max(y_pred, axis: -1, keepdims: true);
+ // make sure [0, 0, 0] doesn't become [1, 1, 1]
+ // Use abs(x) > eps, instead of x != 0 to check for zero
+ y_pred = tf.logical_and(y_pred >= threshold, tf.abs(y_pred) > 1e-12);
+ }
+ else
+ {
+ y_pred = y_pred > threshold;
+ }
+
+
+ y_true = tf.cast(y_true, tf.int32);
+ y_pred = tf.cast(y_pred, tf.int32);
+
+ if (mode == "multiclass")
+ {
+ var nonzero = tf.cast(tf.math.count_nonzero(y_true * y_pred, axis: -1), tf.float32);
+ return 1.0 - nonzero;
+ }
+ else
+ {
+ var nonzero = tf.cast(tf.math.count_nonzero(y_true - y_pred, axis: -1), tf.float32);
+ return nonzero / y_true.shape[-1];
+ }
+ }
+
///
/// Creates float Tensor, 1.0 for label-prediction match, 0.0 for mismatch.
///
diff --git a/test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs b/test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs
index 2b38449b..267cef81 100644
--- a/test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs
+++ b/test/TensorFlowNET.Keras.UnitTest/Metrics/MetricsTest.cs
@@ -142,6 +142,51 @@ public class MetricsTest : EagerModeTestBase
Assert.AreEqual(r, new[] { 0.3846154f, 0.90909094f, 0.8333334f });
}
+ ///
+ /// https://www.tensorflow.org/addons/api_docs/python/tfa/metrics/HammingLoss
+ ///
+ [TestMethod]
+ public void HammingLoss()
+ {
+ // multi-class hamming loss
+ var y_true = np.array(new[,]
+ {
+ { 1, 0, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 },
+ { 0, 1, 0, 0 }
+ });
+ var y_pred = np.array(new[,]
+ {
+ { 0.8f, 0.1f, 0.1f, 0.0f },
+ { 0.2f, 0.0f, 0.8f, 0.0f },
+ { 0.05f, 0.05f, 0.1f, 0.8f },
+ { 1.0f, 0.0f, 0.0f, 0.0f }
+ });
+ var m = tf.keras.metrics.HammingLoss(mode: "multiclass", threshold: 0.6f);
+ m.update_state(y_true, y_pred);
+ var r = m.result().numpy();
+ Assert.AreEqual(r, 0.25f);
+
+ // multi-label hamming loss
+ y_true = np.array(new[,]
+ {
+ { 1, 0, 1, 0 },
+ { 0, 1, 0, 1 },
+ { 0, 0, 0, 1 }
+ });
+ y_pred = np.array(new[,]
+ {
+ { 0.82f, 0.5f, 0.9f, 0.0f },
+ { 0f, 1f, 0.4f, 0.98f },
+ { 0.89f, 0.79f, 0f, 0.3f }
+ });
+ m = tf.keras.metrics.HammingLoss(mode: "multilabel", threshold: 0.8f);
+ m.update_state(y_true, y_pred);
+ r = m.result().numpy();
+ Assert.AreEqual(r, 0.16666667f);
+ }
+
///
/// https://www.tensorflow.org/api_docs/python/tf/keras/metrics/TopKCategoricalAccuracy
///