| @@ -0,0 +1,124 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| using static Tensorflow.Binding; | |||||
| using static Tensorflow.tensorflow; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| bool RecordGradient(string op_name, | |||||
| Tensor[] inputs, | |||||
| object[] attrs, | |||||
| Tensor[] results) | |||||
| { | |||||
| var input_ids = MakeTensorIDList(inputs); | |||||
| var input_dtypes = MakeTensorDtypeList(inputs); | |||||
| bool should_record = false; | |||||
| foreach (var tape in tf.GetTapeSet()) | |||||
| { | |||||
| if(tape.ShouldRecord(input_ids, input_dtypes)) | |||||
| { | |||||
| should_record = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!should_record) | |||||
| { | |||||
| /*for (TFE_Py_ForwardAccumulator* accumulator : SafeAccumulatorSet()) | |||||
| { | |||||
| if (accumulator->accumulator->ShouldRecord(input_ids, input_dtypes)) | |||||
| { | |||||
| should_record = true; | |||||
| break; | |||||
| } | |||||
| }*/ | |||||
| } | |||||
| if (!should_record) return should_record; | |||||
| Tensor[] op_outputs; | |||||
| bool op_outputs_tuple_created = false; | |||||
| var unused_output_indices = gradient_exclustions.OpGradientUnusedOutputIndices(op_name); | |||||
| if (unused_output_indices != null) | |||||
| { | |||||
| if (unused_output_indices.Length == 0) | |||||
| op_outputs = new Tensor[0]; | |||||
| else | |||||
| { | |||||
| op_outputs_tuple_created = true; | |||||
| // op_outputs = CopySequenceSettingIndicesToNull(results, *unused_output_indices); | |||||
| } | |||||
| } | |||||
| else | |||||
| op_outputs = results; | |||||
| Tensor[] op_inputs; | |||||
| bool op_inputs_tuple_created = false; | |||||
| var unused_input_indices = gradient_exclustions.OpGradientUnusedInputIndices(op_name); | |||||
| if(unused_input_indices != null) | |||||
| { | |||||
| if (unused_input_indices.Length == 0) | |||||
| op_inputs = new Tensor[0]; | |||||
| else | |||||
| { | |||||
| op_inputs_tuple_created = true; | |||||
| // op_inputs = CopySequenceSettingIndicesToNull(inputs, *unused_input_indices); | |||||
| } | |||||
| } | |||||
| else | |||||
| op_inputs = inputs; | |||||
| TapeSetRecordOperation(op_name, inputs, results, input_ids, input_dtypes, | |||||
| () => GetGradientFunction(op_name, inputs, attrs, results)); | |||||
| return true; | |||||
| } | |||||
| BackwardFunction GetGradientFunction(string op_name, | |||||
| Tensor[] op_inputs, | |||||
| object[] attrs, | |||||
| Tensor[] op_outputs) | |||||
| => (output_grads, unneeded_gradients) => | |||||
| { | |||||
| var gradients = ops.gradientFunctions[op_name](new EagerOperation | |||||
| { | |||||
| Name = op_name, | |||||
| NumInputs = op_inputs.Length, | |||||
| Inputs = op_inputs, | |||||
| NumOutputs = op_outputs.Length, | |||||
| Outputs = op_outputs, | |||||
| SkipInputIndices = unneeded_gradients, | |||||
| Attrs = attrs | |||||
| }, output_grads); | |||||
| return gradients; | |||||
| }; | |||||
| bool CouldForwardprop() | |||||
| { | |||||
| return HasAccumulator(); | |||||
| } | |||||
| bool CouldBackprop() | |||||
| { | |||||
| return HasGradientTape(); | |||||
| } | |||||
| long[] MakeTensorIDList(Tensor[] tensors) | |||||
| { | |||||
| return tensors.Select(x => x.Id).ToArray(); | |||||
| } | |||||
| TF_DataType[] MakeTensorDtypeList(Tensor[] tensors) | |||||
| { | |||||
| return tensors.Select(x => x.dtype).ToArray(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| bool RunCallbacks(FastPathOpExecInfo op_exec_info, | |||||
| int num_inferred_attrs, | |||||
| Tensor[] inputs, | |||||
| object[] attrs, | |||||
| Tensor[] flattened_result) | |||||
| { | |||||
| if (op_exec_info.run_gradient_callback) | |||||
| { | |||||
| if (!RecordGradient(op_exec_info.op_name, inputs, attrs, | |||||
| flattened_result)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (op_exec_info.run_post_exec_callbacks) | |||||
| { | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,61 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System; | |||||
| using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| /// <summary> | |||||
| /// python\eager\pywrap_tfe_src.cc | |||||
| /// </summary> | |||||
| public partial class EagerRunner | |||||
| { | |||||
| public Tensor[] TFE_Execute(Context ctx, | |||||
| string device_name, | |||||
| string op_name, | |||||
| Tensor[] inputs, | |||||
| object[] attrs, | |||||
| int num_outputs) | |||||
| => TFE_ExecuteCancelable(ctx, device_name, op_name, inputs, attrs, num_outputs); | |||||
| public Tensor[] TFE_ExecuteCancelable(Context ctx, | |||||
| string device_name, | |||||
| string op_name, | |||||
| Tensor[] inputs, | |||||
| object[] attrs, | |||||
| int num_outputs) | |||||
| { | |||||
| var status = tf.status; | |||||
| var op = GetOp(ctx, op_name, status); | |||||
| status.Check(true); | |||||
| c_api.TFE_OpSetDevice(op, device_name, status.Handle); | |||||
| if (status.ok()) | |||||
| { | |||||
| for (int i = 0; i < inputs.Length; ++i) | |||||
| { | |||||
| IntPtr tensor_handle; | |||||
| switch (inputs[i]) | |||||
| { | |||||
| case EagerTensor et: | |||||
| tensor_handle = et.EagerTensorHandle; | |||||
| break; | |||||
| default: | |||||
| tensor_handle = c_api.TFE_NewTensorHandle(inputs[i], status.Handle); | |||||
| break; | |||||
| } | |||||
| c_api.TFE_OpAddInput(op, tensor_handle, status.Handle); | |||||
| } | |||||
| } | |||||
| if (status.ok()) | |||||
| SetOpAttrs(op, attrs, status.Handle); | |||||
| var outputs = new IntPtr[num_outputs]; | |||||
| if (status.ok()) | |||||
| { | |||||
| c_api.TFE_Execute(op, outputs, ref num_outputs, status.Handle); | |||||
| status.Check(true); | |||||
| } | |||||
| return outputs.Select(x => new EagerTensor(x)).ToArray(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,321 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System; | |||||
| using static Tensorflow.OpDef.Types; | |||||
| using static Tensorflow.Binding; | |||||
| using Google.Protobuf.WellKnownTypes; | |||||
| using System.Threading; | |||||
| using Tensorflow.Util; | |||||
| using System.Runtime.InteropServices.ComTypes; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| /// <summary> | |||||
| /// python\eager\pywrap_tfe_src.cc | |||||
| /// </summary> | |||||
| public partial class EagerRunner | |||||
| { | |||||
| int kFastPathExecuteInputStartIndex = 0; | |||||
| public Tensor[] TFE_FastPathExecute(Context ctx, | |||||
| string device_name, | |||||
| string opName, | |||||
| string name, | |||||
| Action callbacks, | |||||
| params object[] args) | |||||
| { | |||||
| if (ctx == null) | |||||
| throw new ValueError("This function does not handle the case of the path where " + | |||||
| "all inputs are not already EagerTensors."); | |||||
| int args_size = args.Length; | |||||
| var attr_list_sizes = new Dictionary<string, long>(); | |||||
| FastPathOpExecInfo op_exec_info = new FastPathOpExecInfo() | |||||
| { | |||||
| ctx = ctx, | |||||
| args = args, | |||||
| device_name = device_name, | |||||
| op_name = opName, | |||||
| name = name, | |||||
| }; | |||||
| op_exec_info.run_gradient_callback = HasAccumulatorOrTape(); | |||||
| op_exec_info.run_post_exec_callbacks = callbacks != null; | |||||
| op_exec_info.run_callbacks = op_exec_info.run_gradient_callback || op_exec_info.run_post_exec_callbacks; | |||||
| var status = tf.status; | |||||
| var op = GetOp(ctx, opName, status); | |||||
| var op_def = tf.get_default_graph().GetOpDef(opName); | |||||
| // Set non-inferred attrs, including setting defaults if the attr is passed in | |||||
| // as None. | |||||
| for (int i = kFastPathExecuteInputStartIndex + op_def.InputArg.Count; i < args_size; i += 2) | |||||
| { | |||||
| var attr_name = args[i].ToString(); | |||||
| var attr_value = args[i + 1]; | |||||
| var attr = op_def.Attr.FirstOrDefault(x => x.Name == attr_name); | |||||
| if(attr != null) | |||||
| { | |||||
| SetOpAttrWithDefaults(ctx, op, attr, attr_name, attr_value, attr_list_sizes, status); | |||||
| status.Check(true); | |||||
| } | |||||
| } | |||||
| 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(); | |||||
| c_api.TFE_OpSetDevice(op, device_name, status.Handle); | |||||
| status.Check(true); | |||||
| // Add inferred attrs and inputs. | |||||
| for (int i = 0; i < op_def.InputArg.Count; i++) | |||||
| { | |||||
| var input_arg = op_def.InputArg[i]; | |||||
| if (!string.IsNullOrEmpty(input_arg.NumberAttr)) | |||||
| { | |||||
| int len = (args[kFastPathExecuteInputStartIndex + i] as object[]).Length; | |||||
| c_api.TFE_OpSetAttrInt(op, input_arg.NumberAttr, 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)) | |||||
| 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)) | |||||
| return null; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (!string.IsNullOrEmpty(input_arg.TypeListAttr)) | |||||
| { | |||||
| } | |||||
| else | |||||
| { | |||||
| // The item is a single item. | |||||
| AddInputToOp(args[i], true, input_arg, op, status); | |||||
| } | |||||
| } | |||||
| int num_retvals = 0; | |||||
| for (int i = 0; i < op_def.OutputArg.Count; i++) | |||||
| { | |||||
| var output_arg = op_def.OutputArg[i]; | |||||
| var delta = 1L; | |||||
| if (!string.IsNullOrEmpty(output_arg.NumberAttr)) | |||||
| delta = attr_list_sizes[output_arg.NumberAttr]; | |||||
| else if (!string.IsNullOrEmpty(output_arg.TypeListAttr)) | |||||
| delta = attr_list_sizes[output_arg.TypeListAttr]; | |||||
| if (delta < 0) | |||||
| throw new RuntimeError("Attributes suggest that the size of an output list is less than 0"); | |||||
| num_retvals += (int)delta; | |||||
| } | |||||
| var retVals = new IntPtr[num_retvals]; | |||||
| c_api.TFE_Execute(op, retVals, ref num_retvals, status.Handle); | |||||
| status.Check(true); | |||||
| var flat_result = retVals.Select(x => new EagerTensor(x)).ToArray(); | |||||
| if (op_exec_info.run_callbacks) | |||||
| { | |||||
| if (!RunCallbacks( | |||||
| op_exec_info, | |||||
| kFastPathExecuteInputStartIndex + op_def.InputArg.Count(), | |||||
| flattened_inputs, flattened_attrs, flat_result)) | |||||
| { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| return flat_result; | |||||
| } | |||||
| TFE_Op GetOp(Context ctx, string op_or_function_name, Status status) | |||||
| { | |||||
| if (thread_local_eager_operation_map.find(ctx, out var op)) | |||||
| c_api.TFE_OpReset(op, op_or_function_name, ctx.device_name, status.Handle); | |||||
| else | |||||
| { | |||||
| op = c_api.TFE_NewOp(ctx.Handle, op_or_function_name, status.Handle); | |||||
| thread_local_eager_operation_map[ctx] = op; | |||||
| } | |||||
| status.Check(true); | |||||
| return op; | |||||
| } | |||||
| static UnorderedMap<Context, TFE_Op> thread_local_eager_operation_map = new UnorderedMap<Context, TFE_Op>(); | |||||
| bool HasAccumulator() | |||||
| { | |||||
| //return !GetAccumulatorSet()->empty(); | |||||
| return false; | |||||
| } | |||||
| bool HasGradientTape() | |||||
| { | |||||
| return tf.GetTapeSet().Count > 0; | |||||
| } | |||||
| bool HasAccumulatorOrTape() | |||||
| { | |||||
| return HasGradientTape() || HasAccumulator(); | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds input and type attr to the op, and to the list of flattened | |||||
| /// inputs/attrs. | |||||
| /// </summary> | |||||
| /// <param name="inputs"></param> | |||||
| /// <param name="add_type_attr"></param> | |||||
| /// <param name="input_arg"></param> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="status"></param> | |||||
| /// <returns></returns> | |||||
| bool AddInputToOp(object inputs, | |||||
| bool add_type_attr, | |||||
| ArgDef input_arg, | |||||
| IntPtr op, | |||||
| Status status) | |||||
| { | |||||
| IntPtr input_handle; | |||||
| // ConvertToTensor(); | |||||
| switch (inputs) | |||||
| { | |||||
| case EagerTensor input: | |||||
| input_handle = input.EagerTensorHandle; | |||||
| break; | |||||
| case EagerTensor[] input_list: | |||||
| input_handle = input_list[0].EagerTensorHandle; | |||||
| break; | |||||
| default: | |||||
| var tensor = tf.convert_to_tensor(inputs); | |||||
| input_handle = (tensor as EagerTensor).EagerTensorHandle; | |||||
| break; | |||||
| } | |||||
| if (add_type_attr && !string.IsNullOrEmpty(input_arg.TypeAttr)) | |||||
| { | |||||
| var dtype = c_api.TFE_TensorHandleDataType(input_handle); | |||||
| c_api.TFE_OpSetAttrType(op, input_arg.TypeAttr, dtype); | |||||
| } | |||||
| c_api.TFE_OpAddInput(op, input_handle, status.Handle); | |||||
| status.Check(true); | |||||
| return true; | |||||
| } | |||||
| public void SetOpAttrs(TFE_Op op, params object[] attrs) | |||||
| { | |||||
| var status = tf.status; | |||||
| var len = attrs.Length; | |||||
| for (int i = 0; i < len; i += 2) | |||||
| { | |||||
| var key = attrs[i].ToString(); | |||||
| var value = attrs[i + 1]; | |||||
| byte is_list = 0; | |||||
| var type = c_api.TFE_OpGetAttrType(op, key, ref is_list, status.Handle); | |||||
| if (!status.ok()) return; | |||||
| if (is_list != 0) | |||||
| SetOpAttrList(tf.context, op, key, value, type, null, status); | |||||
| else | |||||
| SetOpAttrScalar(tf.context, op, key, value, type, null, status); | |||||
| status.Check(true); | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// This function will set the op attrs required. If an attr has the value of | |||||
| /// None, then it will read the AttrDef to get the default value and set that | |||||
| /// instead. Any failure in this function will simply fall back to the slow | |||||
| /// path. | |||||
| /// </summary> | |||||
| /// <param name="ctx"></param> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="attr"></param> | |||||
| /// <param name="attr_name"></param> | |||||
| /// <param name="attr_value"></param> | |||||
| /// <param name="attr_list_sizes"></param> | |||||
| /// <param name="status"></param> | |||||
| void SetOpAttrWithDefaults(Context ctx, IntPtr op, AttrDef attr, | |||||
| string attr_name, object attr_value, | |||||
| Dictionary<string, long> attr_list_sizes, | |||||
| Status status) | |||||
| { | |||||
| byte is_list = 0; | |||||
| var type = c_api.TFE_OpGetAttrType(op, attr_name, ref is_list, status.Handle); | |||||
| if (status.Code != TF_Code.TF_OK) return; | |||||
| if(attr_value == null) | |||||
| { | |||||
| if (is_list != 0) | |||||
| ; | |||||
| //SetOpAttrListDefault | |||||
| else | |||||
| ; | |||||
| //SetOpAttrScalarDefault | |||||
| } | |||||
| else | |||||
| { | |||||
| if (is_list != 0) | |||||
| ;// SetOpAttrList | |||||
| else | |||||
| SetOpAttrScalar(ctx, op, attr_name, attr_value, type, attr_list_sizes, status); | |||||
| } | |||||
| } | |||||
| bool SetOpAttrList(Context ctx, IntPtr op, | |||||
| string key, object value, TF_AttrType type, | |||||
| Dictionary<string, long> attr_list_sizes, | |||||
| Status status) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| bool SetOpAttrScalar(Context ctx, IntPtr op, | |||||
| string key, object value, TF_AttrType type, | |||||
| Dictionary<string, long> attr_list_sizes, | |||||
| Status status) | |||||
| { | |||||
| switch(type) | |||||
| { | |||||
| case TF_AttrType.TF_ATTR_STRING: | |||||
| c_api.TFE_OpSetAttrString(op, key, value.ToString(), (uint)value.ToString().Length); | |||||
| break; | |||||
| case TF_AttrType.TF_ATTR_TYPE: | |||||
| c_api.TFE_OpSetAttrType(op, key, (TF_DataType)value); | |||||
| break; | |||||
| case TF_AttrType.TF_ATTR_BOOL: | |||||
| c_api.TFE_OpSetAttrBool(op, key, Convert.ToBoolean(value)); | |||||
| break; | |||||
| case TF_AttrType.TF_ATTR_INT: | |||||
| c_api.TFE_OpSetAttrInt(op, key, Convert.ToInt64(value)); | |||||
| break; | |||||
| case TF_AttrType.TF_ATTR_SHAPE: | |||||
| var dims = (value as int[]).Select(x => (long)x).ToArray(); | |||||
| c_api.TFE_OpSetAttrShape(op, key, dims, dims.Length, status.Handle); | |||||
| status.Check(true); | |||||
| break; | |||||
| default: | |||||
| throw new NotImplementedException($"SetOpAttrScalar for {type}"); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,53 @@ | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System; | |||||
| using static Tensorflow.OpDef.Types; | |||||
| using Tensorflow.Gradients; | |||||
| using Tensorflow.Util; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| /// <summary> | |||||
| /// python\eager\pywrap_tfe_src.cc | |||||
| /// </summary> | |||||
| public partial class EagerRunner | |||||
| { | |||||
| public Tensor[] TFE_TapeGradient(ITape tape, | |||||
| Tensor[] target, | |||||
| Tensor[] sources, | |||||
| Tensor[] output_gradients) | |||||
| { | |||||
| var target_vec = MakeTensorIDList(target); | |||||
| var sources_vec = MakeTensorIDList(sources); | |||||
| var sources_set = sources_vec; | |||||
| var seq_array = target; | |||||
| var source_tensors_that_are_targets = new UnorderedMap<long, TapeTensor>(); | |||||
| for (int i = 0; i < target.Length; ++i) | |||||
| { | |||||
| var target_id = target_vec[i]; | |||||
| var tensor = seq_array[i]; | |||||
| source_tensors_that_are_targets.Add(target_id, TapeTensorFromTensor(tensor)); | |||||
| } | |||||
| if(output_gradients != null) | |||||
| { | |||||
| throw new NotImplementedException(""); | |||||
| } | |||||
| else | |||||
| { | |||||
| output_gradients = new Tensor[0]; | |||||
| } | |||||
| var outgrad_vec = MakeTensorList(output_gradients); | |||||
| return tape.ComputeGradient(target_vec, sources_vec, source_tensors_that_are_targets, outgrad_vec); | |||||
| } | |||||
| Tensor[] MakeTensorList(Tensor[] tensors) | |||||
| { | |||||
| return tensors; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| using static Tensorflow.Binding; | |||||
| using static Tensorflow.tensorflow; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| void TapeSetRecordBackprop(string op_type, | |||||
| Tensor[] input_tensors, | |||||
| TapeTensor[] output_tensors, | |||||
| long[] input_ids, | |||||
| TF_DataType[] input_dtypes, | |||||
| Func<BackwardFunction> backward_function_getter) | |||||
| { | |||||
| if (!CouldBackprop()) | |||||
| { | |||||
| return; | |||||
| } | |||||
| foreach(var tape in tf.GetTapeSet()) | |||||
| { | |||||
| tape.RecordOperation(op_type, input_tensors, output_tensors, | |||||
| input_ids, input_dtypes, | |||||
| backward_function_getter); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| using static Tensorflow.tensorflow; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| bool TapeSetRecordForwardprop(string op_type, | |||||
| Tensor[] input_tensors, | |||||
| TapeTensor[] output_tensors, | |||||
| long[] input_ids, | |||||
| TF_DataType[] input_dtypes, | |||||
| Func<BackwardFunction> backward_function_getter) | |||||
| { | |||||
| if (!CouldForwardprop()) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| throw new NotImplementedException(""); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,34 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| using static Tensorflow.tensorflow; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| bool TapeSetRecordOperation(string op_type, | |||||
| Tensor[] input_tensors, | |||||
| Tensor[] output_tensors, | |||||
| long[] input_ids, | |||||
| TF_DataType[] input_dtypes, | |||||
| Func<BackwardFunction> backward_function_getter) | |||||
| { | |||||
| var output_info = new List<TapeTensor>(); | |||||
| if (!TapeTensorsFromTensorSequence(output_tensors, output_info)) | |||||
| return false; | |||||
| if (!TapeSetRecordForwardprop(op_type, input_tensors, output_info.ToArray(), | |||||
| input_ids, input_dtypes, backward_function_getter)) | |||||
| return false; | |||||
| TapeSetRecordBackprop(op_type, input_tensors, output_info.ToArray(), | |||||
| input_ids, input_dtypes, backward_function_getter); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,16 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| TapeTensor TapeTensorFromTensor(Tensor tensor) | |||||
| { | |||||
| return new TapeTensor(tensor.Id, tensor.dtype, tensor.shape); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using Tensorflow.Gradients; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| bool TapeTensorsFromTensorSequence(Tensor[] output_seq, | |||||
| List<TapeTensor> output_info) | |||||
| { | |||||
| for (var i = 0; i < output_seq.Length; ++i) | |||||
| { | |||||
| output_info.Add(TapeTensorFromTensor(output_seq[i])); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -2,24 +2,15 @@ | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Text; | using System.Text; | ||||
| using Tensorflow.Gradients; | using Tensorflow.Gradients; | ||||
| using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Eager | namespace Tensorflow.Eager | ||||
| { | { | ||||
| public class EagerRunner : IEagerRunner | |||||
| /// <summary> | |||||
| /// Eager mode runner | |||||
| /// </summary> | |||||
| public partial class EagerRunner : IEagerRunner | |||||
| { | { | ||||
| public Tensor[] TFE_Execute(Context ctx, string device_name, string op_name, Tensor[] inputs, object[] attrs, int num_outputs) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| public Tensor[] TFE_FastPathExecute(Context ctx, string device_name, string opName, string name, Action callbacks, params object[] args) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| public Tensor[] TFE_TapeGradient(ITape tape, Tensor[] target, Tensor[] sources, Tensor[] output_gradients) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||