From 1ded2ed3cbe86431184d0be6d80091f611f10053 Mon Sep 17 00:00:00 2001 From: Haiping Date: Thu, 25 Jul 2019 20:35:53 -0500 Subject: [PATCH] v0.10.2 (#318) * fixed a bug about unzip * added a test case for MnistModelLoader * cleared warnings in the UnitTest project * Upgrade NumSharp with signature. * Testing gradients function: (#307) - Concat - LGamma - Tanh - Slice * Adding new ctor for `Session` (#309) Meant to be a shortcut to directly set some basic configuration. E.g. ``` var sessionOptions = new SessionOptions(); sessionOptions.SetConfig(new ConfigProto { LogDevicePlacement = true, InterOpParallelismThreads = 4, GpuOptions = new GPUOptions { AllowGrowth = true, PerProcessGpuMemoryFraction = 0.12 } }); var session = tf.Session(sessionOptions); ``` Adding `AbsGrad` To be aligned with Tensorflow, introducing also `Sign` operation and its gradient. * fix strided_slice_grad type convention error. #307 * added a static load method for MnistModelLoader * adjusted the assembly name for hub * add Prediction function for Transfer Learning * fixed the path combining issue * Init TensorFlow.Text project. put all UnitTest and Example into one project. * Predict image category in bootleneck file. * Adding `IsNan` operation (#315) Exposing also the `IsFinite` one. Unit testing both operations. * change bottleneck file to raw image as input for transfer learning. * remove unnecessary projects (#316) * implementing some additional methods (#317) * got rid off the reference to the TF.NET * updated project file for packaging --- TensorFlow.NET.sln | 24 +-- src/TensorFlowHub/MnistDataSet.cs | 6 +- src/TensorFlowHub/MnistModelLoader.cs | 22 +- src/TensorFlowHub/ModelLoadSetting.cs | 2 +- src/TensorFlowHub/TensorFlowHub.csproj | 18 +- src/TensorFlowHub/Utils.cs | 39 ++-- src/TensorFlowNET.Core/APIs/tf.graph.cs | 4 +- src/TensorFlowNET.Core/APIs/tf.math.cs | 9 + src/TensorFlowNET.Core/APIs/tf.ops.cs | 12 +- .../Learn/Estimators/tensor_signature.cs | 39 ++++ .../Framework/sparse_tensor.py.cs | 13 +- .../Framework/tensor_shape.cs | 34 +++ .../Gradients/array_grad.cs | 10 +- src/TensorFlowNET.Core/Gradients/math_grad.cs | 19 ++ src/TensorFlowNET.Core/Graphs/Graph.Import.cs | 4 +- .../Operations/gen_array_ops.cs | 2 +- .../Operations/gen_math_ops.cs | 14 ++ src/TensorFlowNET.Core/Operations/math_ops.cs | 9 + .../Operations/resource_variable_ops.cs | 57 ++++- .../TensorFlowNET.Core.csproj | 18 +- src/TensorFlowNET.Core/Tensors/Tensor.cs | 5 +- src/TensorFlowNET.Core/Tensors/dtypes.cs | 5 + src/TensorFlowNET.Core/Tensors/tensor_util.cs | 6 + .../Variables/RefVariable.cs | 2 + .../Variables/gen_state_ops.py.cs | 204 +++++++++--------- src/TensorFlowNET.Core/Variables/state_ops.cs | 162 +++++++------- src/TensorFlowNET.Core/tf.cs | 7 +- .../TensorFlowNET.Benchmark.csproj | 20 ++ src/TensorFlowText/README.md | 9 + src/TensorFlowText/TensorFlowText.csproj | 7 + src/TensorFlowText/Tokenizer.cs | 8 + test/KerasNET.Example/Keras.Example.csproj | 23 -- test/KerasNET.Example/packages.config | 10 - test/KerasNET.Test/Keras.UnitTest.csproj | 41 ---- test/KerasNET.Test/packages.config | 11 - test/TensorFlowHub.Examples/Program.cs | 12 -- .../TensorFlowHub.Examples.csproj | 16 -- .../ImageProcessing/RetrainImageClassifier.cs | 81 ++++++- .../Keras.cs} | 47 +++- .../TensorFlowNET.Examples.csproj | 2 + .../Hub/MnistModelLoaderTest.cs | 28 +++ .../KerasTests.cs} | 2 +- test/TensorFlowNET.UnitTest/OperationsTest.cs | 28 +++ test/TensorFlowNET.UnitTest/PythonTest.cs | 3 +- .../TensorFlowNET.UnitTest.csproj | 3 + .../control_flow_ops_test/SwitchTestCase.cs | 2 + .../gradients_test/GradientsTest.cs | 90 ++++++-- .../ops_test/ControlDependenciesTest.cs | 2 +- 48 files changed, 789 insertions(+), 402 deletions(-) create mode 100644 src/TensorFlowNET.Core/Contrib/Learn/Estimators/tensor_signature.cs create mode 100644 src/TensorFlowNET.Core/Framework/tensor_shape.cs create mode 100644 src/TensorFlowNet.Benchmarks/TensorFlowNET.Benchmark.csproj create mode 100644 src/TensorFlowText/README.md create mode 100644 src/TensorFlowText/TensorFlowText.csproj create mode 100644 src/TensorFlowText/Tokenizer.cs delete mode 100644 test/KerasNET.Example/Keras.Example.csproj delete mode 100644 test/KerasNET.Example/packages.config delete mode 100644 test/KerasNET.Test/Keras.UnitTest.csproj delete mode 100644 test/KerasNET.Test/packages.config delete mode 100644 test/TensorFlowHub.Examples/Program.cs delete mode 100644 test/TensorFlowHub.Examples/TensorFlowHub.Examples.csproj rename test/{KerasNET.Example/Program.cs => TensorFlowNET.Examples/Keras.cs} (64%) create mode 100644 test/TensorFlowNET.UnitTest/Hub/MnistModelLoaderTest.cs rename test/{KerasNET.Test/BaseTests.cs => TensorFlowNET.UnitTest/KerasTests.cs} (95%) diff --git a/TensorFlow.NET.sln b/TensorFlow.NET.sln index 7046347d..5d6e5fe7 100644 --- a/TensorFlow.NET.sln +++ b/TensorFlow.NET.sln @@ -11,17 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Core", "src\T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keras.Core", "src\KerasNET.Core\Keras.Core.csproj", "{902E188F-A953-43B4-9991-72BAB1697BC3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keras.Example", "test\KerasNET.Example\Keras.Example.csproj", "{17E1AC16-9E0E-4545-905A-E92C6300C7AF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keras.UnitTest", "test\KerasNET.Test\Keras.UnitTest.csproj", "{A5839A45-A117-4BEA-898B-DE1ED6E0D58F}" -EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TensorFlowNET.Examples.FSharp", "test\TensorFlowNET.Examples.FSharp\TensorFlowNET.Examples.FSharp.fsproj", "{62BC3801-F0D3-44A9-A0AC-712F40C8F961}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowBenchmark", "src\TensorFlowNet.Benchmarks\TensorFlowBenchmark.csproj", "{68861442-971A-4196-876E-C9330F0B3C54}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorFlowHub", "src\TensorFlowHub\TensorFlowHub.csproj", "{8FD59A5A-97EB-457E-B9F1-D88B0C822C6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowHub", "src\TensorFlowHub\TensorFlowHub.csproj", "{8FD59A5A-97EB-457E-B9F1-D88B0C822C6E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorFlowHub.Examples", "test\TensorFlowHub.Examples\TensorFlowHub.Examples.csproj", "{5FF6CB67-DC99-4741-9545-E803DFCE5CDC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorFlowText", "src\TensorFlowText\TensorFlowText.csproj", "{B598E5D5-BD2D-4191-8532-F2FBAC31AB81}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,14 +41,6 @@ Global {902E188F-A953-43B4-9991-72BAB1697BC3}.Debug|Any CPU.Build.0 = Debug|Any CPU {902E188F-A953-43B4-9991-72BAB1697BC3}.Release|Any CPU.ActiveCfg = Release|Any CPU {902E188F-A953-43B4-9991-72BAB1697BC3}.Release|Any CPU.Build.0 = Release|Any CPU - {17E1AC16-9E0E-4545-905A-E92C6300C7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17E1AC16-9E0E-4545-905A-E92C6300C7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17E1AC16-9E0E-4545-905A-E92C6300C7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17E1AC16-9E0E-4545-905A-E92C6300C7AF}.Release|Any CPU.Build.0 = Release|Any CPU - {A5839A45-A117-4BEA-898B-DE1ED6E0D58F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A5839A45-A117-4BEA-898B-DE1ED6E0D58F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5839A45-A117-4BEA-898B-DE1ED6E0D58F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5839A45-A117-4BEA-898B-DE1ED6E0D58F}.Release|Any CPU.Build.0 = Release|Any CPU {62BC3801-F0D3-44A9-A0AC-712F40C8F961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62BC3801-F0D3-44A9-A0AC-712F40C8F961}.Debug|Any CPU.Build.0 = Debug|Any CPU {62BC3801-F0D3-44A9-A0AC-712F40C8F961}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -65,10 +53,10 @@ Global {8FD59A5A-97EB-457E-B9F1-D88B0C822C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8FD59A5A-97EB-457E-B9F1-D88B0C822C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FD59A5A-97EB-457E-B9F1-D88B0C822C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {5FF6CB67-DC99-4741-9545-E803DFCE5CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5FF6CB67-DC99-4741-9545-E803DFCE5CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5FF6CB67-DC99-4741-9545-E803DFCE5CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5FF6CB67-DC99-4741-9545-E803DFCE5CDC}.Release|Any CPU.Build.0 = Release|Any CPU + {B598E5D5-BD2D-4191-8532-F2FBAC31AB81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B598E5D5-BD2D-4191-8532-F2FBAC31AB81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B598E5D5-BD2D-4191-8532-F2FBAC31AB81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B598E5D5-BD2D-4191-8532-F2FBAC31AB81}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/TensorFlowHub/MnistDataSet.cs b/src/TensorFlowHub/MnistDataSet.cs index 65b8ebae..e0717ccb 100644 --- a/src/TensorFlowHub/MnistDataSet.cs +++ b/src/TensorFlowHub/MnistDataSet.cs @@ -12,7 +12,7 @@ namespace Tensorflow.Hub public int EpochsCompleted { get; private set; } public int IndexInEpoch { get; private set; } - public MnistDataSet(NDArray images, NDArray labels, TF_DataType dtype, bool reshape) + public MnistDataSet(NDArray images, NDArray labels, Type dataType, bool reshape) { EpochsCompleted = 0; IndexInEpoch = 0; @@ -20,11 +20,11 @@ namespace Tensorflow.Hub NumOfExamples = images.shape[0]; images = images.reshape(images.shape[0], images.shape[1] * images.shape[2]); - images.astype(dtype.as_numpy_datatype()); + images.astype(dataType); images = np.multiply(images, 1.0f / 255.0f); Data = images; - labels.astype(dtype.as_numpy_datatype()); + labels.astype(dataType); Labels = labels; } } diff --git a/src/TensorFlowHub/MnistModelLoader.cs b/src/TensorFlowHub/MnistModelLoader.cs index 72a4dcbb..7c4ff109 100644 --- a/src/TensorFlowHub/MnistModelLoader.cs +++ b/src/TensorFlowHub/MnistModelLoader.cs @@ -15,6 +15,16 @@ namespace Tensorflow.Hub private const string TEST_IMAGES = "t10k-images-idx3-ubyte.gz"; private const string TEST_LABELS = "t10k-labels-idx1-ubyte.gz"; + public static async Task> LoadAsync(string trainDir, bool oneHot = false) + { + var loader = new MnistModelLoader(); + return await loader.LoadAsync(new ModelLoadSetting + { + TrainDir = trainDir, + OneHot = oneHot + }); + } + public async Task> LoadAsync(ModelLoadSetting setting) { if (setting.TrainSize.HasValue && setting.ValidationSize >= setting.TrainSize.Value) @@ -71,7 +81,7 @@ namespace Tensorflow.Hub trainImages = trainImages[np.arange(validationSize, end)]; trainLabels = trainLabels[np.arange(validationSize, end)]; - var dtype = setting.DtType; + var dtype = setting.DataType; var reshape = setting.ReShape; var train = new MnistDataSet(trainImages, trainLabels, dtype, reshape); @@ -83,11 +93,14 @@ namespace Tensorflow.Hub private NDArray ExtractImages(string file, int? limit = null) { + if (!Path.IsPathRooted(file)) + file = Path.Combine(AppContext.BaseDirectory, file); + using (var bytestream = new FileStream(file, FileMode.Open)) { var magic = Read32(bytestream); if (magic != 2051) - throw new ValueError($"Invalid magic number {magic} in MNIST image file: {file}"); + throw new Exception($"Invalid magic number {magic} in MNIST image file: {file}"); var num_images = Read32(bytestream); num_images = limit == null ? num_images : Math.Min(num_images, (uint)limit); @@ -108,11 +121,14 @@ namespace Tensorflow.Hub private NDArray ExtractLabels(string file, bool one_hot = false, int num_classes = 10, int? limit = null) { + if (!Path.IsPathRooted(file)) + file = Path.Combine(AppContext.BaseDirectory, file); + using (var bytestream = new FileStream(file, FileMode.Open)) { var magic = Read32(bytestream); if (magic != 2049) - throw new ValueError($"Invalid magic number {magic} in MNIST label file: {file}"); + throw new Exception($"Invalid magic number {magic} in MNIST label file: {file}"); var num_items = Read32(bytestream); num_items = limit == null ? num_items : Math.Min(num_items, (uint)limit); diff --git a/src/TensorFlowHub/ModelLoadSetting.cs b/src/TensorFlowHub/ModelLoadSetting.cs index 91b4059c..89e46748 100644 --- a/src/TensorFlowHub/ModelLoadSetting.cs +++ b/src/TensorFlowHub/ModelLoadSetting.cs @@ -9,7 +9,7 @@ namespace Tensorflow.Hub { public string TrainDir { get; set; } public bool OneHot { get; set; } - public TF_DataType DtType { get; set; } = TF_DataType.TF_FLOAT; + public Type DataType { get; set; } = typeof(float); public bool ReShape { get; set; } public int ValidationSize { get; set; } = 5000; public int? TrainSize { get; set; } diff --git a/src/TensorFlowHub/TensorFlowHub.csproj b/src/TensorFlowHub/TensorFlowHub.csproj index ddf8ca25..5e1e29a7 100644 --- a/src/TensorFlowHub/TensorFlowHub.csproj +++ b/src/TensorFlowHub/TensorFlowHub.csproj @@ -1,14 +1,18 @@  - TensorFlow.Net.Hub Tensorflow.Hub netstandard2.0 + 0.0.1 + SciSharp + SciSharp STACK + Apache 2.0 + https://github.com/SciSharp/TensorFlow.NET + git + http://scisharpstack.org + TensorFlow, Training model + TensorFlow Hub is a library to foster the publication, discovery, and consumption of reusable parts of machine learning models. - + - - - - - + \ No newline at end of file diff --git a/src/TensorFlowHub/Utils.cs b/src/TensorFlowHub/Utils.cs index 9f71c61d..10aaf958 100644 --- a/src/TensorFlowHub/Utils.cs +++ b/src/TensorFlowHub/Utils.cs @@ -1,14 +1,11 @@ using System; using System.IO; +using System.IO.Compression; using System.Collections.Generic; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; -using NumSharp; -using SharpCompress; -using SharpCompress.Common; -using SharpCompress.Readers; namespace Tensorflow.Hub { @@ -37,7 +34,7 @@ namespace Tensorflow.Hub } } - public static void Unzip(this IModelLoader modelLoader, string zipFile, string saveTo) + public static async Task UnzipAsync(this IModelLoader modelLoader, string zipFile, string saveTo) where TDataSet : IDataSet { if (!Path.IsPathRooted(saveTo)) @@ -45,28 +42,26 @@ namespace Tensorflow.Hub if (!Directory.Exists(saveTo)) Directory.CreateDirectory(saveTo); + + if (!Path.IsPathRooted(zipFile)) + zipFile = Path.Combine(AppContext.BaseDirectory, zipFile); + + var destFilePath = Path.Combine(saveTo, Path.GetFileNameWithoutExtension(zipFile)); - using (var stream = File.OpenRead(zipFile)) - using (var reader = ReaderFactory.Open(stream)) + if (File.Exists(destFilePath)) + File.Delete(destFilePath); + + using (GZipStream unzipStream = new GZipStream(File.OpenRead(zipFile), CompressionMode.Decompress)) { - while (reader.MoveToNextEntry()) + using (var destStream = File.Create(destFilePath)) { - if (!reader.Entry.IsDirectory) - { - reader.WriteEntryToDirectory(saveTo, new ExtractionOptions() - { - ExtractFullPath = true, - Overwrite = true - }); - } + await unzipStream.CopyToAsync(destStream); + await destStream.FlushAsync(); + destStream.Close(); } - } - } - public static async Task UnzipAsync(this IModelLoader modelLoader, string zipFile, string saveTo) - where TDataSet : IDataSet - { - await Task.Run(() => modelLoader.Unzip(zipFile, saveTo)); + unzipStream.Close(); + } } public static async Task ShowProgressInConsole(this Task task) diff --git a/src/TensorFlowNET.Core/APIs/tf.graph.cs b/src/TensorFlowNET.Core/APIs/tf.graph.cs index e6e60e32..402ffca1 100644 --- a/src/TensorFlowNET.Core/APIs/tf.graph.cs +++ b/src/TensorFlowNET.Core/APIs/tf.graph.cs @@ -24,7 +24,7 @@ namespace Tensorflow return ops.get_default_graph(); } - public static Graph Graph() => new Graph(); - + public static Graph Graph() + => new Graph(); } } diff --git a/src/TensorFlowNET.Core/APIs/tf.math.cs b/src/TensorFlowNET.Core/APIs/tf.math.cs index 1be2970a..d3b24373 100644 --- a/src/TensorFlowNET.Core/APIs/tf.math.cs +++ b/src/TensorFlowNET.Core/APIs/tf.math.cs @@ -57,6 +57,12 @@ namespace Tensorflow public static Tensor arg_min(Tensor input, int dimension, TF_DataType output_type = TF_DataType.TF_INT64, string name = null) => gen_math_ops.arg_min(input, dimension, output_type: output_type, name: name); + public static Tensor is_finite(Tensor input, string name = null) + => gen_math_ops.is_finite(input, name); + + public static Tensor is_nan(Tensor input, string name = null) + => gen_math_ops.is_nan(input, name); + /// /// Returns element-wise smallest integer not less than x. /// @@ -203,6 +209,9 @@ namespace Tensorflow public static Tensor sqrt(Tensor a, string name = null) => gen_math_ops.sqrt(a, name); + public static Tensor sign(Tensor a, string name = null) + => gen_math_ops.sign(a, name); + public static Tensor subtract(Tensor x, T[] y, string name = null) where T : struct => gen_math_ops.sub(x, ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"), name); diff --git a/src/TensorFlowNET.Core/APIs/tf.ops.cs b/src/TensorFlowNET.Core/APIs/tf.ops.cs index 51189bda..dbd1b246 100644 --- a/src/TensorFlowNET.Core/APIs/tf.ops.cs +++ b/src/TensorFlowNET.Core/APIs/tf.ops.cs @@ -18,8 +18,11 @@ namespace Tensorflow { public static partial class tf { - public static object get_collection(string key, string scope = "") => get_default_graph() - .get_collection(key, scope: scope); + public static Tensor assign(Tensor @ref, object value, bool validate_shape = true, bool use_locking = true, string name = null) + => state_ops.assign(@ref, value, validate_shape, use_locking, name); + + public static object get_collection(string key, string scope = "") + => get_default_graph().get_collection(key, scope: scope); /// /// Returns a context manager that creates hierarchical names for operations. @@ -28,8 +31,7 @@ namespace Tensorflow /// The default name to use if the name argument is None. /// The list of Tensor arguments that are passed to the op function. /// The scope name. - public static ops.NameScope name_scope(string name, - string default_name = "", - object values = null) => new ops.NameScope(name, default_name, values); + public static ops.NameScope name_scope(string name, string default_name = "", object values = null) + => new ops.NameScope(name, default_name, values); } } diff --git a/src/TensorFlowNET.Core/Contrib/Learn/Estimators/tensor_signature.cs b/src/TensorFlowNET.Core/Contrib/Learn/Estimators/tensor_signature.cs new file mode 100644 index 00000000..cf53a8dc --- /dev/null +++ b/src/TensorFlowNET.Core/Contrib/Learn/Estimators/tensor_signature.cs @@ -0,0 +1,39 @@ +using System.Linq; +using NumSharp; +using Tensorflow.Framework; + +namespace Tensorflow.Contrib.Learn.Estimators +{ + public static class tensor_signature + { + public static bool is_compatible_with(this Tensor self, Tensor other) + { + bool _shape_is_compatible_0dim(Shape _this, Shape _other) + { + var __other = tensor_shape.as_shape(_other); + if (_this.Dimensions == null || __other.Dimensions == null) + return true; + + if (_this.NDim != __other.NDim) + return false; + + foreach (var (x_dim, y_dim) in _this.Dimensions.Zip(__other.Dimensions, (x_dim, y_dim) => (x_dim, y_dim))) + { + if (x_dim != y_dim) + return false; + } + + return true; + } + + if (other.is_sparse()) + { + return self.dtype.is_compatible_with(other.dtype); + } + + return self.dtype.is_compatible_with(other.dtype) && + _shape_is_compatible_0dim(self.shape, other.shape) && + !self.is_sparse(); + } + } +} diff --git a/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs b/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs index 547d4516..8d0ea53b 100644 --- a/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs +++ b/src/TensorFlowNET.Core/Framework/sparse_tensor.py.cs @@ -1,8 +1,19 @@ namespace Tensorflow.Framework { - public static class SparseTensor + public interface _TensorLike + { } + + public class SparseTensor : CompositeTensor, _TensorLike { private static Tensor _dense_shape { get; set; } } + + public static class sparse_tensor + { + public static bool is_sparse(this _TensorLike x) + { + return x is SparseTensor; + } + } } diff --git a/src/TensorFlowNET.Core/Framework/tensor_shape.cs b/src/TensorFlowNET.Core/Framework/tensor_shape.cs new file mode 100644 index 00000000..4972c1b4 --- /dev/null +++ b/src/TensorFlowNET.Core/Framework/tensor_shape.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Text; +using NumSharp; +using Tensorflow.Contrib.Learn.Estimators; + +namespace Tensorflow.Framework +{ + public static class tensor_shape + { + public static void assert_is_compatible_with(this Tensor self, Tensor other) + { + if (!self.is_compatible_with(other)) + { + var selfDim = self.shape + .Aggregate(new StringBuilder("{"), (sb, i) => sb.Append(i).Append(", "), sb => sb.ToString()) + .Replace(", }", "}"); + + var otherDim = other.shape + .Aggregate(new StringBuilder("{"), (sb, i) => sb.Append(i).Append(", "), sb => sb.ToString()) + .Replace(", }", "}"); + + throw new ArgumentException($"Dimensions {selfDim} and {otherDim} are not compatible"); + } + } + + public static TensorShape as_shape(this Shape shape) + { + if (shape is TensorShape tshape) + return tshape; + return new TensorShape(shape); + } + } +} diff --git a/src/TensorFlowNET.Core/Gradients/array_grad.cs b/src/TensorFlowNET.Core/Gradients/array_grad.cs index 1a4956d2..11dfcc89 100644 --- a/src/TensorFlowNET.Core/Gradients/array_grad.cs +++ b/src/TensorFlowNET.Core/Gradients/array_grad.cs @@ -220,11 +220,11 @@ namespace Tensorflow.Gradients end, strides, grad, - begin_mask: (int)op.get_attr("begin_mask"), - end_mask: (int)op.get_attr("end_mask"), - ellipsis_mask: (int)op.get_attr("ellipsis_mask"), - new_axis_mask: (int)op.get_attr("new_axis_mask"), - shrink_axis_mask: (int)op.get_attr("shrink_axis_mask")), + begin_mask: int.Parse(op.get_attr("begin_mask").ToString()), + end_mask: int.Parse(op.get_attr("end_mask").ToString()), + ellipsis_mask: int.Parse(op.get_attr("ellipsis_mask").ToString()), + new_axis_mask: int.Parse(op.get_attr("new_axis_mask").ToString()), + shrink_axis_mask: int.Parse(op.get_attr("shrink_axis_mask").ToString())), null, null, null diff --git a/src/TensorFlowNET.Core/Gradients/math_grad.cs b/src/TensorFlowNET.Core/Gradients/math_grad.cs index e6fd68e7..a84185f3 100644 --- a/src/TensorFlowNET.Core/Gradients/math_grad.cs +++ b/src/TensorFlowNET.Core/Gradients/math_grad.cs @@ -16,6 +16,7 @@ using System; using System.Linq; +using Tensorflow.Operations; using static Tensorflow.Python; namespace Tensorflow.Gradients @@ -26,6 +27,15 @@ namespace Tensorflow.Gradients [RegisterGradient("math_grad")] public class math_grad { + [RegisterGradient("Abs")] + public static Tensor[] _AbsGrad(Operation op, Tensor[] grads) + { + var x = op.inputs[0]; + var grad = grads[0]; + + return new Tensor[] { gen_ops.mul(grad, gen_math_ops.sign(x)) }; + } + [RegisterGradient("Add")] public static Tensor[] _AddGrad(Operation op, Tensor[] grads) { @@ -428,6 +438,15 @@ namespace Tensorflow.Gradients }); } + [RegisterGradient("Sign")] + public static Tensor[] _SignGrad(Operation op, Tensor[] grads) + { + var x = op.inputs[0]; + var zero = constant_op.constant(0.0f, x.dtype, x.shape); + + return new Tensor[] {zero}; + } + [RegisterGradient("Square")] public static Tensor[] _SquareGrad(Operation op, Tensor[] grads) { diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs index 958106ee..7fcfdbd7 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs @@ -55,11 +55,11 @@ namespace Tensorflow return Status; } - public static Graph ImportFromPB(string file_path) + public static Graph ImportFromPB(string file_path, string name = null) { var graph = tf.Graph().as_default(); var graph_def = GraphDef.Parser.ParseFrom(File.ReadAllBytes(file_path)); - importer.import_graph_def(graph_def); + importer.import_graph_def(graph_def, name: name); return graph; } } diff --git a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs index b8880fae..092d152c 100644 --- a/src/TensorFlowNET.Core/Operations/gen_array_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_array_ops.cs @@ -426,7 +426,7 @@ namespace Tensorflow /// A name for the operation (optional). /// A `Tensor`. Has the same type as `dy`. public static Tensor strided_slice_grad(Tensor shape, Tensor begin, Tensor end, Tensor strides, Tensor dy, - int begin_mask = 0, int end_mask = 0, int ellipsis_mask = 0, int new_axis_mask = 0, + int begin_mask = 0, int end_mask = 0, int ellipsis_mask = 0, int new_axis_mask = 0, int shrink_axis_mask = 0, string name = null) { var op = _op_def_lib._apply_op_helper("StridedSliceGrad", name: name, args: new diff --git a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs index dca38a9a..7ab6f858 100644 --- a/src/TensorFlowNET.Core/Operations/gen_math_ops.cs +++ b/src/TensorFlowNET.Core/Operations/gen_math_ops.cs @@ -210,6 +210,13 @@ namespace Tensorflow return op.outputs[0]; } + public static Tensor sign(Tensor x, string name = "Sign") + { + var op = _op_def_lib._apply_op_helper("Sign", name: name, args: new {x}); + + return op.outputs[0]; + } + public static Tensor sinh(Tensor x, string name = null) { var _op = _op_def_lib._apply_op_helper("Sinh", name, args: new { x }); @@ -369,6 +376,13 @@ namespace Tensorflow return _op.outputs[0]; } + public static Tensor is_nan(Tensor x, string name = null) + { + var _op = _op_def_lib._apply_op_helper("IsNan", name: name, args: new { x }); + + return _op.outputs[0]; + } + /// /// Computes exponential of x element-wise. \\(y = e^x\\). /// diff --git a/src/TensorFlowNET.Core/Operations/math_ops.cs b/src/TensorFlowNET.Core/Operations/math_ops.cs index 9410b1b0..486fea8d 100644 --- a/src/TensorFlowNET.Core/Operations/math_ops.cs +++ b/src/TensorFlowNET.Core/Operations/math_ops.cs @@ -216,6 +216,15 @@ namespace Tensorflow return gen_math_ops.sigmoid(x_tensor, name: name); } + public static Tensor sign(Tensor x, string name = null) + { + return with(ops.name_scope(name, "Sign", new {x}), scope => + { + x = ops.convert_to_tensor(x, name: "x"); + return gen_math_ops.sign(x); + }); + } + /// /// Returns (x - y)(x - y) element-wise. /// diff --git a/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs b/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs index 416e88b4..41bd0ddf 100644 --- a/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs +++ b/src/TensorFlowNET.Core/Operations/resource_variable_ops.cs @@ -14,12 +14,15 @@ limitations under the License. ******************************************************************************/ +using System; +using Tensorflow.Framework; + namespace Tensorflow { /// /// tensorflow\python\ops\resource_variable_ops.py /// - public class resource_variable_ops + public static class resource_variable_ops { public static ITensorOrOperation shape_safe_assign_variable_handle(Tensor handle, int[] shape, Tensor value, string name = null) { @@ -29,9 +32,61 @@ namespace Tensorflow name: name); } + /// + /// + /// + /// + /// + /// + /// + /// + /// If `read_value` is `True`, this method will return the new value of the + /// variable after the assignment has completed.Otherwise, when in graph mode + /// it will return the `Operation` that does the assignment, and when in eager + /// mode it will return `None`. + /// + public static Operation assign(this Tensor self, Tensor value, bool use_locking = false, string name = null, bool read_value = true) + { + var value_tensor = ops.convert_to_tensor(value, dtype: self.dtype); + self.assert_is_compatible_with(value_tensor); + var assign_op = gen_resource_variable_ops.assign_variable_op(self, value_tensor, name: name); + if (read_value) + { + return self._lazy_read(assign_op); + } + + return assign_op; + } + + public static Operation _lazy_read(this Tensor self, Operation op) + { + variable_accessed(self); + throw new NotImplementedException(); + } + + public static void variable_accessed(this Tensor variable) + { + throw new NotImplementedException(); + } + public static bool is_resource_variable(VariableV1 var) { return var is ResourceVariable; } + + /// + /// Represents a future for a read of a variable. + /// Pretends to be the tensor if anyone looks. + /// + public class _UnreadVariable : BaseResourceVariable + { + } + + /// + /// A python variable from an existing handle. + /// + public class BaseResourceVariable : VariableV1 + { + } } } diff --git a/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj b/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj index 5e85d29f..1ec4f6f3 100644 --- a/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj +++ b/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj @@ -5,7 +5,7 @@ TensorFlow.NET Tensorflow 1.14.0 - 0.10.0 + 0.10.3 Haiping Chen, Meinrad Recheis SciSharp STACK true @@ -16,9 +16,8 @@ https://avatars3.githubusercontent.com/u/44989469?s=200&v=4 TensorFlow, NumSharp, SciSharp, MachineLearning, TensorFlow.NET, C# Google's TensorFlow full binding in .NET Standard. -Docs: https://tensorflownet.readthedocs.io -Medium: https://medium.com/scisharp - 0.10.0.0 +Docs: https://tensorflownet.readthedocs.io + 0.10.3.0 Changes since v0.9.0: 1. Added full connected Convolution Neural Network example. @@ -27,11 +26,14 @@ Medium: https://medium.com/scisharp 4. Fix path issue of Transfer Learning example on Linux. 5. Add back gen_ops.cs. 6. Add StridedSliceGrad. -7. Add BatchMatMulGrad. +7. Add BatchMatMulGrad. +8. Upgrade NumSharp. +9. Fix strided_slice_grad type convention error. +10. Add AbsGrad. 7.2 - 0.10.0.0 + 0.10.3.0 LICENSE - false + true true Open.snk @@ -61,7 +63,7 @@ Medium: https://medium.com/scisharp - + diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index a2a77a7e..e7049e7e 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Tensorflow.Framework; using static Tensorflow.Python; namespace Tensorflow @@ -27,7 +28,7 @@ namespace Tensorflow /// A tensor is a generalization of vectors and matrices to potentially higher dimensions. /// Internally, TensorFlow represents tensors as n-dimensional arrays of base datatypes. /// - public partial class Tensor : IDisposable, ITensorOrOperation + public partial class Tensor : IDisposable, ITensorOrOperation, _TensorLike { private IntPtr _handle; @@ -109,6 +110,8 @@ namespace Tensorflow this.shape = shape.Dimensions; } + public int[] dims => shape; + /// /// number of dimensions /// 0 Scalar (magnitude only) diff --git a/src/TensorFlowNET.Core/Tensors/dtypes.cs b/src/TensorFlowNET.Core/Tensors/dtypes.cs index c809c96b..16b09d05 100644 --- a/src/TensorFlowNET.Core/Tensors/dtypes.cs +++ b/src/TensorFlowNET.Core/Tensors/dtypes.cs @@ -205,5 +205,10 @@ namespace Tensorflow { return (int)type > 100; } + + public static bool is_compatible_with(this TF_DataType self, TF_DataType other) + { + return self.as_datatype_enum() == other.as_datatype_enum(); + } } } diff --git a/src/TensorFlowNET.Core/Tensors/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs index 473bb7ca..2153e2d7 100644 --- a/src/TensorFlowNET.Core/Tensors/tensor_util.cs +++ b/src/TensorFlowNET.Core/Tensors/tensor_util.cs @@ -214,6 +214,12 @@ namespace Tensorflow else nparray = Convert.ToString(values); break; + case "Boolean": + if (values.GetType().IsArray) + nparray = np.array((bool[])values, np_dt); + else + nparray = Convert.ToBoolean(values); + break; default: throw new NotImplementedException($"make_tensor_proto: Support for type {np_dt.Name} Not Implemented"); } diff --git a/src/TensorFlowNET.Core/Variables/RefVariable.cs b/src/TensorFlowNET.Core/Variables/RefVariable.cs index d380975e..78a241c2 100644 --- a/src/TensorFlowNET.Core/Variables/RefVariable.cs +++ b/src/TensorFlowNET.Core/Variables/RefVariable.cs @@ -40,6 +40,8 @@ namespace Tensorflow public override string name => _variable.name; + public Tensor eval() => _variable; + public RefVariable(object initial_value = null, bool trainable = true, List collections = null, diff --git a/src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs b/src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs index e1ab9e20..af34a2ba 100644 --- a/src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs +++ b/src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs @@ -12,102 +12,102 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -******************************************************************************/ - -using System.Collections.Generic; -using Tensorflow.Eager; - -namespace Tensorflow -{ - public class gen_state_ops - { - public static OpDefLibrary _op_def_lib = new OpDefLibrary(); - public static Execute _execute = new Execute(); - - /// - /// Holds state in the form of a tensor that persists across steps. - /// Outputs a ref to the tensor state so it may be read or modified. - /// - /// The shape of the variable tensor. - /// The type of elements in the variable tensor. - /// - /// - /// - /// - public static Tensor variable_v2(int[] shape, TF_DataType dtype, string name = null, string container = "", string shared_name = "") - { - 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; - - var _attrs = new Dictionary(); - _attrs["dtype"] = _op.get_attr("dtype"); - _attrs["shape"] = _op.get_attr("shape"); - _attrs["container"] = _op.get_attr("container"); - _attrs["shared_name"] = _op.get_attr("shared_name"); - - _execute.record_gradient("VariableV2", _inputs_flat, _attrs, _result, name); - - return _result[0]; - } - - /// - /// Update 'ref' by assigning 'value' to it - /// - /// - /// - /// - /// - /// - public static Tensor assign(Tensor @ref, object value, - bool validate_shape = true, - bool use_locking = true, - string name = null) - { - var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { @ref, value, validate_shape, use_locking }); - - var _result = _op.outputs; - var _inputs_flat = _op.inputs; - - var _attrs = new Dictionary(); - _attrs["T"] = _op.get_attr("T"); - _attrs["validate_shape"] = _op.get_attr("validate_shape"); - _attrs["use_locking"] = _op.get_attr("use_locking"); - - _execute.record_gradient("Assign", _inputs_flat, _attrs, _result, name); - - return _result[0]; - } - +******************************************************************************/ + +using System.Collections.Generic; +using Tensorflow.Eager; + +namespace Tensorflow +{ + public class gen_state_ops + { + public static OpDefLibrary _op_def_lib = new OpDefLibrary(); + public static Execute _execute = new Execute(); + + /// + /// Holds state in the form of a tensor that persists across steps. + /// Outputs a ref to the tensor state so it may be read or modified. + /// + /// The shape of the variable tensor. + /// The type of elements in the variable tensor. + /// + /// + /// + /// + public static Tensor variable_v2(int[] shape, TF_DataType dtype, string name = null, string container = "", string shared_name = "") + { + 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; + + var _attrs = new Dictionary(); + _attrs["dtype"] = _op.get_attr("dtype"); + _attrs["shape"] = _op.get_attr("shape"); + _attrs["container"] = _op.get_attr("container"); + _attrs["shared_name"] = _op.get_attr("shared_name"); + + _execute.record_gradient("VariableV2", _inputs_flat, _attrs, _result, name); + + return _result[0]; + } + + /// + /// Update 'ref' by assigning 'value' to it + /// + /// + /// + /// + /// + /// + public static Tensor assign(Tensor @ref, object value, + bool validate_shape = true, + bool use_locking = true, + string name = null) + { + var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { @ref, value, validate_shape, use_locking }); + + var _result = _op.outputs; + var _inputs_flat = _op.inputs; + + var _attrs = new Dictionary(); + _attrs["T"] = _op.get_attr("T"); + _attrs["validate_shape"] = _op.get_attr("validate_shape"); + _attrs["use_locking"] = _op.get_attr("use_locking"); + + _execute.record_gradient("Assign", _inputs_flat, _attrs, _result, name); + + return _result[0]; + } + public static Tensor assign(RefVariable @ref, object value, bool validate_shape = true, - bool use_locking = true, - string name = null) - { - var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { @ref, value, validate_shape, use_locking }); - - var _result = _op.outputs; - var _inputs_flat = _op.inputs; - - var _attrs = new Dictionary(); - _attrs["T"] = _op.get_attr("T"); - _attrs["validate_shape"] = _op.get_attr("validate_shape"); - _attrs["use_locking"] = _op.get_attr("use_locking"); - - _execute.record_gradient("Assign", _inputs_flat, _attrs, _result, name); - - return _result[0]; - } - - public static Tensor assign_sub(RefVariable @ref, - Tensor value, - bool use_locking = false, - string name = null) - { - var _op = _op_def_lib._apply_op_helper("AssignSub", name: name, args: new { @ref, value, use_locking }); - - return _op.outputs[0]; + bool use_locking = true, + string name = null) + { + var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { @ref, value, validate_shape, use_locking }); + + var _result = _op.outputs; + var _inputs_flat = _op.inputs; + + var _attrs = new Dictionary(); + _attrs["T"] = _op.get_attr("T"); + _attrs["validate_shape"] = _op.get_attr("validate_shape"); + _attrs["use_locking"] = _op.get_attr("use_locking"); + + _execute.record_gradient("Assign", _inputs_flat, _attrs, _result, name); + + return _result[0]; + } + + public static Tensor assign_sub(RefVariable @ref, + Tensor value, + bool use_locking = false, + string name = null) + { + var _op = _op_def_lib._apply_op_helper("AssignSub", name: name, args: new { @ref, value, use_locking }); + + return _op.outputs[0]; } @@ -125,10 +125,10 @@ namespace Tensorflow // name: A name for the operation(optional). // Returns: // A mutable `Tensor`. Has the same type as `ref`. - public static Tensor assign_add(RefVariable @ref, Tensor value, bool use_locking = false, string name = null) - { - var _op = _op_def_lib._apply_op_helper("AssignAdd", name: name, args: new { @ref, value, use_locking }); - return _op.outputs[0]; + public static Tensor assign_add(RefVariable @ref, Tensor value, bool use_locking = false, string name = null) + { + var _op = _op_def_lib._apply_op_helper("AssignAdd", name: name, args: new { @ref, value, use_locking }); + return _op.outputs[0]; } /// @@ -142,8 +142,8 @@ namespace Tensorflow /// public static Tensor scatter_add(RefVariable @ref, Tensor indices, Tensor updates, bool use_locking = false, string name = null) { - var _op = _op_def_lib._apply_op_helper("ScatterAdd", name: name, args: new { @ref, indices, updates, use_locking }); + var _op = _op_def_lib._apply_op_helper("ScatterAdd", name: name, args: new { @ref, indices, updates, use_locking }); return _op.outputs[0]; - } - } -} + } + } +} diff --git a/src/TensorFlowNET.Core/Variables/state_ops.cs b/src/TensorFlowNET.Core/Variables/state_ops.cs index e89844f9..502c3c1e 100644 --- a/src/TensorFlowNET.Core/Variables/state_ops.cs +++ b/src/TensorFlowNET.Core/Variables/state_ops.cs @@ -12,99 +12,99 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -******************************************************************************/ - -using System; - -namespace Tensorflow -{ - public class state_ops - { - /// - /// Create a variable Operation. - /// - /// - /// - /// - /// - /// - /// - public static Tensor variable_op_v2(int[] shape, - TF_DataType dtype, - string name = "Variable", - string container = "", - string shared_name = "") => gen_state_ops.variable_v2(shape, - dtype, - name: name, - container: container, - shared_name: shared_name); - - public static Tensor assign(Tensor @ref, object value, - bool validate_shape = true, - bool use_locking = true, - string name = null) - { - if (@ref.dtype.is_ref_dtype()) - return gen_state_ops.assign(@ref, - value, - validate_shape: validate_shape, - use_locking: use_locking, - name: name); - throw new NotImplementedException("state_ops.assign"); - //return @ref.assign(value, name: name); - } - - public static Tensor assign(RefVariable @ref, object value, - bool validate_shape = true, - bool use_locking = true, - string name = null) +******************************************************************************/ + +using System; + +namespace Tensorflow +{ + public class state_ops + { + /// + /// Create a variable Operation. + /// + /// + /// + /// + /// + /// + /// + public static Tensor variable_op_v2(int[] shape, + TF_DataType dtype, + string name = "Variable", + string container = "", + string shared_name = "") => gen_state_ops.variable_v2(shape, + dtype, + name: name, + container: container, + shared_name: shared_name); + + public static Tensor assign(Tensor @ref, object value, + bool validate_shape = true, + bool use_locking = true, + string name = null) + { + if (@ref.dtype.is_ref_dtype()) + return gen_state_ops.assign(@ref, + value, + validate_shape: validate_shape, + use_locking: use_locking, + name: name); + + return @ref.assign((Tensor)value, name: name); + } + + public static Tensor assign(RefVariable @ref, object value, + bool validate_shape = true, + bool use_locking = true, + string name = null) { return gen_state_ops.assign(@ref, value, validate_shape: validate_shape, use_locking: use_locking, - name: name); - } - - public static Tensor assign_sub(RefVariable @ref, - Tensor value, - bool use_locking = false, - string name = null) => gen_state_ops.assign_sub(@ref, - value, - use_locking: use_locking, - name: name); - - //"""Update 'ref' by adding 'value' to it. - // - // This operation outputs "ref" after the update is done. - // This makes it easier to chain operations that need to use the reset value. - // - // Args: - // ref: A mutable `Tensor`. Must be one of the following types: - // `float32`, `float64`, `int64`, `int32`, `uint8`, `uint16`, `int16`, - // `int8`, `complex64`, `complex128`, `qint8`, `quint8`, `qint32`, `half`. - // Should be from a `Variable` node. - // value: A `Tensor`. Must have the same type as `ref`. - // The value to be added to the variable. - // use_locking: An optional `bool`. Defaults to `False`. - // If True, the addition will be protected by a lock; - // otherwise the behavior is undefined, but may exhibit less contention. - // name: A name for the operation (optional). + name: name); + } + + public static Tensor assign_sub(RefVariable @ref, + Tensor value, + bool use_locking = false, + string name = null) => gen_state_ops.assign_sub(@ref, + value, + use_locking: use_locking, + name: name); + + //"""Update 'ref' by adding 'value' to it. + // + // This operation outputs "ref" after the update is done. + // This makes it easier to chain operations that need to use the reset value. + // + // Args: + // ref: A mutable `Tensor`. Must be one of the following types: + // `float32`, `float64`, `int64`, `int32`, `uint8`, `uint16`, `int16`, + // `int8`, `complex64`, `complex128`, `qint8`, `quint8`, `qint32`, `half`. + // Should be from a `Variable` node. + // value: A `Tensor`. Must have the same type as `ref`. + // The value to be added to the variable. + // use_locking: An optional `bool`. Defaults to `False`. + // If True, the addition will be protected by a lock; + // otherwise the behavior is undefined, but may exhibit less contention. + // name: A name for the operation (optional). // // Returns: // Same as "ref". Returned as a convenience for operations that want // to use the new value after the variable has been updated. - public static Tensor assign_add(RefVariable @ref, - Tensor value, - bool use_locking = false, - string name = null) => gen_state_ops.assign_add(@ref, value, use_locking: use_locking, name: name); - + public static Tensor assign_add(RefVariable @ref, + Tensor value, + bool use_locking = false, + string name = null) => gen_state_ops.assign_add(@ref, value, use_locking: use_locking, name: name); + public static Tensor scatter_add(RefVariable @ref, Tensor indices, Tensor updates, bool use_locking = false, string name = null) { if (@ref.dtype.is_ref_dtype()) return gen_state_ops.scatter_add(@ref, indices, updates, use_locking: use_locking, name: name); throw new NotImplementedException("scatter_add"); - } - } -} + } + } +} diff --git a/src/TensorFlowNET.Core/tf.cs b/src/TensorFlowNET.Core/tf.cs index 87cedc59..b10f41b0 100644 --- a/src/TensorFlowNET.Core/tf.cs +++ b/src/TensorFlowNET.Core/tf.cs @@ -46,7 +46,7 @@ namespace Tensorflow trainable: trainable, validate_shape: validate_shape, name: name, - dtype: TF_DataType.DtInvalid); + dtype: dtype); } public static unsafe Tensor placeholder(TF_DataType dtype, TensorShape shape = null, string name = null) @@ -72,5 +72,10 @@ namespace Tensorflow { return new Session(graph); } + + public static Session Session(SessionOptions opts) + { + return new Session(null, opts); + } } } diff --git a/src/TensorFlowNet.Benchmarks/TensorFlowNET.Benchmark.csproj b/src/TensorFlowNet.Benchmarks/TensorFlowNET.Benchmark.csproj new file mode 100644 index 00000000..a0af6db4 --- /dev/null +++ b/src/TensorFlowNet.Benchmarks/TensorFlowNET.Benchmark.csproj @@ -0,0 +1,20 @@ + + + + Exe + netcoreapp2.2 + + + + + + + + + + + + + + + diff --git a/src/TensorFlowText/README.md b/src/TensorFlowText/README.md new file mode 100644 index 00000000..2d82ea9d --- /dev/null +++ b/src/TensorFlowText/README.md @@ -0,0 +1,9 @@ +# TensorFlow Text - Text processing in Tensorflow + +TensorFlow Text provides a collection of text related classes and ops ready to use with TensorFlow 2.0. The library can perform the preprocessing regularly required by text-based models, and includes other features useful for sequence modeling not provided by core TensorFlow. + +The benefit of using these ops in your text preprocessing is that they are done in the TensorFlow graph. You do not need to worry about tokenization in training being different than the tokenization at inference, or managing preprocessing scripts. + + + +https://github.com/tensorflow/text \ No newline at end of file diff --git a/src/TensorFlowText/TensorFlowText.csproj b/src/TensorFlowText/TensorFlowText.csproj new file mode 100644 index 00000000..2bd48b21 --- /dev/null +++ b/src/TensorFlowText/TensorFlowText.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp2.2 + + + diff --git a/src/TensorFlowText/Tokenizer.cs b/src/TensorFlowText/Tokenizer.cs new file mode 100644 index 00000000..5621bcf7 --- /dev/null +++ b/src/TensorFlowText/Tokenizer.cs @@ -0,0 +1,8 @@ +using System; + +namespace TensorFlowText +{ + public class Tokenizer + { + } +} diff --git a/test/KerasNET.Example/Keras.Example.csproj b/test/KerasNET.Example/Keras.Example.csproj deleted file mode 100644 index 14fa0138..00000000 --- a/test/KerasNET.Example/Keras.Example.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exe - netcoreapp2.2 - false - Keras.Example.Program - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/KerasNET.Example/packages.config b/test/KerasNET.Example/packages.config deleted file mode 100644 index e7c17277..00000000 --- a/test/KerasNET.Example/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/test/KerasNET.Test/Keras.UnitTest.csproj b/test/KerasNET.Test/Keras.UnitTest.csproj deleted file mode 100644 index 980ad049..00000000 --- a/test/KerasNET.Test/Keras.UnitTest.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - netcoreapp2.2 - - false - - Keras.UnitTest - - Keras.UnitTest - - - - Exe - - - - - - DEBUG;TRACE - true - - - - true - - - - - - - - - - - - - - - - diff --git a/test/KerasNET.Test/packages.config b/test/KerasNET.Test/packages.config deleted file mode 100644 index 7e0fea67..00000000 --- a/test/KerasNET.Test/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/test/TensorFlowHub.Examples/Program.cs b/test/TensorFlowHub.Examples/Program.cs deleted file mode 100644 index 3fdbad12..00000000 --- a/test/TensorFlowHub.Examples/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace TensorFlowHub.Examples -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} diff --git a/test/TensorFlowHub.Examples/TensorFlowHub.Examples.csproj b/test/TensorFlowHub.Examples/TensorFlowHub.Examples.csproj deleted file mode 100644 index ae01aa36..00000000 --- a/test/TensorFlowHub.Examples/TensorFlowHub.Examples.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - netcoreapp2.2 - - - - - - - - - - - diff --git a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs index 628a7b5c..4d3a858f 100644 --- a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs +++ b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs @@ -80,6 +80,16 @@ namespace TensorFlowNET.Examples.ImageProcess { PrepareData(); + #region For debug purpose + + // predict images + Predict(null); + + // load saved pb and test new images. + Test(null); + + #endregion + var graph = IsImportingGraph ? ImportGraph() : BuildGraph(); with(tf.Session(graph), sess => @@ -708,14 +718,77 @@ namespace TensorFlowNET.Examples.ImageProcess File.WriteAllText(output_labels, string.Join("\n", image_lists.Keys)); } - public void Predict(Session sess) + /// + /// Prediction + /// labels mapping, it's from output_lables.txt + /// 0 - daisy + /// 1 - dandelion + /// 2 - roses + /// 3 - sunflowers + /// 4 - tulips + /// + /// + public void Predict(Session sess_) { - throw new NotImplementedException(); + if (!File.Exists(output_graph)) + return; + + var labels = File.ReadAllLines(output_labels); + + // predict image + var img_path = Path.Join(image_dir, "roses", "12240303_80d87f77a3_n.jpg"); + var fileBytes = ReadTensorFromImageFile(img_path); + + // import graph and variables + var graph = Graph.ImportFromPB(output_graph, ""); + + Tensor input = graph.OperationByName("Placeholder"); + Tensor output = graph.OperationByName("final_result"); + + with(tf.Session(graph), sess => + { + var result = sess.run(output, new FeedItem(input, fileBytes)); + var prob = np.squeeze(result); + var idx = np.argmax(prob); + print($"Prediction result: [{labels[idx]} {prob[idx][0]}] for {img_path}."); + }); } - public void Test(Session sess) + private NDArray ReadTensorFromImageFile(string file_name, + int input_height = 299, + int input_width = 299, + int input_mean = 0, + int input_std = 255) { - throw new NotImplementedException(); + return with(tf.Graph().as_default(), graph => + { + var file_reader = tf.read_file(file_name, "file_reader"); + var image_reader = tf.image.decode_jpeg(file_reader, channels: 3, name: "jpeg_reader"); + var caster = tf.cast(image_reader, tf.float32); + var dims_expander = tf.expand_dims(caster, 0); + var resize = tf.constant(new int[] { input_height, input_width }); + var bilinear = tf.image.resize_bilinear(dims_expander, resize); + var sub = tf.subtract(bilinear, new float[] { input_mean }); + var normalized = tf.divide(sub, new float[] { input_std }); + + return with(tf.Session(graph), sess => sess.run(normalized)); + }); + } + + public void Test(Session sess_) + { + if (!File.Exists(output_graph)) + return; + + var graph = Graph.ImportFromPB(output_graph); + var (jpeg_data_tensor, decoded_image_tensor) = add_jpeg_decoding(); + + with(tf.Session(graph), sess => + { + (test_accuracy, predictions) = run_final_eval(sess, null, class_count, image_lists, + jpeg_data_tensor, decoded_image_tensor, resized_image_tensor, + bottleneck_tensor); + }); } } } diff --git a/test/KerasNET.Example/Program.cs b/test/TensorFlowNET.Examples/Keras.cs similarity index 64% rename from test/KerasNET.Example/Program.cs rename to test/TensorFlowNET.Examples/Keras.cs index f85fe127..3bf46c00 100644 --- a/test/KerasNET.Example/Program.cs +++ b/test/TensorFlowNET.Examples/Keras.cs @@ -3,12 +3,18 @@ using System.Collections.Generic; using Tensorflow; using Keras.Layers; using NumSharp; +using Keras; -namespace Keras.Example +namespace TensorFlowNET.Examples { - class Program + public class Keras : IExample { - static void Main(string[] args) + public bool Enabled { get; set; } = true; + public bool IsImportingGraph { get; set; } = false; + + public string Name => "Keras"; + + public bool Run() { Console.WriteLine("================================== Keras =================================="); @@ -25,7 +31,7 @@ namespace Keras.Example #region model var m = new Model(); - + //m.Add(new Dense(8, name: "Hidden", activation: tf.nn.relu())).Add(new Dense(1, name:"Output")); m.Add( @@ -37,8 +43,9 @@ namespace Keras.Example m.train(num_steps, (X, Y)); #endregion - Console.ReadKey(); + return true; } + static (NDArray, NDArray) XOR(int samples) { var X = new List(); @@ -57,5 +64,35 @@ namespace Keras.Example return (np.array(X.ToArray()), np.array(Y.ToArray())); } + + public Graph BuildGraph() + { + throw new NotImplementedException(); + } + + public Graph ImportGraph() + { + throw new NotImplementedException(); + } + + public void Predict(Session sess) + { + throw new NotImplementedException(); + } + + public void PrepareData() + { + throw new NotImplementedException(); + } + + public void Test(Session sess) + { + throw new NotImplementedException(); + } + + public void Train(Session sess) + { + throw new NotImplementedException(); + } } } diff --git a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj index 83f4a3ba..149bd549 100644 --- a/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj +++ b/test/TensorFlowNET.Examples/TensorFlowNET.Examples.csproj @@ -15,6 +15,8 @@ + + diff --git a/test/TensorFlowNET.UnitTest/Hub/MnistModelLoaderTest.cs b/test/TensorFlowNET.UnitTest/Hub/MnistModelLoaderTest.cs new file mode 100644 index 00000000..09997d5c --- /dev/null +++ b/test/TensorFlowNET.UnitTest/Hub/MnistModelLoaderTest.cs @@ -0,0 +1,28 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Tensorflow; +using Tensorflow.Hub; + +namespace TensorFlowNET.UnitTest.Hub +{ + [TestClass] + public class MnistModelLoaderTest + { + [TestMethod] + public async Task TestLoad() + { + var loader = new MnistModelLoader(); + var result = await loader.LoadAsync(new ModelLoadSetting + { + TrainDir = "mnist", + OneHot = true, + ValidationSize = 5000, + }); + + Assert.IsNotNull(result); + } + } +} diff --git a/test/KerasNET.Test/BaseTests.cs b/test/TensorFlowNET.UnitTest/KerasTests.cs similarity index 95% rename from test/KerasNET.Test/BaseTests.cs rename to test/TensorFlowNET.UnitTest/KerasTests.cs index 05cde48c..707a6083 100644 --- a/test/KerasNET.Test/BaseTests.cs +++ b/test/TensorFlowNET.UnitTest/KerasTests.cs @@ -3,7 +3,7 @@ using Keras.Layers; using NumSharp; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Keras.Test +namespace TensorFlowNET.UnitTest { [TestClass] public class BaseTests diff --git a/test/TensorFlowNET.UnitTest/OperationsTest.cs b/test/TensorFlowNET.UnitTest/OperationsTest.cs index 0359e4c3..7f099fde 100644 --- a/test/TensorFlowNET.UnitTest/OperationsTest.cs +++ b/test/TensorFlowNET.UnitTest/OperationsTest.cs @@ -61,6 +61,34 @@ namespace TensorFlowNET.UnitTest } } + [TestMethod] + public void isFinite() + { + var a = tf.constant(new[] { 1, np.nan, 2, np.nan, 3, np.nan, 4, np.nan }); + var b = tf.cast(tf.is_finite(a), tf.float32); + var check = np.array(1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); + + using (var sess = tf.Session()) + { + var o = sess.run(b); + Assert.IsTrue(o.array_equal(check)); + } + } + + [TestMethod] + public void isNan() + { + var a = tf.constant(new[] { 1, np.nan, 2, np.nan, 3, np.nan, 4, np.nan }); + var b = tf.cast(tf.is_nan(a), tf.float32); + var check = np.array(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); + + using (var sess = tf.Session()) + { + var o = sess.run(b); + Assert.IsTrue(o.array_equal(check)); + } + } + [TestMethod] public void addOpTests() { diff --git a/test/TensorFlowNET.UnitTest/PythonTest.cs b/test/TensorFlowNET.UnitTest/PythonTest.cs index d5d78425..6ea4fd19 100644 --- a/test/TensorFlowNET.UnitTest/PythonTest.cs +++ b/test/TensorFlowNET.UnitTest/PythonTest.cs @@ -126,7 +126,6 @@ namespace TensorFlowNET.UnitTest if (tensors == null) return null; return nest.map_structure(self._eval_tensor, tensors); - return null; } protected object _eval_tensor(object tensor) @@ -145,7 +144,7 @@ namespace TensorFlowNET.UnitTest // tensor.dense_shape) //return (tensor as Tensor).numpy(); } - catch (Exception e) + catch (Exception) { throw new ValueError("Unsupported type: " + tensor.GetType()); } diff --git a/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj b/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj index deb8d447..848512f0 100644 --- a/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj +++ b/test/TensorFlowNET.UnitTest/TensorFlowNET.UnitTest.csproj @@ -23,7 +23,10 @@ + + + diff --git a/test/TensorFlowNET.UnitTest/control_flow_ops_test/SwitchTestCase.cs b/test/TensorFlowNET.UnitTest/control_flow_ops_test/SwitchTestCase.cs index b4322ab3..5d5c1b6e 100644 --- a/test/TensorFlowNET.UnitTest/control_flow_ops_test/SwitchTestCase.cs +++ b/test/TensorFlowNET.UnitTest/control_flow_ops_test/SwitchTestCase.cs @@ -18,10 +18,12 @@ namespace TensorFlowNET.UnitTest.control_flow_ops_test //var embedding_matrix = variable_scope.get_variable( //"embedding_matrix", initializer: new double[,] { { 2.0 }, { 3.0 } }, use_resource: true); + /* Tensor cond(Tensor it, Tensor _) { return it < 5; } + */ // TODO: below code doesn't compile //(Tensor, Tensor) body(Tensor it, Tensor cost) diff --git a/test/TensorFlowNET.UnitTest/gradients_test/GradientsTest.cs b/test/TensorFlowNET.UnitTest/gradients_test/GradientsTest.cs index b9149c88..d1249cf0 100644 --- a/test/TensorFlowNET.UnitTest/gradients_test/GradientsTest.cs +++ b/test/TensorFlowNET.UnitTest/gradients_test/GradientsTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using NumSharp; using Tensorflow; @@ -35,10 +34,10 @@ namespace TensorFlowNET.UnitTest.gradients_test [TestMethod] public void testBatchMatMulGradient() { - var a = tf.constant(np.array(Enumerable.Range(1, 18).Select(elem => (float)elem).ToArray()), shape:new []{2, 3, 3}); + var a = tf.constant(np.array(Enumerable.Range(1, 18).Select(elem => (float)elem).ToArray()), shape: new[] { 2, 3, 3 }); var b = tf.divide(a, tf.constant(2.0f)); var c = tf.batch_matmul(a, b); - var g = tf.gradients(c, new[] {a, b}, stop_gradients: new[] {a, b}); + var g = tf.gradients(c, new[] { a, b }, stop_gradients: new[] { a, b }); var checkG = new[] { 3.0f, 7.5f, 12.0f, @@ -64,20 +63,87 @@ namespace TensorFlowNET.UnitTest.gradients_test } } + [TestMethod] + public void testTanhGradient() + { + var a = tf.constant(1f); + var b = tf.tanh(a); + var g = tf.gradients(b, a); + using (var sess = tf.Session()) + { + var result = sess.run(g); + var actual = result[0].GetData()[0]; + self.assertEquals(0.41997434127f, actual); + } + } + + + [TestMethod] + public void testLgammaGrad() + { + var a = tf.constant(5f); + var b = tf.lgamma(a); + var g = tf.gradients(b, a); + using (var sess = tf.Session()) + { + var result = sess.run(new object[] { g, b }); + var actualDeriv = result[0].GetData()[0]; + var actual = result[1].GetData()[0]; + self.assertEquals(1.5061177f, actualDeriv); + self.assertEquals(3.17805386f, actual); + } + } + + [TestMethod] + public void testSliceGrad() + { + var a = tf.tanh(tf.constant(new[] { 2f, 3f }, shape: new[] { 2, 1 })); + var b = tf.strided_slice(a, + tf.constant(new[] { 0 }, tf.int32, new[] { 1 }), + tf.constant(new[] { 1 }, tf.int32, new[] { 1 }), + tf.constant(new[] { 1 }, tf.int32, new[] { 1 }) + ); + var g = tf.gradients(b, a); + with(tf.Session(), sess => + { + var result = sess.run(new object[] { g, b }); + var actualDeriv = np.squeeze(result[0]); + var actual = np.squeeze(result[1]); + self.assertEquals(new float[] { 1, 0 }, new float[] { actualDeriv[0], actualDeriv[1] }); + self.assertEquals(0.9640276f, (float)actual); + }); + } + + [TestMethod] + public void testConcatGrad() + { + var a1 = tf.constant(new[] { 2f }, shape: new[] { 1 }); + var a2 = tf.constant(new[] { 3f }, shape: new[] { 1 }); + var a = tf.concat(new List(new[] { a1, a2 }), 0); + var g = tf.gradients(a, a1); + using (var sess = tf.Session()) + { + var result = sess.run(new object[] { g, a }); + var actualDeriv = result[0].GetData()[0]; + var actual = result[1].GetData()[0]; + self.assertEquals(1f, actualDeriv); + self.assertEquals(2f, actual); + } + } [Ignore("TODO")] [TestMethod] public void testUnusedOutput() { - //def testUnusedOutput(self): - // with ops.Graph().as_default(): - // w = constant(1.0, shape=[2, 2]) - // x = constant(1.0, shape=[2, 2]) - // wx = math_ops.matmul(w, x) - // split_wx = array_ops.split(value=wx, num_or_size_splits=2, axis=0) - // c = math_ops.reduce_sum(split_wx[1]) - // gw = gradients.gradients(c, [w])[0] - // self.assertEquals("MatMul", gw.op.type) + //def testUnusedOutput(self): + // with ops.Graph().as_default(): + // w = constant(1.0, shape=[2, 2]) + // x = constant(1.0, shape=[2, 2]) + // wx = math_ops.matmul(w, x) + // split_wx = array_ops.split(value=wx, num_or_size_splits=2, axis=0) + // c = math_ops.reduce_sum(split_wx[1]) + // gw = gradients.gradients(c, [w])[0] + // self.assertEquals("MatMul", gw.op.type) } [Ignore("TODO")] diff --git a/test/TensorFlowNET.UnitTest/ops_test/ControlDependenciesTest.cs b/test/TensorFlowNET.UnitTest/ops_test/ControlDependenciesTest.cs index 16618b4b..21ee3f6d 100644 --- a/test/TensorFlowNET.UnitTest/ops_test/ControlDependenciesTest.cs +++ b/test/TensorFlowNET.UnitTest/ops_test/ControlDependenciesTest.cs @@ -39,7 +39,7 @@ namespace TensorFlowNET.UnitTest.ops_test [TestMethod] public void TestEager() { - Tensor a = null, c = null, d = null, e = null; + Tensor a = null, c = null; object b = null; var calls = 0; Func future = () =>