| @@ -149,6 +149,8 @@ namespace Tensorflow | |||
| return ndArray.ndim == 0 ? 1 : ndArray.shape[0]; | |||
| case IEnumerable enumerable: | |||
| return enumerable.OfType<object>().Count(); | |||
| case TensorShape arr: | |||
| return arr.ndim; | |||
| } | |||
| throw new NotImplementedException("len() not implemented for type: " + a.GetType()); | |||
| } | |||
| @@ -156,6 +158,9 @@ namespace Tensorflow | |||
| public static float min(float a, float b) | |||
| => Math.Min(a, b); | |||
| public static int max(int a, int b) | |||
| => Math.Max(a, b); | |||
| public static T[] list<T>(IEnumerable<T> list) | |||
| => list.ToArray(); | |||
| @@ -15,6 +15,8 @@ | |||
| ******************************************************************************/ | |||
| using System; | |||
| using System.Linq; | |||
| using static Tensorflow.Binding; | |||
| namespace Tensorflow.Framework | |||
| { | |||
| @@ -52,7 +54,14 @@ namespace Tensorflow.Framework | |||
| { | |||
| var pred_value = tensor_util.constant_value(pred); | |||
| if (pred_value is null) | |||
| return pred.eval(new Session(pred.graph)); | |||
| { | |||
| var result = range(pred.op.NumOutputs).Select(x => IntPtr.Zero).ToArray(); | |||
| var evaluated = c_api.TF_TryEvaluateConstant(pred.graph, pred._as_tf_output(), result, tf.Status.Handle); | |||
| if (!evaluated || c_api.TF_GetCode(tf.Status.Handle) != TF_Code.TF_OK) | |||
| return null; | |||
| else | |||
| throw new NotImplementedException(""); | |||
| } | |||
| return pred_value; | |||
| } | |||
| @@ -322,5 +322,18 @@ namespace Tensorflow | |||
| [DllImport(TensorFlowLibName)] | |||
| public static extern void TF_UpdateEdge(IntPtr graph, TF_Output new_src, TF_Input dst, SafeStatusHandle status); | |||
| /// <summary> | |||
| /// Attempts to evaluate `output`. This will only be possible if `output` doesn't | |||
| /// depend on any graph inputs (this function is safe to call if this isn't the | |||
| /// case though). | |||
| /// </summary> | |||
| /// <param name="graph"></param> | |||
| /// <param name="output"></param> | |||
| /// <param name="result"></param> | |||
| /// <param name="status"></param> | |||
| /// <returns></returns> | |||
| [DllImport(TensorFlowLibName)] | |||
| public static extern bool TF_TryEvaluateConstant(IntPtr graph, TF_Output output, IntPtr[] result, SafeStatusHandle status); | |||
| } | |||
| } | |||
| @@ -50,6 +50,6 @@ namespace Tensorflow.Keras.Engine | |||
| } | |||
| public override string ToString() | |||
| => $"min_ndim={min_ndim}, , axes={axes.Count}"; | |||
| => $"ndim={ndim}, min_ndim={min_ndim}, axes={axes.Count}"; | |||
| } | |||
| } | |||
| @@ -21,6 +21,31 @@ namespace Tensorflow | |||
| { | |||
| public class nn_impl | |||
| { | |||
| public static Tensor conv2d_transpose(Tensor value = null, | |||
| IVariableV1 filter = null, | |||
| Tensor output_shape = null, | |||
| TensorShape strides = null, | |||
| string padding = "SAME", | |||
| string data_format = "NHWC", | |||
| string name = null, | |||
| TensorShape dilations = null) | |||
| { | |||
| if (dilations == null) | |||
| dilations = (1, 1, 1, 1); | |||
| return tf_with(ops.name_scope(name, "conv2d_transpose", new { value, filter, output_shape }), scope => | |||
| { | |||
| return gen_nn_ops.conv2d_backprop_input( | |||
| input_sizes: output_shape, | |||
| filter: filter.AsTensor(), | |||
| out_backprop: value, | |||
| strides: strides, | |||
| padding: padding, | |||
| data_format: data_format, | |||
| dilations: dilations, | |||
| name: name); | |||
| }); | |||
| } | |||
| /// <summary> | |||
| /// Normalizes along dimension `axis` using an L2 norm. | |||
| /// </summary> | |||
| @@ -83,6 +108,23 @@ namespace Tensorflow | |||
| }); | |||
| } | |||
| public static Tensor batch_normalization(Tensor x, | |||
| Tensor mean, | |||
| Tensor variance, | |||
| Tensor offset, | |||
| Tensor scale, | |||
| float variance_epsilon = 0.001f, | |||
| string name = null) | |||
| { | |||
| return tf_with(ops.name_scope(name, "batchnorm", new { x, mean, variance, scale, offset }), scope => | |||
| { | |||
| var inv = math_ops.rsqrt(variance + variance_epsilon); | |||
| inv *= scale; | |||
| return x * math_ops.cast(inv, x.dtype) + math_ops.cast( | |||
| offset == null ? (-mean * inv) : (offset - mean * inv), x.dtype); | |||
| }); | |||
| } | |||
| /// <summary> | |||
| /// Batch normalization. | |||
| /// </summary> | |||
| @@ -15,6 +15,10 @@ namespace Tensorflow | |||
| else if (rank != shape1.rank) | |||
| return false; | |||
| return Enumerable.SequenceEqual(shape1.dims, dims); | |||
| case int[] shape2: | |||
| if (rank != shape2.Length) | |||
| return false; | |||
| return Enumerable.SequenceEqual(dims, shape2); | |||
| default: | |||
| return false; | |||
| } | |||
| @@ -317,5 +317,30 @@ namespace Tensorflow.Keras | |||
| return array_ops.concat(tensors, axis); | |||
| } | |||
| public Tensor conv2d_transpose(Tensor x, | |||
| IVariableV1 kernel, | |||
| Tensor output_shape, | |||
| TensorShape strides = null, | |||
| string padding = "valid", | |||
| string data_format = null, | |||
| TensorShape dilation_rate = null) | |||
| { | |||
| var force_transpose = false; | |||
| if (data_format == "channels_first" && !dilation_rate.Equals(new[] { 1, 1 })) | |||
| force_transpose = true; | |||
| // x, tf_data_format = _preprocess_conv2d_input(x, data_format, force_transpose) | |||
| var tf_data_format = "NHWC"; | |||
| padding = padding.ToUpper(); | |||
| strides = new TensorShape(1, strides[0], strides[1], 1); | |||
| if (dilation_rate.Equals(new[] { 1, 1 })) | |||
| x = nn_impl.conv2d_transpose(x, kernel, output_shape, strides, | |||
| padding: padding, | |||
| data_format: tf_data_format); | |||
| else | |||
| throw new NotImplementedException(""); | |||
| return x; | |||
| } | |||
| } | |||
| } | |||
| @@ -301,9 +301,9 @@ namespace Tensorflow.Keras.Engine | |||
| nodes_in_decreasing_depth.append(node); | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return run_internal_graph(inputs, is_training); | |||
| return run_internal_graph(inputs, training.Value); | |||
| } | |||
| Tensors run_internal_graph(Tensors inputs, bool training = false, Tensors mask = null) | |||
| @@ -10,9 +10,9 @@ namespace Tensorflow.Keras.Engine | |||
| /// </summary> | |||
| /// <param name="input"></param> | |||
| /// <param name="state"></param> | |||
| /// <param name="is_training"></param> | |||
| /// <param name="training"></param> | |||
| /// <returns></returns> | |||
| public Tensors Apply(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| public Tensors Apply(Tensors inputs, Tensor state = null, bool training = false) | |||
| { | |||
| callContext = callContext ?? new ThreadLocal<CallContext>() | |||
| { | |||
| @@ -38,7 +38,7 @@ namespace Tensorflow.Keras.Engine | |||
| if (!built) | |||
| MaybeBuild(inputs); | |||
| outputs = Call(inputs, state: state, is_training: is_training); | |||
| outputs = Call(inputs, state: state, training: training); | |||
| // memory leak | |||
| // _set_connectivity_metadata_(inputs, outputs); | |||
| @@ -155,7 +155,7 @@ namespace Tensorflow.Keras.Engine | |||
| /// <param name="state"></param> | |||
| /// <param name="is_training"></param> | |||
| /// <returns></returns> | |||
| protected virtual Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected virtual Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| throw new NotImplementedException(""); | |||
| } | |||
| @@ -73,7 +73,7 @@ namespace Tensorflow.Keras.Engine | |||
| List<(string, Tensor)> test_step(Tensor x, Tensor y) | |||
| { | |||
| (x, y) = data_handler.DataAdapter.Expand1d(x, y); | |||
| var y_pred = Apply(x, is_training: false); | |||
| var y_pred = Apply(x, training: false); | |||
| var loss = compiled_loss.Call(y, y_pred); | |||
| compiled_metrics.update_state(y, y_pred); | |||
| @@ -76,7 +76,7 @@ namespace Tensorflow.Keras.Engine | |||
| Tensors predict_step(Tensor data) | |||
| { | |||
| return Apply(data, is_training: false); | |||
| return Apply(data, training: false); | |||
| } | |||
| } | |||
| } | |||
| @@ -25,7 +25,7 @@ namespace Tensorflow.Keras.Engine | |||
| { | |||
| (x, y) = data_handler.DataAdapter.Expand1d(x, y); | |||
| using var tape = tf.GradientTape(); | |||
| var y_pred = Apply(x, is_training: true); | |||
| var y_pred = Apply(x, training: true); | |||
| var loss = compiled_loss.Call(y, y_pred); | |||
| // For custom training steps, users can just write: | |||
| @@ -8,9 +8,10 @@ using Tensorflow.Keras.Saving; | |||
| namespace Tensorflow.Keras.Engine | |||
| { | |||
| public partial class Model | |||
| public partial class Model | |||
| { | |||
| public List<(IVariableV1, NDArray)> load_weights(string filepath, bool by_name = false, bool skip_mismatch = false, object options = null) | |||
| List<(IVariableV1, NDArray)> LoadedWeights; | |||
| public void load_weights(string filepath, bool by_name = false, bool skip_mismatch = false, object options = null) | |||
| { | |||
| long fileId = Hdf5.OpenFile(filepath, true); | |||
| @@ -25,10 +26,8 @@ namespace Tensorflow.Keras.Engine | |||
| throw new NotImplementedException(""); | |||
| else | |||
| { | |||
| var weights = hdf5_format.load_weights_from_hdf5_group(fileId, Layers); | |||
| LoadedWeights = hdf5_format.load_weights_from_hdf5_group(fileId, Layers); | |||
| Hdf5.CloseFile(fileId); | |||
| // return a reference to prevent GC collect Variable. | |||
| return weights; | |||
| } | |||
| } | |||
| @@ -19,7 +19,7 @@ namespace Tensorflow.Keras.Layers | |||
| this.args = args; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return tf.nn.leaky_relu(inputs, alpha: alpha); | |||
| } | |||
| @@ -0,0 +1,150 @@ | |||
| /***************************************************************************** | |||
| Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| ******************************************************************************/ | |||
| using System; | |||
| using static Tensorflow.Binding; | |||
| using Tensorflow.Keras.ArgsDefinition; | |||
| using Tensorflow.Keras.Utils; | |||
| using static Tensorflow.KerasApi; | |||
| namespace Tensorflow.Keras.Layers | |||
| { | |||
| public class Conv2DTranspose : Conv2D | |||
| { | |||
| public Conv2DTranspose(Conv2DArgs args) : base(args) | |||
| { | |||
| } | |||
| protected override void build(Tensors inputs) | |||
| { | |||
| var input_shape = inputs.shape; | |||
| if (len(input_shape) != 4) | |||
| throw new ValueError($"Inputs should have rank 4. Received input shape: {input_shape}"); | |||
| var channel_axis = _get_channel_axis(); | |||
| var input_dim = input_shape[-1]; | |||
| var kernel_shape = new TensorShape(kernel_size[0], kernel_size[1], filters, input_dim); | |||
| kernel = add_weight(name: "kernel", | |||
| shape: kernel_shape, | |||
| initializer: kernel_initializer, | |||
| regularizer: kernel_regularizer, | |||
| trainable: true, | |||
| dtype: inputs.dtype); | |||
| if (use_bias) | |||
| bias = add_weight(name: "bias", | |||
| shape: filters, | |||
| initializer: bias_initializer, | |||
| trainable: true, | |||
| dtype: inputs.dtype); | |||
| built = true; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| var inputs_shape = array_ops.shape(inputs); | |||
| var batch_size = inputs_shape[0]; | |||
| var (h_axis, w_axis) = (1, 2); | |||
| if (data_format == "channels_first") | |||
| (h_axis, w_axis) = (2, 3); | |||
| var (height, width) = (-1, -1); | |||
| if(inputs.shape.rank > -1) | |||
| { | |||
| var dims = inputs.shape.dims; | |||
| (height, width) = (dims[h_axis], dims[w_axis]); | |||
| } | |||
| var (kernel_h, kernel_w) = kernel_size; | |||
| var (stride_h, stride_w) = strides; | |||
| var (out_pad_h, out_pad_w) = (-1, -1); | |||
| // Infer the dynamic output shape: | |||
| var out_height = conv_utils.deconv_output_length(height, | |||
| kernel_h, | |||
| padding: padding, | |||
| output_padding: out_pad_h, | |||
| stride: stride_h, | |||
| dilation: dilation_rate[0]); | |||
| var out_width = conv_utils.deconv_output_length(width, | |||
| kernel_w, | |||
| padding: padding, | |||
| output_padding: out_pad_w, | |||
| stride: stride_w, | |||
| dilation: dilation_rate[1]); | |||
| Tensor output_shape_tensor; | |||
| if (data_format == "channels_first") | |||
| output_shape_tensor = array_ops.stack(new object[] { batch_size, filters, out_height, out_width }); | |||
| else | |||
| output_shape_tensor = array_ops.stack(new object[] { batch_size, out_height, out_width, filters }); | |||
| var outputs = keras.backend.conv2d_transpose( | |||
| inputs, | |||
| kernel, | |||
| output_shape_tensor, | |||
| strides: strides, | |||
| padding: padding, | |||
| data_format: data_format, | |||
| dilation_rate: dilation_rate); | |||
| if (!tf.Context.executing_eagerly()) | |||
| { | |||
| var out_shape = ComputeOutputShape(inputs.shape); | |||
| outputs.set_shape(out_shape); | |||
| } | |||
| if (use_bias) | |||
| throw new NotImplementedException(""); | |||
| if (activation != null) | |||
| return activation(outputs); | |||
| return outputs; | |||
| } | |||
| public override TensorShape ComputeOutputShape(TensorShape input_shape) | |||
| { | |||
| var output_shape = input_shape.dims; | |||
| var (c_axis, h_axis, w_axis) = (3, 1, 2); | |||
| if (data_format == "channels_first") | |||
| (c_axis, h_axis, w_axis) = (1, 2, 3); | |||
| var (kernel_h, kernel_w) = kernel_size; | |||
| var (stride_h, stride_w) = strides; | |||
| var (out_pad_h, out_pad_w) = (-1, -1); | |||
| output_shape[c_axis] = filters; | |||
| output_shape[h_axis] = conv_utils.deconv_output_length( | |||
| output_shape[h_axis], | |||
| kernel_h, | |||
| padding: padding, | |||
| output_padding: out_pad_h, | |||
| stride: stride_h, | |||
| dilation: dilation_rate[0]); | |||
| output_shape[w_axis] = conv_utils.deconv_output_length( | |||
| output_shape[w_axis], | |||
| kernel_w, | |||
| padding: padding, | |||
| output_padding: out_pad_w, | |||
| stride: stride_w, | |||
| dilation: dilation_rate[1]); | |||
| return new TensorShape(output_shape); | |||
| } | |||
| } | |||
| } | |||
| @@ -99,7 +99,7 @@ namespace Tensorflow.Keras.Layers | |||
| built = true; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = false) | |||
| { | |||
| var outputs = _convolution_op.Apply(inputs, kernel); | |||
| if (use_bias) | |||
| @@ -119,5 +119,8 @@ namespace Tensorflow.Keras.Layers | |||
| return outputs; | |||
| } | |||
| protected virtual int _get_channel_axis() | |||
| => data_format == "channels_first" ? -1 - rank : -1; | |||
| } | |||
| } | |||
| @@ -65,7 +65,7 @@ namespace Tensorflow.Keras.Layers | |||
| built = true; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| Tensor outputs = null; | |||
| var rank = inputs.rank; | |||
| @@ -62,7 +62,7 @@ namespace Tensorflow.Keras.Layers | |||
| 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 = null) | |||
| { | |||
| var dtype = inputs.dtype; | |||
| if (dtype != tf.int32 && dtype != tf.int64) | |||
| @@ -26,9 +26,9 @@ namespace Tensorflow.Keras.Layers | |||
| .ToArray(); | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return base.Call(inputs, state: state, is_training: is_training); | |||
| return base.Call(inputs, state: state, training: training); | |||
| } | |||
| } | |||
| } | |||
| @@ -140,6 +140,51 @@ namespace Tensorflow.Keras.Layers | |||
| Activation = GetActivationByName(activation) | |||
| }); | |||
| /// <summary> | |||
| /// Transposed convolution layer (sometimes called Deconvolution). | |||
| /// </summary> | |||
| /// <param name="filters"></param> | |||
| /// <param name="kernel_size"></param> | |||
| /// <param name="strides"></param> | |||
| /// <param name="padding"></param> | |||
| /// <param name="data_format"></param> | |||
| /// <param name="dilation_rate"></param> | |||
| /// <param name="activation"></param> | |||
| /// <param name="use_bias"></param> | |||
| /// <param name="kernel_initializer"></param> | |||
| /// <param name="bias_initializer"></param> | |||
| /// <param name="kernel_regularizer"></param> | |||
| /// <param name="bias_regularizer"></param> | |||
| /// <param name="activity_regularizer"></param> | |||
| /// <returns></returns> | |||
| public Conv2DTranspose Conv2DTranspose(int filters, | |||
| TensorShape kernel_size = null, | |||
| TensorShape strides = null, | |||
| string padding = "valid", | |||
| string data_format = null, | |||
| TensorShape dilation_rate = null, | |||
| string activation = null, | |||
| bool use_bias = true, | |||
| string kernel_initializer = null, | |||
| string bias_initializer = null, | |||
| string kernel_regularizer = null, | |||
| string bias_regularizer = null, | |||
| string activity_regularizer = null) | |||
| => new Conv2DTranspose(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, | |||
| UseBias = use_bias, | |||
| KernelInitializer = GetInitializerByName(kernel_initializer), | |||
| BiasInitializer = GetInitializerByName(bias_initializer), | |||
| Activation = GetActivationByName(activation) | |||
| }); | |||
| public Dense Dense(int units, | |||
| Activation activation = null, | |||
| IInitializer kernel_initializer = null, | |||
| @@ -19,7 +19,7 @@ namespace Tensorflow.Keras.Layers | |||
| // output_shape = input_shape.dims[1^]; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return _merge_function(inputs); | |||
| } | |||
| @@ -36,6 +36,7 @@ namespace Tensorflow.Keras.Layers | |||
| bool fused; | |||
| int[] axis; | |||
| string _data_format; | |||
| TensorShape kernel_size; | |||
| IInitializer beta_initializer => args.BetaInitializer; | |||
| IInitializer gamma_initializer => args.GammaInitializer; | |||
| IInitializer moving_mean_initializer => args.MovingMeanInitializer; | |||
| @@ -120,10 +121,35 @@ namespace Tensorflow.Keras.Layers | |||
| built = true; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool training = false) | |||
| public override TensorShape ComputeOutputShape(TensorShape input_shape) | |||
| { | |||
| return input_shape; | |||
| } | |||
| (Tensor, Tensor) _moments(Tensors inputs, int[] reduction_axes, bool keep_dims) | |||
| { | |||
| var (mean, variance) = _calculate_mean_and_var(inputs, reduction_axes, keep_dims); | |||
| if (_support_zero_size_input()) | |||
| throw new NotImplementedException(""); | |||
| return (mean, variance); | |||
| } | |||
| (Tensor, Tensor) _calculate_mean_and_var(Tensors inputs, int[] reduction_axes, bool keep_dims) | |||
| { | |||
| return nn_impl.moments(inputs, reduction_axes, keep_dims: keep_dims); | |||
| } | |||
| bool _support_zero_size_input() | |||
| { | |||
| return false; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| Tensor outputs = null; | |||
| var training_tensor = tf.logical_and(training, Trainable); | |||
| var training_tensor = training == null | |||
| ? tf.placeholder(tf.@bool, TensorShape.Scalar) | |||
| : tf.logical_and(training.Value, Trainable); | |||
| if (fused) | |||
| { | |||
| // var training = tf.convert_to_tensor(training); | |||
| @@ -131,7 +157,49 @@ namespace Tensorflow.Keras.Layers | |||
| return outputs; | |||
| } | |||
| throw new NotImplementedException("BatchNormalization call"); | |||
| var inputs_dtype = inputs.dtype.as_base_dtype(); | |||
| var input_shape = inputs.shape; | |||
| var ndims = len(input_shape); | |||
| var reduction_axes = range(ndims).Where(x => !axis.Contains(x)).ToArray(); | |||
| // Broadcasting only necessary for single-axis batch norm where the axis is | |||
| // not the last dimension | |||
| var broadcast_shape = range(ndims).Select(x => 1).ToArray(); | |||
| broadcast_shape[axis[0]] = input_shape.dims[axis[0]]; | |||
| var (scale, offset) = (gamma, beta); | |||
| var training_value = tf_utils.constant_value(training_tensor); | |||
| Tensor mean; | |||
| Tensor variance; | |||
| if (training_value.HasValue && training_value.Value == false) | |||
| { | |||
| (mean, variance) = (moving_mean.AsTensor(), moving_variance.AsTensor()); | |||
| } | |||
| else | |||
| { | |||
| var keep_dims = len(axis) > 1; | |||
| (mean, variance) = _moments(inputs, reduction_axes, keep_dims: keep_dims); | |||
| mean = tf_utils.smart_cond(training_tensor, | |||
| () => new[] { mean }, | |||
| () => new[] { ops.convert_to_tensor(moving_mean) }).FirstOrDefault(); | |||
| variance = tf_utils.smart_cond(training_tensor, | |||
| () => new[] { variance }, | |||
| () => new[] { ops.convert_to_tensor(moving_variance) }).FirstOrDefault(); | |||
| var (new_mean, new_variance) = (mean, variance); | |||
| } | |||
| mean = math_ops.cast(mean, inputs.dtype); | |||
| variance = math_ops.cast(variance, inputs.dtype); | |||
| var offset_tensor = math_ops.cast(offset, inputs.dtype); | |||
| var scale_tensor = math_ops.cast(scale, inputs.dtype); | |||
| outputs = nn_impl.batch_normalization(inputs, mean, variance, | |||
| offset_tensor, scale_tensor, epsilon); | |||
| // If some components of the shape got lost due to adjustments, fix that. | |||
| outputs.set_shape(input_shape); | |||
| return outputs; | |||
| } | |||
| private Tensor _fused_batch_norm(Tensor inputs, Tensor training) | |||
| @@ -12,7 +12,7 @@ namespace Tensorflow.Keras.Layers | |||
| { | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| if (data_format == "channels_last") | |||
| return math_ops.reduce_mean(inputs, new int[] { 1, 2 }, false); | |||
| @@ -36,7 +36,7 @@ namespace Tensorflow.Keras.Layers | |||
| input_spec = new InputSpec(ndim: 4); | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| int[] pool_shape; | |||
| int[] strides; | |||
| @@ -15,9 +15,12 @@ namespace Tensorflow.Keras.Layers | |||
| this.args = args; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| var output = tf_utils.smart_cond(is_training, | |||
| if (training == null) | |||
| training = false; | |||
| var output = tf_utils.smart_cond(training.Value, | |||
| () => tf.nn.dropout(inputs, | |||
| noise_shape: get_noise_shape(inputs), | |||
| seed: args.Seed, | |||
| @@ -17,7 +17,7 @@ namespace Tensorflow.Keras.Layers | |||
| this.args = args; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| scale = math_ops.cast(args.Scale, args.DType); | |||
| offset = math_ops.cast(args.Offset, args.DType); | |||
| @@ -22,7 +22,7 @@ namespace Tensorflow.Keras.Layers | |||
| _channels_first = args.DataFormat == "channels_first"; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| if (_channels_first) | |||
| { | |||
| @@ -19,7 +19,7 @@ namespace Tensorflow.Keras.Layers | |||
| this.args = args; | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| var shapes = new List<object>(); | |||
| shapes.Add(array_ops.shape(inputs)[0]); | |||
| @@ -24,7 +24,7 @@ namespace Tensorflow.Keras.Layers | |||
| inputSpec = new InputSpec(ndim: 4); | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return keras.backend.resize_images(inputs, | |||
| size[0], size[1], | |||
| @@ -26,7 +26,7 @@ namespace Tensorflow.Keras.Layers | |||
| this.input_spec = new InputSpec(ndim: 4); | |||
| } | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool is_training = false) | |||
| protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
| { | |||
| return keras.backend.spatial_2d_padding(inputs, | |||
| padding: padding, | |||
| @@ -32,7 +32,7 @@ namespace Tensorflow.Keras.Layers | |||
| 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 = null) | |||
| { | |||
| if (tf.Context.executing_eagerly()) | |||
| return _defun_call(inputs); | |||
| @@ -55,10 +55,6 @@ Keras is an API designed for human beings, not machines. Keras follows best prac | |||
| <PackageReference Include="SharpZipLib" Version="1.3.1" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\TensorFlowNET.Core\Tensorflow.Binding.csproj" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <None Include="..\..\LICENSE"> | |||
| <Pack>True</Pack> | |||
| @@ -70,4 +66,8 @@ Keras is an API designed for human beings, not machines. Keras follows best prac | |||
| <Folder Include="Engine\Interfaces\" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\TensorFlowNET.Core\Tensorflow.Binding.csproj" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -14,7 +14,9 @@ | |||
| limitations under the License. | |||
| ******************************************************************************/ | |||
| using System; | |||
| using System.Linq; | |||
| using static Tensorflow.Binding; | |||
| namespace Tensorflow.Keras.Utils | |||
| { | |||
| @@ -63,5 +65,33 @@ namespace Tensorflow.Keras.Utils | |||
| return ImageDataFormat.channels_last.ToString(); | |||
| return value.ToLower(); | |||
| } | |||
| public static int deconv_output_length(int input_length, | |||
| int filter_size, | |||
| string padding, | |||
| int output_padding = -1, | |||
| int stride = 0, | |||
| int dilation = 1) | |||
| { | |||
| // Get the dilated kernel size | |||
| filter_size = filter_size + (filter_size - 1) * (dilation - 1); | |||
| // Infer length if output padding is None, else compute the exact length | |||
| int length = -1; | |||
| if (output_padding == -1) | |||
| { | |||
| if (padding == "valid") | |||
| length = input_length * stride + max(filter_size - stride, 0); | |||
| else if (padding == "full") | |||
| length = input_length * stride - (stride + filter_size - 2); | |||
| else if (padding == "same") | |||
| length = input_length * stride; | |||
| } | |||
| else | |||
| { | |||
| throw new NotImplementedException(""); | |||
| } | |||
| return length; | |||
| } | |||
| } | |||
| } | |||
| @@ -37,6 +37,17 @@ namespace Tensorflow.Keras.Utils | |||
| return true; | |||
| } | |||
| public static Tensor[] smart_cond<T>(IVariableV1 pred, | |||
| Func<T[]> true_fn = null, | |||
| Func<T[]> false_fn = null, | |||
| string name = null) | |||
| { | |||
| return control_flow_ops.cond(pred.AsTensor(), | |||
| true_fn: true_fn, | |||
| false_fn: false_fn, | |||
| name: name); | |||
| } | |||
| public static Tensor[] smart_cond<T>(Tensor pred, | |||
| Func<T[]> true_fn = null, | |||
| Func<T[]> false_fn = null, | |||