@@ -64,10 +64,8 @@ namespace Tensorflow.Eager | |||
} | |||
} | |||
var flattened_inputs = args.Take(op_def.InputArg.Count) | |||
.Select(x => x as Tensor) | |||
.ToArray(); | |||
var flattened_attrs = args.Skip(op_def.InputArg.Count).ToArray(); | |||
var flattened_attrs = new List<object>(op_def.InputArg.Count); | |||
var flattened_inputs = new List<Tensor>(op_def.InputArg.Count); | |||
c_api.TFE_OpSetDevice(op, device_name, status.Handle); | |||
status.Check(true); | |||
@@ -80,31 +78,36 @@ namespace Tensorflow.Eager | |||
{ | |||
int len = (args[kFastPathExecuteInputStartIndex + i] as object[]).Length; | |||
c_api.TFE_OpSetAttrInt(op, input_arg.NumberAttr, len); | |||
if (op_exec_info.run_callbacks) | |||
{ | |||
flattened_attrs.Add(input_arg.NumberAttr); | |||
flattened_attrs.Add(len); | |||
} | |||
attr_list_sizes[input_arg.NumberAttr] = len; | |||
if (len > 0) | |||
{ | |||
var fast_input_array = (object[])args[i]; | |||
// First item adds the type attr. | |||
if (!AddInputToOp(fast_input_array[i], true, input_arg, op, status)) | |||
if (!AddInputToOp(fast_input_array[i], true, input_arg, flattened_attrs, flattened_inputs, op, status)) | |||
return null; | |||
for (var j = 1; j < len; j++) | |||
{ | |||
// Since the list is homogeneous, we don't need to re-add the attr. | |||
if (!AddInputToOp(fast_input_array[j], false, input_arg, op, status)) | |||
if (!AddInputToOp(fast_input_array[j], false, input_arg, flattened_attrs, flattened_inputs, op, status)) | |||
return null; | |||
} | |||
} | |||
} | |||
else if (!string.IsNullOrEmpty(input_arg.TypeListAttr)) | |||
{ | |||
throw new NotImplementedException(""); | |||
} | |||
else | |||
{ | |||
// The item is a single item. | |||
AddInputToOp(args[i], true, input_arg, op, status); | |||
AddInputToOp(args[i], true, input_arg, flattened_attrs, flattened_inputs, op, status); | |||
} | |||
} | |||
@@ -133,7 +136,7 @@ namespace Tensorflow.Eager | |||
if (!RunCallbacks( | |||
op_exec_info, | |||
kFastPathExecuteInputStartIndex + op_def.InputArg.Count(), | |||
flattened_inputs, flattened_attrs, flat_result)) | |||
flattened_inputs.ToArray(), flattened_attrs.ToArray(), flat_result)) | |||
{ | |||
return null; | |||
} | |||
@@ -187,6 +190,8 @@ namespace Tensorflow.Eager | |||
bool AddInputToOp(object inputs, | |||
bool add_type_attr, | |||
ArgDef input_arg, | |||
List<object> flattened_attrs, | |||
List<Tensor> flattened_inputs, | |||
IntPtr op, | |||
Status status) | |||
{ | |||
@@ -197,6 +202,7 @@ namespace Tensorflow.Eager | |||
{ | |||
case EagerTensor input: | |||
input_handle = input.EagerTensorHandle; | |||
flattened_inputs.Add(input); | |||
break; | |||
case EagerTensor[] input_list: | |||
input_handle = input_list[0].EagerTensorHandle; | |||
@@ -211,6 +217,8 @@ namespace Tensorflow.Eager | |||
{ | |||
var dtype = c_api.TFE_TensorHandleDataType(input_handle); | |||
c_api.TFE_OpSetAttrType(op, input_arg.TypeAttr, dtype); | |||
flattened_attrs.Add(input_arg.TypeAttr); | |||
flattened_attrs.Add(dtype); | |||
} | |||
c_api.TFE_OpAddInput(op, input_handle, status.Handle); | |||
@@ -34,7 +34,7 @@ namespace Tensorflow.Eager | |||
public EagerTensor Resolve() | |||
{ | |||
_id = get_uid(); | |||
_id = ops.uid(); | |||
if (_handle == IntPtr.Zero) | |||
_handle = c_api.TFE_TensorHandleResolve(EagerTensorHandle, tf.status.Handle); | |||
@@ -55,8 +55,5 @@ namespace Tensorflow.Eager | |||
//print($"deleting DeleteTensorHandle {Id} {EagerTensorHandle.ToString("x16")}"); | |||
c_api.TFE_DeleteTensorHandle(EagerTensorHandle); | |||
} | |||
static long _uid = 0; | |||
long get_uid() => _uid++; | |||
} | |||
} |
@@ -24,8 +24,8 @@ namespace Tensorflow.Gradients | |||
/// </summary> | |||
public class GradientTape : IDisposable | |||
{ | |||
static bool _recording; | |||
public static bool Recording => _recording; | |||
bool _recording; | |||
public bool Recording => _recording; | |||
bool _persistent; | |||
bool _watch_accessed_variables; | |||
ResourceVariable[] _watched_variables; | |||
@@ -13,12 +13,14 @@ namespace Tensorflow.Gradients | |||
"FusedBatchNormGradV3" => new[] { 5 }, | |||
"FusedBatchNormV2" => new[] { 2 }, | |||
"FusedBatchNormV3" => new[] { 2 }, | |||
"ReadVariableOp" => new int[0], | |||
_ => null | |||
}; | |||
public static int[] OpGradientUnusedOutputIndices(string op_name) | |||
=> op_name switch | |||
{ | |||
"ReadVariableOp" => new int[0], | |||
"SoftmaxCrossEntropyWithLogits" => new[] { 0 }, | |||
"TensorArrayConcat" => new[] { 0 }, | |||
"TensorArrayConcatV2" => new[] { 0 }, | |||
@@ -64,6 +64,22 @@ namespace Tensorflow.Gradients | |||
return new Tensor[] { r1, r2 }; | |||
} | |||
/// <summary> | |||
/// Copies the gradient to all inputs. | |||
/// </summary> | |||
/// <param name="op"></param> | |||
/// <param name="grads"></param> | |||
/// <returns></returns> | |||
[RegisterGradient("AddN")] | |||
public static Tensor[] _AddNGrad(Operation op, Tensor[] grads) | |||
{ | |||
var grad = grads[0]; | |||
return Enumerable.Range(0, len(op.inputs)) | |||
.Select(x => grad) | |||
.ToArray(); | |||
} | |||
[RegisterGradient("Cumsum")] | |||
public static Tensor[] _CumsumGrad(Operation op, Tensor[] grads) | |||
{ | |||
@@ -124,6 +124,16 @@ namespace Tensorflow | |||
/// </remarks> | |||
public static Tensor diag(Tensor diagonal, string name = null) | |||
{ | |||
if (tf.context.executing_eagerly()) | |||
{ | |||
var results = tf.Runner.TFE_FastPathExecute(tf.context, tf.context.device_name, | |||
"Diag", name, | |||
null, | |||
diagonal); | |||
return results[0]; | |||
} | |||
var op = tf._op_def_lib._apply_op_helper("Diag", name: name, args: new { diagonal }); | |||
return op.output; | |||
@@ -131,6 +141,16 @@ namespace Tensorflow | |||
public static Tensor expand_dims(Tensor input, int axis, string name = null) | |||
{ | |||
if (tf.context.executing_eagerly()) | |||
{ | |||
var results = tf.Runner.TFE_FastPathExecute(tf.context, tf.context.device_name, | |||
"ExpandDims", name, | |||
null, | |||
input, tf.convert_to_tensor(axis)); | |||
return results[0]; | |||
} | |||
var _op = tf._op_def_lib._apply_op_helper("ExpandDims", name: name, args: new { input, dim = axis }); | |||
return _op.outputs[0]; | |||
@@ -39,5 +39,8 @@ namespace Tensorflow.Util | |||
} | |||
public override bool IsInvalid => handle == IntPtr.Zero; | |||
public override string ToString() | |||
=> $"0x{handle.ToString("x16")}"; | |||
} | |||
} |
@@ -28,10 +28,10 @@ namespace Tensorflow.Util | |||
} | |||
public void push_back(Tk key, Tv value) | |||
=> Add(key, value); | |||
=> this[key] = value; | |||
public void emplace(Tk key, Tv value) | |||
=> Add(key, value); | |||
=> this[key] = value; | |||
public bool find(Tk key) | |||
=> ContainsKey(key); | |||
@@ -22,56 +22,21 @@ namespace Tensorflow | |||
{ | |||
public partial class ResourceVariable | |||
{ | |||
public static Tensor operator +(ResourceVariable x, int y) => op_helper("add", x, y); | |||
public static Tensor operator +(ResourceVariable x, float y) => op_helper("add", x, y); | |||
public static Tensor operator +(ResourceVariable x, double y) => op_helper("add", x, y); | |||
public static Tensor operator +(ResourceVariable x, ResourceVariable y) => op_helper("add", x, y); | |||
public static Tensor operator -(ResourceVariable x, int y) => op_helper("sub", x, y); | |||
public static Tensor operator -(ResourceVariable x, float y) => op_helper("sub", x, y); | |||
public static Tensor operator -(ResourceVariable x, double y) => op_helper("sub", x, y); | |||
public static Tensor operator -(ResourceVariable x, Tensor y) => op_helper("sub", x, y); | |||
public static Tensor operator -(ResourceVariable x, ResourceVariable y) => op_helper("sub", x, y); | |||
public static Tensor operator *(ResourceVariable x, ResourceVariable y) => op_helper("mul", x, y); | |||
public static Tensor operator *(ResourceVariable x, NDArray y) => op_helper("mul", x, y); | |||
public static Tensor operator <(ResourceVariable x, Tensor y) => op_helper("less", x, y); | |||
public static Tensor operator >(ResourceVariable x, Tensor y) => op_helper("greater", x, y); | |||
private static Tensor op_helper<T>(string default_name, ResourceVariable x, T y) | |||
=> tf_with(ops.name_scope(null, default_name, new { x, y }), scope => | |||
{ | |||
string name = scope; | |||
var xVal = x.value(); | |||
var yTensor = ops.convert_to_tensor(y, xVal.dtype.as_base_dtype(), "y"); | |||
Tensor result = null; | |||
switch (default_name) | |||
{ | |||
case "add": | |||
result = x.dtype == TF_DataType.TF_STRING ? | |||
gen_math_ops.add(xVal, yTensor, name) : | |||
gen_math_ops.add_v2(xVal, yTensor, name); | |||
break; | |||
case "sub": | |||
result = gen_math_ops.sub(xVal, yTensor, name); | |||
break; | |||
case "mul": | |||
result = gen_math_ops.mul(xVal, yTensor, name: name); | |||
break; | |||
case "less": | |||
result = gen_math_ops.less(xVal, yTensor, name); | |||
break; | |||
case "greater": | |||
result = gen_math_ops.greater(xVal, yTensor, name); | |||
break; | |||
default: | |||
throw new NotImplementedException(""); | |||
} | |||
// x.assign(result); | |||
// result.ResourceVar = x; | |||
return result; | |||
}); | |||
public static Tensor operator +(ResourceVariable x, int y) => x.value() + y; | |||
public static Tensor operator +(ResourceVariable x, float y) => x.value() + y; | |||
public static Tensor operator +(ResourceVariable x, double y) => x.value() + y; | |||
public static Tensor operator +(ResourceVariable x, ResourceVariable y) => x.value() + y.value(); | |||
public static Tensor operator -(ResourceVariable x, int y) => x.value() - y; | |||
public static Tensor operator -(ResourceVariable x, float y) => x.value() - y; | |||
public static Tensor operator -(ResourceVariable x, double y) => x.value() - y; | |||
public static Tensor operator -(ResourceVariable x, Tensor y) => x.value() - y; | |||
public static Tensor operator -(ResourceVariable x, ResourceVariable y) => x.value() - y.value(); | |||
public static Tensor operator *(ResourceVariable x, ResourceVariable y) => x.value() * y.value(); | |||
public static Tensor operator *(ResourceVariable x, NDArray y) => x.value() * y; | |||
public static Tensor operator <(ResourceVariable x, Tensor y) => x.value() < y; | |||
public static Tensor operator >(ResourceVariable x, Tensor y) => x.value() > y; | |||
} | |||
} |
@@ -277,7 +277,7 @@ namespace Tensorflow | |||
return ops.control_dependencies(null); | |||
} | |||
private static int uid_number = 0; | |||
private static int uid_number = -1; | |||
/// <summary> | |||
/// A unique (within this program execution) integer. | |||
@@ -24,6 +24,25 @@ namespace TensorFlowNET.UnitTest.Gradient | |||
Assert.AreEqual((float)grad, 3.0f); | |||
} | |||
/// <summary> | |||
/// Calcute the gradient of w * w * w | |||
/// 高阶梯度 | |||
/// </summary> | |||
[TestMethod] | |||
public void HighGradient() | |||
{ | |||
var x = tf.Variable(1.0f); | |||
using var tape1 = tf.GradientTape(); | |||
using var tape2 = tf.GradientTape(); | |||
var y = x * x * x; | |||
tape2.Dispose(); | |||
var dy_dx = tape2.gradient(y, x); | |||
Assert.AreEqual((float)dy_dx, 3.0f); | |||
tape1.Dispose(); | |||
var d2y_d2x = tape1.gradient(dy_dx, x); | |||
Assert.AreEqual((float)d2y_d2x, 6.0f); | |||
} | |||
[TestMethod] | |||
public void ConstantMultiply() | |||
{ | |||
@@ -56,5 +75,33 @@ namespace TensorFlowNET.UnitTest.Gradient | |||
var dz_dy = tape.gradient(z, y); | |||
Assert.AreEqual((float)dz_dy, 8.0f); | |||
} | |||
[TestMethod] | |||
public void ConditionalMultiply() | |||
{ | |||
Func<Tensor, int, Tensor> func = (x, y) => | |||
{ | |||
Tensor output = tf.constant(1.0f); | |||
foreach (var i in range(y)) | |||
{ | |||
if (i > 1) | |||
output = tf.multiply(output, x); | |||
} | |||
return output; | |||
}; | |||
Func<Tensor, int, Tensor> grad = (x, y) => | |||
{ | |||
using var tape = tf.GradientTape(); | |||
tape.watch(x); | |||
var output = func(x, y); | |||
var grad = tape.gradient(output, x); | |||
return grad; | |||
}; | |||
var x = tf.constant(2.0f); | |||
var result = grad(x, 4); | |||
Assert.AreEqual((float)result, 4.0f); | |||
} | |||
} | |||
} |