From 4bdfbfb74fe5b77825fa7b4f5f251834d1751203 Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Sat, 18 Dec 2021 10:42:24 -0600 Subject: [PATCH] add tf.clip_by_global_norm #857 --- src/TensorFlowNET.Core/APIs/tf.ops.cs | 3 +++ src/TensorFlowNET.Core/Operations/clip_ops.cs | 21 +++++++++++++++++++ .../ManagedAPI/ClipTest.cs | 21 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 test/TensorFlowNET.UnitTest/ManagedAPI/ClipTest.cs diff --git a/src/TensorFlowNET.Core/APIs/tf.ops.cs b/src/TensorFlowNET.Core/APIs/tf.ops.cs index 7e4c3395..e17d6b01 100644 --- a/src/TensorFlowNET.Core/APIs/tf.ops.cs +++ b/src/TensorFlowNET.Core/APIs/tf.ops.cs @@ -27,6 +27,9 @@ namespace Tensorflow public void add_to_collections(List names, T value) => get_default_graph().add_to_collections(names, value); + public (Tensors, Tensor) clip_by_global_norm(Tensor[] t_list, float clip_norm, Tensor use_norm = null, string name = null) + => clip_ops.clip_by_global_norm(t_list, clip_norm, use_norm: use_norm, name: name); + public Tensor assign(IVariableV1 @ref, object value, bool validate_shape = true, bool use_locking = true, string name = null) => state_ops.assign(@ref, value, validate_shape, use_locking, name); diff --git a/src/TensorFlowNET.Core/Operations/clip_ops.cs b/src/TensorFlowNET.Core/Operations/clip_ops.cs index 7b48c9e5..59d74fde 100644 --- a/src/TensorFlowNET.Core/Operations/clip_ops.cs +++ b/src/TensorFlowNET.Core/Operations/clip_ops.cs @@ -21,6 +21,27 @@ namespace Tensorflow { public class clip_ops { + public static (Tensors, Tensor) clip_by_global_norm(Tensor[] t_list, float clip_norm, Tensor use_norm = null, string name = null) + { + use_norm = global_norm(t_list, name); + return tf_with(ops.name_scope(name, "clip_by_global_norm", t_list), delegate + { + // Calculate L2-norm, clip elements by ratio of clip_norm to L2-norm + var scale_for_finite = clip_norm * math_ops.minimum( + 1.0f / use_norm, + constant_op.constant(1.0, dtype: use_norm.dtype) / clip_norm); + + // If use_norm is any finite number, this is a no-op. For inf/-inf/NaN, + // this will make scale NaN. + var scale = scale_for_finite + (use_norm - use_norm); + + Tensors values_clipped = new Tensors(); + foreach (var (i, v) in enumerate(t_list)) + values_clipped.Add(array_ops.identity(v * scale, name: $"{name}_{i}")); + return (values_clipped, use_norm); + }); + } + public static Tensor clip_by_value(Tensor t, T1 clip_value_min, T2 clip_value_max, string name = null) { return tf_with(ops.name_scope(name, "clip_by_value", new { t, clip_value_min, clip_value_max }), delegate diff --git a/test/TensorFlowNET.UnitTest/ManagedAPI/ClipTest.cs b/test/TensorFlowNET.UnitTest/ManagedAPI/ClipTest.cs new file mode 100644 index 00000000..6cbc69ad --- /dev/null +++ b/test/TensorFlowNET.UnitTest/ManagedAPI/ClipTest.cs @@ -0,0 +1,21 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Tensorflow.Binding; +using Tensorflow; + +namespace TensorFlowNET.UnitTest.ClipOps +{ + [TestClass] + public class ClipTest : EagerModeTestBase + { + [TestMethod] + public void clip_by_global_norm() + { + var t_list = new Tensors(tf.constant(new float[] { 1, 2, 3, 4 }), tf.constant(new float[] { 5, 6, 7, 8 })); + var clip_norm = .8f; + var (res, norm) = tf.clip_by_global_norm(t_list, clip_norm); + Equal(res[0].ToArray(), new[] { 0.0560112074f, 0.112022415f, 0.16803363f, 0.22404483f }); + Equal(res[1].ToArray(), new[] { 0.28005603f, 0.336067259f, 0.392078459f, 0.448089659f }); + Assert.AreEqual(norm.numpy(), 14.282857f); + } + } +}