@@ -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> | ||||