From 512f3ce4e9d3964698bc4069de3de5f9369fd1cc Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Fri, 14 Jun 2019 14:55:41 -0500 Subject: [PATCH] add _MinOrMaxGrad and _MaximumMinimumGrad --- src/TensorFlowNET.Core/APIs/tf.array.cs | 10 +++ src/TensorFlowNET.Core/APIs/tf.gradients.cs | 20 ++++- src/TensorFlowNET.Core/APIs/tf.math.cs | 10 +++ src/TensorFlowNET.Core/Gradients/math_grad.cs | 90 +++++++++++++++++++ .../Operations/array_ops.py.cs | 23 +++++ src/TensorFlowNET.Core/Operations/math_ops.cs | 25 ++++++ 6 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/TensorFlowNET.Core/APIs/tf.array.cs b/src/TensorFlowNET.Core/APIs/tf.array.cs index cbcfed28..4e733f18 100644 --- a/src/TensorFlowNET.Core/APIs/tf.array.cs +++ b/src/TensorFlowNET.Core/APIs/tf.array.cs @@ -36,6 +36,16 @@ namespace Tensorflow public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1) => array_ops.expand_dims(input, axis, name, dim); + /// + /// Creates a tensor filled with a scalar value. + /// + /// + /// + /// + /// + public static Tensor fill(Tensor dims, T value, string name = null) + => gen_array_ops.fill(dims, value, name: name); + /// /// Return the elements, either from `x` or `y`, depending on the `condition`. /// diff --git a/src/TensorFlowNET.Core/APIs/tf.gradients.cs b/src/TensorFlowNET.Core/APIs/tf.gradients.cs index 2e7c68c3..0961288c 100644 --- a/src/TensorFlowNET.Core/APIs/tf.gradients.cs +++ b/src/TensorFlowNET.Core/APIs/tf.gradients.cs @@ -6,7 +6,7 @@ namespace Tensorflow { public static partial class tf { - public static object gradients(Tensor[] ys, + public static Tensor[] gradients(Tensor[] ys, Tensor[] xs, Tensor[] grad_ys = null, string name = "gradients", @@ -41,5 +41,23 @@ namespace Tensorflow gate_gradients, stop_gradients: stop_gradients); } + + public static Tensor[] gradients(Tensor ys, + Tensor xs, + Tensor[] grad_ys = null, + string name = "gradients", + bool colocate_gradients_with_ops = false, + bool gate_gradients = false, + int? aggregation_method = null, + Tensor[] stop_gradients = null) + { + return gradients_util._GradientsHelper(new Tensor[] { ys }, + new Tensor[] { xs }, + grad_ys, + name, + colocate_gradients_with_ops, + gate_gradients, + stop_gradients: stop_gradients); + } } } diff --git a/src/TensorFlowNET.Core/APIs/tf.math.cs b/src/TensorFlowNET.Core/APIs/tf.math.cs index bad41103..a8ec223a 100644 --- a/src/TensorFlowNET.Core/APIs/tf.math.cs +++ b/src/TensorFlowNET.Core/APIs/tf.math.cs @@ -257,6 +257,16 @@ namespace Tensorflow public static Tensor negative(Tensor x, string name = null) => gen_math_ops.neg(x, name); + /// + /// Divides x / y elementwise (using Python 2 division operator semantics). + /// + /// + /// + /// + /// + public static Tensor div(Tensor x, Tensor y, string name = null) + => math_ops.div(x, y, name: name); + public static Tensor divide(Tensor x, T[] y, string name = null) where T : struct => x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"); diff --git a/src/TensorFlowNET.Core/Gradients/math_grad.cs b/src/TensorFlowNET.Core/Gradients/math_grad.cs index 3f4ab94d..f7f8e35f 100644 --- a/src/TensorFlowNET.Core/Gradients/math_grad.cs +++ b/src/TensorFlowNET.Core/Gradients/math_grad.cs @@ -168,6 +168,96 @@ namespace Tensorflow.Gradients return new Tensor[] { math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null }; } + /// + /// Gradient for Max. + /// + /// + /// + /// + [RegisterGradient("Max")] + public static Tensor[] _MaxGrad(Operation op, Tensor[] grads) + { + return _MinOrMaxGrad(op, grads); + } + + /// + /// Gradient for Min. + /// + /// + /// + /// + [RegisterGradient("Min")] + public static Tensor[] _MinGrad(Operation op, Tensor[] grads) + { + return _MinOrMaxGrad(op, grads); + } + + private static Tensor[] _MinOrMaxGrad(Operation op, Tensor[] grads) + { + var grad = grads[0]; + var input_shape = array_ops.shape(op.inputs[0]); + var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]); + var y = op.outputs[0]; + y = array_ops.reshape(y, output_shape_kept_dims); + grad = array_ops.reshape(grad, output_shape_kept_dims); + + // Compute the number of selected (maximum or minimum) elements in each + // reduction dimension. If there are multiple minimum or maximum elements + // then the gradient will be divided between them. + var indicators = math_ops.cast(math_ops.equal(y, op.inputs[0]), grad.dtype); + var num_selected = array_ops.reshape(math_ops.reduce_sum(indicators, op.inputs[1]), output_shape_kept_dims); + + return new Tensor[] { math_ops.div(indicators, num_selected) * grad, null }; + } + + /// + /// Returns grad*(x > y, x <= y) with type of grad. + /// + /// + /// + /// + [RegisterGradient("Maximum")] + public static Tensor[] _MaximumGrad(Operation op, Tensor[] grads) + { + return _MaximumMinimumGrad(op, grads[0]); + } + + /// + /// Returns grad*(x < y, x >= y) with type of grad. + /// + /// + /// + /// + [RegisterGradient("Minimum")] + public static Tensor[] _MinimumGrad(Operation op, Tensor[] grads) + { + return _MaximumMinimumGrad(op, grads[0]); + } + + /// + /// Factor out the code for the gradient of Maximum or Minimum. + /// + /// + /// + /// + private static Tensor[] _MaximumMinimumGrad(Operation op, Tensor grad) + { + var x = op.inputs[0]; + var y = op.inputs[1]; + var gdtype = grad.dtype; + var sx = array_ops.shape(x); + var sy = array_ops.shape(y); + var gradshape = array_ops.shape(grad); + var zeros = array_ops.zeros(gradshape, gdtype); + var xmask = gen_math_ops.greater_equal(x, y); + var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy); + var xgrad = array_ops.where(xmask, grad, zeros); + var ygrad = array_ops.where(xmask, zeros, grad); + var gx = array_ops.reshape(math_ops.reduce_sum(xgrad, rx), sx); + var gy = array_ops.reshape(math_ops.reduce_sum(ygrad, ry), sy); + return new Tensor[] { gx, gy }; + } + [RegisterGradient("Neg")] public static Tensor[] _NegGrad(Operation op, Tensor[] grads) { diff --git a/src/TensorFlowNET.Core/Operations/array_ops.py.cs b/src/TensorFlowNET.Core/Operations/array_ops.py.cs index cb371dfe..4a6ee82d 100644 --- a/src/TensorFlowNET.Core/Operations/array_ops.py.cs +++ b/src/TensorFlowNET.Core/Operations/array_ops.py.cs @@ -36,6 +36,29 @@ namespace Tensorflow }); } + public static Tensor zeros(Tensor shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null) + { + dtype = dtype.as_base_dtype(); + return with(ops.name_scope(name, "zeros", shape), scope => + { + name = scope; + switch (dtype) + { + case TF_DataType.TF_BOOL: + return gen_array_ops.fill(shape, tf.constant(false, dtype: dtype), name: name); + case TF_DataType.TF_DOUBLE: + return gen_array_ops.fill(shape, tf.constant(0.0D, dtype: dtype), name: name); + case TF_DataType.TF_FLOAT: + return gen_array_ops.fill(shape, tf.constant(0.0F, dtype: dtype), name: name); + case TF_DataType.TF_INT32: + return gen_array_ops.fill(shape, tf.constant(0, dtype: dtype), name: name); + default: + throw new TypeError("can't find type for zeros"); + } + + }); + } + private static Tensor _constant_if_small(int value, Tensor shape) { return shape < 1000; diff --git a/src/TensorFlowNET.Core/Operations/math_ops.cs b/src/TensorFlowNET.Core/Operations/math_ops.cs index 051feecf..580ae33c 100644 --- a/src/TensorFlowNET.Core/Operations/math_ops.cs +++ b/src/TensorFlowNET.Core/Operations/math_ops.cs @@ -65,6 +65,31 @@ namespace Tensorflow }); } + /// + /// Divide two values using Python 2 semantics. Used for Tensor.__div__. + /// + /// `Tensor` numerator of real numeric type. + /// `Tensor` denominator of real numeric type. + /// A name for the operation + /// `x / y` returns the quotient of x and y. + public static Tensor div(Tensor x, Tensor y, string name = null) + { + return with(ops.name_scope(name, "div", (x, y)), name_scope => + { + name = name_scope; + x = ops.convert_to_tensor(x, name: "x"); + y = ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name = "y"); + var x_dtype = x.dtype.as_base_dtype(); + var y_dtype = y.dtype.as_base_dtype(); + if (x_dtype != y_dtype) + throw new TypeError($"x and y must have the same dtype, got {x_dtype} != {y_dtype}"); + if (x_dtype.is_floating() || x_dtype.is_complex()) + return gen_math_ops.real_div(x, y, name: name); + else + return gen_math_ops.floor_div(x, y, name: name); + }); + } + /// /// Returns 0 if the denominator is zero. ///