diff --git a/src/TensorFlowNET.Core/Training/Saving/SavedModel/save.cs b/src/TensorFlowNET.Core/Training/Saving/SavedModel/save.cs index 4313920f5..23e0a9295 100644 --- a/src/TensorFlowNET.Core/Training/Saving/SavedModel/save.cs +++ b/src/TensorFlowNET.Core/Training/Saving/SavedModel/save.cs @@ -88,7 +88,7 @@ private static (MetaGraphDef, Graph, TrackableSaver, AssetInfo, IList { if (ops.inside_function()) { - throw new AssertionError("`tf.saved_model.save` is not supported inside a traced @tf.function. " + + throw new AssertionError("`tf.saved_model.save` is not supported inside a traced [AutoGraph]. " + "Move the call to the outer eagerly-executed context."); } diff --git a/src/TensorFlowNET.Keras/Engine/Layer.Apply.cs b/src/TensorFlowNET.Keras/Engine/Layer.Apply.cs index 7d3721f12..c04304580 100644 --- a/src/TensorFlowNET.Keras/Engine/Layer.Apply.cs +++ b/src/TensorFlowNET.Keras/Engine/Layer.Apply.cs @@ -41,5 +41,19 @@ public Tensors Apply(Tensors inputs, Tensor state = null, bool training = false) return outputs; } + + // TODO(Rinne): remove it and completely fix issue 1084 + [Obsolete] + private bool _enforce_layer_construction = false; + [Obsolete] + internal void enforce_layer_construction() + { + _enforce_layer_construction = true; + } + [Obsolete] + internal void unset_layer_construction() + { + _enforce_layer_construction = false; + } } } diff --git a/src/TensorFlowNET.Keras/Engine/Layer.cs b/src/TensorFlowNET.Keras/Engine/Layer.cs index 7462b1367..5942efd92 100644 --- a/src/TensorFlowNET.Keras/Engine/Layer.cs +++ b/src/TensorFlowNET.Keras/Engine/Layer.cs @@ -291,7 +291,7 @@ internal virtual void Initialize(LayerArgs args) bool _in_functional_construction_mode(Tensors inputs) { return tf.Context.executing_eagerly() - && inputs.Count(x => x is not EagerTensor && x is not NDArray) == inputs.Count(); + && inputs.Count(x => x is not EagerTensor && x is not NDArray) == inputs.Count() || _enforce_layer_construction; } public void SetConnectivityMetadata(Tensors inputs, Tensors outputs) diff --git a/src/TensorFlowNET.Keras/Engine/Sequential.cs b/src/TensorFlowNET.Keras/Engine/Sequential.cs index 90167a9d9..278747515 100644 --- a/src/TensorFlowNET.Keras/Engine/Sequential.cs +++ b/src/TensorFlowNET.Keras/Engine/Sequential.cs @@ -62,7 +62,17 @@ public void InitLayers(IEnumerable layers) { foreach(var layer in layers) { + // TODO(Rinne): remove it and completely fix issue 1084 + if(layer is Sequential s) + { + s.Layers.ForEach(x => ((Layer)x).enforce_layer_construction()); + } add(layer); + // TODO(Rinne): remove it and completely fix issue 1084 + if (layer is Sequential s2) + { + s2.Layers.ForEach(x => ((Layer)x).unset_layer_construction()); + } } } @@ -163,7 +173,7 @@ void _build_graph_network_for_inferred_shape(Shape input_shape, TF_DataType inpu Tensors layer_output = null; Tensors outputs = null; List created_nodes = new List(); - foreach (var layer in args.Layers) + foreach (var layer in Layers) { clear_previously_created_nodes(layer, _created_nodes); layer_output = layer.Apply(layer_input); diff --git a/test/TensorFlowNET.Keras.UnitTest/Model/ModelBuildTest.cs b/test/TensorFlowNET.Keras.UnitTest/Model/ModelBuildTest.cs index e1fe9ff4f..d4b11a9b2 100644 --- a/test/TensorFlowNET.Keras.UnitTest/Model/ModelBuildTest.cs +++ b/test/TensorFlowNET.Keras.UnitTest/Model/ModelBuildTest.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using static Tensorflow.Binding; +using static Tensorflow.KerasApi; namespace Tensorflow.Keras.UnitTest.Model { @@ -14,24 +16,47 @@ public void DenseBuild() var dense = tf.keras.layers.Dense(64); var output = dense.Apply(input); var model = tf.keras.Model(input, output); + model.compile(tf.keras.optimizers.Adam(), tf.keras.losses.CategoricalCrossentropy()); // one dimensions input with unknown batchsize var input_2 = tf.keras.layers.Input((60)); var dense_2 = tf.keras.layers.Dense(64); - var output_2 = dense.Apply(input_2); + var output_2 = dense_2.Apply(input_2); var model_2 = tf.keras.Model(input_2, output_2); + model_2.compile(tf.keras.optimizers.Adam(), tf.keras.losses.CategoricalCrossentropy()); // two dimensions input with specified batchsize var input_3 = tf.keras.layers.Input((17, 60), 8); var dense_3 = tf.keras.layers.Dense(64); - var output_3 = dense.Apply(input_3); + var output_3 = dense_3.Apply(input_3); var model_3 = tf.keras.Model(input_3, output_3); + model_3.compile(tf.keras.optimizers.Adam(), tf.keras.losses.CategoricalCrossentropy()); // one dimensions input with specified batchsize var input_4 = tf.keras.layers.Input((60), 8); var dense_4 = tf.keras.layers.Dense(64); - var output_4 = dense.Apply(input_4); + var output_4 = dense_4.Apply(input_4); var model_4 = tf.keras.Model(input_4, output_4); + model_4.compile(tf.keras.optimizers.Adam(), tf.keras.losses.CategoricalCrossentropy()); + } + + [TestMethod] + public void NestedSequential() + { + var block1 = keras.Sequential(new[] { + keras.layers.InputLayer((3, 3)), + keras.Sequential(new [] + { + keras.layers.Flatten(), + keras.layers.Dense(5) + } + ) + }); + block1.compile(tf.keras.optimizers.Adam(), tf.keras.losses.CategoricalCrossentropy()); + + var x = tf.ones((1, 3, 3)); + var y = block1.predict(x); + Console.WriteLine(y); } } }