diff --git a/src/TensorFlowNET.Console/Program.cs b/src/TensorFlowNET.Console/Program.cs index 091456f8c..5f12badb0 100644 --- a/src/TensorFlowNET.Console/Program.cs +++ b/src/TensorFlowNET.Console/Program.cs @@ -8,8 +8,6 @@ class Program { static void Main(string[] args) { - tf.UseKeras(); - var diag = new Diagnostician(); // diag.Diagnose(@"D:\memory.txt"); diff --git a/src/TensorFlowNET.Core/Keras/IKerasApi.cs b/src/TensorFlowNET.Core/Keras/IKerasApi.cs index 7fb3635ca..0b46d27d4 100644 --- a/src/TensorFlowNET.Core/Keras/IKerasApi.cs +++ b/src/TensorFlowNET.Core/Keras/IKerasApi.cs @@ -1,19 +1,24 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; +using Tensorflow.Framework.Models; using Tensorflow.Keras.Engine; using Tensorflow.Keras.Layers; using Tensorflow.Keras.Losses; using Tensorflow.Keras.Metrics; +using Tensorflow.Keras.Models; namespace Tensorflow.Keras { public interface IKerasApi { - public ILayersApi layers { get; } - public ILossesApi losses { get; } - public IMetricsApi metrics { get; } - public IInitializersApi initializers { get; } + IInitializersApi initializers { get; } + ILayersApi layers { get; } + ILossesApi losses { get; } + IOptimizerApi optimizers { get; } + IMetricsApi metrics { get; } + IModelsApi models { get; } /// /// `Model` groups layers into an object with training and inference features. @@ -21,6 +26,35 @@ public interface IKerasApi /// /// /// - public IModel Model(Tensors inputs, Tensors outputs, string name = null); + IModel Model(Tensors inputs, Tensors outputs, string name = null); + + /// + /// Instantiate a Keras tensor. + /// + /// + /// + /// + /// + /// + /// A boolean specifying whether the placeholder to be created is sparse. + /// + /// + /// A boolean specifying whether the placeholder to be created is ragged. + /// + /// + /// Optional existing tensor to wrap into the `Input` layer. + /// If set, the layer will not create a placeholder tensor. + /// + /// + Tensors Input(Shape shape = null, + int batch_size = -1, + string name = null, + TF_DataType dtype = TF_DataType.DtInvalid, + bool sparse = false, + Tensor tensor = null, + bool ragged = false, + TypeSpec type_spec = null, + Shape batch_input_shape = null, + Shape batch_shape = null); } } diff --git a/src/TensorFlowNET.Core/Keras/IOptimizerApi.cs b/src/TensorFlowNET.Core/Keras/IOptimizerApi.cs new file mode 100644 index 000000000..961ce91ae --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/IOptimizerApi.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Keras.Engine; + +namespace Tensorflow.Keras +{ + public interface IOptimizerApi + { + /// + /// Adam optimization is a stochastic gradient descent method that is based on + /// adaptive estimation of first-order and second-order moments. + /// + /// + /// + /// + /// + /// + /// + /// + IOptimizer Adam(float learning_rate = 0.001f, + float beta_1 = 0.9f, + float beta_2 = 0.999f, + float epsilon = 1e-7f, + bool amsgrad = false, + string name = "Adam"); + + /// + /// Construct a new RMSprop optimizer. + /// + /// + /// + /// + /// + /// + /// + /// + IOptimizer RMSprop(float learning_rate = 0.001f, + float rho = 0.9f, + float momentum = 0.0f, + float epsilon = 1e-7f, + bool centered = false, + string name = "RMSprop"); + + IOptimizer SGD(float learning_rate); + } +} diff --git a/src/TensorFlowNET.Core/Keras/Models/IModelsApi.cs b/src/TensorFlowNET.Core/Keras/Models/IModelsApi.cs new file mode 100644 index 000000000..007c82a17 --- /dev/null +++ b/src/TensorFlowNET.Core/Keras/Models/IModelsApi.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Tensorflow.Keras.Engine; + +namespace Tensorflow.Keras.Models +{ + public interface IModelsApi + { + public IModel load_model(string filepath, bool compile = true, LoadOptions? options = null); + } +} diff --git a/src/TensorFlowNET.Core/tensorflow.cs b/src/TensorFlowNET.Core/tensorflow.cs index 6e655a196..e02723b7c 100644 --- a/src/TensorFlowNET.Core/tensorflow.cs +++ b/src/TensorFlowNET.Core/tensorflow.cs @@ -65,14 +65,6 @@ public tensorflow() InitGradientEnvironment(); } - public void UseKeras() where T : IKerasApi, new() - { - if (keras == null) - { - keras = new T(); - } - } - public string VERSION => c_api.StringPiece(c_api.TF_Version()); private void InitGradientEnvironment() diff --git a/src/TensorFlowNET.Keras/KerasApi.cs b/src/TensorFlowNET.Keras/KerasApi.cs index f79c2b5f2..69c59ab82 100644 --- a/src/TensorFlowNET.Keras/KerasApi.cs +++ b/src/TensorFlowNET.Keras/KerasApi.cs @@ -7,6 +7,6 @@ namespace Tensorflow /// public static class KerasApi { - public static KerasInterface keras { get; } = new KerasInterface(); + public static KerasInterface keras { get; } = KerasInterface.Instance; } } diff --git a/src/TensorFlowNET.Keras/KerasInterface.cs b/src/TensorFlowNET.Keras/KerasInterface.cs index 6816e6902..8bd1e682a 100644 --- a/src/TensorFlowNET.Keras/KerasInterface.cs +++ b/src/TensorFlowNET.Keras/KerasInterface.cs @@ -18,6 +18,28 @@ namespace Tensorflow.Keras { public class KerasInterface : IKerasApi { + private static KerasInterface _instance = null; + private static readonly object _lock = new object(); + private KerasInterface() + { + Tensorflow.Binding.tf.keras = this; + } + + public static KerasInterface Instance + { + get + { + lock (_lock) + { + if (_instance is null) + { + _instance = new KerasInterface(); + } + return _instance; + } + } + } + public KerasDataset datasets { get; } = new KerasDataset(); public IInitializersApi initializers { get; } = new InitializersApi(); public Regularizers regularizers { get; } = new Regularizers(); @@ -27,9 +49,9 @@ public class KerasInterface : IKerasApi public Preprocessing preprocessing { get; } = new Preprocessing(); ThreadLocal _backend = new ThreadLocal(() => new BackendImpl()); public BackendImpl backend => _backend.Value; - public OptimizerApi optimizers { get; } = new OptimizerApi(); + public IOptimizerApi optimizers { get; } = new OptimizerApi(); public IMetricsApi metrics { get; } = new MetricsApi(); - public ModelsApi models { get; } = new ModelsApi(); + public IModelsApi models { get; } = new ModelsApi(); public KerasUtils utils { get; } = new KerasUtils(); public Sequential Sequential(List layers = null, diff --git a/src/TensorFlowNET.Keras/Models/ModelsApi.cs b/src/TensorFlowNET.Keras/Models/ModelsApi.cs index 6597f5cdc..3a997ff2f 100644 --- a/src/TensorFlowNET.Keras/Models/ModelsApi.cs +++ b/src/TensorFlowNET.Keras/Models/ModelsApi.cs @@ -9,12 +9,12 @@ namespace Tensorflow.Keras.Models { - public class ModelsApi + public class ModelsApi: IModelsApi { public Functional from_config(ModelConfig config) => Functional.from_config(config); - public Model load_model(string filepath, bool compile = true, LoadOptions? options = null) + public IModel load_model(string filepath, bool compile = true, LoadOptions? options = null) { return KerasLoadModelUtils.load_model(filepath, compile: compile, options: options) as Model; } diff --git a/src/TensorFlowNET.Keras/Optimizers/OptimizerApi.cs b/src/TensorFlowNET.Keras/Optimizers/OptimizerApi.cs index c8e69bc88..31eb88be7 100644 --- a/src/TensorFlowNET.Keras/Optimizers/OptimizerApi.cs +++ b/src/TensorFlowNET.Keras/Optimizers/OptimizerApi.cs @@ -1,8 +1,9 @@ using Tensorflow.Keras.ArgsDefinition; +using Tensorflow.Keras.Engine; namespace Tensorflow.Keras.Optimizers { - public class OptimizerApi + public class OptimizerApi: IOptimizerApi { /// /// Adam optimization is a stochastic gradient descent method that is based on @@ -15,7 +16,7 @@ public class OptimizerApi /// /// /// - public OptimizerV2 Adam(float learning_rate = 0.001f, + public IOptimizer Adam(float learning_rate = 0.001f, float beta_1 = 0.9f, float beta_2 = 0.999f, float epsilon = 1e-7f, @@ -38,7 +39,7 @@ public OptimizerV2 Adam(float learning_rate = 0.001f, /// /// /// - public OptimizerV2 RMSprop(float learning_rate = 0.001f, + public IOptimizer RMSprop(float learning_rate = 0.001f, float rho = 0.9f, float momentum = 0.0f, float epsilon = 1e-7f, @@ -54,7 +55,7 @@ public OptimizerV2 RMSprop(float learning_rate = 0.001f, Name = name }); - public SGD SGD(float learning_rate) + public IOptimizer SGD(float learning_rate) => new SGD(learning_rate); } } diff --git a/test/TensorFlowNET.Keras.UnitTest/EagerModeTestBase.cs b/test/TensorFlowNET.Keras.UnitTest/EagerModeTestBase.cs index 04ed3df4d..576c641d7 100644 --- a/test/TensorFlowNET.Keras.UnitTest/EagerModeTestBase.cs +++ b/test/TensorFlowNET.Keras.UnitTest/EagerModeTestBase.cs @@ -10,8 +10,6 @@ public class EagerModeTestBase [TestInitialize] public void TestInit() { - tf.UseKeras(); - if (!tf.executing_eagerly()) tf.enable_eager_execution(); tf.Context.ensure_initialized(); diff --git a/test/TensorFlowNET.Keras.UnitTest/Layers/LayersTest.cs b/test/TensorFlowNET.Keras.UnitTest/Layers/LayersTest.cs index f8a6174d9..78397c8ea 100644 --- a/test/TensorFlowNET.Keras.UnitTest/Layers/LayersTest.cs +++ b/test/TensorFlowNET.Keras.UnitTest/Layers/LayersTest.cs @@ -150,7 +150,6 @@ public void EinsumDense() [TestMethod, Ignore("WIP")] public void SimpleRNN() { - tf.UseKeras(); var inputs = np.arange(6 * 10 * 8).reshape((6, 10, 8)).astype(np.float32); /*var simple_rnn = keras.layers.SimpleRNN(4); var output = simple_rnn.Apply(inputs); diff --git a/test/TensorFlowNET.Keras.UnitTest/Layers/ModelSaveTest.cs b/test/TensorFlowNET.Keras.UnitTest/Layers/ModelSaveTest.cs index 12bf02a51..647b2ad78 100644 --- a/test/TensorFlowNET.Keras.UnitTest/Layers/ModelSaveTest.cs +++ b/test/TensorFlowNET.Keras.UnitTest/Layers/ModelSaveTest.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using static Tensorflow.KerasApi; using Tensorflow.Keras.Saving; +using Tensorflow.Keras.Models; namespace TensorFlowNET.Keras.UnitTest { @@ -18,7 +19,7 @@ public void GetAndFromConfig() var model = GetFunctionalModel(); var config = model.get_config(); Debug.Assert(config is ModelConfig); - var new_model = keras.models.from_config(config as ModelConfig); + var new_model = new ModelsApi().from_config(config as ModelConfig); Assert.AreEqual(model.Layers.Count, new_model.Layers.Count); }