| @@ -12,20 +12,16 @@ namespace Tensorflow | |||||
| { | { | ||||
| public void Run() | public void Run() | ||||
| { | { | ||||
| tf.keras = new KerasInterface(); | |||||
| var inputs = np.random.random((32, 10, 8)).astype(np.float32); | |||||
| var simple_rnn = tf.keras.layers.SimpleRNN(4); | |||||
| var output = simple_rnn.Apply(inputs); // The output has shape `[32, 4]`. | |||||
| if (output.shape == (32, 4)) | |||||
| { | |||||
| tf.UseKeras<KerasInterface>(); | |||||
| var inputs = np.random.random((6, 10, 8)).astype(np.float32); | |||||
| //var simple_rnn = tf.keras.layers.SimpleRNN(4); | |||||
| //var output = simple_rnn.Apply(inputs); // The output has shape `[32, 4]`. | |||||
| } | |||||
| /*simple_rnn = tf.keras.layers.SimpleRNN( | |||||
| 4, return_sequences = True, return_state = True) | |||||
| var simple_rnn = tf.keras.layers.SimpleRNN(4, return_sequences: true, return_state: true); | |||||
| # whole_sequence_output has shape `[32, 10, 4]`. | |||||
| # final_state has shape `[32, 4]`. | |||||
| whole_sequence_output, final_state = simple_rnn(inputs)*/ | |||||
| // whole_sequence_output has shape `[32, 10, 4]`. | |||||
| // final_state has shape `[32, 4]`. | |||||
| var (whole_sequence_output, final_state) = simple_rnn.Apply(inputs); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -9,6 +9,7 @@ namespace Tensorflow.Keras | |||||
| string Name { get; } | string Name { get; } | ||||
| bool Trainable { get; } | bool Trainable { get; } | ||||
| bool Built { get; } | bool Built { get; } | ||||
| void build(Shape input_shape); | |||||
| List<ILayer> Layers { get; } | List<ILayer> Layers { get; } | ||||
| List<INode> InboundNodes { get; } | List<INode> InboundNodes { get; } | ||||
| List<INode> OutboundNodes { get; } | List<INode> OutboundNodes { get; } | ||||
| @@ -163,7 +163,9 @@ namespace Tensorflow.Keras.Layers | |||||
| string activation = "tanh", | string activation = "tanh", | ||||
| string kernel_initializer = "glorot_uniform", | string kernel_initializer = "glorot_uniform", | ||||
| string recurrent_initializer = "orthogonal", | string recurrent_initializer = "orthogonal", | ||||
| string bias_initializer = "zeros"); | |||||
| string bias_initializer = "zeros", | |||||
| bool return_sequences = false, | |||||
| bool return_state = false); | |||||
| public ILayer Subtract(); | public ILayer Subtract(); | ||||
| } | } | ||||
| @@ -1,12 +1,32 @@ | |||||
| using System; | using System; | ||||
| using System.Linq; | |||||
| using static Tensorflow.TensorShapeProto.Types; | |||||
| namespace Tensorflow.Operations.Initializers | namespace Tensorflow.Operations.Initializers | ||||
| { | { | ||||
| public class Orthogonal : IInitializer | public class Orthogonal : IInitializer | ||||
| { | { | ||||
| float _gain = 0f; | |||||
| public Orthogonal(float gain = 1.0f, int? seed = null) | |||||
| { | |||||
| } | |||||
| public Tensor Apply(InitializerArgs args) | public Tensor Apply(InitializerArgs args) | ||||
| { | { | ||||
| throw new NotImplementedException(); | |||||
| return _generate_init_val(args.Shape, args.DType); | |||||
| } | |||||
| private Tensor _generate_init_val(Shape shape, TF_DataType dtype) | |||||
| { | |||||
| var num_rows = 1L; | |||||
| foreach (var dim in shape.dims.Take(shape.ndim - 1)) | |||||
| num_rows *= dim; | |||||
| var num_cols = shape.dims.Last(); | |||||
| var flat_shape = (Math.Max(num_cols, num_rows), Math.Min(num_cols, num_rows)); | |||||
| throw new NotImplementedException(""); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -147,5 +147,10 @@ namespace Tensorflow | |||||
| { | { | ||||
| throw new NotImplementedException(); | throw new NotImplementedException(); | ||||
| } | } | ||||
| public void build(Shape input_shape) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -65,6 +65,11 @@ namespace Tensorflow | |||||
| InitGradientEnvironment(); | InitGradientEnvironment(); | ||||
| } | } | ||||
| public void UseKeras<T>() where T : IKerasApi, new() | |||||
| { | |||||
| keras = new T(); | |||||
| } | |||||
| public string VERSION => c_api.StringPiece(c_api.TF_Version()); | public string VERSION => c_api.StringPiece(c_api.TF_Version()); | ||||
| private void InitGradientEnvironment() | private void InitGradientEnvironment() | ||||
| @@ -65,7 +65,12 @@ namespace Tensorflow.Keras.Engine | |||||
| } | } | ||||
| // Keep track of the network's nodes and layers. | // Keep track of the network's nodes and layers. | ||||
| (NetworkNodes, NodesByDepth, _self_tracked_trackables, _) = MapGraphNetwork(inputs, outputs); | |||||
| (NetworkNodes, NodesByDepth, var layers, _) = MapGraphNetwork(inputs, outputs); | |||||
| if (!_self_tracked_trackables.Any()) | |||||
| { | |||||
| _self_tracked_trackables = layers; | |||||
| } | |||||
| // Build self.input_names and self.output_names. | // Build self.input_names and self.output_names. | ||||
| _set_output_names(); | _set_output_names(); | ||||
| @@ -1,9 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Linq; | using System.Linq; | ||||
| using Tensorflow.Graphs; | using Tensorflow.Graphs; | ||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Losses; | |||||
| using Tensorflow.Keras.Optimizers; | |||||
| using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
| using static Tensorflow.KerasApi; | using static Tensorflow.KerasApi; | ||||
| @@ -13,6 +10,12 @@ namespace Tensorflow.Keras.Engine | |||||
| { | { | ||||
| public override void build(Shape input_shape) | public override void build(Shape input_shape) | ||||
| { | { | ||||
| if (this is Functional || this is Sequential) | |||||
| { | |||||
| base.build(input_shape); | |||||
| return; | |||||
| } | |||||
| var graph = tf.executing_eagerly() ? new FuncGraph("build_graph") : keras.backend.get_graph(); | var graph = tf.executing_eagerly() ? new FuncGraph("build_graph") : keras.backend.get_graph(); | ||||
| graph.as_default(); | graph.as_default(); | ||||
| @@ -122,15 +122,9 @@ namespace Tensorflow.Keras.Engine | |||||
| else | else | ||||
| { | { | ||||
| _self_tracked_trackables.add(layer); | _self_tracked_trackables.add(layer); | ||||
| _handle_deferred_layer_dependencies(layer); | |||||
| } | } | ||||
| } | } | ||||
| void _handle_deferred_layer_dependencies(params ILayer[] layers) | |||||
| { | |||||
| _self_tracked_trackables.AddRange(layers); | |||||
| } | |||||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | ||||
| { | { | ||||
| if (!_has_explicit_input_shape) | if (!_has_explicit_input_shape) | ||||
| @@ -156,7 +150,7 @@ namespace Tensorflow.Keras.Engine | |||||
| ops.init_scope(); | ops.init_scope(); | ||||
| var inputs = keras.Input(batch_input_shape: input_shape, | var inputs = keras.Input(batch_input_shape: input_shape, | ||||
| dtype: input_dtype, | dtype: input_dtype, | ||||
| name: $"{_self_tracked_trackables[0].Name}_input"); | |||||
| name: _self_tracked_trackables[0].Name.EndsWith("_input") ? _self_tracked_trackables[0].Name : $"{_self_tracked_trackables[0].Name}_input"); | |||||
| Tensors layer_input = inputs; | Tensors layer_input = inputs; | ||||
| Tensors layer_output = null; | Tensors layer_output = null; | ||||
| Tensors outputs = null; | Tensors outputs = null; | ||||
| @@ -658,14 +658,18 @@ namespace Tensorflow.Keras.Layers | |||||
| string activation = "tanh", | string activation = "tanh", | ||||
| string kernel_initializer = "glorot_uniform", | string kernel_initializer = "glorot_uniform", | ||||
| string recurrent_initializer = "orthogonal", | string recurrent_initializer = "orthogonal", | ||||
| string bias_initializer = "zeros") | |||||
| string bias_initializer = "zeros", | |||||
| bool return_sequences = false, | |||||
| bool return_state = false) | |||||
| => new SimpleRNN(new SimpleRNNArgs | => new SimpleRNN(new SimpleRNNArgs | ||||
| { | { | ||||
| Units = units, | Units = units, | ||||
| Activation = GetActivationByName(activation), | Activation = GetActivationByName(activation), | ||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | KernelInitializer = GetInitializerByName(kernel_initializer), | ||||
| RecurrentInitializer= GetInitializerByName(recurrent_initializer), | |||||
| BiasInitializer= GetInitializerByName(bias_initializer) | |||||
| RecurrentInitializer = GetInitializerByName(recurrent_initializer), | |||||
| BiasInitializer = GetInitializerByName(bias_initializer), | |||||
| ReturnSequences = return_sequences, | |||||
| ReturnState = return_state | |||||
| }); | }); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -18,7 +18,7 @@ namespace Tensorflow.Keras.Layers.Rnn | |||||
| private int _num_constants = 0; | private int _num_constants = 0; | ||||
| protected IVariableV1 kernel; | protected IVariableV1 kernel; | ||||
| protected IVariableV1 bias; | protected IVariableV1 bias; | ||||
| protected ILayer cell; | |||||
| public RNN(RNNArgs args) : base(PreConstruct(args)) | public RNN(RNNArgs args) : base(PreConstruct(args)) | ||||
| { | { | ||||
| this.args = args; | this.args = args; | ||||
| @@ -37,6 +37,14 @@ namespace Tensorflow.Keras.Layers.Rnn | |||||
| //} | //} | ||||
| } | } | ||||
| public override void build(Shape input_shape) | |||||
| { | |||||
| if (!cell.Built) | |||||
| { | |||||
| cell.build(input_shape); | |||||
| } | |||||
| } | |||||
| private static RNNArgs PreConstruct(RNNArgs args) | private static RNNArgs PreConstruct(RNNArgs args) | ||||
| { | { | ||||
| if (args.Kwargs == null) | if (args.Kwargs == null) | ||||
| @@ -9,22 +9,10 @@ namespace Tensorflow.Keras.Layers.Rnn | |||||
| public class SimpleRNN : RNN | public class SimpleRNN : RNN | ||||
| { | { | ||||
| SimpleRNNArgs args; | SimpleRNNArgs args; | ||||
| SimpleRNNCell cell; | |||||
| public SimpleRNN(SimpleRNNArgs args) : base(args) | public SimpleRNN(SimpleRNNArgs args) : base(args) | ||||
| { | { | ||||
| this.args = args; | this.args = args; | ||||
| } | |||||
| public override void build(Shape input_shape) | |||||
| { | |||||
| var input_dim = input_shape[-1]; | |||||
| kernel = add_weight("kernel", (input_shape[-1], args.Units), | |||||
| initializer: args.KernelInitializer | |||||
| //regularizer = self.kernel_regularizer, | |||||
| //constraint = self.kernel_constraint, | |||||
| //caching_device = default_caching_device, | |||||
| ); | |||||
| cell = new SimpleRNNCell(args); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -8,14 +8,36 @@ namespace Tensorflow.Keras.Layers.Rnn | |||||
| { | { | ||||
| public class SimpleRNNCell : Layer | public class SimpleRNNCell : Layer | ||||
| { | { | ||||
| SimpleRNNArgs args; | |||||
| IVariableV1 kernel; | |||||
| IVariableV1 recurrent_kernel; | |||||
| IVariableV1 bias; | |||||
| public SimpleRNNCell(SimpleRNNArgs args) : base(args) | public SimpleRNNCell(SimpleRNNArgs args) : base(args) | ||||
| { | { | ||||
| this.args = args; | |||||
| } | } | ||||
| public override void build(Shape input_shape) | public override void build(Shape input_shape) | ||||
| { | { | ||||
| var input_dim = input_shape[-1]; | |||||
| kernel = add_weight("kernel", (input_shape[-1], args.Units), | |||||
| initializer: args.KernelInitializer | |||||
| ); | |||||
| recurrent_kernel = add_weight("recurrent_kernel", (args.Units, args.Units), | |||||
| initializer: args.RecurrentInitializer | |||||
| ); | |||||
| if (args.UseBias) | |||||
| { | |||||
| bias = add_weight("bias", (args.Units), | |||||
| initializer: args.RecurrentInitializer | |||||
| ); | |||||
| } | |||||
| built = true; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -150,10 +150,13 @@ namespace TensorFlowNET.Keras.UnitTest | |||||
| [TestMethod] | [TestMethod] | ||||
| public void SimpleRNN() | public void SimpleRNN() | ||||
| { | { | ||||
| var inputs = np.random.random((32, 10, 8)).astype(np.float32); | |||||
| var simple_rnn = keras.layers.SimpleRNN(4); | |||||
| tf.UseKeras<KerasInterface>(); | |||||
| 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); | var output = simple_rnn.Apply(inputs); | ||||
| Assert.AreEqual((32, 4), output.shape); | |||||
| Assert.AreEqual((32, 4), output.shape);*/ | |||||
| var simple_rnn = tf.keras.layers.SimpleRNN(4, return_sequences: true, return_state: true); | |||||
| var (whole_sequence_output, final_state) = simple_rnn.Apply(inputs); | |||||
| } | } | ||||
| [TestMethod] | [TestMethod] | ||||
| @@ -47,7 +47,7 @@ | |||||
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="FluentAssertions" Version="5.10.3" /> | <PackageReference Include="FluentAssertions" Version="5.10.3" /> | ||||
| <PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.144" /> | |||||
| <PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.148" /> | |||||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | ||||
| <PackageReference Include="MSTest.TestAdapter" Version="2.2.8" /> | <PackageReference Include="MSTest.TestAdapter" Version="2.2.8" /> | ||||
| <PackageReference Include="MSTest.TestFramework" Version="2.2.8" /> | <PackageReference Include="MSTest.TestFramework" Version="2.2.8" /> | ||||