diff --git a/src/TensorFlowNET.Core/Binding.Util.cs b/src/TensorFlowNET.Core/Binding.Util.cs index 1e8c21a9..7c260810 100644 --- a/src/TensorFlowNET.Core/Binding.Util.cs +++ b/src/TensorFlowNET.Core/Binding.Util.cs @@ -265,15 +265,17 @@ namespace Tensorflow yield return (i, values[i]); } - public static IEnumerable<(int, T)> enumerate(IEnumerable values, int start = 0) + public static IEnumerable<(int, T)> enumerate(IEnumerable values, int start = 0, int step = 1) { int i = 0; - foreach(var val in values) + foreach (var val in values) { - if (i++ < start) + i += step; + + if (i < start) continue; - - yield return (i - start, val); + + yield return (i - step - start, val); } } diff --git a/src/TensorFlowNET.Core/Data/DatasetManager.cs b/src/TensorFlowNET.Core/Data/DatasetManager.cs index ddfa1835..515e0dd3 100644 --- a/src/TensorFlowNET.Core/Data/DatasetManager.cs +++ b/src/TensorFlowNET.Core/Data/DatasetManager.cs @@ -1,7 +1,7 @@ -using NumSharp; -using System; +using System; using System.Collections.Generic; using System.Text; +using Tensorflow.Data; namespace Tensorflow { @@ -9,5 +9,8 @@ namespace Tensorflow { public IDatasetV2 from_tensor_slices(Tensor features, Tensor labels) => new TensorSliceDataset(features, labels); + + public IDatasetV2 range(int count, TF_DataType output_type = TF_DataType.TF_INT64) + => new RangeDataset(count, output_type: output_type); } } diff --git a/src/TensorFlowNET.Core/Data/DatasetV2.cs b/src/TensorFlowNET.Core/Data/DatasetV2.cs index 071cbb29..d111ac2c 100644 --- a/src/TensorFlowNET.Core/Data/DatasetV2.cs +++ b/src/TensorFlowNET.Core/Data/DatasetV2.cs @@ -78,9 +78,8 @@ namespace Tensorflow { var ownedIterator = new OwnedIterator(this); - bool stop = false; Tensor[] results = null; - while (!stop) + while (true) { try { @@ -88,10 +87,10 @@ namespace Tensorflow } catch (StopIteration) { - stop = true; + break; } - yield return (results[0], results[1]); + yield return (results[0], results.Length == 1 ? null : results[1]); } } diff --git a/src/TensorFlowNET.Core/Data/RangeDataset.cs b/src/TensorFlowNET.Core/Data/RangeDataset.cs new file mode 100644 index 00000000..72ef91b8 --- /dev/null +++ b/src/TensorFlowNET.Core/Data/RangeDataset.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Framework.Models; +using static Tensorflow.Binding; + +namespace Tensorflow.Data +{ + public class RangeDataset : DatasetSource + { + Tensor start; + Tensor step; + Tensor stop; + + public RangeDataset(int stop, + int start = 0, + int step = 1, + TF_DataType output_type = TF_DataType.TF_INT64) + { + this.start = tf.convert_to_tensor((long)start); + this.step = tf.convert_to_tensor((long)step); + this.stop = tf.convert_to_tensor((long)stop); + + structure = new TensorSpec[] { new TensorSpec(new int[0], dtype: output_type) }; + variant_tensor = ops.range_dataset(this.start, this.stop, this.step, output_types, output_shapes); + } + } +} diff --git a/src/TensorFlowNET.Core/Data/TensorSliceDataset.cs b/src/TensorFlowNET.Core/Data/TensorSliceDataset.cs index 0fc88f31..feea6d61 100644 --- a/src/TensorFlowNET.Core/Data/TensorSliceDataset.cs +++ b/src/TensorFlowNET.Core/Data/TensorSliceDataset.cs @@ -7,7 +7,7 @@ using System.Text; using Tensorflow.Framework.Models; using static Tensorflow.Binding; -namespace Tensorflow +namespace Tensorflow.Data { public class TensorSliceDataset : DatasetSource { diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/DataHandlerArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/DataHandlerArgs.cs new file mode 100644 index 00000000..585ca19b --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/DataHandlerArgs.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Keras.Engine; + +namespace Tensorflow.Keras.ArgsDefinition +{ + public class DataHandlerArgs + { + public Tensor X { get; set; } + public Tensor Y { get; set; } + public int BatchSize { get; set; } = 32; + public int StepsPerEpoch { get; set; } = -1; + public int InitialEpoch { get; set; } = 0; + public int Epochs { get; set; } = 1; + public bool Shuffle { get; set; } = false; + public int MaxQueueSize { get; set; } = 10; + public int Workers { get; set; } = 1; + public bool UseMultiprocessing { get; set; } = false; + public Model Model { get; set; } + public IVariableV1 StepsPerExecution { get; set; } + } +} diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/SequentialArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/SequentialArgs.cs new file mode 100644 index 00000000..925ceca5 --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/SequentialArgs.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Keras.Engine; + +namespace Tensorflow.Keras.ArgsDefinition +{ + public class SequentialArgs : ModelArgs + { + public List Layers { get; set; } + } +} diff --git a/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/DataHandler.cs b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/DataHandler.cs new file mode 100644 index 00000000..de29d299 --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/DataHandler.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Keras.ArgsDefinition; + +namespace Tensorflow.Keras.Engine.DataAdapters +{ + /// + /// Handles iterating over epoch-level `tf.data.Iterator` objects. + /// + public class DataHandler + { + DataHandlerArgs args; + + Tensor x => args.X; + Tensor y => args.Y; + int batch_size => args.BatchSize; + int steps_per_epoch => args.StepsPerEpoch; + int initial_epoch => args.InitialEpoch; + int epochs => args.Epochs; + bool shuffle => args.Shuffle; + int max_queue_size => args.MaxQueueSize; + int workers => args.Workers; + bool use_multiprocessing => args.UseMultiprocessing; + Model model => args.Model; + IVariableV1 steps_per_execution => args.StepsPerExecution; + + public DataHandler(DataHandlerArgs args) + { + + } + } +} diff --git a/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/IDataAdapter.cs b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/IDataAdapter.cs new file mode 100644 index 00000000..c50a05b8 --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/IDataAdapter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow.Keras.Engine.DataAdapters +{ + /// + /// In TF 2.0, tf.data is the preferred API for user to feed in data. In order + /// to simplify the training code path, all the input data object will be + /// converted to `tf.data.Dataset` if possible. + /// + public interface IDataAdapter + { + /// + /// Whether the current DataAdapter could handle the input x and y. + /// + /// input features + /// target labels + /// + bool CanHandle(Tensor x, Tensor y = null); + } +} diff --git a/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/TensorLikeDataAdapter.cs b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/TensorLikeDataAdapter.cs new file mode 100644 index 00000000..0311219a --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/Engine/DataAdapters/TensorLikeDataAdapter.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using static Tensorflow.Binding; + +namespace Tensorflow.Keras.Engine.DataAdapters +{ + /// + /// Adapter that handles Tensor-like objects, e.g. EagerTensor and NumPy. + /// + public class TensorLikeDataAdapter : IDataAdapter + { + public TensorLikeDataAdapter() + { + tf.data.Dataset.range(5); + } + + public bool CanHandle(Tensor x, Tensor y = null) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/TensorFlowNET.Core/Keras/Engine/Model.cs b/src/TensorFlowNET.Core/Keras/Engine/Model.cs index c98bd62d..48c670fc 100644 --- a/src/TensorFlowNET.Core/Keras/Engine/Model.cs +++ b/src/TensorFlowNET.Core/Keras/Engine/Model.cs @@ -1,4 +1,6 @@ -using Tensorflow.Keras.ArgsDefinition; +using NumSharp; +using System; +using Tensorflow.Keras.ArgsDefinition; using Tensorflow.Keras.Optimizers; namespace Tensorflow.Keras.Engine @@ -39,5 +41,30 @@ namespace Tensorflow.Keras.Engine // Prepare list of loss functions, same size of model outputs. } + + /// + /// Generates output predictions for the input samples. + /// + /// Input samples + /// Number of samples per batch + /// Verbosity mode + /// + /// Total number of steps (batches of samples) + /// before declaring the prediction round finished. + /// + /// + /// + /// + /// + public Tensor predict(Tensor x, + int batch_size = 32, + int verbose = 0, + int steps = -1, + int max_queue_size = 10, + int workers = 1, + bool use_multiprocessing = false) + { + throw new NotImplementedException(""); + } } } diff --git a/src/TensorFlowNET.Core/Keras/Engine/Sequential.cs b/src/TensorFlowNET.Core/Keras/Engine/Sequential.cs index 09d10e70..16ce0e0a 100644 --- a/src/TensorFlowNET.Core/Keras/Engine/Sequential.cs +++ b/src/TensorFlowNET.Core/Keras/Engine/Sequential.cs @@ -26,8 +26,9 @@ namespace Tensorflow.Keras.Engine /// `Sequential` groups a linear stack of layers into a `tf.keras.Model`. /// `Sequential` provides training and inference features on this model. /// - public class Sequential + public class Sequential : Model { + SequentialArgs args; bool _is_graph_network; Tensor inputs; Tensor outputs; @@ -37,13 +38,19 @@ namespace Tensorflow.Keras.Engine TensorShape inferredInputShape; bool hasExplicitInputShape; TF_DataType inputDType; - List layers; + List layers => args.Layers; public TensorShape output_shape => outputs.TensorShape; bool built = false; - public Sequential(Layer[] layers = null, string name = null) + public Sequential(SequentialArgs args) + : base(new ModelArgs + { + Name = args.Name + }) { - this.layers = layers == null ? new List() : layers.ToList(); + this.args = args; + if (args.Layers == null) + args.Layers = new List(); // SupportsMasking = true; computeOutputAndMaskJointly = true; autoTrackSubLayers = false; diff --git a/src/TensorFlowNET.Core/Keras/KerasApi.cs b/src/TensorFlowNET.Core/Keras/KerasApi.cs index 84bf61c2..0acb594c 100644 --- a/src/TensorFlowNET.Core/Keras/KerasApi.cs +++ b/src/TensorFlowNET.Core/Keras/KerasApi.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using System.Linq; using Tensorflow.Keras; @@ -19,10 +20,13 @@ namespace Tensorflow public BackendImpl backend { get; } = new BackendImpl(); - public Models models { get; } = new Models(); - - public Sequential Sequential() - => new Sequential(); + public Sequential Sequential(List layers = null, + string name = null) + => new Sequential(new SequentialArgs + { + Layers = layers, + Name = name + }); /// /// Instantiate a Keras tensor. diff --git a/src/TensorFlowNET.Core/Keras/Models.cs b/src/TensorFlowNET.Core/Keras/Models.cs deleted file mode 100644 index 545c6143..00000000 --- a/src/TensorFlowNET.Core/Keras/Models.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Tensorflow.Keras.Engine; - -namespace Tensorflow.Keras -{ - public class Models - { - public Sequential Sequential() - => new Sequential(); - } -} diff --git a/src/TensorFlowNET.Core/Operations/dataset_ops.cs b/src/TensorFlowNET.Core/Operations/dataset_ops.cs index f3aca7a7..fbbb12b6 100644 --- a/src/TensorFlowNET.Core/Operations/dataset_ops.cs +++ b/src/TensorFlowNET.Core/Operations/dataset_ops.cs @@ -33,6 +33,22 @@ namespace Tensorflow throw new NotImplementedException(""); } + public Tensor range_dataset(Tensor start, Tensor stop, Tensor step, TF_DataType[] output_types, TensorShape[] output_shapes, string name = null) + { + if (tf.Context.executing_eagerly()) + { + var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName, + "RangeDataset", name, + null, + start, stop, step, + "output_types", output_types, + "output_shapes", output_shapes); + return results[0]; + } + + throw new NotImplementedException(""); + } + public Tensor repeat_dataset(Tensor input_dataset, Tensor count, TF_DataType[] output_types, TensorShape[] output_shapes, string name = null) { if (tf.Context.executing_eagerly()) diff --git a/test/TensorFlowNET.UnitTest/Dataset/DatasetTest.cs b/test/TensorFlowNET.UnitTest/Dataset/DatasetTest.cs new file mode 100644 index 00000000..8db484d7 --- /dev/null +++ b/test/TensorFlowNET.UnitTest/Dataset/DatasetTest.cs @@ -0,0 +1,30 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.UnitTest; +using static Tensorflow.Binding; + +namespace TensorFlowNET.UnitTest.Dataset +{ + [TestClass] + public class DatasetTest : EagerModeTestBase + { + [TestMethod] + public void Range() + { + int iStep = 0; + long value = 0; + + var dataset = tf.data.Dataset.range(3); + foreach(var (step, item) in enumerate(dataset)) + { + Assert.AreEqual(iStep, step); + iStep++; + + Assert.AreEqual(value, (long)item.Item1); + value++; + } + } + } +} diff --git a/test/TensorFlowNET.UnitTest/Keras/LayersTest.cs b/test/TensorFlowNET.UnitTest/Keras/LayersTest.cs index 759ecd5c..35fc46b2 100644 --- a/test/TensorFlowNET.UnitTest/Keras/LayersTest.cs +++ b/test/TensorFlowNET.UnitTest/Keras/LayersTest.cs @@ -19,7 +19,7 @@ namespace TensorFlowNET.UnitTest.Keras [TestMethod] public void Sequential() { - var model = tf.keras.models.Sequential(); + var model = tf.keras.Sequential(); model.add(tf.keras.Input(shape: 16)); } @@ -29,7 +29,7 @@ namespace TensorFlowNET.UnitTest.Keras [TestMethod] public void Embedding() { - var model = new Sequential(); + var model = tf.keras.Sequential(); var layer = tf.keras.layers.Embedding(1000, 64, input_length: 10); model.add(layer); // the model will take as input an integer matrix of size (batch, @@ -39,8 +39,8 @@ namespace TensorFlowNET.UnitTest.Keras // now model.output_shape == (None, 10, 64), where None is the batch // dimension. var input_array = np.random.randint(1000, size: (32, 10)); - // model.compile("rmsprop", "mse"); - // output_array = model.predict(input_array) + model.compile("rmsprop", "mse"); + var output_array = model.predict(input_array); } ///