@@ -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> | ||||