Added doc comment to array_ops.gather(), and implemented using ExecuteOp() Elaborated unit tests for gather, added one for slice()tags/v0.40-tf2.4-tstring
@@ -69,7 +69,17 @@ namespace Tensorflow | |||||
float maxval = 1, | float maxval = 1, | ||||
TF_DataType dtype = TF_DataType.TF_FLOAT, | TF_DataType dtype = TF_DataType.TF_FLOAT, | ||||
int? seed = null, | int? seed = null, | ||||
string name = null) => random_ops.random_uniform(shape, minval, maxval, dtype, seed, name); | |||||
string name = null) | |||||
{ | |||||
if (dtype.is_integer()) | |||||
{ | |||||
return random_ops.random_uniform_int(shape, (int)minval, (int)maxval, dtype, seed, name); | |||||
} | |||||
else | |||||
{ | |||||
return random_ops.random_uniform(shape, minval, maxval, dtype, seed, name); | |||||
} | |||||
} | |||||
public Tensor truncated_normal(TensorShape shape, | public Tensor truncated_normal(TensorShape shape, | ||||
float mean = 0.0f, | float mean = 0.0f, | ||||
@@ -843,7 +843,22 @@ namespace Tensorflow | |||||
return gen_array_ops.concat_v2(values, axis, name: name); | return gen_array_ops.concat_v2(values, axis, name: name); | ||||
} | } | ||||
public static Tensor gather<T1, T2>(T1 @params, T2 indices, string name = null, int axis = 0) | |||||
/// <summary> | |||||
/// Gather slices from `params` according to `indices`. `indices` must be an integer tensor of any dimension(often 1-D). | |||||
/// </summary> | |||||
/// <typeparam name="T1">Element type of the indexed tensor.</typeparam> | |||||
/// <typeparam name="T2">Element type of the index tensor.</typeparam> | |||||
/// <param name="params">The `Tensor` from which to gather values. Must be at least rank `axis + 1`.</param> | |||||
/// <param name="indices">The index `Tensor`. Must be one of the following types: `int32`, `int64`. The values must be in range `[0, params.shape[axis])`.</param> | |||||
/// <param name="name">A name for the operation (optional).</param> | |||||
/// <param name="axis"> | |||||
/// A `Tensor`. Must be one of the following types: `int32`, `int64`. | |||||
/// The `axis` in `params` to gather `indices` from.Must be greater than or equal to `batch_dims`. | |||||
/// Defaults to the first non-batch dimension. Supports negative indexes. | |||||
/// </param> | |||||
/// <param name="batch_dims">An integer. The number of batch dimensions. Must be less than or equal to rank(indices).</param> | |||||
/// <returns></returns> | |||||
public static Tensor gather<T1, T2>(T1 @params, T2 indices, string name = null, int axis = 0, int batch_dims = 0) | |||||
{ | { | ||||
if (axis != 0) | if (axis != 0) | ||||
return gen_array_ops.gather_v2(@params, indices, axis, name: name); | return gen_array_ops.gather_v2(@params, indices, axis, name: name); | ||||
@@ -913,7 +928,7 @@ namespace Tensorflow | |||||
} | } | ||||
public static Tensor slice(Tensor input, Tensor[] begin, Tensor[] size, string name = null) | public static Tensor slice(Tensor input, Tensor[] begin, Tensor[] size, string name = null) | ||||
=> gen_array_ops.slice(input, begin, size, name: name); | |||||
=> gen_array_ops.slice(input, begin, size, name: name); | |||||
public static Tensor slice<Tb, Ts>(Tensor input, Tb begin, Ts size, string name = null) | public static Tensor slice<Tb, Ts>(Tensor input, Tb begin, Ts size, string name = null) | ||||
=> gen_array_ops.slice(input, begin, size, name: name); | => gen_array_ops.slice(input, begin, size, name: name); | ||||
@@ -928,6 +943,7 @@ namespace Tensorflow | |||||
} | } | ||||
}); | }); | ||||
public static Tensor stack(object values, int axis = 0, string name = "stack") | public static Tensor stack(object values, int axis = 0, string name = "stack") | ||||
{ | { | ||||
if (axis == 0) | if (axis == 0) | ||||
@@ -117,28 +117,13 @@ namespace Tensorflow | |||||
=> tf.Context.ExecuteOp("ExpandDims", name, new ExecuteOpArgs(input, axis) | => tf.Context.ExecuteOp("ExpandDims", name, new ExecuteOpArgs(input, axis) | ||||
.SetAttributes(new { dim = axis })); | .SetAttributes(new { dim = axis })); | ||||
public static Tensor gather_v2<T1, T2>(T1 @params, T2 indices, int axis, string name = null) | |||||
public static Tensor gather_v2<T1, T2>(T1 @params, T2 indices, int axis, int batch_dims = 0, string name = null) | |||||
{ | { | ||||
if (tf.Context.executing_eagerly()) | |||||
{ | |||||
try | |||||
{ | |||||
var results = tf.Runner.TFE_FastPathExecute(new FastPathOpExecInfo("GatherV2", name, @params, indices, axis, "batch_dims", 0) | |||||
{ | |||||
ctx = tf.Context, | |||||
device_name = tf.Context.DeviceName | |||||
}); | |||||
return results[0]; | |||||
} | |||||
catch (Exception exc) | |||||
{ | |||||
return gather_v2_eager_fallback(@params, indices, axis, name, tf.Context); | |||||
} | |||||
} | |||||
var _op = tf.OpDefLib._apply_op_helper("GatherV2", name: name, new { @params, indices, axis }); | |||||
return _op.outputs[0]; | |||||
var result = tf.Context.ExecuteOp("GatherV2", name, new ExecuteOpArgs( | |||||
@params, | |||||
indices, | |||||
axis).SetAttributes(new { batch_dims })); | |||||
return result [0]; | |||||
} | } | ||||
private static Tensor gather_v2_eager_fallback(object @params, object indices, int axis, string name, Context ctx) | private static Tensor gather_v2_eager_fallback(object @params, object indices, int axis, string name, Context ctx) | ||||
@@ -380,6 +365,12 @@ namespace Tensorflow | |||||
public static Tensor slice<Tb, Ts>(Tensor input, Tb begin, Ts size, string name = null) | public static Tensor slice<Tb, Ts>(Tensor input, Tb begin, Ts size, string name = null) | ||||
{ | { | ||||
if (tf.executing_eagerly()) | |||||
{ | |||||
var outputs = tf.Runner.TFE_FastPathExecute(new FastPathOpExecInfo("Slice", name, input, begin, size)); | |||||
return outputs[0]; | |||||
} | |||||
var _op = tf.OpDefLib._apply_op_helper("Slice", name, new { input, begin, size }); | var _op = tf.OpDefLib._apply_op_helper("Slice", name, new { input, begin, size }); | ||||
return _op.outputs[0]; | return _op.outputs[0]; | ||||
} | } | ||||
@@ -43,18 +43,8 @@ namespace Tensorflow | |||||
/// <param name="name"></param> | /// <param name="name"></param> | ||||
/// <returns></returns> | /// <returns></returns> | ||||
public static Tensor random_uniform_int(Tensor shape, Tensor minval, Tensor maxval, int? seed = 0, int? seed2 = 0, string name = null) | public static Tensor random_uniform_int(Tensor shape, Tensor minval, Tensor maxval, int? seed = 0, int? seed2 = 0, string name = null) | ||||
{ | |||||
if (!seed.HasValue) | |||||
seed = 0; | |||||
if (!seed2.HasValue) | |||||
seed2 = 0; | |||||
var _op = tf.OpDefLib._apply_op_helper("RandomUniformInt", | |||||
name: name, | |||||
args: new { shape, minval, maxval, seed, seed2 }); | |||||
return _op.outputs[0]; | |||||
} | |||||
=> tf.Context.ExecuteOp("RandomUniformInt", name, new ExecuteOpArgs(shape, minval, maxval) | |||||
.SetAttributes(new { seed = seed ?? 0, seed2 = seed2 ?? 0 })); | |||||
/// <summary> | /// <summary> | ||||
/// Outputs random values from a uniform distribution. | /// Outputs random values from a uniform distribution. | ||||
@@ -81,6 +81,34 @@ namespace Tensorflow | |||||
}); | }); | ||||
} | } | ||||
/// <summary> | |||||
/// Outputs random values from a uniform distribution. | |||||
/// </summary> | |||||
/// <param name="shape"></param> | |||||
/// <param name="minval"></param> | |||||
/// <param name="maxval"></param> | |||||
/// <param name="dtype">The type of the output</param> | |||||
/// <param name="seed">Used to create a random seed for the distribution.</param> | |||||
/// <param name="name">A name for the operation</param> | |||||
/// <returns>A tensor of the specified shape filled with random uniform values.</returns> | |||||
public static Tensor random_uniform_int(int[] shape, | |||||
int minval = 0, | |||||
int maxval = 1, | |||||
TF_DataType dtype = TF_DataType.TF_FLOAT, | |||||
int? seed = null, | |||||
string name = null) | |||||
{ | |||||
return tf_with(ops.name_scope(name, "random_uniform_int", new { shape, minval, maxval }), scope => | |||||
{ | |||||
name = scope; | |||||
var (seed1, seed2) = random_seed.get_seed(seed); | |||||
var tensorShape = tensor_util.shape_tensor(shape); | |||||
var minTensor = ops.convert_to_tensor(minval, dtype: dtype, name: "min"); | |||||
var maxTensor = ops.convert_to_tensor(maxval, dtype: dtype, name: "max"); | |||||
return gen_random_ops.random_uniform_int(tensorShape, minTensor, maxTensor, seed: seed1, seed2: seed2); | |||||
}); | |||||
} | |||||
public static Tensor random_uniform(Tensor shape, | public static Tensor random_uniform(Tensor shape, | ||||
int minval = 0, | int minval = 0, | ||||
Tensor maxval = null, | Tensor maxval = null, | ||||
@@ -1,5 +1,6 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
using NumSharp; | using NumSharp; | ||||
using NumSharp.Utilities; | |||||
using Tensorflow; | using Tensorflow; | ||||
using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
@@ -7,9 +8,48 @@ namespace TensorFlowNET.UnitTest.ManagedAPI | |||||
{ | { | ||||
[TestClass] | [TestClass] | ||||
public class ArrayOpsTest : EagerModeTestBase | public class ArrayOpsTest : EagerModeTestBase | ||||
{ | |||||
{ | |||||
/// <summary> | /// <summary> | ||||
/// https://www.tensorflow.org/api_docs/python/tf/keras/layers/Embedding | |||||
/// https://www.tensorflow.org/api_docs/python/tf/slice | |||||
/// </summary> | |||||
[TestMethod] | |||||
public void Slice() | |||||
{ | |||||
// Tests based on example code in TF documentation | |||||
var input_array = tf.constant(np.array(new int[] { 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 }).reshape(3,2,3)); | |||||
var indices = tf.constant(np.array(new int[] { 0, 2 })); | |||||
var r1 = array_ops.slice(input_array, new int[] { 1, 0, 0 }, new int[] { 1, 1, 3 }); | |||||
Assert.AreEqual(new TensorShape(1,1,3), r1.shape); | |||||
var r1np = r1.numpy(); | |||||
Assert.AreEqual(r1np[0, 0, 0], 3); | |||||
Assert.AreEqual(r1np[0, 0, 1], 3); | |||||
Assert.AreEqual(r1np[0, 0, 2], 3); | |||||
var r2 = array_ops.slice(input_array, new int[] { 1, 0, 0 }, new int[] { 1, 2, 3 }); | |||||
Assert.AreEqual(new TensorShape(1, 2, 3), r2.shape); | |||||
var r2np = r2.numpy(); | |||||
Assert.AreEqual(r2np[0, 0, 0], 3); | |||||
Assert.AreEqual(r2np[0, 0, 1], 3); | |||||
Assert.AreEqual(r2np[0, 0, 2], 3); | |||||
Assert.AreEqual(r2np[0, 1, 0], 4); | |||||
Assert.AreEqual(r2np[0, 1, 1], 4); | |||||
Assert.AreEqual(r2np[0, 1, 2], 4); | |||||
var r3 = array_ops.slice(input_array, new int[] { 1, 0, 0 }, new int[] { 2, 1, 3 }); | |||||
Assert.AreEqual(new TensorShape(2, 1, 3), r3.shape); | |||||
var r3np = r3.numpy(); | |||||
Assert.AreEqual(r3np[0, 0, 0], 3); | |||||
Assert.AreEqual(r3np[0, 0, 1], 3); | |||||
Assert.AreEqual(r3np[0, 0, 2], 3); | |||||
Assert.AreEqual(r3np[1, 0, 0], 5); | |||||
Assert.AreEqual(r3np[1, 0, 1], 5); | |||||
Assert.AreEqual(r3np[1, 0, 2], 5); | |||||
} | |||||
/// <summary> | |||||
/// https://www.tensorflow.org/api_docs/python/tf/gather | |||||
/// </summary> | /// </summary> | ||||
[TestMethod] | [TestMethod] | ||||
public void Gather() | public void Gather() | ||||
@@ -19,9 +59,24 @@ namespace TensorFlowNET.UnitTest.ManagedAPI | |||||
var result = array_ops.gather(input_array, indices); | var result = array_ops.gather(input_array, indices); | ||||
Assert.AreEqual(new TensorShape(2, 4), result.shape); | Assert.AreEqual(new TensorShape(2, 4), result.shape); | ||||
Assert.AreEqual(result.numpy()[0,0], 0.0f); | |||||
Assert.AreEqual(result.numpy()[0,1], 1.0f); | |||||
Assert.AreEqual(result.numpy()[1,3], 11.0f); | |||||
Assert.AreEqual(result.numpy()[0, 0], 0.0f); | |||||
Assert.AreEqual(result.numpy()[0, 1], 1.0f); | |||||
Assert.AreEqual(result.numpy()[1, 3], 11.0f); | |||||
// Tests based on example code in Python doc string for tf.gather() | |||||
var p1 = tf.random.normal(new TensorShape(5, 6, 7, 8)); | |||||
var i1 = tf.random_uniform(new TensorShape(10, 11), maxval: 7, dtype: tf.int32); | |||||
var r1 = tf.gather(p1, i1, axis:2); | |||||
Assert.AreEqual(new TensorShape(5, 6, 10, 11, 8), r1.shape); | |||||
var p2 = tf.random.normal(new TensorShape(4,3)); | |||||
var i2 = tf.constant(new int[,] { { 0, 2} }); | |||||
var r2 = tf.gather(p2, i2, axis: 0); | |||||
Assert.AreEqual(new TensorShape(1, 2, 3), r2.shape); | |||||
var r3 = tf.gather(p2, i2, axis: 1); | |||||
Assert.AreEqual(new TensorShape(4,1,2), r3.shape); | |||||
} | } | ||||
} | } | ||||
} | } |