| @@ -0,0 +1,11 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace Tensorflow.Keras.ArgsDefinition | |||||
| { | |||||
| public class MergeArgs : LayerArgs | |||||
| { | |||||
| public Tensors Inputs { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| namespace Tensorflow.Keras.Layers | |||||
| { | |||||
| public class Add : Merge | |||||
| { | |||||
| public Add(MergeArgs args) : base(args) | |||||
| { | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -119,14 +119,14 @@ namespace Tensorflow.Keras.Layers | |||||
| built = true; | built = true; | ||||
| } | } | ||||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool training = false) | |||||
| { | { | ||||
| Tensor outputs = null; | Tensor outputs = null; | ||||
| var training_tensor = tf.logical_and(training, Trainable); | |||||
| if (fused) | if (fused) | ||||
| { | { | ||||
| Tensor training = tf.convert_to_tensor(is_training); | |||||
| outputs = _fused_batch_norm(inputs, training: training); | |||||
| // var training = tf.convert_to_tensor(training); | |||||
| outputs = _fused_batch_norm(inputs, training: training_tensor); | |||||
| return outputs; | return outputs; | ||||
| } | } | ||||
| @@ -150,20 +150,21 @@ namespace Tensorflow.Keras.Layers | |||||
| inputs, | inputs, | ||||
| gamma, | gamma, | ||||
| beta, | beta, | ||||
| epsilon: epsilon, | |||||
| data_format: _data_format); | |||||
| mean: moving_mean, | |||||
| variance: moving_variance, | |||||
| epsilon: epsilon, is_training: true, | |||||
| data_format: _data_format, | |||||
| exponential_avg_factor: exponential_avg_factor); | |||||
| }; | }; | ||||
| Func<Tensor[]> _fused_batch_norm_inference = () => | Func<Tensor[]> _fused_batch_norm_inference = () => | ||||
| { | { | ||||
| var moving_mean_tensor = moving_mean.AsTensor(); | |||||
| var moving_variance_tensor = moving_variance.AsTensor(); | |||||
| return tf.nn.fused_batch_norm( | return tf.nn.fused_batch_norm( | ||||
| inputs, | inputs, | ||||
| gamma, | gamma, | ||||
| beta, | beta, | ||||
| mean: moving_mean_tensor, | |||||
| variance: moving_variance_tensor, | |||||
| mean: moving_mean, | |||||
| variance: moving_variance, | |||||
| epsilon: epsilon, | epsilon: epsilon, | ||||
| is_training: false, | is_training: false, | ||||
| data_format: _data_format); | data_format: _data_format); | ||||
| @@ -176,35 +177,54 @@ namespace Tensorflow.Keras.Layers | |||||
| var (output, mean, variance) = (results[0], results[1], results[2]); | var (output, mean, variance) = (results[0], results[1], results[2]); | ||||
| var training_value = tf_utils.constant_value(training); | var training_value = tf_utils.constant_value(training); | ||||
| Tensor momentum_tensor; | |||||
| if (training_value == null) | |||||
| if (!training_value.HasValue || (training_value.HasValue && training_value.Value)) | |||||
| { | { | ||||
| momentum_tensor = tf_utils.smart_cond(training, | |||||
| () => new float[] { momentum }, () => new float[] { 1.0f })[0]; | |||||
| } | |||||
| else | |||||
| { | |||||
| momentum_tensor = ops.convert_to_tensor(momentum); | |||||
| } | |||||
| Tensor momentum_tensor = null; | |||||
| if (!use_fused_avg_updates) | |||||
| { | |||||
| if (training_value == null) | |||||
| momentum_tensor = tf_utils.smart_cond(training, | |||||
| () => new float[] { momentum }, | |||||
| () => new float[] { 1.0f })[0]; | |||||
| else | |||||
| momentum_tensor = ops.convert_to_tensor(momentum); | |||||
| } | |||||
| if (use_fused_avg_updates) | |||||
| _assign_new_value(moving_mean, mean); | |||||
| else | |||||
| _assign_moving_average(moving_variance, variance, momentum_tensor); | |||||
| if (training_value == null) | |||||
| { | |||||
| var mean_update = _assign_moving_average(moving_mean.AsTensor(), mean, momentum_tensor); | |||||
| var variance_update = _assign_moving_average(moving_variance.AsTensor(), variance, momentum_tensor); | |||||
| add_update(new Tensor[] { mean_update }, inputs: true); | |||||
| add_update(new Tensor[] { variance_update }, inputs: true); | |||||
| if (use_fused_avg_updates) | |||||
| _assign_new_value(moving_variance, mean); | |||||
| else | |||||
| _assign_moving_average(moving_variance, variance, momentum_tensor); | |||||
| // var mean_update = _assign_moving_average(moving_mean.AsTensor(), mean, momentum_tensor); | |||||
| // var variance_update = _assign_moving_average(moving_variance.AsTensor(), variance, momentum_tensor); | |||||
| // add_update(new Tensor[] { mean_update }, inputs: true); | |||||
| // add_update(new Tensor[] { variance_update }, inputs: true); | |||||
| } | } | ||||
| return output; | return output; | ||||
| } | } | ||||
| public Tensor _assign_moving_average(RefVariable variable, Tensor value, Tensor momentum) | |||||
| Tensor _assign_new_value(IVariableV1 variable, Tensor value) | |||||
| { | |||||
| return tf_with(ops.name_scope("AssignNewValue", null, new { variable, value, momentum }), scope => | |||||
| { | |||||
| // var cm = ops.colocate_with(variable); | |||||
| return state_ops.assign_sub(variable, value, name: scope); | |||||
| }); | |||||
| } | |||||
| Tensor _assign_moving_average(IVariableV1 variable, Tensor value, Tensor momentum) | |||||
| { | { | ||||
| return tf_with(ops.name_scope(null, "AssignMovingAvg", new { variable, value, momentum }), scope => | |||||
| return tf_with(ops.name_scope("AssignMovingAvg", null, new { variable, value, momentum }), scope => | |||||
| { | { | ||||
| // var cm = ops.colocate_with(variable); | // var cm = ops.colocate_with(variable); | ||||
| var decay = ops.convert_to_tensor(1.0f - momentum, name: "decay"); | var decay = ops.convert_to_tensor(1.0f - momentum, name: "decay"); | ||||
| var update_delta = (variable - math_ops.cast(value, variable.dtype)) * decay; | |||||
| var update_delta = (variable.AsTensor() - math_ops.cast(value, variable.dtype)) * decay; | |||||
| return state_ops.assign_sub(variable, update_delta, name: scope); | return state_ops.assign_sub(variable, update_delta, name: scope); | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -20,6 +20,7 @@ using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Engine; | using Tensorflow.Keras.Engine; | ||||
| using Tensorflow.Keras.Utils; | using Tensorflow.Keras.Utils; | ||||
| using Tensorflow.Operations; | using Tensorflow.Operations; | ||||
| using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Keras.Layers | namespace Tensorflow.Keras.Layers | ||||
| { | { | ||||
| @@ -0,0 +1,23 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| namespace Tensorflow.Keras.Layers | |||||
| { | |||||
| public class GlobalAveragePooling2D : GlobalPooling2D | |||||
| { | |||||
| public GlobalAveragePooling2D(Pooling2DArgs args) | |||||
| : base(args) | |||||
| { | |||||
| } | |||||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||||
| { | |||||
| if (data_format == "channels_last") | |||||
| return math_ops.reduce_mean(inputs, new int[] { 1, 2 }, false); | |||||
| else | |||||
| return math_ops.reduce_mean(inputs, new int[] { 2, 3 }, false); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Engine; | |||||
| using Tensorflow.Keras.Utils; | |||||
| namespace Tensorflow.Keras.Layers | |||||
| { | |||||
| public abstract class GlobalPooling2D : Layer | |||||
| { | |||||
| Pooling2DArgs args; | |||||
| protected string data_format => args.DataFormat; | |||||
| protected InputSpec input_spec; | |||||
| public GlobalPooling2D(Pooling2DArgs args) : base(args) | |||||
| { | |||||
| this.args = args; | |||||
| args.DataFormat = conv_utils.normalize_data_format(data_format); | |||||
| input_spec = new InputSpec(ndim: 4); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -28,8 +28,7 @@ namespace Tensorflow.Keras.Layers | |||||
| /// <param name="renorm"></param> | /// <param name="renorm"></param> | ||||
| /// <param name="renorm_momentum"></param> | /// <param name="renorm_momentum"></param> | ||||
| /// <returns></returns> | /// <returns></returns> | ||||
| public Tensors batch_normalization(Tensor inputs, | |||||
| int axis = -1, | |||||
| public BatchNormalization BatchNormalization(int axis = -1, | |||||
| float momentum = 0.99f, | float momentum = 0.99f, | ||||
| float epsilon = 0.001f, | float epsilon = 0.001f, | ||||
| bool center = true, | bool center = true, | ||||
| @@ -38,31 +37,26 @@ namespace Tensorflow.Keras.Layers | |||||
| IInitializer gamma_initializer = null, | IInitializer gamma_initializer = null, | ||||
| IInitializer moving_mean_initializer = null, | IInitializer moving_mean_initializer = null, | ||||
| IInitializer moving_variance_initializer = null, | IInitializer moving_variance_initializer = null, | ||||
| Tensor training = null, | |||||
| bool trainable = true, | bool trainable = true, | ||||
| string name = null, | string name = null, | ||||
| bool renorm = false, | bool renorm = false, | ||||
| float renorm_momentum = 0.99f) | float renorm_momentum = 0.99f) | ||||
| { | |||||
| var layer = new BatchNormalization(new BatchNormalizationArgs | |||||
| { | |||||
| Axis = axis, | |||||
| Momentum = momentum, | |||||
| Epsilon = epsilon, | |||||
| Center = center, | |||||
| Scale = scale, | |||||
| BetaInitializer = beta_initializer, | |||||
| GammaInitializer = gamma_initializer, | |||||
| MovingMeanInitializer = moving_mean_initializer, | |||||
| MovingVarianceInitializer = moving_variance_initializer, | |||||
| Renorm = renorm, | |||||
| RenormMomentum = renorm_momentum, | |||||
| Trainable = trainable, | |||||
| Name = name | |||||
| }); | |||||
| return layer.Apply(inputs); | |||||
| } | |||||
| => new BatchNormalization(new BatchNormalizationArgs | |||||
| { | |||||
| Axis = axis, | |||||
| Momentum = momentum, | |||||
| Epsilon = epsilon, | |||||
| Center = center, | |||||
| Scale = scale, | |||||
| BetaInitializer = beta_initializer ?? tf.zeros_initializer, | |||||
| GammaInitializer = gamma_initializer ?? tf.ones_initializer, | |||||
| MovingMeanInitializer = moving_mean_initializer ?? tf.zeros_initializer, | |||||
| MovingVarianceInitializer = moving_variance_initializer ?? tf.ones_initializer, | |||||
| Renorm = renorm, | |||||
| RenormMomentum = renorm_momentum, | |||||
| Trainable = trainable, | |||||
| Name = name | |||||
| }); | |||||
| /// <summary> | /// <summary> | ||||
| /// | /// | ||||
| @@ -115,53 +109,64 @@ namespace Tensorflow.Keras.Layers | |||||
| Activation = activation ?? keras.activations.Linear | Activation = activation ?? keras.activations.Linear | ||||
| }); | }); | ||||
| public Tensor conv2d(Tensor inputs, | |||||
| int filters, | |||||
| int[] kernel_size, | |||||
| int[] strides = null, | |||||
| public Conv2D Conv2D(int filters, | |||||
| TensorShape kernel_size = null, | |||||
| TensorShape strides = null, | |||||
| string padding = "valid", | string padding = "valid", | ||||
| string data_format = "channels_last", | |||||
| int[] dilation_rate = null, | |||||
| string data_format = null, | |||||
| TensorShape dilation_rate = null, | |||||
| int groups = 1, | |||||
| string activation = null, | |||||
| bool use_bias = true, | bool use_bias = true, | ||||
| string kernel_initializer = "glorot_uniform", | |||||
| string bias_initializer = "zeros", | |||||
| string kernel_regularizer = null, | |||||
| string bias_regularizer = null, | |||||
| string activity_regularizer = null) | |||||
| => new Conv2D(new Conv2DArgs | |||||
| { | |||||
| Rank = 2, | |||||
| Filters = filters, | |||||
| KernelSize = kernel_size, | |||||
| Strides = strides == null ? (1, 1) : strides, | |||||
| Padding = padding, | |||||
| DataFormat = data_format, | |||||
| DilationRate = dilation_rate == null ? (1, 1) : dilation_rate, | |||||
| Groups = groups, | |||||
| UseBias = use_bias, | |||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | |||||
| BiasInitializer = GetInitializerByName(bias_initializer), | |||||
| Activation = GetActivationByName(activation) | |||||
| }); | |||||
| public Dense Dense(int units, | |||||
| Activation activation = null, | Activation activation = null, | ||||
| IInitializer kernel_initializer = null, | IInitializer kernel_initializer = null, | ||||
| IInitializer bias_initializer = null, | IInitializer bias_initializer = null, | ||||
| bool trainable = true, | |||||
| string name = null) | |||||
| { | |||||
| if (strides == null) | |||||
| strides = new int[] { 1, 1 }; | |||||
| if (dilation_rate == null) | |||||
| dilation_rate = new int[] { 1, 1 }; | |||||
| if (bias_initializer == null) | |||||
| bias_initializer = tf.zeros_initializer; | |||||
| var layer = new Conv2D(new Conv2DArgs | |||||
| TensorShape input_shape = null) | |||||
| => new Dense(new DenseArgs | |||||
| { | { | ||||
| Filters = filters, | |||||
| KernelSize = kernel_size, | |||||
| Strides = strides, | |||||
| Padding = padding, | |||||
| DataFormat = data_format, | |||||
| DilationRate = dilation_rate, | |||||
| Activation = activation, | |||||
| UseBias = use_bias, | |||||
| KernelInitializer = kernel_initializer, | |||||
| BiasInitializer = bias_initializer, | |||||
| Trainable = trainable, | |||||
| Name = name | |||||
| Units = units, | |||||
| Activation = activation ?? keras.activations.Linear, | |||||
| KernelInitializer = kernel_initializer ?? tf.glorot_uniform_initializer, | |||||
| BiasInitializer = bias_initializer ?? tf.zeros_initializer, | |||||
| InputShape = input_shape | |||||
| }); | }); | ||||
| return layer.Apply(inputs); | |||||
| } | |||||
| public Dense Dense(int units) | |||||
| => new Dense(new DenseArgs | |||||
| { | |||||
| Units = units, | |||||
| Activation = GetActivationByName("linear") | |||||
| }); | |||||
| public Dense Dense(int units, | public Dense Dense(int units, | ||||
| Activation activation = null, | |||||
| string activation = null, | |||||
| TensorShape input_shape = null) | TensorShape input_shape = null) | ||||
| => new Dense(new DenseArgs | => new Dense(new DenseArgs | ||||
| { | { | ||||
| Units = units, | Units = units, | ||||
| Activation = activation ?? keras.activations.Linear, | |||||
| Activation = GetActivationByName(activation), | |||||
| InputShape = input_shape | InputShape = input_shape | ||||
| }); | }); | ||||
| @@ -367,6 +372,12 @@ namespace Tensorflow.Keras.Layers | |||||
| Padding = padding | Padding = padding | ||||
| }); | }); | ||||
| public Tensor add(params Tensor[] inputs) | |||||
| => new Add(new MergeArgs { Inputs = inputs }).Apply(inputs); | |||||
| public GlobalAveragePooling2D GlobalAveragePooling2D() | |||||
| => new GlobalAveragePooling2D(new Pooling2DArgs { }); | |||||
| Activation GetActivationByName(string name) | Activation GetActivationByName(string name) | ||||
| => name switch | => name switch | ||||
| { | { | ||||
| @@ -376,5 +387,14 @@ namespace Tensorflow.Keras.Layers | |||||
| "tanh" => keras.activations.Tanh, | "tanh" => keras.activations.Tanh, | ||||
| _ => keras.activations.Linear | _ => keras.activations.Linear | ||||
| }; | }; | ||||
| IInitializer GetInitializerByName(string name) | |||||
| => name switch | |||||
| { | |||||
| "glorot_uniform" => tf.glorot_uniform_initializer, | |||||
| "zeros" => tf.zeros_initializer, | |||||
| "ones" => tf.ones_initializer, | |||||
| _ => tf.glorot_uniform_initializer | |||||
| }; | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,35 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using static Tensorflow.Binding; | |||||
| using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Engine; | |||||
| namespace Tensorflow.Keras.Layers | |||||
| { | |||||
| public abstract class Merge : Layer | |||||
| { | |||||
| public Merge(MergeArgs args) : base(args) | |||||
| { | |||||
| } | |||||
| protected override void build(TensorShape input_shape) | |||||
| { | |||||
| // output_shape = input_shape.dims[1^]; | |||||
| } | |||||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||||
| { | |||||
| return _merge_function(inputs); | |||||
| } | |||||
| Tensors _merge_function(Tensors inputs) | |||||
| { | |||||
| var output = inputs[0]; | |||||
| foreach (var i in range(1, inputs.Length)) | |||||
| output += inputs[i]; | |||||
| return output; | |||||
| } | |||||
| } | |||||
| } | |||||