| @@ -18,7 +18,7 @@ namespace Tensorflow | |||
| embeddings_initializer, | |||
| mask_zero); | |||
| public static InputLayer Input(int[] batch_shape = null, | |||
| public static Tensor[] Input(int[] batch_shape = null, | |||
| TF_DataType dtype = TF_DataType.DtInvalid, | |||
| string name = null, | |||
| bool sparse = false, | |||
| @@ -35,7 +35,9 @@ namespace Tensorflow | |||
| sparse: sparse, | |||
| input_tensor: tensor); | |||
| throw new NotImplementedException(""); | |||
| var outputs = input_layer.inbound_nodes[0].output_tensors; | |||
| return outputs; | |||
| } | |||
| } | |||
| } | |||
| @@ -33,6 +33,12 @@ namespace Tensorflow.Keras.Engine | |||
| batch_shape: batch_shape, | |||
| dtype: dtype, | |||
| name: layer._name + "_input"); | |||
| // This will build the current layer | |||
| // and create the node connecting the current layer | |||
| // to the input layer we just created. | |||
| layer.__call__(x); | |||
| set_inputs = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -9,6 +9,8 @@ namespace Tensorflow.Keras.Layers | |||
| private int input_dim; | |||
| private int output_dim; | |||
| private bool mask_zero; | |||
| public RefVariable embeddings; | |||
| public IInitializer embeddings_initializer; | |||
| public Embedding(int input_dim, int output_dim, | |||
| IInitializer embeddings_initializer = null, | |||
| @@ -18,10 +20,17 @@ namespace Tensorflow.Keras.Layers | |||
| { | |||
| this.input_dim = input_dim; | |||
| this.output_dim = output_dim; | |||
| if (embeddings_initializer == null) | |||
| embeddings_initializer = tf.uniform_initializer; | |||
| this.embeddings_initializer = embeddings_initializer == null ? tf.uniform_initializer : embeddings_initializer; | |||
| this.mask_zero = mask_zero; | |||
| supports_masking = mask_zero; | |||
| } | |||
| protected override void build(TensorShape input_shape) | |||
| { | |||
| embeddings = add_weight(shape: new int[] { input_dim, output_dim }, | |||
| initializer: embeddings_initializer, | |||
| name: "embeddings"); | |||
| built = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -11,6 +11,7 @@ namespace Tensorflow.Keras.Layers | |||
| { | |||
| public bool sparse; | |||
| public int? batch_size; | |||
| public bool is_placeholder; | |||
| public InputLayer(int[] input_shape = null, | |||
| int? batch_size = null, | |||
| @@ -24,7 +25,7 @@ namespace Tensorflow.Keras.Layers | |||
| this.batch_size = batch_size; | |||
| this.supports_masking = true; | |||
| if(input_tensor == null) | |||
| if (input_tensor == null) | |||
| { | |||
| var batch_input_shape = new int[] { batch_size.HasValue ? batch_size.Value : -1, -1 }; | |||
| @@ -39,7 +40,17 @@ namespace Tensorflow.Keras.Layers | |||
| dtype: dtype, | |||
| name: name); | |||
| } | |||
| is_placeholder = true; | |||
| _batch_input_shape = batch_input_shape; | |||
| } | |||
| new Node(this, | |||
| inbound_layers: new Layer[0], | |||
| node_indices: new int[0], | |||
| tensor_indices: new int[0], | |||
| input_tensors: new Tensor[] { input_tensor }, | |||
| output_tensors: new Tensor[] { input_tensor }); | |||
| } | |||
| } | |||
| } | |||
| @@ -39,6 +39,12 @@ namespace Tensorflow.Keras.Layers | |||
| protected List<Operation> _updates; | |||
| public int[] _batch_input_shape; | |||
| private List<Node> _inbound_nodes; | |||
| public List<Node> inbound_nodes => _inbound_nodes; | |||
| private List<Node> _outbound_nodes; | |||
| public List<Node> outbound_nodes => _outbound_nodes; | |||
| public Layer(bool trainable = true, | |||
| string name = null, | |||
| TF_DataType dtype = TF_DataType.DtInvalid, | |||
| @@ -59,13 +65,15 @@ namespace Tensorflow.Keras.Layers | |||
| _batch_input_shape = new int[] { -1, -1 }; | |||
| _dtype = dtype; | |||
| _inbound_nodes = new List<Node>(); | |||
| } | |||
| public Tensor __call__(Tensor inputs, | |||
| public Tensor __call__(Tensor[] inputs, | |||
| Tensor training = null, | |||
| VariableScope scope = null) | |||
| { | |||
| var input_list = new Tensor[] { inputs }; | |||
| var input_list = inputs; | |||
| Tensor outputs = null; | |||
| // We will attempt to build a TF graph if & only if all inputs are symbolic. | |||
| @@ -88,9 +96,9 @@ namespace Tensorflow.Keras.Layers | |||
| // Symbolic execution on symbolic tensors. We will attempt to build | |||
| // the corresponding TF subgraph inside `backend.get_graph()` | |||
| var graph = backend.get_graph(); | |||
| outputs = call(inputs, training: training); | |||
| _handle_activity_regularization(inputs, outputs); | |||
| _set_mask_metadata(inputs, outputs, null); | |||
| outputs = call(inputs[0], training: training); | |||
| _handle_activity_regularization(inputs[0], outputs); | |||
| _set_mask_metadata(inputs[0], outputs, null); | |||
| } | |||
| }); | |||
| @@ -125,10 +133,10 @@ namespace Tensorflow.Keras.Layers | |||
| return null; | |||
| } | |||
| protected void _maybe_build(Tensor inputs) | |||
| protected void _maybe_build(Tensor[] inputs) | |||
| { | |||
| var input_list = new Tensor[] { inputs }; | |||
| build(inputs.getShape()); | |||
| var input_list = inputs; | |||
| build(input_list[0].getShape()); | |||
| } | |||
| protected virtual void build(TensorShape input_shape) | |||
| @@ -143,10 +151,16 @@ namespace Tensorflow.Keras.Layers | |||
| bool? trainable = null, | |||
| Func<string, int[], TF_DataType, IInitializer, bool, RefVariable> getter = null) | |||
| { | |||
| if (dtype == TF_DataType.DtInvalid) | |||
| dtype = TF_DataType.TF_FLOAT; | |||
| if (trainable == null) | |||
| trainable = true; | |||
| var variable = _add_variable_with_custom_getter(name, | |||
| shape, | |||
| dtype: dtype, | |||
| getter: getter, | |||
| getter: getter == null ? base_layer_utils.make_variable : getter, | |||
| overwrite: true, | |||
| initializer: initializer, | |||
| trainable: trainable.Value); | |||
| @@ -0,0 +1,71 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| namespace Tensorflow.Keras.Layers | |||
| { | |||
| /// <summary> | |||
| /// A `Node` describes the connectivity between two layers. | |||
| /// </summary> | |||
| public class Node | |||
| { | |||
| public InputLayer outbound_layer; | |||
| public Layer[] inbound_layers; | |||
| public int[] node_indices; | |||
| public int[] tensor_indices; | |||
| public Tensor[] input_tensors; | |||
| public Tensor[] output_tensors; | |||
| public int[][] input_shapes; | |||
| public int[][] output_shapes; | |||
| /// <summary> | |||
| /// | |||
| /// </summary> | |||
| /// <param name="outbound_layer"> | |||
| /// the layer that takes | |||
| /// `input_tensors` and turns them into `output_tensors` | |||
| /// (the node gets created when the `call` | |||
| /// method of the layer was called). | |||
| /// </param> | |||
| /// <param name="inbound_layers"> | |||
| /// a list of layers, the same length as `input_tensors`, | |||
| /// the layers from where `input_tensors` originate. | |||
| /// </param> | |||
| /// <param name="node_indices"> | |||
| /// a list of integers, the same length as `inbound_layers`. | |||
| /// `node_indices[i]` is the origin node of `input_tensors[i]` | |||
| /// (necessary since each inbound layer might have several nodes, | |||
| /// e.g. if the layer is being shared with a different data stream). | |||
| /// </param> | |||
| /// <param name="tensor_indices"></param> | |||
| /// <param name="input_tensors">list of input tensors.</param> | |||
| /// <param name="output_tensors">list of output tensors.</param> | |||
| public Node(InputLayer outbound_layer, | |||
| Layer[] inbound_layers, | |||
| int[] node_indices, | |||
| int[] tensor_indices, | |||
| Tensor[] input_tensors, | |||
| Tensor[] output_tensors) | |||
| { | |||
| this.outbound_layer = outbound_layer; | |||
| this.inbound_layers = inbound_layers; | |||
| this.node_indices = node_indices; | |||
| this.tensor_indices = tensor_indices; | |||
| this.input_tensors = input_tensors; | |||
| this.output_tensors = output_tensors; | |||
| input_shapes = input_tensors.Select(x => x._shape_tuple()).ToArray(); | |||
| output_shapes = output_tensors.Select(x => x._shape_tuple()).ToArray(); | |||
| // Add nodes to all layers involved. | |||
| foreach (var layer in inbound_layers) | |||
| { | |||
| if (layer != null) | |||
| layer.outbound_nodes.Add(this); | |||
| } | |||
| outbound_layer.inbound_nodes.Add(this); | |||
| } | |||
| } | |||
| } | |||
| @@ -2,10 +2,19 @@ | |||
| using System.Collections.Generic; | |||
| using System.Text; | |||
| namespace Tensorflow.Keras.Engine | |||
| namespace Tensorflow.Keras.Utils | |||
| { | |||
| public class base_layer_utils | |||
| { | |||
| public static RefVariable make_variable(string name, | |||
| int[] shape, | |||
| TF_DataType dtype = TF_DataType.TF_FLOAT, | |||
| IInitializer initializer = null, | |||
| bool trainable = false) | |||
| { | |||
| throw new NotImplementedException(""); | |||
| } | |||
| /// <summary> | |||
| /// Makes a layer name (or arbitrary string) unique within a TensorFlow graph. | |||
| /// </summary> | |||
| @@ -52,7 +52,7 @@ namespace Tensorflow.Layers | |||
| Python.with(scope_context_manager, scope2 => _current_scope = scope2); | |||
| // Actually call layer | |||
| var outputs = base.__call__(inputs, training: training); | |||
| var outputs = base.__call__(new Tensor[] { inputs }, training: training); | |||
| // Update global default collections. | |||
| _add_elements_to_collection(_updates.ToArray(), new string[] { ops.GraphKeys.UPDATE_OPS }); | |||
| @@ -43,7 +43,7 @@ Docs: https://tensorflownet.readthedocs.io</Description> | |||
| <ItemGroup> | |||
| <PackageReference Include="Google.Protobuf" Version="3.7.0" /> | |||
| <PackageReference Include="NumSharp" Version="0.7.4" /> | |||
| <PackageReference Include="NumSharp" Version="0.8.0" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| @@ -6,7 +6,7 @@ | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="NumSharp" Version="0.7.4" /> | |||
| <PackageReference Include="NumSharp" Version="0.8.0" /> | |||
| <PackageReference Include="SharpZipLib" Version="1.1.0" /> | |||
| <PackageReference Include="TensorFlow.NET" Version="0.4.2" /> | |||
| </ItemGroup> | |||
| @@ -19,7 +19,7 @@ | |||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" /> | |||
| <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" /> | |||
| <PackageReference Include="MSTest.TestFramework" Version="1.4.0" /> | |||
| <PackageReference Include="NumSharp" Version="0.7.4" /> | |||
| <PackageReference Include="NumSharp" Version="0.8.0" /> | |||
| <PackageReference Include="TensorFlow.NET" Version="0.4.2" /> | |||
| </ItemGroup> | |||