@@ -18,7 +18,7 @@ namespace Tensorflow | |||||
embeddings_initializer, | embeddings_initializer, | ||||
mask_zero); | mask_zero); | ||||
public static InputLayer Input(int[] batch_shape = null, | |||||
public static Tensor[] Input(int[] batch_shape = null, | |||||
TF_DataType dtype = TF_DataType.DtInvalid, | TF_DataType dtype = TF_DataType.DtInvalid, | ||||
string name = null, | string name = null, | ||||
bool sparse = false, | bool sparse = false, | ||||
@@ -35,7 +35,9 @@ namespace Tensorflow | |||||
sparse: sparse, | sparse: sparse, | ||||
input_tensor: tensor); | 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, | batch_shape: batch_shape, | ||||
dtype: dtype, | dtype: dtype, | ||||
name: layer._name + "_input"); | 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 input_dim; | ||||
private int output_dim; | private int output_dim; | ||||
private bool mask_zero; | private bool mask_zero; | ||||
public RefVariable embeddings; | |||||
public IInitializer embeddings_initializer; | |||||
public Embedding(int input_dim, int output_dim, | public Embedding(int input_dim, int output_dim, | ||||
IInitializer embeddings_initializer = null, | IInitializer embeddings_initializer = null, | ||||
@@ -18,10 +20,17 @@ namespace Tensorflow.Keras.Layers | |||||
{ | { | ||||
this.input_dim = input_dim; | this.input_dim = input_dim; | ||||
this.output_dim = output_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; | this.mask_zero = mask_zero; | ||||
supports_masking = 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 bool sparse; | ||||
public int? batch_size; | public int? batch_size; | ||||
public bool is_placeholder; | |||||
public InputLayer(int[] input_shape = null, | public InputLayer(int[] input_shape = null, | ||||
int? batch_size = null, | int? batch_size = null, | ||||
@@ -24,7 +25,7 @@ namespace Tensorflow.Keras.Layers | |||||
this.batch_size = batch_size; | this.batch_size = batch_size; | ||||
this.supports_masking = true; | 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 }; | var batch_input_shape = new int[] { batch_size.HasValue ? batch_size.Value : -1, -1 }; | ||||
@@ -39,7 +40,17 @@ namespace Tensorflow.Keras.Layers | |||||
dtype: dtype, | dtype: dtype, | ||||
name: name); | 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; | protected List<Operation> _updates; | ||||
public int[] _batch_input_shape; | 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, | public Layer(bool trainable = true, | ||||
string name = null, | string name = null, | ||||
TF_DataType dtype = TF_DataType.DtInvalid, | TF_DataType dtype = TF_DataType.DtInvalid, | ||||
@@ -59,13 +65,15 @@ namespace Tensorflow.Keras.Layers | |||||
_batch_input_shape = new int[] { -1, -1 }; | _batch_input_shape = new int[] { -1, -1 }; | ||||
_dtype = dtype; | _dtype = dtype; | ||||
_inbound_nodes = new List<Node>(); | |||||
} | } | ||||
public Tensor __call__(Tensor inputs, | |||||
public Tensor __call__(Tensor[] inputs, | |||||
Tensor training = null, | Tensor training = null, | ||||
VariableScope scope = null) | VariableScope scope = null) | ||||
{ | { | ||||
var input_list = new Tensor[] { inputs }; | |||||
var input_list = inputs; | |||||
Tensor outputs = null; | Tensor outputs = null; | ||||
// We will attempt to build a TF graph if & only if all inputs are symbolic. | // 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 | // Symbolic execution on symbolic tensors. We will attempt to build | ||||
// the corresponding TF subgraph inside `backend.get_graph()` | // the corresponding TF subgraph inside `backend.get_graph()` | ||||
var graph = 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; | 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) | protected virtual void build(TensorShape input_shape) | ||||
@@ -143,10 +151,16 @@ namespace Tensorflow.Keras.Layers | |||||
bool? trainable = null, | bool? trainable = null, | ||||
Func<string, int[], TF_DataType, IInitializer, bool, RefVariable> getter = 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, | var variable = _add_variable_with_custom_getter(name, | ||||
shape, | shape, | ||||
dtype: dtype, | dtype: dtype, | ||||
getter: getter, | |||||
getter: getter == null ? base_layer_utils.make_variable : getter, | |||||
overwrite: true, | overwrite: true, | ||||
initializer: initializer, | initializer: initializer, | ||||
trainable: trainable.Value); | 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.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace Tensorflow.Keras.Engine | |||||
namespace Tensorflow.Keras.Utils | |||||
{ | { | ||||
public class base_layer_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> | /// <summary> | ||||
/// Makes a layer name (or arbitrary string) unique within a TensorFlow graph. | /// Makes a layer name (or arbitrary string) unique within a TensorFlow graph. | ||||
/// </summary> | /// </summary> |
@@ -52,7 +52,7 @@ namespace Tensorflow.Layers | |||||
Python.with(scope_context_manager, scope2 => _current_scope = scope2); | Python.with(scope_context_manager, scope2 => _current_scope = scope2); | ||||
// Actually call layer | // Actually call layer | ||||
var outputs = base.__call__(inputs, training: training); | |||||
var outputs = base.__call__(new Tensor[] { inputs }, training: training); | |||||
// Update global default collections. | // Update global default collections. | ||||
_add_elements_to_collection(_updates.ToArray(), new string[] { ops.GraphKeys.UPDATE_OPS }); | _add_elements_to_collection(_updates.ToArray(), new string[] { ops.GraphKeys.UPDATE_OPS }); | ||||
@@ -43,7 +43,7 @@ Docs: https://tensorflownet.readthedocs.io</Description> | |||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Google.Protobuf" Version="3.7.0" /> | <PackageReference Include="Google.Protobuf" Version="3.7.0" /> | ||||
<PackageReference Include="NumSharp" Version="0.7.4" /> | |||||
<PackageReference Include="NumSharp" Version="0.8.0" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -6,7 +6,7 @@ | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="NumSharp" Version="0.7.4" /> | |||||
<PackageReference Include="NumSharp" Version="0.8.0" /> | |||||
<PackageReference Include="SharpZipLib" Version="1.1.0" /> | <PackageReference Include="SharpZipLib" Version="1.1.0" /> | ||||
<PackageReference Include="TensorFlow.NET" Version="0.4.2" /> | <PackageReference Include="TensorFlow.NET" Version="0.4.2" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -19,7 +19,7 @@ | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" /> | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" /> | ||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" /> | <PackageReference Include="MSTest.TestAdapter" Version="1.4.0" /> | ||||
<PackageReference Include="MSTest.TestFramework" 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" /> | <PackageReference Include="TensorFlow.NET" Version="0.4.2" /> | ||||
</ItemGroup> | </ItemGroup> | ||||