Skip to content

Commit 512f3ce

Browse files
committed
add _MinOrMaxGrad and _MaximumMinimumGrad
1 parent 06e6ae6 commit 512f3ce

File tree

6 files changed

+177
-1
lines changed

6 files changed

+177
-1
lines changed

src/TensorFlowNET.Core/APIs/tf.array.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public static Tensor concat(IList<Tensor> values, int axis, string name = "conca
3636
public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1)
3737
=> array_ops.expand_dims(input, axis, name, dim);
3838

39+
/// <summary>
40+
/// Creates a tensor filled with a scalar value.
41+
/// </summary>
42+
/// <param name="dims"></param>
43+
/// <param name="value"></param>
44+
/// <param name="name"></param>
45+
/// <returns></returns>
46+
public static Tensor fill<T>(Tensor dims, T value, string name = null)
47+
=> gen_array_ops.fill(dims, value, name: name);
48+
3949
/// <summary>
4050
/// Return the elements, either from `x` or `y`, depending on the `condition`.
4151
/// </summary>

src/TensorFlowNET.Core/APIs/tf.gradients.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Tensorflow
66
{
77
public static partial class tf
88
{
9-
public static object gradients(Tensor[] ys,
9+
public static Tensor[] gradients(Tensor[] ys,
1010
Tensor[] xs,
1111
Tensor[] grad_ys = null,
1212
string name = "gradients",
@@ -41,5 +41,23 @@ public static Tensor[] gradients(Tensor ys,
4141
gate_gradients,
4242
stop_gradients: stop_gradients);
4343
}
44+
45+
public static Tensor[] gradients(Tensor ys,
46+
Tensor xs,
47+
Tensor[] grad_ys = null,
48+
string name = "gradients",
49+
bool colocate_gradients_with_ops = false,
50+
bool gate_gradients = false,
51+
int? aggregation_method = null,
52+
Tensor[] stop_gradients = null)
53+
{
54+
return gradients_util._GradientsHelper(new Tensor[] { ys },
55+
new Tensor[] { xs },
56+
grad_ys,
57+
name,
58+
colocate_gradients_with_ops,
59+
gate_gradients,
60+
stop_gradients: stop_gradients);
61+
}
4462
}
4563
}

src/TensorFlowNET.Core/APIs/tf.math.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,16 @@ public static Tensor multiply<Tx, Ty>(Tx x, Ty y)
257257
public static Tensor negative(Tensor x, string name = null)
258258
=> gen_math_ops.neg(x, name);
259259

260+
/// <summary>
261+
/// Divides x / y elementwise (using Python 2 division operator semantics).
262+
/// </summary>
263+
/// <param name="x"></param>
264+
/// <param name="y"></param>
265+
/// <param name="name"></param>
266+
/// <returns></returns>
267+
public static Tensor div(Tensor x, Tensor y, string name = null)
268+
=> math_ops.div(x, y, name: name);
269+
260270
public static Tensor divide<T>(Tensor x, T[] y, string name = null) where T : struct
261271
=> x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y");
262272

src/TensorFlowNET.Core/Gradients/math_grad.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,96 @@ public static Tensor[] _MeanGrad(Operation op, Tensor[] grads)
168168
return new Tensor[] { math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null };
169169
}
170170

171+
/// <summary>
172+
/// Gradient for Max.
173+
/// </summary>
174+
/// <param name="op"></param>
175+
/// <param name="grads"></param>
176+
/// <returns></returns>
177+
[RegisterGradient("Max")]
178+
public static Tensor[] _MaxGrad(Operation op, Tensor[] grads)
179+
{
180+
return _MinOrMaxGrad(op, grads);
181+
}
182+
183+
/// <summary>
184+
/// Gradient for Min.
185+
/// </summary>
186+
/// <param name="op"></param>
187+
/// <param name="grads"></param>
188+
/// <returns></returns>
189+
[RegisterGradient("Min")]
190+
public static Tensor[] _MinGrad(Operation op, Tensor[] grads)
191+
{
192+
return _MinOrMaxGrad(op, grads);
193+
}
194+
195+
private static Tensor[] _MinOrMaxGrad(Operation op, Tensor[] grads)
196+
{
197+
var grad = grads[0];
198+
var input_shape = array_ops.shape(op.inputs[0]);
199+
var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]);
200+
var y = op.outputs[0];
201+
y = array_ops.reshape(y, output_shape_kept_dims);
202+
grad = array_ops.reshape(grad, output_shape_kept_dims);
203+
204+
// Compute the number of selected (maximum or minimum) elements in each
205+
// reduction dimension. If there are multiple minimum or maximum elements
206+
// then the gradient will be divided between them.
207+
var indicators = math_ops.cast(math_ops.equal(y, op.inputs[0]), grad.dtype);
208+
var num_selected = array_ops.reshape(math_ops.reduce_sum(indicators, op.inputs[1]), output_shape_kept_dims);
209+
210+
return new Tensor[] { math_ops.div(indicators, num_selected) * grad, null };
211+
}
212+
213+
/// <summary>
214+
/// Returns grad*(x > y, x <= y) with type of grad.
215+
/// </summary>
216+
/// <param name="op"></param>
217+
/// <param name="grads"></param>
218+
/// <returns></returns>
219+
[RegisterGradient("Maximum")]
220+
public static Tensor[] _MaximumGrad(Operation op, Tensor[] grads)
221+
{
222+
return _MaximumMinimumGrad(op, grads[0]);
223+
}
224+
225+
/// <summary>
226+
/// Returns grad*(x < y, x >= y) with type of grad.
227+
/// </summary>
228+
/// <param name="op"></param>
229+
/// <param name="grads"></param>
230+
/// <returns></returns>
231+
[RegisterGradient("Minimum")]
232+
public static Tensor[] _MinimumGrad(Operation op, Tensor[] grads)
233+
{
234+
return _MaximumMinimumGrad(op, grads[0]);
235+
}
236+
237+
/// <summary>
238+
/// Factor out the code for the gradient of Maximum or Minimum.
239+
/// </summary>
240+
/// <param name="op"></param>
241+
/// <param name="grad"></param>
242+
/// <returns></returns>
243+
private static Tensor[] _MaximumMinimumGrad(Operation op, Tensor grad)
244+
{
245+
var x = op.inputs[0];
246+
var y = op.inputs[1];
247+
var gdtype = grad.dtype;
248+
var sx = array_ops.shape(x);
249+
var sy = array_ops.shape(y);
250+
var gradshape = array_ops.shape(grad);
251+
var zeros = array_ops.zeros(gradshape, gdtype);
252+
var xmask = gen_math_ops.greater_equal(x, y);
253+
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);
254+
var xgrad = array_ops.where(xmask, grad, zeros);
255+
var ygrad = array_ops.where(xmask, zeros, grad);
256+
var gx = array_ops.reshape(math_ops.reduce_sum(xgrad, rx), sx);
257+
var gy = array_ops.reshape(math_ops.reduce_sum(ygrad, ry), sy);
258+
return new Tensor[] { gx, gy };
259+
}
260+
171261
[RegisterGradient("Neg")]
172262
public static Tensor[] _NegGrad(Operation op, Tensor[] grads)
173263
{

src/TensorFlowNET.Core/Operations/array_ops.py.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,29 @@ public static Tensor zeros(Shape shape, TF_DataType dtype = TF_DataType.TF_FLOAT
3636
});
3737
}
3838

39+
public static Tensor zeros(Tensor shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
40+
{
41+
dtype = dtype.as_base_dtype();
42+
return with(ops.name_scope(name, "zeros", shape), scope =>
43+
{
44+
name = scope;
45+
switch (dtype)
46+
{
47+
case TF_DataType.TF_BOOL:
48+
return gen_array_ops.fill(shape, tf.constant(false, dtype: dtype), name: name);
49+
case TF_DataType.TF_DOUBLE:
50+
return gen_array_ops.fill(shape, tf.constant(0.0D, dtype: dtype), name: name);
51+
case TF_DataType.TF_FLOAT:
52+
return gen_array_ops.fill(shape, tf.constant(0.0F, dtype: dtype), name: name);
53+
case TF_DataType.TF_INT32:
54+
return gen_array_ops.fill(shape, tf.constant(0, dtype: dtype), name: name);
55+
default:
56+
throw new TypeError("can't find type for zeros");
57+
}
58+
59+
});
60+
}
61+
3962
private static Tensor _constant_if_small(int value, Tensor shape)
4063
{
4164
return shape < 1000;

src/TensorFlowNET.Core/Operations/math_ops.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,31 @@ public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, s
6565
});
6666
}
6767

68+
/// <summary>
69+
/// Divide two values using Python 2 semantics. Used for Tensor.__div__.
70+
/// </summary>
71+
/// <param name="x">`Tensor` numerator of real numeric type.</param>
72+
/// <param name="y">`Tensor` denominator of real numeric type.</param>
73+
/// <param name="name">A name for the operation</param>
74+
/// <returns>`x / y` returns the quotient of x and y.</returns>
75+
public static Tensor div(Tensor x, Tensor y, string name = null)
76+
{
77+
return with(ops.name_scope(name, "div", (x, y)), name_scope =>
78+
{
79+
name = name_scope;
80+
x = ops.convert_to_tensor(x, name: "x");
81+
y = ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name = "y");
82+
var x_dtype = x.dtype.as_base_dtype();
83+
var y_dtype = y.dtype.as_base_dtype();
84+
if (x_dtype != y_dtype)
85+
throw new TypeError($"x and y must have the same dtype, got {x_dtype} != {y_dtype}");
86+
if (x_dtype.is_floating() || x_dtype.is_complex())
87+
return gen_math_ops.real_div(x, y, name: name);
88+
else
89+
return gen_math_ops.floor_div(x, y, name: name);
90+
});
91+
}
92+
6893
/// <summary>
6994
/// Returns 0 if the denominator is zero.
7095
/// </summary>

0 commit comments

Comments
 (0)