diff --git a/TensorFlow.NET.sln b/TensorFlow.NET.sln index 84a1aa7e..cae87d55 100644 --- a/TensorFlow.NET.sln +++ b/TensorFlow.NET.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Examples", "t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Core", "src\TensorFlowNET.Core\TensorFlowNET.Core.csproj", "{FD682AC0-7B2D-45D3-8B0D-C6D678B04144}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorFlowNET.Utility", "src\TensorFlowNET.Utility\TensorFlowNET.Utility.csproj", "{00D9085C-0FC7-453C-A0CC-BAD98F44FEA0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Release|Any CPU.Build.0 = Release|Any CPU + {00D9085C-0FC7-453C-A0CC-BAD98F44FEA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00D9085C-0FC7-453C-A0CC-BAD98F44FEA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00D9085C-0FC7-453C-A0CC-BAD98F44FEA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00D9085C-0FC7-453C-A0CC-BAD98F44FEA0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/TensorFlowNET.Core/APIs/tf.array.cs b/src/TensorFlowNET.Core/APIs/tf.array.cs new file mode 100644 index 00000000..9fb04b98 --- /dev/null +++ b/src/TensorFlowNET.Core/APIs/tf.array.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public static partial class tf + { + /// + /// Inserts a dimension of 1 into a tensor's shape. + /// + /// + /// + /// + /// + /// + /// A `Tensor` with the same data as `input`, but its shape has an additional + /// dimension of size 1 added. + /// + public static Tensor expand_dims(Tensor input, int axis = -1, string name = "", int dim = -1) + => array_ops.expand_dims(input, axis, name, dim); + } +} diff --git a/src/TensorFlowNET.Core/Operations/tf.init_ops.cs b/src/TensorFlowNET.Core/APIs/tf.init.cs similarity index 100% rename from src/TensorFlowNET.Core/Operations/tf.init_ops.cs rename to src/TensorFlowNET.Core/APIs/tf.init.cs diff --git a/src/TensorFlowNET.Core/APIs/tf.io.cs b/src/TensorFlowNET.Core/APIs/tf.io.cs new file mode 100644 index 00000000..c792862e --- /dev/null +++ b/src/TensorFlowNET.Core/APIs/tf.io.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public partial class tf + { + public static Tensor read_file(string filename, string name = "") => gen_io_ops.read_file(filename, name); + + public static gen_image_ops image => new gen_image_ops(); + } +} diff --git a/src/TensorFlowNET.Core/APIs/tf.linalg.cs b/src/TensorFlowNET.Core/APIs/tf.linalg.cs index ccfbbc2b..923cf581 100644 --- a/src/TensorFlowNET.Core/APIs/tf.linalg.cs +++ b/src/TensorFlowNET.Core/APIs/tf.linalg.cs @@ -6,9 +6,6 @@ namespace Tensorflow { public static partial class tf { - public static unsafe Tensor matmul(Tensor a, Tensor b) - { - return gen_math_ops.mat_mul(a, b); - } + public static unsafe Tensor matmul(Tensor a, Tensor b) => gen_math_ops.mat_mul(a, b); } } diff --git a/src/TensorFlowNET.Core/Operations/Losses/tf.loss.cs b/src/TensorFlowNET.Core/APIs/tf.loss.cs similarity index 100% rename from src/TensorFlowNET.Core/Operations/Losses/tf.loss.cs rename to src/TensorFlowNET.Core/APIs/tf.loss.cs diff --git a/src/TensorFlowNET.Core/APIs/tf.math.cs b/src/TensorFlowNET.Core/APIs/tf.math.cs index 9df2f333..44e04b3e 100644 --- a/src/TensorFlowNET.Core/APIs/tf.math.cs +++ b/src/TensorFlowNET.Core/APIs/tf.math.cs @@ -6,25 +6,19 @@ namespace Tensorflow { public static partial class tf { - public static Tensor add(Tensor a, Tensor b) - { - return gen_math_ops.add(a, b); - } - - public static Tensor sub(Tensor a, Tensor b) - { - return gen_math_ops.sub(a, b); - } - - public static Tensor multiply(Tensor x, Tensor y) - { - return gen_math_ops.mul(x, y); - } - - public static Tensor pow(Tensor x, double y) - { - return gen_math_ops.pow(x, y); - } + public static Tensor add(Tensor a, Tensor b) => gen_math_ops.add(a, b); + + public static Tensor sub(Tensor a, Tensor b) => gen_math_ops.sub(a, b); + + public static Tensor subtract(Tensor x, T[] y, string name = "") where T : struct + => gen_math_ops.sub(x, ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"), name); + + public static Tensor multiply(Tensor x, Tensor y) => gen_math_ops.mul(x, y); + + public static Tensor divide(Tensor x, T[] y, string name = "") where T : struct + => x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"); + + public static Tensor pow(Tensor x, double y) => gen_math_ops.pow(x, y); /// /// Computes the sum of elements across dimensions of a tensor. @@ -32,9 +26,9 @@ namespace Tensorflow /// /// /// - public static Tensor reduce_sum(Tensor input, int[] axis = null) - { - return math_ops.reduce_sum(input); - } + public static Tensor reduce_sum(Tensor input, int[] axis = null) => math_ops.reduce_sum(input); + + public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, string name = "") + => math_ops.cast(x, dtype, name); } } diff --git a/src/TensorFlowNET.Core/Framework/importer.py.cs b/src/TensorFlowNET.Core/Framework/importer.py.cs index e8c971c7..80faf264 100644 --- a/src/TensorFlowNET.Core/Framework/importer.py.cs +++ b/src/TensorFlowNET.Core/Framework/importer.py.cs @@ -28,8 +28,8 @@ namespace Tensorflow var graph = ops.get_default_graph(); Python.with(new ops.name_scope(name, "import", input_map.Values), scope => { - /*prefix = scope; - if (!string.IsNullOrEmpty(prefix)) + prefix = scope; + /*if (!string.IsNullOrEmpty(prefix)) prefix = prefix.Substring(0, prefix.Length - 1); else prefix = "";*/ @@ -113,9 +113,16 @@ namespace Tensorflow var key = attr_def.Name; if(attr_def.DefaultValue != null) { - var value = node_def.Attr[key]; - if (value == null) + if (node_def.Attr.ContainsKey(key)) + { + var value = node_def.Attr[key]; + if (value == null) + node_def.Attr[key] = attr_def.DefaultValue; + } + else + { node_def.Attr[key] = attr_def.DefaultValue; + } } } } diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs index 2f16b880..a9ad7a14 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Operation.cs @@ -24,6 +24,9 @@ namespace Tensorflow return c_api.TF_NewOperation(_handle, opType, opName); } + public Operation get_operation_by_name(string name) + => as_graph_element(name, allow_tensor: false, allow_operation: true) as Operation; + public ITensorOrOperation _get_operation_by_name_unsafe(string name) { return _nodes_by_name.ContainsKey(name) ? _nodes_by_name[name] : null; diff --git a/src/TensorFlowNET.Core/Operations/array_ops.py.cs b/src/TensorFlowNET.Core/Operations/array_ops.py.cs index 38ca2799..5bfd279c 100644 --- a/src/TensorFlowNET.Core/Operations/array_ops.py.cs +++ b/src/TensorFlowNET.Core/Operations/array_ops.py.cs @@ -46,6 +46,10 @@ namespace Tensorflow } } + public static Tensor expand_dims(Tensor input, int axis = -1, string name = "", int dim = -1) => expand_dims_v2(input, axis, name); + + private static Tensor expand_dims_v2(Tensor input, int axis, string name = "") => gen_array_ops.expand_dims(input, axis, name); + public static Tensor rank(Tensor input, string name = "") { return math_ops.rank_internal(input, name, optimize: true); diff --git a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs index 8bf345fc..dbf25111 100644 --- a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs @@ -12,6 +12,13 @@ namespace Tensorflow public static OpDefLibrary _op_def_lib = new OpDefLibrary(); public static Execute _execute = new Execute(); + public static Tensor expand_dims(Tensor input, int axis, string name = "") + { + var _op = _op_def_lib._apply_op_helper("ExpandDims", name: name, args: new { input, dim = axis }); + + return _op.outputs[0]; + } + public static Tensor greater(Tx x, Ty y, string name = "") { var _op = _op_def_lib._apply_op_helper("Greater", name: name, args: new { x, y }); diff --git a/src/TensorFlowNET.Core/Operations/gen_image_ops.py.cs b/src/TensorFlowNET.Core/Operations/gen_image_ops.py.cs new file mode 100644 index 00000000..c6eeae8f --- /dev/null +++ b/src/TensorFlowNET.Core/Operations/gen_image_ops.py.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tensorflow +{ + public class gen_image_ops + { + public static OpDefLibrary _op_def_lib = new OpDefLibrary(); + + public Tensor decode_jpeg(Tensor contents, + int channels = 0, + int ratio = 1, + bool fancy_upscaling = true, + bool try_recover_truncated = false, + float acceptable_fraction = 1, + string dct_method = "", + string name = "") + { + // Add nodes to the TensorFlow graph. + if (tf.context.executing_eagerly()) + { + throw new NotImplementedException("decode_jpeg"); + } + else + { + var _op = _op_def_lib._apply_op_helper("DecodeJpeg", name: name, args: new + { + contents, + channels, + ratio, + fancy_upscaling, + try_recover_truncated, + acceptable_fraction, + dct_method + }); + + return _op.outputs[0]; + } + } + + public Tensor resize_bilinear(Tensor images, int[] size, bool align_corners = false, string name = "") + { + if (tf.context.executing_eagerly()) + { + throw new NotImplementedException("resize_bilinear"); + } + else + { + var _op = _op_def_lib._apply_op_helper("ResizeBilinear", name: name, args: new + { + images, + size, + align_corners + }); + + return _op.outputs[0]; + } + } + } +} diff --git a/src/TensorFlowNET.Core/Operations/gen_io_ops.py.cs b/src/TensorFlowNET.Core/Operations/gen_io_ops.py.cs index 6c33a80e..34300e78 100644 --- a/src/TensorFlowNET.Core/Operations/gen_io_ops.py.cs +++ b/src/TensorFlowNET.Core/Operations/gen_io_ops.py.cs @@ -21,5 +21,12 @@ namespace Tensorflow return _op.outputs; } + + public static Tensor read_file(string filename, string name = "") + { + var _op = _op_def_lib._apply_op_helper("ReadFile", name: name, args: new { filename }); + + return _op.outputs[0]; + } } } diff --git a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs index 57921209..a93ffd8c 100644 --- a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs @@ -17,6 +17,13 @@ namespace Tensorflow return _op.outputs[0]; } + public static Tensor cast(Tensor x, TF_DataType DstT, bool Truncate= false, string name= "") + { + var _op = _op_def_lib._apply_op_helper("Cast", name, args: new { x, DstT, Truncate }); + + return _op.outputs[0]; + } + public static Tensor neg(Tensor x, string name = "") { var _op = _op_def_lib._apply_op_helper("Neg", name, args: new { x }); diff --git a/src/TensorFlowNET.Core/Operations/math_ops.py.cs b/src/TensorFlowNET.Core/Operations/math_ops.py.cs index 1e78285a..ed0a5609 100644 --- a/src/TensorFlowNET.Core/Operations/math_ops.py.cs +++ b/src/TensorFlowNET.Core/Operations/math_ops.py.cs @@ -4,7 +4,7 @@ using System.Text; namespace Tensorflow { - public class math_ops + public class math_ops : Python { public static Tensor add(Tensor x, Tensor y, string name = "") => gen_math_ops.add(x, y, name); @@ -14,7 +14,14 @@ namespace Tensorflow if(base_type == x.dtype) return x; - throw new NotImplementedException("math_ops.cast"); + return with(new ops.name_scope(name, "Cast", new { x }), scope => + { + x = ops.convert_to_tensor(x, name: "x"); + if (x.dtype.as_base_dtype() != base_type) + x = gen_math_ops.cast(x, base_type, name: name); + + return x; + }); } /// diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index 9748e33f..553ec32f 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -35,8 +35,7 @@ namespace Tensorflow c_api.TF_DeleteSessionOptions(opts); } - - public virtual NDArray run(object fetches, FeedItem[] feed_dict = null) + public virtual NDArray run(object fetches, params FeedItem[] feed_dict) { return _run(fetches, feed_dict); } @@ -62,6 +61,9 @@ namespace Tensorflow var subfeed_dtype = subfeed_t.dtype.as_numpy_datatype(); switch (subfeed_val) { + case NDArray nd: + feed_dict_tensor[subfeed_t] = nd; + break; case float floatVal: feed_dict_tensor[subfeed_t] = (NDArray)floatVal; break; @@ -193,25 +195,25 @@ namespace Tensorflow case TF_DataType.TF_INT16: var shorts = new short[tensor.size]; for (ulong i = 0; i < tensor.size; i++) - shorts[i] = *(short*)(c_api.TF_TensorData(output) + (int)(tensor.dataTypeSize * i)); + shorts[i] = *(short*)(c_api.TF_TensorData(output) + (int)(tensor.itemsize * i)); nd = np.array(shorts).reshape(ndims); 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)); + ints[i] = *(int*)(c_api.TF_TensorData(output) + (int)(tensor.itemsize * 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)); + floats[i] = *(float*)(c_api.TF_TensorData(output) + (int)(tensor.itemsize * 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)); + doubles[i] = *(double*)(c_api.TF_TensorData(output) + (int)(tensor.itemsize * i)); nd = np.array(doubles).reshape(ndims); break; default: diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 7cd0e95f..50a5bbdd 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -35,8 +35,8 @@ namespace Tensorflow private TF_DataType _dtype = TF_DataType.DtInvalid; public TF_DataType dtype => _handle == IntPtr.Zero ? _dtype : c_api.TF_TensorType(_handle); public ulong bytesize => _handle == IntPtr.Zero ? 0 : c_api.TF_TensorByteSize(_handle); - public ulong dataTypeSize => _handle == IntPtr.Zero ? 0 : c_api.TF_DataTypeSize(dtype); - public ulong size => _handle == IntPtr.Zero ? 0 : bytesize / dataTypeSize; + public ulong itemsize => _handle == IntPtr.Zero ? 0 : c_api.TF_DataTypeSize(dtype); + public ulong size => _handle == IntPtr.Zero ? 0 : bytesize / itemsize; public IntPtr buffer => _handle == IntPtr.Zero ? IntPtr.Zero : c_api.TF_TensorData(_handle); public int num_consumers(TF_Output oper_out) => _handle == IntPtr.Zero ? 0 : c_api.TF_OperationOutputNumConsumers(oper_out); @@ -122,7 +122,7 @@ namespace Tensorflow for (ulong i = 0; i < size; i++) { - data[i] = Marshal.PtrToStructure(buffer + (int)(i * dataTypeSize)); + data[i] = Marshal.PtrToStructure(buffer + (int)(i * itemsize)); } return data; diff --git a/src/TensorFlowNET.Core/Tensors/dtypes.cs b/src/TensorFlowNET.Core/Tensors/dtypes.cs index 4c0bd693..2075d554 100644 --- a/src/TensorFlowNET.Core/Tensors/dtypes.cs +++ b/src/TensorFlowNET.Core/Tensors/dtypes.cs @@ -21,7 +21,7 @@ namespace Tensorflow case TF_DataType.TF_STRING: return typeof(string); default: - throw new NotImplementedException("as_numpy_datatype failed"); + return null; } } diff --git a/src/TensorFlowNET.Core/Tensors/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs index a7d728d2..11761f56 100644 --- a/src/TensorFlowNET.Core/Tensors/tensor_util.cs +++ b/src/TensorFlowNET.Core/Tensors/tensor_util.cs @@ -44,6 +44,7 @@ namespace Tensorflow // We first convert value to a numpy array or scalar. NDArray nparray = null; + var np_dt = dtype.as_numpy_datatype(); if (values is NDArray nd) { @@ -54,31 +55,61 @@ namespace Tensorflow if (values == null) throw new ValueError("None values not supported."); - switch (values) + if(np_dt == null) { - case bool boolVal: - nparray = boolVal; - break; - case int intVal: - nparray = intVal; - break; - case int[] intVals: - nparray = np.array(intVals); - break; - case float floatVal: - nparray = floatVal; - break; - case double doubleVal: - nparray = doubleVal; - break; - case string strVal: - nparray = strVal; - break; - case string[] strVals: - nparray = strVals; - break; - default: - throw new Exception("make_tensor_proto Not Implemented"); + switch (values) + { + case bool boolVal: + nparray = boolVal; + break; + case int intVal: + nparray = intVal; + break; + case int[] intVals: + nparray = np.array(intVals); + break; + case float floatVal: + nparray = floatVal; + break; + case double doubleVal: + nparray = doubleVal; + break; + case string strVal: + nparray = strVal; + break; + case string[] strVals: + nparray = strVals; + break; + default: + throw new NotImplementedException("make_tensor_proto Not Implemented"); + } + } + else + { + // convert data type + switch (np_dt.Name) + { + case "Int32": + if (values.GetType().IsArray) + nparray = np.array((int[])values, np_dt); + else + nparray = (int)values; + break; + case "Single": + if (values.GetType().IsArray) + nparray = np.array((float[])values, np_dt); + else + nparray = (float)values; + break; + case "Double": + nparray = (double)values; + break; + case "String": + nparray = values.ToString(); + break; + default: + throw new NotImplementedException("make_tensor_proto Not Implemented"); + } } } diff --git a/src/TensorFlowNET.Core/Train/Saving/Saver.cs b/src/TensorFlowNET.Core/Train/Saving/Saver.cs index 4cdc85a5..cb95628d 100644 --- a/src/TensorFlowNET.Core/Train/Saving/Saver.cs +++ b/src/TensorFlowNET.Core/Train/Saving/Saver.cs @@ -169,9 +169,9 @@ namespace Tensorflow if (!_is_empty) { - model_checkpoint_path = sess.run(_saver_def.SaveTensorName, new FeedItem[] { + model_checkpoint_path = sess.run(_saver_def.SaveTensorName, new FeedItem(_saver_def.FilenameTensorName, checkpoint_file) - }); + ); if (write_state) { @@ -227,10 +227,8 @@ namespace Tensorflow if (tf.context.executing_eagerly()) ; else - sess.run(_saver_def.RestoreOpName, new FeedItem[] - { - new FeedItem(_saver_def.FilenameTensorName, save_path) - }); + sess.run(_saver_def.RestoreOpName, + new FeedItem(_saver_def.FilenameTensorName, save_path)); } /// diff --git a/src/TensorFlowNET.Core/ops.py.cs b/src/TensorFlowNET.Core/ops.py.cs index 026e7feb..42833978 100644 --- a/src/TensorFlowNET.Core/ops.py.cs +++ b/src/TensorFlowNET.Core/ops.py.cs @@ -418,6 +418,9 @@ namespace Tensorflow string name = "", TF_DataType preferred_dtype = TF_DataType.DtInvalid, bool as_ref = false) { + if (dtype == TF_DataType.DtInvalid) + dtype = preferred_dtype; + switch (value) { case Tensor tensor: @@ -432,6 +435,8 @@ namespace Tensorflow return constant_op.constant(intArray, dtype: dtype, name: name); case float floatVal: return constant_op.constant(floatVal, dtype: dtype, name: name); + case float[] floatArray: + return constant_op.constant(floatArray, dtype: dtype, name: name); case double doubleVal: return constant_op.constant(doubleVal, dtype: dtype, name: name); case RefVariable varVal: diff --git a/src/TensorFlowNET.Utility/Compress.cs b/src/TensorFlowNET.Utility/Compress.cs new file mode 100644 index 00000000..086f7a9b --- /dev/null +++ b/src/TensorFlowNET.Utility/Compress.cs @@ -0,0 +1,37 @@ +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Tar; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace TensorFlowNET.Utility +{ + public class Compress + { + public static void ExtractTGZ(String gzArchiveName, String destFolder) + { + Console.WriteLine($"Extracting."); + var task = Task.Run(() => + { + using (var inStream = File.OpenRead(gzArchiveName)) + { + using (var gzipStream = new GZipInputStream(inStream)) + { + using (TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream)) + tarArchive.ExtractContents(destFolder); + } + } + }); + + while (!task.IsCompleted) + { + Thread.Sleep(200); + Console.Write("."); + } + + Console.WriteLine(""); + Console.WriteLine("Extracting is completed."); + } + } +} diff --git a/src/TensorFlowNET.Utility/TensorFlowNET.Utility.csproj b/src/TensorFlowNET.Utility/TensorFlowNET.Utility.csproj new file mode 100644 index 00000000..efeb641a --- /dev/null +++ b/src/TensorFlowNET.Utility/TensorFlowNET.Utility.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + TensorFlowNET.Utility + TensorFlowNET.Utility + + + + + + + diff --git a/src/TensorFlowNET.Utility/Web.cs b/src/TensorFlowNET.Utility/Web.cs new file mode 100644 index 00000000..dfaf5236 --- /dev/null +++ b/src/TensorFlowNET.Utility/Web.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace TensorFlowNET.Utility +{ + public class Web + { + public static bool Download(string url, string file) + { + if (File.Exists(file)) + { + Console.WriteLine($"{file} already exists."); + return false; + } + + var wc = new WebClient(); + Console.WriteLine($"Downloading {file}"); + var download = Task.Run(() => wc.DownloadFile(url, file)); + while (!download.IsCompleted) + { + Thread.Sleep(1000); + Console.Write("."); + } + Console.WriteLine(""); + Console.WriteLine($"Downloaded {file}"); + + return true; + } + } +} diff --git a/test/TensorFlowNET.Examples/LabelImage.cs b/test/TensorFlowNET.Examples/LabelImage.cs new file mode 100644 index 00000000..c0f4193a --- /dev/null +++ b/test/TensorFlowNET.Examples/LabelImage.cs @@ -0,0 +1,120 @@ +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Tar; +using NumSharp.Core; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tensorflow; + +namespace TensorFlowNET.Examples +{ + /// + /// Port from tensorflow\examples\label_image\label_image.py + /// + public class LabelImage : Python, IExample + { + string dir = "label_image_data"; + string pbFile = "inception_v3_2016_08_28_frozen.pb"; + string labelFile = "imagenet_slim_labels.txt"; + string picFile = "grace_hopper.jpg"; + int input_height = 299; + int input_width = 299; + int input_mean = 0; + int input_std = 255; + string input_layer = "input"; + string output_layer = "InceptionV3/Predictions/Reshape_1"; + + public void Run() + { + PrepareData(); + var graph = LoadGraph(Path.Join(dir, pbFile)); + var t = ReadTensorFromImageFile(Path.Join(dir, picFile), + input_height: input_height, + input_width: input_width, + input_mean: input_mean, + input_std: input_std); + + var input_name = "import/" + input_layer; + var output_name = "import/" + output_layer; + + var input_operation = graph.get_operation_by_name(input_name); + var output_operation = graph.get_operation_by_name(output_name); + + NDArray results = null; + with(tf.Session(graph), sess => + { + results = sess.run(output_operation.outputs[0], new FeedItem(input_operation.outputs[0], t)); + }); + + // equivalent np.squeeze + results.reshape(results.shape.Where(x => x > 1).ToArray()); + // top_k = results.argsort()[-5:][::-1] + var top_k = results.Data().Take(5).ToArray(); + var labels = LoadLabels(Path.Join(dir, labelFile)); + foreach (var i in top_k) + Console.WriteLine($"{labels[i]}, {results[i]}"); + } + + private string[] LoadLabels(string file) + { + return File.ReadAllLines(file); + } + + private Graph LoadGraph(string modelFile) + { + var graph = tf.Graph(); + var graph_def = GraphDef.Parser.ParseFrom(File.ReadAllBytes(modelFile)); + importer.import_graph_def(graph_def); + return graph; + } + + private NDArray ReadTensorFromImageFile(string file_name, + int input_height = 299, + int input_width = 299, + int input_mean = 0, + int input_std = 255) + { + string input_name = "file_reader"; + string output_name = "normalized"; + Tensor image_reader = null; + + var file_reader = tf.read_file(file_name, input_name); + image_reader = tf.image.decode_jpeg(file_reader, channels: 3, name: "jpeg_reader"); + + var float_caster = tf.cast(image_reader, tf.float32); + var dims_expander = tf.expand_dims(float_caster, 0); + var resized = tf.image.resize_bilinear(dims_expander, new int[] { input_height, input_width }); + var normalized = tf.divide(tf.subtract(resized, new float[] { input_mean }), new float[] { input_std }); + + return with(tf.Session(), sess => + { + var result = sess.run(normalized); + return result; + }); + } + + private void PrepareData() + { + + Directory.CreateDirectory(dir); + + // get model file + string url = "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz"; + + string zipFile = Path.Join(dir, $"{pbFile}.tar.gz"); + Utility.Web.Download(url, zipFile); + + if (!File.Exists(Path.Join(dir, pbFile))) + Utility.Compress.ExtractTGZ(zipFile, dir); + + // download sample picture + string pic = "grace_hopper.jpg"; + Utility.Web.Download($"https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/label_image/data/{pic}", Path.Join(dir, pic)); + } + } +} diff --git a/test/TensorFlowNET.Examples/LinearRegression.cs b/test/TensorFlowNET.Examples/LinearRegression.cs index 79db1ed9..65175cfb 100644 --- a/test/TensorFlowNET.Examples/LinearRegression.cs +++ b/test/TensorFlowNET.Examples/LinearRegression.cs @@ -65,21 +65,17 @@ namespace TensorFlowNET.Examples { foreach (var (x, y) in Python.zip(train_X, train_Y)) { - sess.run(optimizer, feed_dict: new FeedItem[] - { + sess.run(optimizer, new FeedItem(X, x), - new FeedItem(Y, y) - }); + new FeedItem(Y, y)); } // Display logs per epoch step if ((epoch + 1) % display_step == 0) { - var c = sess.run(cost, feed_dict: new FeedItem[] - { + var c = sess.run(cost, new FeedItem(X, train_X), - new FeedItem(Y, train_Y) - }); + new FeedItem(Y, train_Y)); var rW = sess.run(W); Console.WriteLine($"Epoch: {epoch + 1} cost={c} " + $"W={rW} b={sess.run(b)}"); diff --git a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj index 5ed00751..ec79057c 100644 --- a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj +++ b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj @@ -12,6 +12,7 @@ + diff --git a/test/TensorFlowNET.UnitTest/OperationsTest.cs b/test/TensorFlowNET.UnitTest/OperationsTest.cs index f0d0c8bd..601b66b9 100644 --- a/test/TensorFlowNET.UnitTest/OperationsTest.cs +++ b/test/TensorFlowNET.UnitTest/OperationsTest.cs @@ -33,11 +33,9 @@ namespace TensorFlowNET.UnitTest using(var sess = tf.Session()) { - var o = sess.run(c, feed_dict: new FeedItem[] - { + var o = sess.run(c, new FeedItem(a, 3.0f), - new FeedItem(b, 2.0f) - }); + new FeedItem(b, 2.0f)); Assert.AreEqual((float)o, 5.0f); } } diff --git a/test/TensorFlowNET.UnitTest/PlaceholderTest.cs b/test/TensorFlowNET.UnitTest/PlaceholderTest.cs index f4ba9856..3bc6a892 100644 --- a/test/TensorFlowNET.UnitTest/PlaceholderTest.cs +++ b/test/TensorFlowNET.UnitTest/PlaceholderTest.cs @@ -17,10 +17,8 @@ namespace TensorFlowNET.UnitTest Python.with(tf.Session(), sess => { - var result = sess.run(y, feed_dict: new FeedItem[] - { - new FeedItem(x, 2) - }); + var result = sess.run(y, + new FeedItem(x, 2)); Assert.AreEqual((int)result, 6); }); }