| @@ -1,6 +1,7 @@ | |||
| using NumSharp.Core; | |||
| using System; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.ComponentModel; | |||
| using System.Dynamic; | |||
| using System.IO; | |||
| using System.Runtime.InteropServices; | |||
| using System.Text; | |||
| @@ -10,8 +11,9 @@ namespace Tensorflow | |||
| { | |||
| public class OpDefLibrary | |||
| { | |||
| public Operation _apply_op_helper(string op_type_name, string name = "", Dictionary<string, object> keywords = null) | |||
| public Operation _apply_op_helper(string op_type_name, string name = "", dynamic args = null) | |||
| { | |||
| var keywords = ConvertToDict(args); | |||
| var g = ops.get_default_graph(); | |||
| var op_def = g.GetOpDef(op_type_name); | |||
| @@ -20,9 +22,9 @@ namespace Tensorflow | |||
| name = op_type_name; | |||
| // Check for deprecation | |||
| if(op_def.Deprecation != null && op_def.Deprecation.Version > 0) | |||
| if (op_def.Deprecation != null && op_def.Deprecation.Version > 0) | |||
| { | |||
| } | |||
| var default_type_attr_map = new Dictionary<string, object>(); | |||
| @@ -30,7 +32,7 @@ namespace Tensorflow | |||
| { | |||
| if (attr_def.Type != "type") continue; | |||
| var key = attr_def.Name; | |||
| if(attr_def.DefaultValue != null) | |||
| if (attr_def.DefaultValue != null) | |||
| { | |||
| default_type_attr_map[key] = attr_def.DefaultValue.Type; | |||
| } | |||
| @@ -40,11 +42,9 @@ namespace Tensorflow | |||
| var inputs = new List<Tensor>(); | |||
| var input_types = new List<TF_DataType>(); | |||
| string scope = ""; | |||
| using (var namescope = new ops.name_scope(name)) | |||
| Operation op = null; | |||
| Python.with<ops.name_scope>(new ops.name_scope(name), scope => | |||
| { | |||
| scope = namescope; | |||
| // Perform input type inference | |||
| foreach (var input_arg in op_def.InputArg) | |||
| { | |||
| @@ -73,7 +73,7 @@ namespace Tensorflow | |||
| else | |||
| { | |||
| var base_type = value.dtype.as_base_dtype(); | |||
| input_types.Add(base_type); | |||
| } | |||
| } | |||
| @@ -135,19 +135,34 @@ namespace Tensorflow | |||
| } | |||
| // Add Op to graph | |||
| var op = g.create_op(op_type_name, inputs, output_types.ToArray(), | |||
| op = g.create_op(op_type_name, inputs, output_types.ToArray(), | |||
| name: scope, | |||
| input_types: input_types.ToArray(), | |||
| attrs: attr_protos, | |||
| op_def: op_def); | |||
| }); | |||
| return op; | |||
| } | |||
| return op; | |||
| } | |||
| public DataType _MakeType(TF_DataType v, AttrDef attr_def) | |||
| { | |||
| return v.as_base_dtype().as_datatype_enum(); | |||
| } | |||
| private Dictionary<string, object> ConvertToDict(dynamic dyn) | |||
| { | |||
| var dictionary = new Dictionary<string, object>(); | |||
| foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(dyn)) | |||
| { | |||
| object obj = propertyDescriptor.GetValue(dyn); | |||
| string name = propertyDescriptor.Name; | |||
| // avoid .net keyword | |||
| if (name == "_ref_") | |||
| name = "ref"; | |||
| dictionary.Add(name, obj); | |||
| } | |||
| return dictionary; | |||
| } | |||
| } | |||
| } | |||
| @@ -47,9 +47,9 @@ namespace Tensorflow | |||
| } | |||
| else | |||
| { | |||
| tShape = constant_op._tensor_shape_tensor_conversion_function(shape.as_shape(), dtype, name); | |||
| var c = constant_op.Constant(nd, name); | |||
| return gen_array_ops.fill<T>(shape, value, name); | |||
| tShape = constant_op._tensor_shape_tensor_conversion_function(shape.as_shape()); | |||
| var c = constant_op.Constant(0); | |||
| return gen_array_ops.fill(tShape, c, name); | |||
| } | |||
| } | |||
| } | |||
| @@ -14,11 +14,7 @@ namespace Tensorflow | |||
| public static Tensor placeholder(TF_DataType dtype, TensorShape shape = null, string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("dtype", dtype); | |||
| keywords.Add("shape", shape); | |||
| var _op = _op_def_lib._apply_op_helper("Placeholder", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Placeholder", args: new { dtype, shape }); | |||
| var _result = _op.outputs; | |||
| var _inputs_flat = _op.inputs; | |||
| @@ -38,20 +34,14 @@ namespace Tensorflow | |||
| /// <param name="name"></param> | |||
| public static Tensor identity(Tensor input, string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("input", input); | |||
| var _op = _op_def_lib._apply_op_helper("Identity", name, keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Identity", name, new { input }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor rank(Tensor input, string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("input", input); | |||
| var _op = _op_def_lib._apply_op_helper("Rank", name: name, keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Rank", name: name, args: new { input }); | |||
| return _op.outputs[0]; | |||
| } | |||
| @@ -59,18 +49,13 @@ namespace Tensorflow | |||
| /// <summary> | |||
| /// Creates a tensor filled with a scalar value. | |||
| /// </summary> | |||
| /// <typeparam name="T"></typeparam> | |||
| /// <param name="dims"></param> | |||
| /// <param name="value"></param> | |||
| /// <param name="name"></param> | |||
| /// <returns></returns> | |||
| public static Tensor fill<T>(int[] dims, T value, string name = "") | |||
| /// <param name="dims">A `Tensor`.</param> | |||
| /// <param name="value">A `Tensor`.</param> | |||
| /// <param name="name">A name for the operation (optional).</param> | |||
| /// <returns>A `Tensor`. Has the same type as `value`.</returns> | |||
| public static Tensor fill(Tensor dims, Tensor value, string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("dims", dims); | |||
| keywords.Add("value", value); | |||
| var _op = _op_def_lib._apply_op_helper("Fill", name); | |||
| var _op = _op_def_lib._apply_op_helper("Fill", name, new { dims, value }); | |||
| return _op.outputs[0]; | |||
| } | |||
| @@ -10,7 +10,7 @@ namespace Tensorflow | |||
| public static Operation no_op(string name = "") | |||
| { | |||
| var _op = _op_def_lib._apply_op_helper("NoOp", name); | |||
| var _op = _op_def_lib._apply_op_helper("NoOp", name, null); | |||
| return _op; | |||
| } | |||
| @@ -12,80 +12,49 @@ namespace Tensorflow | |||
| public static Tensor add(Tensor x, Tensor y) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("x", x); | |||
| keywords.Add("y", y); | |||
| var _op = _op_def_lib._apply_op_helper("Add", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Add", args: new { x, y }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor sub(Tensor x, Tensor y) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("x", x); | |||
| keywords.Add("y", y); | |||
| var _op = _op_def_lib._apply_op_helper("Sub", name: "sub", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Sub", name: "sub", args: new { x, y }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor mul(Tensor x, Tensor y) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("x", x); | |||
| keywords.Add("y", y); | |||
| var _op = _op_def_lib._apply_op_helper("Mul", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Mul", args: new { x, y }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor real_div(Tensor x, Tensor y) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("x", x); | |||
| keywords.Add("y", y); | |||
| var _op = _op_def_lib._apply_op_helper("RealDiv", name: "truediv", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("RealDiv", name: "truediv", args: new { x, y }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor mat_mul(Tensor a, Tensor b, bool transpose_a = false, bool transpose_b = false) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("a", a); | |||
| keywords.Add("b", b); | |||
| keywords.Add("transpose_a", transpose_a); | |||
| keywords.Add("transpose_b", transpose_b); | |||
| var _op = _op_def_lib._apply_op_helper("MatMul", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("MatMul", args: new { a, b, transpose_a, transpose_b }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor pow(Tensor x, double y) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("x", x); | |||
| keywords.Add("y", y); | |||
| var _op = _op_def_lib._apply_op_helper("Pow", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Pow", args: new { x, y }); | |||
| return _op.outputs[0]; | |||
| } | |||
| public static Tensor sum(Tensor input, Tensor axis = null) | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("input", input); | |||
| keywords.Add("reduction_indices", axis); | |||
| keywords.Add("keep_dims", false); | |||
| var _op = _op_def_lib._apply_op_helper("Sum", keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Sum", args: new { input, reduction_indices = axis, keep_dims = false }); | |||
| return _op.outputs[0]; | |||
| } | |||
| @@ -100,12 +69,7 @@ namespace Tensorflow | |||
| /// <returns></returns> | |||
| public static Tensor range(Tensor start, Tensor limit, Tensor delta, string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("start", start); | |||
| keywords.Add("limit", limit); | |||
| keywords.Add("delta", delta); | |||
| var _op = _op_def_lib._apply_op_helper("Range", name, keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Range", name, new { start, limit, delta }); | |||
| return _op.outputs[0]; | |||
| } | |||
| @@ -23,6 +23,26 @@ namespace Tensorflow | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| Console.WriteLine(ex.ToString()); | |||
| throw ex; | |||
| } | |||
| finally | |||
| { | |||
| py.__exit__(); | |||
| py.Dispose(); | |||
| } | |||
| } | |||
| public static void with<T>(IPython py, Action<T> action) where T : IPython | |||
| { | |||
| try | |||
| { | |||
| py.__enter__(); | |||
| action((T)py); | |||
| } | |||
| catch (Exception ex) | |||
| { | |||
| Console.WriteLine(ex.ToString()); | |||
| throw ex; | |||
| } | |||
| finally | |||
| @@ -125,57 +125,47 @@ namespace Tensorflow | |||
| for (int i = 0; i < fetch_list.Length; i++) | |||
| { | |||
| var tensor = new Tensor(output_values[i]); | |||
| switch (tensor.dtype) | |||
| { | |||
| case TF_DataType.TF_STRING: | |||
| { | |||
| // wired, don't know why we have to start from offset 9. | |||
| var bytes = tensor.Data(); | |||
| var output = UTF8Encoding.Default.GetString(bytes, 9, bytes.Length - 9); | |||
| result[i] = fetchValue(tensor, output); | |||
| } | |||
| break; | |||
| case TF_DataType.TF_FLOAT: | |||
| { | |||
| var output = *(float*)c_api.TF_TensorData(output_values[i]); | |||
| result[i] = fetchValue(tensor, output); | |||
| } | |||
| break; | |||
| case TF_DataType.TF_INT16: | |||
| { | |||
| var output = *(short*)c_api.TF_TensorData(output_values[i]); | |||
| result[i] = fetchValue(tensor, output); | |||
| } | |||
| break; | |||
| case TF_DataType.TF_INT32: | |||
| { | |||
| var output = *(int*)c_api.TF_TensorData(output_values[i]); | |||
| result[i] = fetchValue(tensor, output); | |||
| } | |||
| break; | |||
| default: | |||
| throw new NotImplementedException("can't get output"); | |||
| } | |||
| result[i] = fetchValue(output_values[i]); | |||
| } | |||
| return result; | |||
| } | |||
| private NDArray fetchValue<T>(Tensor tensor, T output) | |||
| private unsafe NDArray fetchValue(IntPtr output) | |||
| { | |||
| NDArray nd; | |||
| var tensor = new Tensor(output); | |||
| NDArray nd = null; | |||
| Type type = tensor.dtype.as_numpy_datatype(); | |||
| var ndims = tensor.shape.Select(x => (int)x).ToArray(); | |||
| if (tensor.NDims == 0) | |||
| { | |||
| nd = np.array(output).reshape(); | |||
| } | |||
| else | |||
| switch (tensor.dtype) | |||
| { | |||
| nd = np.array(output).reshape(ndims); | |||
| case TF_DataType.TF_STRING: | |||
| var bytes = tensor.Data(); | |||
| // wired, don't know why we have to start from offset 9. | |||
| var str = UTF8Encoding.Default.GetString(bytes, 9, bytes.Length - 9); | |||
| nd = np.array(str).reshape(); | |||
| break; | |||
| case TF_DataType.TF_INT32: | |||
| var ints = new int[tensor.size]; | |||
| for (ulong i = 0; i < tensor.size; i++) | |||
| ints[i] = *(int*)(c_api.TF_TensorData(output) + (int)(tensor.dataTypeSize * i)); | |||
| nd = np.array(ints).reshape(ndims); | |||
| break; | |||
| case TF_DataType.TF_FLOAT: | |||
| var floats = new float[tensor.size]; | |||
| for (ulong i = 0; i < tensor.size; i++) | |||
| floats[i] = *(float*)(c_api.TF_TensorData(output) + (int)(tensor.dataTypeSize * i)); | |||
| nd = np.array(floats).reshape(ndims); | |||
| break; | |||
| case TF_DataType.TF_DOUBLE: | |||
| var doubles = new double[tensor.size]; | |||
| for (ulong i = 0; i < tensor.size; i++) | |||
| doubles[i] = *(double*)(c_api.TF_TensorData(output) + (int)(tensor.dataTypeSize * i)); | |||
| nd = np.array(doubles).reshape(ndims); | |||
| break; | |||
| default: | |||
| throw new NotImplementedException("can't fetch output"); | |||
| } | |||
| return nd; | |||
| @@ -4,7 +4,7 @@ using System.Text; | |||
| namespace Tensorflow | |||
| { | |||
| public class Session : BaseSession, IDisposable | |||
| public class Session : BaseSession, IPython | |||
| { | |||
| private IntPtr _handle; | |||
| public Status Status { get; } | |||
| @@ -41,5 +41,15 @@ namespace Tensorflow | |||
| Status.Dispose(); | |||
| c_api.TF_DeleteSession(_handle, Status); | |||
| } | |||
| public void __enter__() | |||
| { | |||
| } | |||
| public void __exit__() | |||
| { | |||
| } | |||
| } | |||
| } | |||
| @@ -65,10 +65,8 @@ namespace Tensorflow | |||
| ops.init_scope(); | |||
| var values = init_from_fn ? new List<object>() : new List<object> { initial_value }; | |||
| using (var namescope = new ops.name_scope(name, "Variable", values)) | |||
| Python.with<ops.name_scope>(new ops.name_scope(name, "Variable", values), scope => | |||
| { | |||
| name = namescope; | |||
| if (init_from_fn) | |||
| { | |||
| @@ -108,7 +106,7 @@ namespace Tensorflow | |||
| } | |||
| ops.add_to_collections(collections, this); | |||
| } | |||
| }); | |||
| } | |||
| public Tensor _ref() | |||
| @@ -23,13 +23,7 @@ namespace Tensorflow | |||
| /// <returns></returns> | |||
| public static Tensor variable_v2(long[] shape, TF_DataType dtype, string name = "", string container = "", string shared_name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("dtype", dtype); | |||
| keywords.Add("shape", shape); | |||
| keywords.Add("container", container); | |||
| keywords.Add("shared_name", shared_name); | |||
| var _op = _op_def_lib._apply_op_helper("VariableV2", name: name, keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("VariableV2", name: name, args: new { dtype, shape, container, shared_name }); | |||
| var _result = _op.outputs; | |||
| var _inputs_flat = _op.inputs; | |||
| @@ -58,13 +52,7 @@ namespace Tensorflow | |||
| bool use_locking = true, | |||
| string name = "") | |||
| { | |||
| var keywords = new Dictionary<string, object>(); | |||
| keywords.Add("ref", tensor); | |||
| keywords.Add("value", value); | |||
| keywords.Add("validate_shape", validate_shape); | |||
| keywords.Add("use_locking", use_locking); | |||
| var _op = _op_def_lib._apply_op_helper("Assign", name: name, keywords: keywords); | |||
| var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { _ref_ = tensor, value, validate_shape, use_locking }); | |||
| var _result = _op.outputs; | |||
| var _inputs_flat = _op.inputs; | |||
| @@ -24,18 +24,42 @@ namespace TensorFlowNET.UnitTest | |||
| [TestMethod] | |||
| public void StringConst() | |||
| { | |||
| tensor = tf.constant("Elephant"); | |||
| string str = "Hello, TensorFlow.NET!"; | |||
| tensor = tf.constant(str); | |||
| Python.with<Session>(tf.Session(), sess => | |||
| { | |||
| var result = sess.run(tensor); | |||
| Assert.IsTrue(result.Data<string>()[0] == str); | |||
| }); | |||
| } | |||
| [TestMethod] | |||
| public void ZerosConst() | |||
| { | |||
| tensor = tf.zeros(new Shape(3, 2), TF_DataType.TF_INT32, "x"); | |||
| Assert.AreEqual(tensor.shape[0], 3); | |||
| Assert.AreEqual(tensor.shape[0], 2); | |||
| Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 0, 0, 0 }, tensor.Data<int>())); | |||
| // small size | |||
| tensor = tf.zeros(new Shape(3, 2), TF_DataType.TF_INT32, "small"); | |||
| Python.with<Session>(tf.Session(), sess => | |||
| { | |||
| var result = sess.run(tensor); | |||
| Assert.AreEqual(result.shape[0], 3); | |||
| Assert.AreEqual(result.shape[1], 2); | |||
| Assert.IsTrue(Enumerable.SequenceEqual(new int[] { 0, 0, 0, 0, 0, 0 }, result.Data<int>())); | |||
| }); | |||
| tensor = tf.zeros(new Shape(200, 300), TF_DataType.TF_INT32, "x"); | |||
| // big size | |||
| tensor = tf.zeros(new Shape(200, 100), TF_DataType.TF_INT32, "big"); | |||
| Python.with<Session>(tf.Session(), sess => | |||
| { | |||
| var result = sess.run(tensor); | |||
| Assert.AreEqual(result.shape[0], 200); | |||
| Assert.AreEqual(result.shape[1], 100); | |||
| var data = result.Data<int>(); | |||
| Assert.AreEqual(0, data[0]); | |||
| Assert.AreEqual(0, data[result.size - 1]); | |||
| }); | |||
| } | |||
| [TestMethod] | |||