From 1e5018334ea76566bb4931f563e18e05bc6fd058 Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Tue, 17 Sep 2019 07:17:10 -0500 Subject: [PATCH] tf.sparse_to_dense #396 --- src/TensorFlowNET.Core/APIs/tf.sparse.cs | 50 +++++++++++++++++ .../Framework/sparse_tensor.py.cs | 51 +++++++++++++++--- .../Operations/gen_sparse_ops.cs | 54 +++++++++++++++++++ .../Operations/sparse_ops.cs | 33 ++++++++++++ src/TensorFlowNET.Core/Tensors/TensorShape.cs | 18 +++++++ test/TensorFlowNET.UnitTest/TensorTest.cs | 19 +++++++ 6 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 src/TensorFlowNET.Core/APIs/tf.sparse.cs create mode 100644 src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs create mode 100644 src/TensorFlowNET.Core/Operations/sparse_ops.cs diff --git a/src/TensorFlowNET.Core/APIs/tf.sparse.cs b/src/TensorFlowNET.Core/APIs/tf.sparse.cs new file mode 100644 index 00000000..bb5bc96d --- /dev/null +++ b/src/TensorFlowNET.Core/APIs/tf.sparse.cs @@ -0,0 +1,50 @@ +/***************************************************************************** + Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using Tensorflow.Framework; + +namespace Tensorflow +{ + public partial class tensorflow + { + public SparseTensor SparseTensor(long[,] indices, T[] values, int[] dense_shape) + => new SparseTensor(indices, values, dense_shape); + + /// + /// Converts a sparse representation into a dense tensor. + /// + /// + /// + /// + /// + /// + /// + /// + /// Dense `Tensor` of shape `output_shape`. Has the same type as `sparse_values`. + public Tensor sparse_to_dense(Tensor sparse_indices, + TensorShape output_shape, + T sparse_values, + T default_value = default, + bool validate_indices = true, + string name = null) + => gen_sparse_ops.sparse_to_dense(sparse_indices, + output_shape, + sparse_values, + default_value: default_value, + validate_indices: validate_indices, + name: name); + } +} diff --git a/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs b/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs index 8d0ea53b..cd0a2893 100644 --- a/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs +++ b/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs @@ -1,19 +1,54 @@ -namespace Tensorflow.Framework -{ - public interface _TensorLike - { } +using static Tensorflow.Binding; - public class SparseTensor : CompositeTensor, _TensorLike +namespace Tensorflow.Framework +{ + /// + /// Represents a sparse tensor. + /// + public class SparseTensor : CompositeTensor, _TensorLike { - private static Tensor _dense_shape { get; set; } + long[,] _indices; + Tensor indices; + + T[] _values; + Tensor values; + + int[] _dense_shape; + Tensor dense_shape; + + public SparseTensor(long[,] indices_, T[] values_, int[] dense_shape_) + { + tf_with(ops.name_scope(null, "SparseTensor", new { }), delegate + { + indices = ops.convert_to_tensor( + indices_, name: "indices", dtype: dtypes.int64); + values = ops.internal_convert_to_tensor(values_, name: "values"); + dense_shape = ops.convert_to_tensor( + dense_shape_, name: "dense_shape", dtype: dtypes.int64); + }); + _indices = indices_; + _values = values_; + _dense_shape = dense_shape_; + + var indices_shape = indices.TensorShape.with_rank(2); + var values_shape = values.TensorShape.with_rank(1); + var dense_shape_shape = dense_shape.TensorShape.with_rank(1); + + indices_shape[0].merge_with(values_shape.dims[0]); + indices_shape[1].merge_with(dense_shape_shape.dims[0]); + } + } + + public interface _TensorLike + { } - public static class sparse_tensor + public static class sparse_tensor_extension { public static bool is_sparse(this _TensorLike x) { - return x is SparseTensor; + return x.GetType().Name.Contains("SparseTensor"); } } } diff --git a/src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs b/src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs new file mode 100644 index 00000000..57a5f860 --- /dev/null +++ b/src/TensorFlowNET.Core/Operations/gen_sparse_ops.cs @@ -0,0 +1,54 @@ +/***************************************************************************** + Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using System.Collections.Generic; + +namespace Tensorflow +{ + public class gen_sparse_ops + { + public static OpDefLibrary _op_def_lib = new OpDefLibrary(); + + /// + /// Converts a sparse representation into a dense tensor. + /// + /// + /// + /// + /// + /// + /// + /// + public static Tensor sparse_to_dense(Tensor sparse_indices, + int[] output_shape, + T sparse_values, + T default_value, + bool validate_indices = true, + string name = null) + { + var _op = _op_def_lib._apply_op_helper("SparseToDense", name, args: new + { + sparse_indices, + output_shape, + sparse_values, + default_value, + validate_indices + }); + + return _op.output; + } + } +} diff --git a/src/TensorFlowNET.Core/Operations/sparse_ops.cs b/src/TensorFlowNET.Core/Operations/sparse_ops.cs new file mode 100644 index 00000000..6a30771c --- /dev/null +++ b/src/TensorFlowNET.Core/Operations/sparse_ops.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public class sparse_ops + { + /// + /// Converts a sparse representation into a dense tensor. + /// + /// + /// + /// + /// + /// + /// + /// + /// Dense `Tensor` of shape `output_shape`. Has the same type as `sparse_values`. + public Tensor sparse_to_dense(Tensor sparse_indices, + int[] output_shape, + T sparse_values, + T default_value = default, + bool validate_indices = true, + string name = null) + => gen_sparse_ops.sparse_to_dense(sparse_indices, + output_shape, + sparse_values, + default_value: default_value, + validate_indices: validate_indices, + name: name); + } +} diff --git a/src/TensorFlowNET.Core/Tensors/TensorShape.cs b/src/TensorFlowNET.Core/Tensors/TensorShape.cs index 853255cb..3e4deac2 100644 --- a/src/TensorFlowNET.Core/Tensors/TensorShape.cs +++ b/src/TensorFlowNET.Core/Tensors/TensorShape.cs @@ -143,6 +143,24 @@ namespace Tensorflow return this; } + public TensorShape with_rank(int rank) + { + return merge_with(unknown_shape(rank: rank)); + } + + /// + /// Returns an unknown TensorShape, optionally with a known rank. + /// + /// + /// + public TensorShape unknown_shape(int rank = -1) + { + if (rank == -1) + return new TensorShape(-1); + else + return new TensorShape(Enumerable.Repeat(-1, rank).ToArray()); + } + /// /// Returns the concatenation of the dimension in `self` and `other`. /// diff --git a/test/TensorFlowNET.UnitTest/TensorTest.cs b/test/TensorFlowNET.UnitTest/TensorTest.cs index 01ebda07..14194ac6 100644 --- a/test/TensorFlowNET.UnitTest/TensorTest.cs +++ b/test/TensorFlowNET.UnitTest/TensorTest.cs @@ -7,6 +7,7 @@ using System.Threading; using FluentAssertions; using Tensorflow; using static Tensorflow.Binding; +using Tensorflow.Framework; namespace TensorFlowNET.UnitTest { @@ -202,5 +203,23 @@ namespace TensorFlowNET.UnitTest // graph.Dispose(); s.Dispose(); } + + [TestMethod] + public void sparse_to_dense() + { + var indices = tf.reshape(tf.range(0, 5), new int[] { 5, 1 }); + var labels = tf.expand_dims(tf.constant(new[] { 0, 1, 2, 3, 4 }),1); + var st = tf.concat(values: new[] { indices, labels }, axis: 1); + var onehot = tf.sparse_to_dense(st, (5, 5), 1); + using (var sess = tf.Session()) + { + var result = sess.run(onehot); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 1, 0, 0, 0, 0 }, result[0].ToArray())); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 1, 0, 0, 0 }, result[1].ToArray())); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 1, 0, 0 }, result[2].ToArray())); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 1, 0 }, result[3].ToArray())); + Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 0, 1 }, result[4].ToArray())); + }; + } } } \ No newline at end of file