diff --git a/src/TensorFlowNET.Core/Eager/Context.cs b/src/TensorFlowNET.Core/Eager/Context.cs index 7f428229..ca01361d 100644 --- a/src/TensorFlowNET.Core/Eager/Context.cs +++ b/src/TensorFlowNET.Core/Eager/Context.cs @@ -9,6 +9,7 @@ namespace Tensorflow.Eager public int default_execution_mode; public string device_name = ""; + public string scope_name = ""; bool _initialized = false; public Context(ContextOptions opts, Status status) @@ -33,6 +34,11 @@ namespace Tensorflow.Eager public bool executing_eagerly() => true; + public string shared_name(string name = null) + => !string.IsNullOrEmpty(name) || !executing_eagerly() ? + name : + "cd2c89b7-88b7-44c8-ad83-06c2a9158347"; + public static implicit operator IntPtr(Context ctx) => ctx._handle; diff --git a/src/TensorFlowNET.Core/Eager/TFE_Op.cs b/src/TensorFlowNET.Core/Eager/TFE_Op.cs index 538ada78..e364f853 100644 --- a/src/TensorFlowNET.Core/Eager/TFE_Op.cs +++ b/src/TensorFlowNET.Core/Eager/TFE_Op.cs @@ -18,6 +18,6 @@ namespace Tensorflow.Eager => tensor._handle; public override string ToString() - => $"TFE_Op {_handle}"; + => $"TFE_Op 0x{_handle.ToString("x16")}"; } } diff --git a/src/TensorFlowNET.Core/Eager/TFE_TensorHandle.cs b/src/TensorFlowNET.Core/Eager/TFE_TensorHandle.cs index a0c59259..685de184 100644 --- a/src/TensorFlowNET.Core/Eager/TFE_TensorHandle.cs +++ b/src/TensorFlowNET.Core/Eager/TFE_TensorHandle.cs @@ -18,6 +18,6 @@ namespace Tensorflow.Eager => tensor._handle; public override string ToString() - => $"TFE_TensorHandle {_handle}"; + => $"TFE_TensorHandle 0x{_handle.ToString("x16")}"; } } diff --git a/src/TensorFlowNET.Core/Eager/c_api.eager.cs b/src/TensorFlowNET.Core/Eager/c_api.eager.cs index a241ee0f..a58a14d0 100644 --- a/src/TensorFlowNET.Core/Eager/c_api.eager.cs +++ b/src/TensorFlowNET.Core/Eager/c_api.eager.cs @@ -89,7 +89,7 @@ namespace Tensorflow /// int* /// TF_Status* [DllImport(TensorFlowLibName)] - public static extern void TFE_Execute(TFE_Op op, IntPtr[] retvals, ref int num_retvals, IntPtr status); + public static extern void TFE_Execute(IntPtr op, IntPtr[] retvals, ref int num_retvals, IntPtr status); /// /// diff --git a/src/TensorFlowNET.Core/Eager/wrap_tfe_src.TFE_FastPathExecute.cs b/src/TensorFlowNET.Core/Eager/wrap_tfe_src.TFE_FastPathExecute.cs index c66aaeef..39c7bd9f 100644 --- a/src/TensorFlowNET.Core/Eager/wrap_tfe_src.TFE_FastPathExecute.cs +++ b/src/TensorFlowNET.Core/Eager/wrap_tfe_src.TFE_FastPathExecute.cs @@ -147,6 +147,7 @@ namespace Tensorflow.Eager c_api.TFE_OpAddInput(op, input_handle, status); status.Check(true); + return true; } @@ -236,8 +237,13 @@ namespace Tensorflow.Eager 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); + status.Check(true); + break; default: - throw new NotImplementedException(""); + throw new NotImplementedException($"SetOpAttrScalar for {type}"); } return true; diff --git a/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs b/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs index 664572a5..297f29b0 100644 --- a/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_resource_variable_ops.cs @@ -14,6 +14,9 @@ limitations under the License. ******************************************************************************/ +using Tensorflow.Eager; +using static Tensorflow.Binding; + namespace Tensorflow { public static class gen_resource_variable_ops @@ -29,6 +32,14 @@ namespace Tensorflow public static Tensor var_is_initialized_op(Tensor resource, string name = null) { + if (tf.context.executing_eagerly()) + { + var _result = wrap_tfe_src.TFE_FastPathExecute(tf.context, tf.context.device_name, + "VarIsInitializedOp", name, null, + resource); + return _result; + } + var _op = _op_def_lib._apply_op_helper("VarIsInitializedOp", name, new { resource }); return _op.output; @@ -46,6 +57,14 @@ namespace Tensorflow public static Tensor var_handle_op(TF_DataType dtype, TensorShape shape, string container ="", string shared_name = "", string name = null) { + if (tf.context.executing_eagerly()) + { + var _result = wrap_tfe_src.TFE_FastPathExecute(tf.context, tf.context.device_name, + "VarHandleOp", name, null, + "container", container, "shared_name", shared_name, "dtype", dtype, "shape", shape.dims); + return _result; + } + var _op = _op_def_lib._apply_op_helper("VarHandleOp", name, new { dtype, shape, diff --git a/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs b/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs index f591402e..2fb9f8cd 100644 --- a/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs +++ b/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs @@ -100,10 +100,10 @@ namespace Tensorflow /// /// /// - /// + /// /// public static Tensor variable_handle_from_shape_and_dtype(TensorShape shape, TF_DataType dtype, - string shared_name, string name, bool graph_mode, Tensor extra_handle_data = null) + string shared_name, string name, bool graph_mode, Tensor initial_value = null) { var container = "";// ops.get_default_graph().container; var handle = gen_resource_variable_ops.var_handle_op(shape: shape, @@ -112,17 +112,22 @@ namespace Tensorflow name: name, container: container); - if (extra_handle_data == null) - extra_handle_data = handle; + if (initial_value == null) + initial_value = handle; if (graph_mode) { - var full_handle_data = _combine_handle_data(handle, extra_handle_data); + var full_handle_data = _combine_handle_data(handle, initial_value); _set_handle_shapes_and_types(handle, full_handle_data, graph_mode); return handle; } else { + // We do not want two distinct ResourceVariable objects for the same + // underlying resource in the runtime. + // When in eager mode, explicitly ensure so here. When in graph mode, it's + // ensured by always generating different variable names. + var exists = gen_resource_variable_ops.var_is_initialized_op(handle); throw new NotImplementedException(""); } } diff --git a/src/TensorFlowNET.Core/Status/Status.cs b/src/TensorFlowNET.Core/Status/Status.cs index 1e2e222c..90597195 100644 --- a/src/TensorFlowNET.Core/Status/Status.cs +++ b/src/TensorFlowNET.Core/Status/Status.cs @@ -71,5 +71,8 @@ namespace Tensorflow protected override void DisposeUnmanagedResources(IntPtr handle) => TF_DeleteStatus(handle); + + public override string ToString() + => $"{Code} 0x{_handle.ToString("x16")}"; } } \ No newline at end of file diff --git a/src/TensorFlowNET.Core/Summaries/Summary.cs b/src/TensorFlowNET.Core/Summaries/Summary.cs index 56772087..39a8c10a 100644 --- a/src/TensorFlowNET.Core/Summaries/Summary.cs +++ b/src/TensorFlowNET.Core/Summaries/Summary.cs @@ -90,13 +90,13 @@ namespace Tensorflow.Summaries string scope_base_name = string.IsNullOrEmpty(family) ? name : $"{family}/{name}"; return tf_with(ops.name_scope(scope_base_name, default_name: default_name, values), scope => { - var tag = scope._name_scope; + var tag = scope.scope_name; if (string.IsNullOrEmpty(family)) tag = tag.Remove(tag.Length - 1); else tag = $"{family}/{tag.Remove(tag.Length - 1)}"; - return (tag, scope._name_scope); + return (tag, scope.scope_name); }); } } diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Value.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Value.cs index 2491eb30..81866bc3 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Value.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Value.cs @@ -181,6 +181,8 @@ namespace Tensorflow return (NDArray)StringData()[0]; case TF_DataType.TF_INT32: return *(int*)buffer; + case TF_DataType.TF_FLOAT: + return *(float*)buffer; case TF_DataType.TF_DOUBLE: return *(double*)buffer; default: diff --git a/src/TensorFlowNET.Core/Tensors/TensorShape.cs b/src/TensorFlowNET.Core/Tensors/TensorShape.cs index 45e76526..1061d4b6 100644 --- a/src/TensorFlowNET.Core/Tensors/TensorShape.cs +++ b/src/TensorFlowNET.Core/Tensors/TensorShape.cs @@ -253,7 +253,7 @@ namespace Tensorflow public static implicit operator Shape(TensorShape shape) => new Shape((int[]) shape.dims.Clone()); public static implicit operator int[](TensorShape shape) => shape == null ? null : (int[])shape.dims.Clone(); //we clone to avoid any changes - public static implicit operator TensorShape(int[] dims) => new TensorShape(dims); + public static implicit operator TensorShape(int[] dims) => dims == null ? new TensorShape(new int[0]) : new TensorShape(dims); public static explicit operator int(TensorShape shape) => shape.size; public static implicit operator TensorShape(int dim) => new TensorShape(dim); diff --git a/src/TensorFlowNET.Core/Tensors/constant_op.cs b/src/TensorFlowNET.Core/Tensors/constant_op.cs index 7b2d9d21..60ee634e 100644 --- a/src/TensorFlowNET.Core/Tensors/constant_op.cs +++ b/src/TensorFlowNET.Core/Tensors/constant_op.cs @@ -91,6 +91,10 @@ namespace Tensorflow return new EagerTensor(str, ctx.device_name); case int int32: return new EagerTensor(int32, ctx.device_name); + case float float32: + return new EagerTensor(float32, ctx.device_name); + case double double64: + return new EagerTensor(double64, ctx.device_name); case float[] float32s: return new EagerTensor(float32s, ctx.device_name); case double[] double64s: diff --git a/src/TensorFlowNET.Core/Variables/ResourceVariable.cs b/src/TensorFlowNET.Core/Variables/ResourceVariable.cs index 1209e442..34dd996e 100644 --- a/src/TensorFlowNET.Core/Variables/ResourceVariable.cs +++ b/src/TensorFlowNET.Core/Variables/ResourceVariable.cs @@ -94,20 +94,28 @@ namespace Tensorflow if(collections == null) collections = new List() { tf.GraphKeys.GLOBAL_VARIABLES }; _trainable = trainable; - _graph_key = ops.get_default_graph().graph_key; + + if (trainable && !collections.Contains(tf.GraphKeys.TRAINABLE_VARIABLES)) + collections.Add(tf.GraphKeys.TRAINABLE_VARIABLES); ops.init_scope(); - _in_graph_mode = true; + _in_graph_mode = !tf.context.executing_eagerly(); tf_with(ops.name_scope(name, "Variable"), scope => { name = scope; var handle_name = ops.name_from_scope_name(name); - var shared_name = handle_name; - var unique_id = shared_name; + var unique_id = $"{handle_name}_{ops.uid()}"; + var shared_name = tf.context.shared_name(); + + if (_in_graph_mode) + { + shared_name = handle_name; + unique_id = shared_name; + } var attr = new AttrValue(); attr.List = new AttrValue.Types.ListValue(); - attr.List.S.Add(ByteString.CopyFromUtf8($"loc:{handle_name}")); + attr.List.S.Add(ByteString.CopyFromUtf8($"loc:@{handle_name}")); tf_with(ops.name_scope("Initializer"), delegate { initial_value = ops.convert_to_tensor(init_from_fn ? (initial_value as Func)() : initial_value, diff --git a/src/TensorFlowNET.Core/ops.cs b/src/TensorFlowNET.Core/ops.cs index 2fe5ef4e..f73278c4 100644 --- a/src/TensorFlowNET.Core/ops.cs +++ b/src/TensorFlowNET.Core/ops.cs @@ -241,6 +241,9 @@ namespace Tensorflow /// public static void init_scope() { + if (tf.context.executing_eagerly()) + return; + // Retrieve the active name scope: entering an `init_scope` preserves // the name scope of the current context. var default_graph = get_default_graph(); diff --git a/src/TensorFlowNET.Core/ops.name_scope.cs b/src/TensorFlowNET.Core/ops.name_scope.cs index 32befbcd..a6e3786b 100644 --- a/src/TensorFlowNET.Core/ops.name_scope.cs +++ b/src/TensorFlowNET.Core/ops.name_scope.cs @@ -15,6 +15,8 @@ ******************************************************************************/ using System.Collections.Generic; +using Tensorflow.Eager; +using static Tensorflow.Binding; namespace Tensorflow { @@ -32,8 +34,8 @@ namespace Tensorflow public string _name; public string _default_name; public object _values; - public string _name_scope; - public string old_stack = ""; + public string scope_name; + public string old_scope_name = ""; public NameScope(string name, string default_name = "", object values = null) { @@ -44,29 +46,54 @@ namespace Tensorflow public void __enter__() { - _name = _name ?? _default_name; - if (_name.EndsWith("basic_r_n_n_cell")) + if (tf.context.executing_eagerly()) { + (scope_name, old_scope_name) = enter_eager_name_scope(tf.context, _name); + } + else + { + _name = _name ?? _default_name; + Graph g = null; + + if (_values is List vList) + g = _get_graph_from_inputs(vList.ToArray()); + else if (_values is Tensor[] vArray) + g = _get_graph_from_inputs(vArray); + + if (g == null) + g = get_default_graph(); + old_scope_name = g._name_stack; + scope_name = g.name_scope(_name); } - Graph g = null; + } - if (_values is List vList) - g = _get_graph_from_inputs(vList.ToArray()); - else if (_values is Tensor[] vArray) - g = _get_graph_from_inputs(vArray); + private (string, string) enter_eager_name_scope(Context ctx, string name) + { + if (name == null) + name = ""; - if (g == null) - g = get_default_graph(); + var scope_name = name; + var old_name = ctx.scope_name; + // A trailing slash breaks out of nested name scopes, indicating a + // fully specified scope name, for compatibility with Graph.name_scope. + if (!name.EndsWith("/")) + { + scope_name = name + "/"; + if (!string.IsNullOrEmpty(old_name)) + scope_name = old_name + scope_name; + } - old_stack = g._name_stack; - _name_scope = g.name_scope(_name); + ctx.scope_name = scope_name; + return (scope_name, old_name); } public void Dispose() { - var g = get_default_graph(); - g._name_stack = old_stack; + if (tf.context.executing_eagerly()) + tf.context.scope_name = old_scope_name; + else + get_default_graph()._name_stack = old_scope_name; } public void __exit__() @@ -89,7 +116,7 @@ namespace Tensorflow /// public static implicit operator string(NameScope ns) { - return ns._name_scope; + return ns.scope_name; } } }