Browse Source

tf.keras.layers #355

tags/v0.20
Oceania2018 5 years ago
parent
commit
de2383162b
31 changed files with 297 additions and 163 deletions
  1. +3
    -3
      src/TensorFlowNET.Core/Gradients/GradientTape.cs
  2. +19
    -1
      src/TensorFlowNET.Core/Gradients/Tape.ComputeGradient.cs
  3. +2
    -4
      src/TensorFlowNET.Core/Keras/Activations/Activations.Linear.cs
  4. +26
    -0
      src/TensorFlowNET.Core/Keras/Activations/Activations.Relu.cs
  5. +9
    -0
      src/TensorFlowNET.Core/Keras/Activations/Activations.cs
  6. +2
    -2
      src/TensorFlowNET.Core/Keras/ArgsDefinition/NodeArgs.cs
  7. +29
    -0
      src/TensorFlowNET.Core/Keras/Engine/Layer.Layers.cs
  8. +39
    -68
      src/TensorFlowNET.Core/Keras/Engine/Layer.cs
  9. +3
    -3
      src/TensorFlowNET.Core/Keras/Engine/Node.cs
  10. +8
    -5
      src/TensorFlowNET.Core/Keras/Engine/Sequential.cs
  11. +25
    -5
      src/TensorFlowNET.Core/Keras/KerasApi.cs
  12. +3
    -3
      src/TensorFlowNET.Core/Keras/Layers/BatchNormalization.cs
  13. +3
    -3
      src/TensorFlowNET.Core/Keras/Layers/Conv.cs
  14. +4
    -4
      src/TensorFlowNET.Core/Keras/Layers/Dense.cs
  15. +5
    -5
      src/TensorFlowNET.Core/Keras/Layers/Embedding.cs
  16. +2
    -2
      src/TensorFlowNET.Core/Keras/Layers/InputLayer.cs
  17. +3
    -3
      src/TensorFlowNET.Core/Keras/Layers/Pooling2D.cs
  18. +13
    -0
      src/TensorFlowNET.Core/Keras/Models.cs
  19. +5
    -5
      src/TensorFlowNET.Core/Keras/Optimizers/OptimizerV2.cs
  20. +1
    -1
      src/TensorFlowNET.Core/Keras/Optimizers/SGD.cs
  21. +2
    -3
      src/TensorFlowNET.Core/Layers/Layer.cs
  22. +4
    -4
      src/TensorFlowNET.Core/Operations/NnOps/BasicLSTMCell.cs
  23. +3
    -3
      src/TensorFlowNET.Core/Operations/NnOps/BasicRNNCell.cs
  24. +33
    -1
      src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs
  25. +40
    -1
      src/TensorFlowNET.Core/Operations/array_ops.cs
  26. +0
    -32
      src/TensorFlowNET.Core/Operations/gen_array_ops.cs
  27. +2
    -1
      src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs
  28. +1
    -0
      src/TensorFlowNET.Core/Variables/IVariableV1.cs
  29. +1
    -0
      src/TensorFlowNET.Core/Variables/RefVariable.cs
  30. +0
    -1
      src/TensorFlowNET.Core/tensorflow.cs
  31. +7
    -0
      test/TensorFlowNET.UnitTest/Keras/LayersTest.cs

+ 3
- 3
src/TensorFlowNET.Core/Gradients/GradientTape.cs View File

@@ -107,19 +107,19 @@ namespace Tensorflow.Gradients


public Tensor gradient(Tensor target, ResourceVariable source) public Tensor gradient(Tensor target, ResourceVariable source)
{ {
var results = gradient(target, new[] { source });
var results = gradient(target, new List<IVariableV1> { source });


return results[0]; return results[0];
} }


public (Tensor, Tensor) gradient(Tensor target, (ResourceVariable, ResourceVariable) sources) public (Tensor, Tensor) gradient(Tensor target, (ResourceVariable, ResourceVariable) sources)
{ {
var results = gradient(target, new[] { sources.Item1, sources.Item2 });
var results = gradient(target, new List<IVariableV1> { sources.Item1, sources.Item2 });


return (results[0], results[1]); return (results[0], results[1]);
} }


public Tensor[] gradient(Tensor target, IEnumerable<IVariableV1> sources)
public Tensor[] gradient(Tensor target, List<IVariableV1> sources)
{ {
if (_recording) if (_recording)
{ {


+ 19
- 1
src/TensorFlowNET.Core/Gradients/Tape.ComputeGradient.cs View File

@@ -54,7 +54,16 @@ namespace Tensorflow.Gradients
var id = trace.output_tensor_info[i].GetID(); var id = trace.output_tensor_info[i].GetID();
if (!gradients.find(id, out var grad_it)) if (!gradients.find(id, out var grad_it))
{ {
throw new NotImplementedException("FunctionsAcceptingNoneForIndicesMap");
if (FunctionsAcceptingNoneForIndicesMap().find(trace.op_type, out var func_name_it) &&
func_name_it.find(i))
{
out_gradients.Add(null);
}
else
{
out_gradients.Add(null);
zero_indices.Add(i);
}
} }
else else
{ {
@@ -184,6 +193,15 @@ namespace Tensorflow.Gradients
return result.ToArray(); return result.ToArray();
} }


UnorderedMap<string, UnorderedSet<int>> FunctionsAcceptingNoneForIndicesMap()
{
var m = new UnorderedMap<string, UnorderedSet<int>>();
m.Add("SoftmaxCrossEntropyWithLogits", new UnorderedSet<int>(new[] { 1 }));
m.Add("SparseSoftmaxCrossEntropyWithLogits", new UnorderedSet<int>(new[] { 1 }));
m.Add("FusedBatchNorm", new UnorderedSet<int>(new[] { 1, 2, 3, 4 }));
return m;
}

UnorderedMapEnumerable<long, List<Tensor>> InitialGradients(long[] target_tensor_ids, UnorderedMapEnumerable<long, List<Tensor>> InitialGradients(long[] target_tensor_ids,
UnorderedMap<long, TapeTensor> sources_that_are_targets, UnorderedMap<long, TapeTensor> sources_that_are_targets,
Tensor[] output_gradients, Tensor[] output_gradients,


src/TensorFlowNET.Core/Keras/Activations.cs → src/TensorFlowNET.Core/Keras/Activations/Activations.Linear.cs View File

@@ -5,13 +5,11 @@ using static Tensorflow.Binding;


namespace Tensorflow.Keras namespace Tensorflow.Keras
{ {
public delegate Tensor Activation(Tensor x);

public class Activations
public partial class Activations
{ {
/// <summary> /// <summary>
/// Linear activation function (pass-through). /// Linear activation function (pass-through).
/// </summary> /// </summary>
public Activation Linear = x => x;
public Activation Linear = (features, name) => features;
} }
} }

+ 26
- 0
src/TensorFlowNET.Core/Keras/Activations/Activations.Relu.cs View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Operations;
using static Tensorflow.Binding;

namespace Tensorflow.Keras
{
public partial class Activations
{
public Activation Relu = (features, name) =>
{
if (tf.executing_eagerly())
{
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"Relu", name,
null,
features);

return results[0];
}

throw new NotImplementedException("");
};
}
}

+ 9
- 0
src/TensorFlowNET.Core/Keras/Activations/Activations.cs View File

@@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using static Tensorflow.Binding;

namespace Tensorflow.Keras
{
public delegate Tensor Activation(Tensor features, string name = null);
}

+ 2
- 2
src/TensorFlowNET.Core/Keras/ArgsDefinition/NodeArgs.cs View File

@@ -11,7 +11,7 @@ namespace Tensorflow.Keras.ArgsDefinition
public Layer[] InboundLayers { get; set; } public Layer[] InboundLayers { get; set; }
public int[] NodeIndices { get; set; } public int[] NodeIndices { get; set; }
public int[] TensorIndices { get; set; } public int[] TensorIndices { get; set; }
public Tensor[] InputTensors { get; set; }
public Tensor[] Outputs { get; set; }
public Tensor InputTensors { get; set; }
public Tensor Outputs { get; set; }
} }
} }

+ 29
- 0
src/TensorFlowNET.Core/Keras/Engine/Layer.Layers.cs View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;
using Tensorflow.Keras.Layers;
using static Tensorflow.Binding;

namespace Tensorflow.Keras.Engine
{
public partial class Layer
{
protected List<Layer> _layers = new List<Layer>();

protected Layer Dense(int units,
Activation activation = null,
TensorShape input_shape = null)
{
var layer = new Dense(new DenseArgs
{
Units = units,
Activation = activation ?? tf.keras.activations.Linear,
InputShape = input_shape
});

_layers.Add(layer);
return layer;
}
}
}

+ 39
- 68
src/TensorFlowNET.Core/Keras/Engine/Layer.cs View File

@@ -18,11 +18,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Tensorflow.Contexts;
using Tensorflow.Keras.ArgsDefinition; using Tensorflow.Keras.ArgsDefinition;
using Tensorflow.Keras.Layers; using Tensorflow.Keras.Layers;
using Tensorflow.Keras.Utils; using Tensorflow.Keras.Utils;
using Tensorflow.Operations.Activation;
using Tensorflow.Train; using Tensorflow.Train;
using static Tensorflow.Binding; using static Tensorflow.Binding;


@@ -34,7 +32,7 @@ namespace Tensorflow.Keras.Engine
/// as convolution, batch norm, etc. These operations require managing weights, /// as convolution, batch norm, etc. These operations require managing weights,
/// losses, updates, and inter-layer connectivity. /// losses, updates, and inter-layer connectivity.
/// </summary> /// </summary>
public abstract class Layer : AutoTrackable
public abstract partial class Layer : AutoTrackable
{ {
/// <summary> /// <summary>
/// Arguments initialize layer. /// Arguments initialize layer.
@@ -60,8 +58,19 @@ namespace Tensorflow.Keras.Engine
protected InputSpec inputSpec; protected InputSpec inputSpec;
public bool SupportsMasking { get; set; } public bool SupportsMasking { get; set; }
protected List<IVariableV1> trainableWeights; protected List<IVariableV1> trainableWeights;
public List<IVariableV1> TrainableVariables => trainableWeights;
public List<IVariableV1> trainable_variables
{
get
{
if(trainableWeights.Count == 0)
_layers.ForEach(x => trainableWeights.AddRange(x.trainableWeights));

return trainableWeights;
}
}

protected List<IVariableV1> nonTrainableWeights; protected List<IVariableV1> nonTrainableWeights;
public List<IVariableV1> non_trainable_variables => nonTrainableWeights;


string name; string name;
public string Name => name; public string Name => name;
@@ -112,20 +121,20 @@ namespace Tensorflow.Keras.Engine
/// <param name="input"></param> /// <param name="input"></param>
/// <param name="is_training"></param> /// <param name="is_training"></param>
/// <returns></returns> /// <returns></returns>
public Tensor[] Apply(Tensor[] inputs, bool is_training = false)
public Tensor Apply(Tensor inputs, bool is_training = false)
{ {
var input = inputs[0];
Tensor[] outputs = null;
Tensor outputs = null;


callContext = callContext ?? new ThreadLocal<CallContext>() callContext = callContext ?? new ThreadLocal<CallContext>()
{ {
Value = new CallContext() Value = new CallContext()
}; };


var eager = tf.executing_eagerly();
using var ctxManager = CallContext.enter(); using var ctxManager = CallContext.enter();


string nameScope = ""; string nameScope = "";
if (tf.executing_eagerly())
if (eager)
{ {
nameScope = name; nameScope = name;
} }
@@ -134,7 +143,7 @@ namespace Tensorflow.Keras.Engine
throw new NotImplementedException(""); throw new NotImplementedException("");
} }


using var graph = tf.keras.backend.get_graph().as_default();
// using var graph = tf.keras.backend.get_graph().as_default();
tf_with(ops.name_scope(nameScope), scope => tf_with(ops.name_scope(nameScope), scope =>
{ {
@@ -143,74 +152,36 @@ namespace Tensorflow.Keras.Engine


outputs = call(inputs, is_training: is_training); outputs = call(inputs, is_training: is_training);


(input, outputs) = _set_connectivity_metadata_(input, outputs);
_handle_activity_regularization(inputs[0], outputs);
_set_mask_metadata(inputs[0], outputs, null);
outputs = _set_connectivity_metadata_(inputs, outputs);
_handle_activity_regularization(inputs, outputs);
_set_mask_metadata(inputs, outputs, null);
}); });


return outputs; return outputs;
} }


[Obsolete("User Apply()")]
public Tensor[] __call__(Tensor[] inputs,
Tensor training = null,
Tensor state = null,
VariableScope scope = null)
private Tensor _set_connectivity_metadata_(Tensor inputs, Tensor outputs)
{ {
var input_list = inputs;
var input = inputs[0];
Tensor[] outputs = null;

// We will attempt to build a TF graph if & only if all inputs are symbolic.
// This is always the case in graph mode. It can also be the case in eager
// mode when all inputs can be traced back to `keras.Input()` (when building
// models using the functional API).
bool build_graph = tf_utils.are_all_symbolic_tensors(input_list);

if (build_graph)
{
// Only create Keras history if at least one tensor originates from a
// `keras.Input`. Otherwise this Layer may be being used outside the Keras
// framework.
// base_layer_utils.create_keras_history(inputs)
}

// with base_layer_utils.call_context(self):

// Handle Keras mask propagation from previous layer to current layer.
// with base_layer_utils.call_context(self):
// Check input assumptions set after layer building, e.g. input shape.
if (build_graph)
/*var returnOutputs = new List<Tensor>();
foreach(var x in outputs)
{ {
// Symbolic execution on symbolic tensors. We will attempt to build
// the corresponding TF subgraph inside `backend.get_graph()`
var graph = tf.keras.backend.get_graph().as_default();
tf_with(ops.name_scope(_name_scope()), delegate
if (inputs.Contains(x))
{ {
// Build layer if applicable (if the `build` method has been
// overridden).
MaybeBuild(inputs);

outputs = call(inputs,
// training: training,
state: state);


(input, outputs) = _set_connectivity_metadata_(input, outputs);
_handle_activity_regularization(inputs[0], outputs);
_set_mask_metadata(inputs[0], outputs, null);
});
}
}
returnOutputs.Add(x);
}*/


return outputs;
}
new Node(this, new NodeArgs
{
Outputs = outputs
});


private (Tensor, Tensor[]) _set_connectivity_metadata_(Tensor inputs, Tensor[] outputs)
{
//_add_inbound_node(input_tensors: inputs, output_tensors: outputs); //_add_inbound_node(input_tensors: inputs, output_tensors: outputs);
return (inputs, outputs);
return outputs;
} }


private void _handle_activity_regularization(Tensor inputs, Tensor[] outputs)
private void _handle_activity_regularization(Tensor inputs, Tensor outputs)
{ {
//if(_activity_regularizer != null) //if(_activity_regularizer != null)
{ {
@@ -218,7 +189,7 @@ namespace Tensorflow.Keras.Engine
} }
} }


private void _set_mask_metadata(Tensor inputs, Tensor[] outputs, Tensor previous_mask)
private void _set_mask_metadata(Tensor inputs, Tensor outputs, Tensor previous_mask)
{ {


} }
@@ -228,7 +199,7 @@ namespace Tensorflow.Keras.Engine
return null; return null;
} }


protected virtual Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected virtual Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
throw new NotImplementedException(""); throw new NotImplementedException("");
} }
@@ -238,15 +209,15 @@ namespace Tensorflow.Keras.Engine
return Name; return Name;
} }


protected void MaybeBuild(Tensor[] inputs)
protected void MaybeBuild(Tensor inputs)
{ {
// Check input assumptions set before layer building, e.g. input rank. // Check input assumptions set before layer building, e.g. input rank.
if (built) if (built)
return; return;
if (DType == TF_DataType.DtInvalid) if (DType == TF_DataType.DtInvalid)
args.DType = inputs[0].dtype;
args.DType = inputs.dtype;


var input_shapes = inputs[0].TensorShape;
var input_shapes = inputs.TensorShape;
build(input_shapes); build(input_shapes);
built = true; built = true;
} }


+ 3
- 3
src/TensorFlowNET.Core/Keras/Engine/Node.cs View File

@@ -35,13 +35,13 @@ namespace Tensorflow.Keras.Engine


public int[] node_indices; public int[] node_indices;
public int[] tensor_indices; public int[] tensor_indices;
public Tensor[] input_tensors;
public Tensor[] Outputs => args.Outputs;
public Tensor input_tensors;
public Tensor Outputs => args.Outputs;
public TensorShape[] input_shapes; public TensorShape[] input_shapes;
public TensorShape[] output_shapes; public TensorShape[] output_shapes;
List<Layer> kerasInputs; List<Layer> kerasInputs;


public Node(InputLayer layer, NodeArgs args)
public Node(Layer layer, NodeArgs args)
{ {
this.args = args; this.args = args;




+ 8
- 5
src/TensorFlowNET.Core/Keras/Engine/Sequential.cs View File

@@ -25,9 +25,7 @@ namespace Tensorflow.Keras.Engine
#pragma warning disable CS0649 // Field 'Sequential._is_graph_network' is never assigned to, and will always have its default value false #pragma warning disable CS0649 // Field 'Sequential._is_graph_network' is never assigned to, and will always have its default value false
bool _is_graph_network; bool _is_graph_network;
#pragma warning restore CS0649 // Field 'Sequential._is_graph_network' is never assigned to, and will always have its default value false #pragma warning restore CS0649 // Field 'Sequential._is_graph_network' is never assigned to, and will always have its default value false
#pragma warning disable CS0169 // The field 'Sequential.outputs' is never used
Tensor[] outputs;
#pragma warning restore CS0169 // The field 'Sequential.outputs' is never used
Tensor outputs;


bool computeOutputAndMaskJointly; bool computeOutputAndMaskJointly;
bool autoTrackSubLayers; bool autoTrackSubLayers;
@@ -51,6 +49,11 @@ namespace Tensorflow.Keras.Engine
} }


public void add(Tensor layer)
{

}

/// <summary> /// <summary>
/// Adds a layer instance on top of the layer stack. /// Adds a layer instance on top of the layer stack.
/// </summary> /// </summary>
@@ -71,7 +74,7 @@ namespace Tensorflow.Keras.Engine
{ {
// Instantiate an input layer. // Instantiate an input layer.
var x = tf.keras.Input( var x = tf.keras.Input(
batch_shape: layer.BatchInputShape,
shape: layer.BatchInputShape,
dtype: layer.DType, dtype: layer.DType,
name: layer.Name + "_input"); name: layer.Name + "_input");


@@ -86,7 +89,7 @@ namespace Tensorflow.Keras.Engine
if (set_inputs) if (set_inputs)
{ {
// If an input layer (placeholder) is available. // If an input layer (placeholder) is available.
// outputs = layer.inbound_nodes;
outputs = layer.InboundNodes[^1].Outputs;
} }


} }


+ 25
- 5
src/TensorFlowNET.Core/Keras/KerasApi.cs View File

@@ -14,15 +14,35 @@ namespace Tensorflow
{ {
public KerasDataset datasets { get; } = new KerasDataset(); public KerasDataset datasets { get; } = new KerasDataset();
public Initializers initializers { get; } = new Initializers(); public Initializers initializers { get; } = new Initializers();
public Layers layers { get; } = new Layers();
public LayersApi layers { get; } = new LayersApi();
public Activations activations { get; } = new Activations(); public Activations activations { get; } = new Activations();


public BackendImpl backend { get; } = new BackendImpl(); public BackendImpl backend { get; } = new BackendImpl();


public Models models { get; } = new Models();

public Sequential Sequential() public Sequential Sequential()
=> new Sequential(); => new Sequential();


public Tensor[] Input(int[] batch_shape = null,
/// <summary>
/// Instantiate a Keras tensor.
/// </summary>
/// <param name="shape"></param>
/// <param name="batch_size"></param>
/// <param name="dtype"></param>
/// <param name="name"></param>
/// <param name="sparse">
/// A boolean specifying whether the placeholder to be created is sparse.
/// </param>
/// <param name="ragged">
/// A boolean specifying whether the placeholder to be created is ragged.
/// </param>
/// <param name="tensor">
/// Optional existing tensor to wrap into the `Input` layer.
/// If set, the layer will not create a placeholder tensor.
/// </param>
/// <returns></returns>
public Tensor Input(TensorShape shape = null,
int batch_size = -1, int batch_size = -1,
TF_DataType dtype = TF_DataType.DtInvalid, TF_DataType dtype = TF_DataType.DtInvalid,
string name = null, string name = null,
@@ -33,7 +53,7 @@ namespace Tensorflow
var args = new InputLayerArgs var args = new InputLayerArgs
{ {
Name = name, Name = name,
BatchInputShape = batch_shape,
InputShape = shape,
BatchSize = batch_size, BatchSize = batch_size,
DType = dtype, DType = dtype,
Sparse = sparse, Sparse = sparse,
@@ -43,7 +63,7 @@ namespace Tensorflow


var layer = new InputLayer(args); var layer = new InputLayer(args);


return layer.InboundNodes[0].Outputs;
return layer.InboundNodes[0].Outputs[0];
} }


public static Embedding Embedding(int input_dim, public static Embedding Embedding(int input_dim,
@@ -55,7 +75,7 @@ namespace Tensorflow
embeddings_initializer, embeddings_initializer,
mask_zero); mask_zero);


public class Layers
public class LayersApi
{ {
public Layer Dense(int units, public Layer Dense(int units,
Activation activation = null, Activation activation = null,


+ 3
- 3
src/TensorFlowNET.Core/Keras/Layers/BatchNormalization.cs View File

@@ -143,15 +143,15 @@ namespace Tensorflow.Keras.Layers
built = true; built = true;
} }


protected override Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
Tensor outputs = null; Tensor outputs = null;


if (fused) if (fused)
{ {
Tensor training = tf.convert_to_tensor(is_training); Tensor training = tf.convert_to_tensor(is_training);
outputs = _fused_batch_norm(inputs[0], training: training);
return new[] { outputs, outputs };
outputs = _fused_batch_norm(inputs, training: training);
return outputs;
} }


throw new NotImplementedException("BatchNormalization call"); throw new NotImplementedException("BatchNormalization call");


+ 3
- 3
src/TensorFlowNET.Core/Keras/Layers/Conv.cs View File

@@ -108,9 +108,9 @@ namespace Tensorflow.Keras.Layers
built = true; built = true;
} }


protected override Tensor[] call(Tensor[] inputs, bool training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool training = false, Tensor state = null)
{ {
var outputs = _convolution_op.__call__(inputs[0], kernel);
var outputs = _convolution_op.__call__(inputs, kernel);
if (use_bias) if (use_bias)
{ {
if (data_format == "channels_first") if (data_format == "channels_first")
@@ -126,7 +126,7 @@ namespace Tensorflow.Keras.Layers
if (activation != null) if (activation != null)
outputs = activation.Activate(outputs); outputs = activation.Activate(outputs);


return new[] { outputs, outputs };
return outputs;
} }
} }
} }

+ 4
- 4
src/TensorFlowNET.Core/Keras/Layers/Dense.cs View File

@@ -65,17 +65,17 @@ namespace Tensorflow.Keras.Layers
built = true; built = true;
} }


protected override Tensor[] call(Tensor[] inputs, bool training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool training = false, Tensor state = null)
{ {
Tensor outputs = null; Tensor outputs = null;
var rank = inputs[0].rank;
var rank = inputs.rank;
if(rank > 2) if(rank > 2)
{ {
throw new NotImplementedException("call rank > 2"); throw new NotImplementedException("call rank > 2");
} }
else else
{ {
outputs = gen_math_ops.mat_mul(inputs[0], kernel.AsTensor());
outputs = gen_math_ops.mat_mul(inputs, kernel.AsTensor());
} }


if (args.UseBias) if (args.UseBias)
@@ -83,7 +83,7 @@ namespace Tensorflow.Keras.Layers
if (args.Activation != null) if (args.Activation != null)
outputs = activation(outputs); outputs = activation(outputs);


return new[] { outputs };
return outputs;
} }
} }
} }

+ 5
- 5
src/TensorFlowNET.Core/Keras/Layers/Embedding.cs View File

@@ -57,14 +57,14 @@ namespace Tensorflow.Keras.Layers
built = true; built = true;
} }


protected override Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
var dtype = inputs[0].dtype;
var dtype = inputs.dtype;
if (dtype != tf.int32 && dtype != tf.int64) if (dtype != tf.int32 && dtype != tf.int64)
inputs[0] = math_ops.cast(inputs[0], tf.int32);
inputs = math_ops.cast(inputs, tf.int32);


var @out = embedding_ops.embedding_lookup(embeddings, inputs[0]);
return new[] { @out, @out };
var outputs = embedding_ops.embedding_lookup(embeddings, inputs[0]);
return outputs;
} }
} }
} }

+ 2
- 2
src/TensorFlowNET.Core/Keras/Layers/InputLayer.cs View File

@@ -84,8 +84,8 @@ namespace Tensorflow.Keras.Layers
// input_tensor._keras_mask = None // input_tensor._keras_mask = None
new Node(this, new NodeArgs new Node(this, new NodeArgs
{ {
InputTensors = new Tensor[] { args.InputTensor },
Outputs = new Tensor[] { args.InputTensor }
InputTensors = args.InputTensor,
Outputs = args.InputTensor
}); });


typeSpec = new TensorSpec(args.InputTensor.TensorShape, typeSpec = new TensorSpec(args.InputTensor.TensorShape,


+ 3
- 3
src/TensorFlowNET.Core/Keras/Layers/Pooling2D.cs View File

@@ -45,7 +45,7 @@ namespace Tensorflow.Keras.Layers
this.input_spec = new InputSpec(ndim: 4); this.input_spec = new InputSpec(ndim: 4);
} }


protected override Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
int[] pool_shape; int[] pool_shape;
if (data_format == "channels_last") if (data_format == "channels_last")
@@ -60,13 +60,13 @@ namespace Tensorflow.Keras.Layers
} }


var outputs = pool_function.Apply( var outputs = pool_function.Apply(
inputs[0],
inputs,
ksize: pool_shape, ksize: pool_shape,
strides: strides, strides: strides,
padding: padding.ToUpper(), padding: padding.ToUpper(),
data_format: conv_utils.convert_data_format(data_format, 4)); data_format: conv_utils.convert_data_format(data_format, 4));


return new[] { outputs, outputs };
return outputs;
} }
} }
} }

+ 13
- 0
src/TensorFlowNET.Core/Keras/Models.cs View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.Engine;

namespace Tensorflow.Keras
{
public class Models
{
public Sequential Sequential()
=> new Sequential();
}
}

+ 5
- 5
src/TensorFlowNET.Core/Keras/Optimizers/OptimizerV2.cs View File

@@ -39,7 +39,7 @@ namespace Tensorflow.Keras.Optimizers
public void apply_gradients((Tensor, ResourceVariable) grads_and_vars, public void apply_gradients((Tensor, ResourceVariable) grads_and_vars,
string name = null, string name = null,
bool experimental_aggregate_gradients = true) bool experimental_aggregate_gradients = true)
=> apply_gradients(new (Tensor, ResourceVariable)[] { grads_and_vars },
=> apply_gradients(grads_and_vars,
name: name, name: name,
experimental_aggregate_gradients: experimental_aggregate_gradients); experimental_aggregate_gradients: experimental_aggregate_gradients);


@@ -77,7 +77,7 @@ namespace Tensorflow.Keras.Optimizers
_resource_apply_dense(var, grad, apply_state); _resource_apply_dense(var, grad, apply_state);
} }


protected virtual Operation _resource_apply_dense(ResourceVariable var,
protected virtual Operation _resource_apply_dense(IVariableV1 var,
EagerTensor grad, EagerTensor grad,
Dictionary<DeviceDType, Dictionary<string, Tensor>> _apply_state) Dictionary<DeviceDType, Dictionary<string, Tensor>> _apply_state)
{ {
@@ -107,7 +107,7 @@ namespace Tensorflow.Keras.Optimizers
return grads_and_vars.Select(x => x.Item1).ToArray(); return grads_and_vars.Select(x => x.Item1).ToArray();
} }


Dictionary<DeviceDType, Dictionary<string, Tensor>> _prepare(ResourceVariable[] var_list)
Dictionary<DeviceDType, Dictionary<string, Tensor>> _prepare(IVariableV1[] var_list)
{ {
var _apply_state = new Dictionary<DeviceDType, Dictionary<string, Tensor>>(); var _apply_state = new Dictionary<DeviceDType, Dictionary<string, Tensor>>();
var keys = var_list.Select(x => new DeviceDType var keys = var_list.Select(x => new DeviceDType
@@ -151,7 +151,7 @@ namespace Tensorflow.Keras.Optimizers
return math_ops.cast(value, dtype); return math_ops.cast(value, dtype);
} }


void _create_all_weights(ResourceVariable[] var_list)
void _create_all_weights(IVariableV1[] var_list)
{ {
if(_iterations == null) if(_iterations == null)
{ {
@@ -190,7 +190,7 @@ namespace Tensorflow.Keras.Optimizers
_hypers_created = true; _hypers_created = true;
} }


void _create_slots(ResourceVariable[] var_list)
void _create_slots(IVariableV1[] var_list)
{ {
if(_momentum) if(_momentum)
{ {


+ 1
- 1
src/TensorFlowNET.Core/Keras/Optimizers/SGD.cs View File

@@ -40,7 +40,7 @@ namespace Tensorflow.Keras.Optimizers
_get_hyper("momentum", device_dtype.DType)); _get_hyper("momentum", device_dtype.DType));
} }


protected override Operation _resource_apply_dense(ResourceVariable var, EagerTensor grad, Dictionary<DeviceDType, Dictionary<string, Tensor>> _apply_state)
protected override Operation _resource_apply_dense(IVariableV1 var, EagerTensor grad, Dictionary<DeviceDType, Dictionary<string, Tensor>> _apply_state)
{ {
if (_momentum) if (_momentum)
{ {


+ 2
- 3
src/TensorFlowNET.Core/Layers/Layer.cs View File

@@ -88,9 +88,8 @@ namespace Tensorflow.Layers
{ {
_current_scope = scope2; _current_scope = scope2;
// Actually call layer // Actually call layer
outputs = base.__call__(new Tensor[] { inputs },
training: training,
state: state);
/*outputs = base.Apply(new Tensor[] { inputs },
is_training: training);*/
}); });






+ 4
- 4
src/TensorFlowNET.Core/Operations/NnOps/BasicLSTMCell.cs View File

@@ -74,7 +74,7 @@ namespace Tensorflow
/// <param name="training"></param> /// <param name="training"></param>
/// <param name="state"></param> /// <param name="state"></param>
/// <returns></returns> /// <returns></returns>
protected override Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
var one = constant_op.constant(1, dtype: dtypes.int32); var one = constant_op.constant(1, dtype: dtypes.int32);
// Parameters of gates are concatenated into one multiply for efficiency. // Parameters of gates are concatenated into one multiply for efficiency.
@@ -87,7 +87,7 @@ namespace Tensorflow
// array_ops.split(value: state, num_or_size_splits: 2, axis: one); // array_ops.split(value: state, num_or_size_splits: 2, axis: one);
throw new NotImplementedException("BasicLstmCell call"); throw new NotImplementedException("BasicLstmCell call");
} }
var gate_inputs = math_ops.matmul(array_ops.concat(new[] { inputs[0], h }, 1), _kernel as RefVariable);
var gate_inputs = math_ops.matmul(array_ops.concat(new[] { inputs, h }, 1), _kernel as RefVariable);
gate_inputs = nn_ops.bias_add(gate_inputs, _bias as RefVariable); gate_inputs = nn_ops.bias_add(gate_inputs, _bias as RefVariable);


// i = input_gate, j = new_input, f = forget_gate, o = output_gate // i = input_gate, j = new_input, f = forget_gate, o = output_gate
@@ -105,9 +105,9 @@ namespace Tensorflow




if (_state_is_tuple) if (_state_is_tuple)
return new[] { new_c, new_h };
return new_c;
else else
return new[] { array_ops.concat(new[] { new_c, new_h }, 1) };
return array_ops.concat(new[] { new_c, new_h }, 1);
} }


public override object get_initial_state(Tensor inputs = null, Tensor batch_size = null, TF_DataType dtype = TF_DataType.DtInvalid) public override object get_initial_state(Tensor inputs = null, Tensor batch_size = null, TF_DataType dtype = TF_DataType.DtInvalid)


+ 3
- 3
src/TensorFlowNET.Core/Operations/NnOps/BasicRNNCell.cs View File

@@ -67,14 +67,14 @@ namespace Tensorflow
built = true; built = true;
} }


protected override Tensor[] call(Tensor[] inputs, bool is_training = false, Tensor state = null)
protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null)
{ {
// Most basic RNN: output = new_state = act(W * input + U * state + B). // Most basic RNN: output = new_state = act(W * input + U * state + B).
var concat = array_ops.concat(new[] { inputs[0], state }, 1);
var concat = array_ops.concat(new[] { inputs, state }, 1);
var gate_inputs = math_ops.matmul(concat, _kernel as RefVariable); var gate_inputs = math_ops.matmul(concat, _kernel as RefVariable);
gate_inputs = nn_ops.bias_add(gate_inputs, _bias as RefVariable); gate_inputs = nn_ops.bias_add(gate_inputs, _bias as RefVariable);
var output = _activation(gate_inputs, null); var output = _activation(gate_inputs, null);
return new[] { output, output };
return output;
} }
} }
} }

+ 33
- 1
src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs View File

@@ -108,6 +108,17 @@ namespace Tensorflow.Operations
string data_format = null, string data_format = null,
string name = null) string name = null)
{ {
if (tf.executing_eagerly())
{
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"BiasAdd", name,
null,
value, bias,
"data_format", data_format);

return results[0];
}

if (data_format == null) if (data_format == null)
data_format = "NHWC"; data_format = "NHWC";


@@ -125,6 +136,17 @@ namespace Tensorflow.Operations
string data_format = "NHWC", string data_format = "NHWC",
string name = null) string name = null)
{ {
if (tf.executing_eagerly())
{
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"BiasAddGrad", name,
null,
out_backprop,
"data_format", data_format);

return results[0];
}

if (data_format == null) if (data_format == null)
data_format = "NHWC"; data_format = "NHWC";


@@ -460,6 +482,16 @@ namespace Tensorflow.Operations
/// </remarks> /// </remarks>
public static (Tensor loss, Tensor backprop) sparse_softmax_cross_entropy_with_logits(Tensor features, Tensor labels, string name = "SparseSoftmaxCrossEntropyWithLogits") public static (Tensor loss, Tensor backprop) sparse_softmax_cross_entropy_with_logits(Tensor features, Tensor labels, string name = "SparseSoftmaxCrossEntropyWithLogits")
{ {
if (tf.executing_eagerly())
{
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"SparseSoftmaxCrossEntropyWithLogits", name,
null,
features, labels);

return (results[0], results[1]);
}

var op = tf.OpDefLib._apply_op_helper("SparseSoftmaxCrossEntropyWithLogits", name: name, args: new { features, labels }); var op = tf.OpDefLib._apply_op_helper("SparseSoftmaxCrossEntropyWithLogits", name: name, args: new { features, labels });
int _idx = 0; int _idx = 0;
var loss = op.outputs[_idx++]; var loss = op.outputs[_idx++];
@@ -475,7 +507,7 @@ namespace Tensorflow.Operations
/// <returns>A `Tensor`. Has the same type as `features`.</returns> /// <returns>A `Tensor`. Has the same type as `features`.</returns>
public static Tensor relu(Tensor features, string name = null) public static Tensor relu(Tensor features, string name = null)
{ {
if (tf.Context.executing_eagerly())
if (tf.executing_eagerly())
{ {
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName, var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"Relu", name, "Relu", name,


+ 40
- 1
src/TensorFlowNET.Core/Operations/array_ops.cs View File

@@ -31,8 +31,47 @@ namespace Tensorflow
public static Tensor placeholder_with_default<T>(T input, int[] shape, string name = null) public static Tensor placeholder_with_default<T>(T input, int[] shape, string name = null)
=> gen_array_ops.placeholder_with_default(input, shape, name); => gen_array_ops.placeholder_with_default(input, shape, name);


/// <summary>
/// An identity op that triggers an error if a gradient is requested.
/// </summary>
/// <param name="input">
/// any tensor.
/// </param>
/// <param name="name">
/// If specified, the created operation in the graph will be this one, otherwise it will be named 'PreventGradient'.
/// </param>
/// <param name="message">
/// Will be printed in the error when anyone tries to differentiate
/// this operation.
/// </param>
/// <returns>
/// the same input tensor.
/// The Operation can be fetched from the resulting Tensor, by fetching the Operation property from the result.
/// </returns>
/// <remarks>
/// When executed in a graph, this op outputs its input tensor as-is.
///
/// When building ops to compute gradients, the TensorFlow gradient system
/// will return an error when trying to lookup the gradient of this op,
/// because no gradient must ever be registered for this function. This
/// op exists to prevent subtle bugs from silently returning unimplemented
/// gradients in some corner cases.
/// </remarks>
public static Tensor prevent_gradient(Tensor input, string message = "", string name = null) public static Tensor prevent_gradient(Tensor input, string message = "", string name = null)
=> gen_array_ops.prevent_gradient(input, message: message, name: name);
{
if (tf.executing_eagerly())
{
var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName,
"PreventGradient", name,
null,
input,
"message", message);
return results[0];
}

var op = tf.OpDefLib._apply_op_helper("PreventGradient", name: name, args: new { input, message });
return op.output;
}


internal static Tensor constant(object value, internal static Tensor constant(object value,
TF_DataType dtype = TF_DataType.DtInvalid, TF_DataType dtype = TF_DataType.DtInvalid,


+ 0
- 32
src/TensorFlowNET.Core/Operations/gen_array_ops.cs View File

@@ -186,38 +186,6 @@ namespace Tensorflow
return _op.output; return _op.output;
} }


/// <summary>
/// An identity op that triggers an error if a gradient is requested.
/// </summary>
/// <param name="input">
/// any tensor.
/// </param>
/// <param name="name">
/// If specified, the created operation in the graph will be this one, otherwise it will be named 'PreventGradient'.
/// </param>
/// <param name="message">
/// Will be printed in the error when anyone tries to differentiate
/// this operation.
/// </param>
/// <returns>
/// the same input tensor.
/// The Operation can be fetched from the resulting Tensor, by fetching the Operation property from the result.
/// </returns>
/// <remarks>
/// When executed in a graph, this op outputs its input tensor as-is.
///
/// When building ops to compute gradients, the TensorFlow gradient system
/// will return an error when trying to lookup the gradient of this op,
/// because no gradient must ever be registered for this function. This
/// op exists to prevent subtle bugs from silently returning unimplemented
/// gradients in some corner cases.
/// </remarks>
public static Tensor prevent_gradient(Tensor input, string message = "", string name = null)
{
var op = tf.OpDefLib._apply_op_helper("PreventGradient", name: name, args: new { input, message });
return op.output;
}

/// <summary> /// <summary>
/// Return a tensor with the same shape and contents as the input tensor or value. /// Return a tensor with the same shape and contents as the input tensor or value.
/// </summary> /// </summary>


+ 2
- 1
src/TensorFlowNET.Core/Variables/BaseResourceVariable.cs View File

@@ -45,6 +45,7 @@ namespace Tensorflow
public Operation Initializer => initializer_op; public Operation Initializer => initializer_op;
public Operation Op => handle.op; public Operation Op => handle.op;
public Graph Graph => handle.graph; public Graph Graph => handle.graph;
public string Device => "";


public BaseResourceVariable() public BaseResourceVariable()
{ {
@@ -148,6 +149,6 @@ namespace Tensorflow
{ {
} }


public Tensor AsTensor() => _graph_element;
public Tensor AsTensor() => read_value();
} }
} }

+ 1
- 0
src/TensorFlowNET.Core/Variables/IVariableV1.cs View File

@@ -33,6 +33,7 @@ namespace Tensorflow
{ {
public string Name { get; } public string Name { get; }
public Tensor Handle { get; } public Tensor Handle { get; }
public string Device { get; }
public Operation Initializer { get; } public Operation Initializer { get; }
public Operation Op { get; } public Operation Op { get; }
public Tensor GraphElement { get; } public Tensor GraphElement { get; }


+ 1
- 0
src/TensorFlowNET.Core/Variables/RefVariable.cs View File

@@ -48,6 +48,7 @@ namespace Tensorflow
public TF_DataType dtype => _variable.dtype; public TF_DataType dtype => _variable.dtype;
public TensorShape shape => tensor_util.to_shape(_variable.shape); public TensorShape shape => tensor_util.to_shape(_variable.shape);
public string Device => "";


public string Name => _variable.name; public string Name => _variable.name;




+ 0
- 1
src/TensorFlowNET.Core/tensorflow.cs View File

@@ -51,7 +51,6 @@ namespace Tensorflow
{ {
Status = new Status(); Status = new Status();
Context = new Context(new ContextOptions(), Status); Context = new Context(new ContextOptions(), Status);
enable_eager_execution();
OpDefLib = new OpDefLibrary(); OpDefLib = new OpDefLibrary();
ConstructThreadingObjects(); ConstructThreadingObjects();
InitGradientEnvironment(); InitGradientEnvironment();


+ 7
- 0
test/TensorFlowNET.UnitTest/Keras/LayersTest.cs View File

@@ -16,6 +16,13 @@ namespace TensorFlowNET.UnitTest.Keras
[TestClass, Ignore] [TestClass, Ignore]
public class LayersTest : GraphModeTestBase public class LayersTest : GraphModeTestBase
{ {
[TestMethod]
public void Sequential()
{
var model = tf.keras.models.Sequential();
model.add(tf.keras.Input(shape: 16));
}

[TestMethod] [TestMethod]
public void Embedding() public void Embedding()
{ {


Loading…
Cancel
Save