| @@ -265,15 +265,17 @@ namespace Tensorflow | |||||
| yield return (i, values[i]); | yield return (i, values[i]); | ||||
| } | } | ||||
| public static IEnumerable<(int, T)> enumerate<T>(IEnumerable<T> values, int start = 0) | |||||
| public static IEnumerable<(int, T)> enumerate<T>(IEnumerable<T> values, int start = 0, int step = 1) | |||||
| { | { | ||||
| int i = 0; | int i = 0; | ||||
| foreach(var val in values) | |||||
| foreach (var val in values) | |||||
| { | { | ||||
| if (i++ < start) | |||||
| i += step; | |||||
| if (i < start) | |||||
| continue; | continue; | ||||
| yield return (i - start, val); | |||||
| yield return (i - step - start, val); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| using NumSharp; | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Text; | using System.Text; | ||||
| using Tensorflow.Data; | |||||
| namespace Tensorflow | namespace Tensorflow | ||||
| { | { | ||||
| @@ -9,5 +9,8 @@ namespace Tensorflow | |||||
| { | { | ||||
| public IDatasetV2 from_tensor_slices(Tensor features, Tensor labels) | public IDatasetV2 from_tensor_slices(Tensor features, Tensor labels) | ||||
| => new TensorSliceDataset(features, 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); | |||||
| } | } | ||||
| } | } | ||||
| @@ -78,9 +78,8 @@ namespace Tensorflow | |||||
| { | { | ||||
| var ownedIterator = new OwnedIterator(this); | var ownedIterator = new OwnedIterator(this); | ||||
| bool stop = false; | |||||
| Tensor[] results = null; | Tensor[] results = null; | ||||
| while (!stop) | |||||
| while (true) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| @@ -88,10 +87,10 @@ namespace Tensorflow | |||||
| } | } | ||||
| catch (StopIteration) | catch (StopIteration) | ||||
| { | { | ||||
| stop = true; | |||||
| break; | |||||
| } | } | ||||
| yield return (results[0], results[1]); | |||||
| yield return (results[0], results.Length == 1 ? null : results[1]); | |||||
| } | } | ||||
| } | } | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -7,7 +7,7 @@ using System.Text; | |||||
| using Tensorflow.Framework.Models; | using Tensorflow.Framework.Models; | ||||
| using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
| namespace Tensorflow | |||||
| namespace Tensorflow.Data | |||||
| { | { | ||||
| public class TensorSliceDataset : DatasetSource | public class TensorSliceDataset : DatasetSource | ||||
| { | { | ||||
| @@ -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; } | |||||
| } | |||||
| } | |||||
| @@ -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<Layer> Layers { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| namespace Tensorflow.Keras.Engine.DataAdapters | |||||
| { | |||||
| /// <summary> | |||||
| /// Handles iterating over epoch-level `tf.data.Iterator` objects. | |||||
| /// </summary> | |||||
| 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) | |||||
| { | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace Tensorflow.Keras.Engine.DataAdapters | |||||
| { | |||||
| /// <summary> | |||||
| /// 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. | |||||
| /// </summary> | |||||
| public interface IDataAdapter | |||||
| { | |||||
| /// <summary> | |||||
| /// Whether the current DataAdapter could handle the input x and y. | |||||
| /// </summary> | |||||
| /// <param name="x">input features</param> | |||||
| /// <param name="y">target labels</param> | |||||
| /// <returns></returns> | |||||
| bool CanHandle(Tensor x, Tensor y = null); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Keras.Engine.DataAdapters | |||||
| { | |||||
| /// <summary> | |||||
| /// Adapter that handles Tensor-like objects, e.g. EagerTensor and NumPy. | |||||
| /// </summary> | |||||
| public class TensorLikeDataAdapter : IDataAdapter | |||||
| { | |||||
| public TensorLikeDataAdapter() | |||||
| { | |||||
| tf.data.Dataset.range(5); | |||||
| } | |||||
| public bool CanHandle(Tensor x, Tensor y = null) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,4 +1,6 @@ | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| using NumSharp; | |||||
| using System; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Optimizers; | using Tensorflow.Keras.Optimizers; | ||||
| namespace Tensorflow.Keras.Engine | namespace Tensorflow.Keras.Engine | ||||
| @@ -39,5 +41,30 @@ namespace Tensorflow.Keras.Engine | |||||
| // Prepare list of loss functions, same size of model outputs. | // Prepare list of loss functions, same size of model outputs. | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Generates output predictions for the input samples. | |||||
| /// </summary> | |||||
| /// <param name="x">Input samples</param> | |||||
| /// <param name="batch_size">Number of samples per batch</param> | |||||
| /// <param name="verbose">Verbosity mode</param> | |||||
| /// <param name="steps"> | |||||
| /// Total number of steps (batches of samples) | |||||
| /// before declaring the prediction round finished. | |||||
| /// </param> | |||||
| /// <param name="max_queue_size"></param> | |||||
| /// <param name="workers"></param> | |||||
| /// <param name="use_multiprocessing"></param> | |||||
| /// <returns></returns> | |||||
| 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(""); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -26,8 +26,9 @@ namespace Tensorflow.Keras.Engine | |||||
| /// `Sequential` groups a linear stack of layers into a `tf.keras.Model`. | /// `Sequential` groups a linear stack of layers into a `tf.keras.Model`. | ||||
| /// `Sequential` provides training and inference features on this model. | /// `Sequential` provides training and inference features on this model. | ||||
| /// </summary> | /// </summary> | ||||
| public class Sequential | |||||
| public class Sequential : Model | |||||
| { | { | ||||
| SequentialArgs args; | |||||
| bool _is_graph_network; | bool _is_graph_network; | ||||
| Tensor inputs; | Tensor inputs; | ||||
| Tensor outputs; | Tensor outputs; | ||||
| @@ -37,13 +38,19 @@ namespace Tensorflow.Keras.Engine | |||||
| TensorShape inferredInputShape; | TensorShape inferredInputShape; | ||||
| bool hasExplicitInputShape; | bool hasExplicitInputShape; | ||||
| TF_DataType inputDType; | TF_DataType inputDType; | ||||
| List<Layer> layers; | |||||
| List<Layer> layers => args.Layers; | |||||
| public TensorShape output_shape => outputs.TensorShape; | public TensorShape output_shape => outputs.TensorShape; | ||||
| bool built = false; | 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<Layer>() : layers.ToList(); | |||||
| this.args = args; | |||||
| if (args.Layers == null) | |||||
| args.Layers = new List<Layer>(); | |||||
| // SupportsMasking = true; | // SupportsMasking = true; | ||||
| computeOutputAndMaskJointly = true; | computeOutputAndMaskJointly = true; | ||||
| autoTrackSubLayers = false; | autoTrackSubLayers = false; | ||||
| @@ -1,4 +1,5 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Data; | using System.Data; | ||||
| using System.Linq; | using System.Linq; | ||||
| using Tensorflow.Keras; | using Tensorflow.Keras; | ||||
| @@ -19,10 +20,13 @@ namespace Tensorflow | |||||
| public BackendImpl backend { get; } = new BackendImpl(); | public BackendImpl backend { get; } = new BackendImpl(); | ||||
| public Models models { get; } = new Models(); | |||||
| public Sequential Sequential() | |||||
| => new Sequential(); | |||||
| public Sequential Sequential(List<Layer> layers = null, | |||||
| string name = null) | |||||
| => new Sequential(new SequentialArgs | |||||
| { | |||||
| Layers = layers, | |||||
| Name = name | |||||
| }); | |||||
| /// <summary> | /// <summary> | ||||
| /// Instantiate a Keras tensor. | /// Instantiate a Keras tensor. | ||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| @@ -33,6 +33,22 @@ namespace Tensorflow | |||||
| throw new NotImplementedException(""); | 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) | public Tensor repeat_dataset(Tensor input_dataset, Tensor count, TF_DataType[] output_types, TensorShape[] output_shapes, string name = null) | ||||
| { | { | ||||
| if (tf.Context.executing_eagerly()) | if (tf.Context.executing_eagerly()) | ||||
| @@ -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++; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -19,7 +19,7 @@ namespace TensorFlowNET.UnitTest.Keras | |||||
| [TestMethod] | [TestMethod] | ||||
| public void Sequential() | public void Sequential() | ||||
| { | { | ||||
| var model = tf.keras.models.Sequential(); | |||||
| var model = tf.keras.Sequential(); | |||||
| model.add(tf.keras.Input(shape: 16)); | model.add(tf.keras.Input(shape: 16)); | ||||
| } | } | ||||
| @@ -29,7 +29,7 @@ namespace TensorFlowNET.UnitTest.Keras | |||||
| [TestMethod] | [TestMethod] | ||||
| public void Embedding() | public void Embedding() | ||||
| { | { | ||||
| var model = new Sequential(); | |||||
| var model = tf.keras.Sequential(); | |||||
| var layer = tf.keras.layers.Embedding(1000, 64, input_length: 10); | var layer = tf.keras.layers.Embedding(1000, 64, input_length: 10); | ||||
| model.add(layer); | model.add(layer); | ||||
| // the model will take as input an integer matrix of size (batch, | // 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 | // now model.output_shape == (None, 10, 64), where None is the batch | ||||
| // dimension. | // dimension. | ||||
| var input_array = np.random.randint(1000, size: (32, 10)); | 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); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||