| @@ -36,6 +36,16 @@ namespace Tensorflow | |||||
| public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1) | public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1) | ||||
| => array_ops.expand_dims(input, axis, name, dim); | => array_ops.expand_dims(input, axis, name, dim); | ||||
| /// <summary> | |||||
| /// Creates a tensor filled with a scalar value. | |||||
| /// </summary> | |||||
| /// <param name="dims"></param> | |||||
| /// <param name="value"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor fill<T>(Tensor dims, T value, string name = null) | |||||
| => gen_array_ops.fill(dims, value, name: name); | |||||
| /// <summary> | /// <summary> | ||||
| /// Return the elements, either from `x` or `y`, depending on the `condition`. | /// Return the elements, either from `x` or `y`, depending on the `condition`. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -6,7 +6,7 @@ namespace Tensorflow | |||||
| { | { | ||||
| public static partial class tf | public static partial class tf | ||||
| { | { | ||||
| public static object gradients(Tensor[] ys, | |||||
| public static Tensor[] gradients(Tensor[] ys, | |||||
| Tensor[] xs, | Tensor[] xs, | ||||
| Tensor[] grad_ys = null, | Tensor[] grad_ys = null, | ||||
| string name = "gradients", | string name = "gradients", | ||||
| @@ -41,5 +41,23 @@ namespace Tensorflow | |||||
| gate_gradients, | gate_gradients, | ||||
| stop_gradients: stop_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); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -257,6 +257,16 @@ namespace Tensorflow | |||||
| public static Tensor negative(Tensor x, string name = null) | public static Tensor negative(Tensor x, string name = null) | ||||
| => gen_math_ops.neg(x, name); | => gen_math_ops.neg(x, name); | ||||
| /// <summary> | |||||
| /// Divides x / y elementwise (using Python 2 division operator semantics). | |||||
| /// </summary> | |||||
| /// <param name="x"></param> | |||||
| /// <param name="y"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor div(Tensor x, Tensor y, string name = null) | |||||
| => math_ops.div(x, y, name: name); | |||||
| public static Tensor divide<T>(Tensor x, T[] y, string name = null) where T : struct | public static Tensor divide<T>(Tensor x, T[] y, string name = null) where T : struct | ||||
| => x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"); | => x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"); | ||||
| @@ -168,6 +168,96 @@ namespace Tensorflow.Gradients | |||||
| return new Tensor[] { math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null }; | return new Tensor[] { math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null }; | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gradient for Max. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grads"></param> | |||||
| /// <returns></returns> | |||||
| [RegisterGradient("Max")] | |||||
| public static Tensor[] _MaxGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| return _MinOrMaxGrad(op, grads); | |||||
| } | |||||
| /// <summary> | |||||
| /// Gradient for Min. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grads"></param> | |||||
| /// <returns></returns> | |||||
| [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 }; | |||||
| } | |||||
| /// <summary> | |||||
| /// Returns grad*(x > y, x <= y) with type of grad. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grads"></param> | |||||
| /// <returns></returns> | |||||
| [RegisterGradient("Maximum")] | |||||
| public static Tensor[] _MaximumGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| return _MaximumMinimumGrad(op, grads[0]); | |||||
| } | |||||
| /// <summary> | |||||
| /// Returns grad*(x < y, x >= y) with type of grad. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grads"></param> | |||||
| /// <returns></returns> | |||||
| [RegisterGradient("Minimum")] | |||||
| public static Tensor[] _MinimumGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| return _MaximumMinimumGrad(op, grads[0]); | |||||
| } | |||||
| /// <summary> | |||||
| /// Factor out the code for the gradient of Maximum or Minimum. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grad"></param> | |||||
| /// <returns></returns> | |||||
| 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")] | [RegisterGradient("Neg")] | ||||
| public static Tensor[] _NegGrad(Operation op, Tensor[] grads) | public static Tensor[] _NegGrad(Operation op, Tensor[] grads) | ||||
| { | { | ||||
| @@ -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) | private static Tensor _constant_if_small(int value, Tensor shape) | ||||
| { | { | ||||
| return shape < 1000; | return shape < 1000; | ||||
| @@ -65,6 +65,31 @@ namespace Tensorflow | |||||
| }); | }); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Divide two values using Python 2 semantics. Used for Tensor.__div__. | |||||
| /// </summary> | |||||
| /// <param name="x">`Tensor` numerator of real numeric type.</param> | |||||
| /// <param name="y">`Tensor` denominator of real numeric type.</param> | |||||
| /// <param name="name">A name for the operation</param> | |||||
| /// <returns>`x / y` returns the quotient of x and y.</returns> | |||||
| 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); | |||||
| }); | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns 0 if the denominator is zero. | /// Returns 0 if the denominator is zero. | ||||
| /// </summary> | /// </summary> | ||||