@@ -11,7 +11,7 @@ namespace Tensorflow.Keras.ArgsDefinition | |||
public Layer[] InboundLayers { get; set; } | |||
public int[] NodeIndices { get; set; } | |||
public int[] TensorIndices { get; set; } | |||
public Tensor InputTensors { get; set; } | |||
public Tensors InputTensors { get; set; } | |||
public Tensors Outputs { get; set; } | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Tensorflow.Keras.ArgsDefinition | |||
{ | |||
public class TensorFlowOpLayerArgs : LayerArgs | |||
{ | |||
public NodeDef NodeDef { get; set; } | |||
} | |||
} |
@@ -1,47 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Security.Cryptography.X509Certificates; | |||
using System.Text; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
public class BaseLayerUtils | |||
{ | |||
public static Layer[] CreateKerasHistoryHelper(Tensors tensors) | |||
{ | |||
var processed_ops = new List<Operation>(); | |||
var created_layers = new List<Layer>(); | |||
foreach (var tensor in tensors) | |||
{ | |||
if (tensor.KerasHistory != null) | |||
continue; | |||
var op = tensor.op; | |||
if (!processed_ops.Contains(op)) | |||
{ | |||
var layer_inputs = new List<Tensor>(); | |||
foreach (var (i, op_input) in enumerate(op.inputs._inputs)) | |||
{ | |||
if (uses_keras_history(op_input)) | |||
layer_inputs.Add(op_input); | |||
else | |||
{ | |||
} | |||
} | |||
} | |||
} | |||
return created_layers.ToArray(); | |||
} | |||
static bool uses_keras_history(Tensor op_input) | |||
{ | |||
return Layer.KerasHistories.Any(x => x.tensor == op_input); | |||
} | |||
} | |||
} |
@@ -4,6 +4,7 @@ using System.Linq; | |||
using System.Security.Cryptography.X509Certificates; | |||
using System.Text; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
using Tensorflow.Keras.Utils; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
@@ -50,7 +51,7 @@ namespace Tensorflow.Keras.Engine | |||
_autocast = false; | |||
if (outputs.Any(x => x.KerasHistory == null)) | |||
BaseLayerUtils.CreateKerasHistoryHelper(outputs); | |||
base_layer_utils.create_keras_history(outputs); | |||
// Build self._output_layers: | |||
foreach (var x in outputs) | |||
@@ -9,7 +9,7 @@ namespace Tensorflow.Keras.Engine | |||
/// </summary> | |||
public class KerasHistory | |||
{ | |||
Layer layer; | |||
public Layer layer; | |||
int node_index; | |||
int tensor_index; | |||
public Tensor tensor; | |||
@@ -20,6 +20,7 @@ namespace Tensorflow.Keras.Engine | |||
this.node_index = node_index; | |||
this.tensor_index = tensor_index; | |||
this.tensor = tensor; | |||
Layer.KerasHistories.Add(this); | |||
Console.WriteLine(tensor.name); | |||
} | |||
@@ -0,0 +1,65 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow.Keras.Utils; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
public partial class Layer | |||
{ | |||
protected virtual IVariableV1 add_weight(string name, | |||
TensorShape shape, | |||
TF_DataType dtype = TF_DataType.TF_FLOAT, | |||
IInitializer initializer = null, | |||
IRegularizer regularizer = null, | |||
VariableSynchronization synchronization = VariableSynchronization.Auto, | |||
VariableAggregation aggregation = VariableAggregation.None, | |||
bool trainable = true, | |||
Func<VariableArgs, IVariableV1> getter = null) | |||
{ | |||
// Initialize variable when no initializer provided | |||
if (initializer == null) | |||
{ | |||
// If dtype is DT_FLOAT, provide a uniform unit scaling initializer | |||
if (dtype.is_floating()) | |||
initializer = tf.glorot_uniform_initializer; | |||
else if (dtype.is_integer()) | |||
initializer = tf.zeros_initializer; | |||
else | |||
throw new ValueError($"An initializer for variable {name} of type {dtype.as_base_dtype()} is required for layer {name}"); | |||
} | |||
if (synchronization == VariableSynchronization.OnRead) | |||
trainable = false; | |||
var args = new VariableArgs | |||
{ | |||
Name = name, | |||
Shape = shape, | |||
DType = dtype, | |||
Getter = getter ?? base_layer_utils.make_variable, | |||
Overwrite = true, | |||
Initializer = initializer, | |||
Synchronization = synchronization, | |||
Aggregation = aggregation, | |||
Trainable = trainable | |||
}; | |||
var variable = _add_variable_with_custom_getter(args); | |||
if (regularizer != null) | |||
{ | |||
var name_in_scope = variable.Name.Split(':')[0]; | |||
_handle_weight_regularization(name_in_scope, variable, regularizer); | |||
} | |||
//backend.track_variable(variable); | |||
if (trainable == true) | |||
trainableWeights.Add(variable); | |||
else | |||
nonTrainableWeights.Add(variable); | |||
return variable; | |||
} | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading; | |||
using Tensorflow.Keras.Utils; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
public partial class Layer | |||
{ | |||
/// <summary> | |||
/// Wraps `call`, applying pre- and post-processing steps. | |||
/// </summary> | |||
/// <param name="input"></param> | |||
/// <param name="state"></param> | |||
/// <param name="is_training"></param> | |||
/// <returns></returns> | |||
public Tensors Apply(Tensors inputs, Tensor state = null, bool is_training = false) | |||
{ | |||
callContext = callContext ?? new ThreadLocal<CallContext>() | |||
{ | |||
Value = new CallContext() | |||
}; | |||
if (_in_functional_construction_mode(inputs)) | |||
return FunctionalConstructionCall(inputs); | |||
Tensors outputs = null; | |||
var eager = tf.executing_eagerly(); | |||
using var ctxManager = CallContext.enter(); | |||
string nameScope = ""; | |||
if (eager) | |||
nameScope = Name; | |||
else | |||
nameScope = _name_scope(); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.graph_mode(); | |||
tf_with(ops.name_scope(nameScope), scope => | |||
{ | |||
if (!built) | |||
MaybeBuild(inputs); | |||
outputs = call(inputs, state: state, is_training: is_training); | |||
outputs = _set_connectivity_metadata_(inputs, outputs); | |||
_handle_activity_regularization(inputs, outputs); | |||
_set_mask_metadata(inputs, outputs, null); | |||
}); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.restore_mode(); | |||
return outputs; | |||
} | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow.Keras.Utils; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
public partial class Layer | |||
{ | |||
Tensors FunctionalConstructionCall(Tensors inputs) | |||
{ | |||
bool mask_arg_passed_by_framework = false; | |||
bool training_arg_passed_by_framework = false; | |||
Tensor training_value = null; | |||
if (training_value == null) | |||
{ | |||
training_arg_passed_by_framework = true; | |||
} | |||
if (base_layer_utils.needs_keras_history(inputs)) | |||
base_layer_utils.create_keras_history(inputs); | |||
Tensors outputs = null; | |||
using var ctxManager = CallContext.enter(); | |||
// using var graph = tf.keras.backend.get_graph().as_default(); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.graph_mode(); | |||
tf_with(ops.name_scope(_name_scope()), scope => | |||
{ | |||
MaybeBuild(inputs); | |||
// Wrapping `call` function in autograph to allow for dynamic control | |||
// flow and control dependencies in call. We are limiting this to | |||
// subclassed layers as autograph is strictly needed only for | |||
// subclassed layers and models. | |||
// tf_convert will respect the value of autograph setting in the | |||
// enclosing tf.function, if any. | |||
if (!dynamic) | |||
throw new NotImplementedException(""); | |||
outputs = call(inputs); | |||
outputs = _set_connectivity_metadata_(inputs, outputs); | |||
_handle_activity_regularization(inputs, outputs); | |||
_set_mask_metadata(inputs, outputs, null); | |||
}); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.restore_mode(); | |||
return outputs; | |||
} | |||
} | |||
} |
@@ -109,116 +109,24 @@ namespace Tensorflow.Keras.Engine | |||
updates = new List<Operation>(); | |||
inboundNodes = new List<Node>(); | |||
outboundNodes = new List<Node>(); | |||
// Manage input shape information if passed. | |||
if(args.BatchInputShape == null && args.InputShape != null) | |||
if (args.BatchInputShape == null && args.InputShape != null) | |||
{ | |||
args.BatchInputShape = new int[] { args.BatchSize }.Concat(args.InputShape.dims).ToArray(); | |||
} | |||
} | |||
/// <summary> | |||
/// Wraps `call`, applying pre- and post-processing steps. | |||
/// </summary> | |||
/// <param name="input"></param> | |||
/// <param name="state"></param> | |||
/// <param name="is_training"></param> | |||
/// <returns></returns> | |||
public Tensors Apply(Tensors inputs, Tensor state = null, bool is_training = false) | |||
{ | |||
callContext = callContext ?? new ThreadLocal<CallContext>() | |||
{ | |||
Value = new CallContext() | |||
}; | |||
var history = inputs.Where(x => x.KerasHistory != null | |||
&& !KerasHistories.Contains(x.KerasHistory)) | |||
.Select(x => x.KerasHistory); | |||
KerasHistories.AddRange(history); | |||
if (_in_functional_construction_mode(inputs)) | |||
return _functional_construction_call(inputs); | |||
Tensors outputs = null; | |||
var eager = tf.executing_eagerly(); | |||
using var ctxManager = CallContext.enter(); | |||
string nameScope = ""; | |||
if (eager) | |||
nameScope = Name; | |||
else | |||
nameScope = _name_scope(); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.graph_mode(); | |||
tf_with(ops.name_scope(nameScope), scope => | |||
{ | |||
if (!built) | |||
MaybeBuild(inputs); | |||
outputs = call(inputs, state: state, is_training: is_training); | |||
outputs = _set_connectivity_metadata_(inputs, outputs); | |||
_handle_activity_regularization(inputs, outputs); | |||
_set_mask_metadata(inputs, outputs, null); | |||
}); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.restore_mode(); | |||
return outputs; | |||
} | |||
bool _in_functional_construction_mode(Tensors inputs) | |||
{ | |||
return tf.Context.executing_eagerly() | |||
&& inputs.Count(x => !x.IsEagerTensor) == inputs.Count(); | |||
} | |||
Tensors _functional_construction_call(Tensors inputs) | |||
public void SetConnectivityMetadata(Tensors inputs, Tensors outputs) | |||
{ | |||
bool mask_arg_passed_by_framework = false; | |||
bool training_arg_passed_by_framework = false; | |||
Tensor training_value = null; | |||
if(training_value == null) | |||
{ | |||
training_arg_passed_by_framework = true; | |||
} | |||
Tensors outputs = null; | |||
using var ctxManager = CallContext.enter(); | |||
// using var graph = tf.keras.backend.get_graph().as_default(); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.graph_mode(); | |||
tf_with(ops.name_scope(_name_scope()), scope => | |||
{ | |||
MaybeBuild(inputs); | |||
// Wrapping `call` function in autograph to allow for dynamic control | |||
// flow and control dependencies in call. We are limiting this to | |||
// subclassed layers as autograph is strictly needed only for | |||
// subclassed layers and models. | |||
// tf_convert will respect the value of autograph setting in the | |||
// enclosing tf.function, if any. | |||
if (!dynamic) | |||
throw new NotImplementedException(""); | |||
outputs = call(inputs); | |||
outputs = _set_connectivity_metadata_(inputs, outputs); | |||
_handle_activity_regularization(inputs, outputs); | |||
_set_mask_metadata(inputs, outputs, null); | |||
}); | |||
if (!inputs.IsEagerTensor) | |||
tf.Context.restore_mode(); | |||
return outputs; | |||
} | |||
private Tensors _set_connectivity_metadata_(Tensors inputs, Tensors outputs) | |||
@@ -235,6 +143,7 @@ namespace Tensorflow.Keras.Engine | |||
new Node(this, new NodeArgs | |||
{ | |||
InputTensors = inputs, | |||
Outputs = outputs | |||
}); | |||
@@ -304,60 +213,6 @@ namespace Tensorflow.Keras.Engine | |||
} | |||
protected virtual IVariableV1 add_weight(string name, | |||
TensorShape shape, | |||
TF_DataType dtype = TF_DataType.TF_FLOAT, | |||
IInitializer initializer = null, | |||
IRegularizer regularizer = null, | |||
VariableSynchronization synchronization = VariableSynchronization.Auto, | |||
VariableAggregation aggregation = VariableAggregation.None, | |||
bool trainable = true, | |||
Func<VariableArgs, IVariableV1> getter = null) | |||
{ | |||
// Initialize variable when no initializer provided | |||
if (initializer == null) | |||
{ | |||
// If dtype is DT_FLOAT, provide a uniform unit scaling initializer | |||
if (dtype.is_floating()) | |||
initializer = tf.glorot_uniform_initializer; | |||
else if (dtype.is_integer()) | |||
initializer = tf.zeros_initializer; | |||
else | |||
throw new ValueError($"An initializer for variable {name} of type {dtype.as_base_dtype()} is required for layer {name}"); | |||
} | |||
if (synchronization == VariableSynchronization.OnRead) | |||
trainable = false; | |||
var args = new VariableArgs | |||
{ | |||
Name = name, | |||
Shape = shape, | |||
DType = dtype, | |||
Getter = getter ?? base_layer_utils.make_variable, | |||
Overwrite = true, | |||
Initializer = initializer, | |||
Synchronization = synchronization, | |||
Aggregation = aggregation, | |||
Trainable = trainable | |||
}; | |||
var variable = _add_variable_with_custom_getter(args); | |||
if(regularizer != null) | |||
{ | |||
var name_in_scope = variable.Name.Split(':')[0]; | |||
_handle_weight_regularization(name_in_scope, variable, regularizer); | |||
} | |||
//backend.track_variable(variable); | |||
if (trainable == true) | |||
trainableWeights.Add(variable); | |||
else | |||
nonTrainableWeights.Add(variable); | |||
return variable; | |||
} | |||
/// <summary> | |||
/// Create lambdas which compute regularization losses. | |||
/// </summary> | |||
@@ -39,20 +39,22 @@ namespace Tensorflow.Keras.Engine | |||
public Tensors Outputs => args.Outputs; | |||
public TensorShape[] input_shapes; | |||
public TensorShape[] output_shapes; | |||
List<Layer> kerasInputs; | |||
List<Tensor> kerasInputs = new List<Tensor>(); | |||
public Node(Layer layer, NodeArgs args) | |||
{ | |||
this.args = args; | |||
kerasInputs = new List<Layer>(); | |||
if (args.InputTensors != null) | |||
kerasInputs.AddRange(args.InputTensors); | |||
// Wire up Node to Layers. | |||
layer.InboundNodes.Add(this); | |||
foreach (var input in kerasInputs) | |||
foreach (var kt in kerasInputs) | |||
{ | |||
if (input != null) | |||
input.OutboundNodes.Add(this); | |||
var inbound_layer = kt.KerasHistory.layer; | |||
if (inbound_layer != null) | |||
inbound_layer.OutboundNodes.Add(this); | |||
} | |||
// Set metadata on outputs. | |||
@@ -0,0 +1,31 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
namespace Tensorflow.Keras.Engine | |||
{ | |||
public class TensorFlowOpLayer : Layer | |||
{ | |||
TensorFlowOpLayerArgs args; | |||
string _TF_OP_LAYER_NAME_PREFIX = ""; | |||
public TensorFlowOpLayer(TensorFlowOpLayerArgs args) | |||
: base(new LayerArgs | |||
{ | |||
Name = "tf_op_layer_" + args.Name, | |||
Trainable = args.Trainable, | |||
DType = args.DType, | |||
Autocast = false | |||
}) | |||
{ | |||
this.args = args; | |||
built = true; | |||
} | |||
protected override Tensors call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
{ | |||
return base.call(inputs, state, is_training); | |||
} | |||
} | |||
} |
@@ -17,6 +17,8 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
using Tensorflow.Keras.Engine; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Utils | |||
@@ -105,5 +107,61 @@ namespace Tensorflow.Keras.Utils | |||
return name_uid_map; | |||
} | |||
public static bool needs_keras_history(Tensors inputs) | |||
{ | |||
if (inputs.Any(x => x.KerasHistory == null)) | |||
return true; | |||
return false; | |||
} | |||
public static Layer[] create_keras_history(Tensors inputs) | |||
{ | |||
var processed_ops = new List<Operation>(); | |||
var created_layers = new List<Layer>(); | |||
CreateKerasHistoryHelper(inputs, processed_ops, created_layers); | |||
return created_layers.ToArray(); | |||
} | |||
public static void CreateKerasHistoryHelper(Tensors tensors, List<Operation> processed_ops, List<Layer> created_layers) | |||
{ | |||
foreach (var tensor in tensors) | |||
{ | |||
if (tensor.KerasHistory != null) | |||
continue; | |||
var op = tensor.op; | |||
if (!processed_ops.Contains(op)) | |||
{ | |||
var layer_inputs = new List<Tensor>(); | |||
foreach (var (i, op_input) in enumerate(op.inputs._inputs)) | |||
{ | |||
if (uses_keras_history(op_input)) | |||
layer_inputs.Add(op_input); | |||
else | |||
{ | |||
} | |||
// recursively | |||
CreateKerasHistoryHelper(layer_inputs, processed_ops, created_layers); | |||
var op_layer = new TensorFlowOpLayer(new TensorFlowOpLayerArgs | |||
{ | |||
NodeDef = op.node_def, | |||
Name = op.name | |||
}); | |||
created_layers.Add(op_layer); | |||
op_layer.SetConnectivityMetadata(layer_inputs, op.outputs); | |||
} | |||
} | |||
} | |||
} | |||
static bool uses_keras_history(Tensor op_input) | |||
{ | |||
return Layer.KerasHistories.Any(x => x.tensor.name == op_input.name); | |||
} | |||
} | |||
} |