From de0ee59665f8dacedf775444f5f1e9175e537a55 Mon Sep 17 00:00:00 2001 From: Meinrad Recheis Date: Sat, 13 Jul 2019 18:22:45 +0200 Subject: [PATCH] fixed transfer learning example and prevent double deallocation of unmanaged memory --- .../Tensors/Tensor.Creation.cs | 183 ++++++++++++------ src/TensorFlowNET.Core/tf.cs | 4 +- .../ImageProcessing/RetrainImageClassifier.cs | 4 +- 3 files changed, 124 insertions(+), 67 deletions(-) diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index 2032374a..e22866f0 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -36,8 +36,8 @@ namespace Tensorflow public Tensor(IntPtr handle) { _handle = handle; - } - + } + #if _REGEN %types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] %foreach types% @@ -66,6 +66,8 @@ namespace Tensorflow var v = (#1*)Marshal.AllocHGlobal(sizeof(#1)); *v = value; Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; @@ -74,13 +76,15 @@ namespace Tensorflow } % #else - + + + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(sbyte[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(sbyte)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(sbyte)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -98,20 +102,23 @@ namespace Tensorflow { var v = (sbyte*)Marshal.AllocHGlobal(sizeof(sbyte)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(sbyte)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(sbyte), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(sbyte)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(sbyte), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(byte[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(byte)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(byte)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -129,20 +136,23 @@ namespace Tensorflow { var v = (byte*)Marshal.AllocHGlobal(sizeof(byte)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(byte)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(byte), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(byte)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(byte), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(short[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(short)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(short)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -160,20 +170,23 @@ namespace Tensorflow { var v = (short*)Marshal.AllocHGlobal(sizeof(short)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(short)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(short), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(short)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(short), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(ushort[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ushort)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ushort)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -191,20 +204,23 @@ namespace Tensorflow { var v = (ushort*)Marshal.AllocHGlobal(sizeof(ushort)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ushort)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ushort), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ushort)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ushort), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(int[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(int)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(int)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -222,20 +238,23 @@ namespace Tensorflow { var v = (int*)Marshal.AllocHGlobal(sizeof(int)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(int)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(int), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(int)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(int), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(uint[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(uint)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(uint)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -253,20 +272,23 @@ namespace Tensorflow { var v = (uint*)Marshal.AllocHGlobal(sizeof(uint)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(uint)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(uint), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(uint)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(uint), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(long[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(long)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(long)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -284,20 +306,23 @@ namespace Tensorflow { var v = (long*)Marshal.AllocHGlobal(sizeof(long)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(long)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(long), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(long)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(long), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(ulong[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ulong)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ulong)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -315,20 +340,23 @@ namespace Tensorflow { var v = (ulong*)Marshal.AllocHGlobal(sizeof(ulong)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ulong)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ulong), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ulong)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ulong), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(float[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(float)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(float)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -346,20 +374,23 @@ namespace Tensorflow { var v = (float*)Marshal.AllocHGlobal(sizeof(float)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(float)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(float), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(float)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(float), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(double[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(double)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(double)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -377,20 +408,23 @@ namespace Tensorflow { var v = (double*)Marshal.AllocHGlobal(sizeof(double)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(double)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(double), deallocator: deallocator, ref _deallocatorArgs); - } - + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(double)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(double), deallocator: deallocator, ref _deallocatorArgs); + } + /// /// Create a 1d Tensor from the given linear array and shape /// public Tensor(Complex[] data, TF_DataType? dType = null) { - _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(Complex)), new long[]{data.Length}, data, Marshal.SizeOf()); + _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(Complex)), new long[] { data.Length }, data, Marshal.SizeOf()); } /// @@ -408,12 +442,15 @@ namespace Tensorflow { var v = (Complex*)Marshal.AllocHGlobal(sizeof(Complex)); *v = value; - Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => { + Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs arg) => + { + if (arg.deallocator_called) + return; Marshal.FreeHGlobal(values); arg.deallocator_called = true; //_handle = IntPtr.Zero; }; - _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(Complex)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(Complex), deallocator: deallocator, ref _deallocatorArgs); + _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(Complex)), dims: new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(Complex), deallocator: deallocator, ref _deallocatorArgs); } #endif @@ -443,7 +480,7 @@ namespace Tensorflow { if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte") { - var buffer=nd.Data(); + var buffer = nd.Data(); var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length); var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8)); @@ -497,11 +534,13 @@ namespace Tensorflow return new Tensor(UTF8Encoding.UTF8.GetBytes(nd.Data(0)), TF_DataType.TF_STRING); default: throw new NotImplementedException($"Marshal.Copy failed for {nd.dtype.Name}."); - } - - // Free the original buffer and set flag + } + + // Free the original buffer and set flag Deallocator deallocator = (IntPtr values, IntPtr len, ref DeallocatorArgs args) => { + if (args.deallocator_called) + return; Marshal.FreeHGlobal(values); args.deallocator_called = true; }; @@ -540,8 +579,22 @@ namespace Tensorflow /// specified dimensions. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) + protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) { + if (dt == TF_DataType.TF_STRING && data is byte[]) + { + var buffer = (byte[])data; + var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length); + var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8)); + + IntPtr tensor = c_api.TF_TensorData(handle); + Marshal.WriteInt64(tensor, 0); + fixed (byte* src = &buffer[0]) + c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); + + status.Check(true); + return handle; + } return CreateTensorWithoutCopying(dt, shape, data, 0, data.Length, element_size); } @@ -568,21 +621,23 @@ namespace Tensorflow // get a handle to the pinned array which we will pass on to the tensor computation engine to use var gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned); _isPinnedArray = true; - _deallocatorArgs=new DeallocatorArgs(){ gc_handle = GCHandle.ToIntPtr( gcHandle) }; + _deallocatorArgs = new DeallocatorArgs() { gc_handle = GCHandle.ToIntPtr(gcHandle) }; // Free the original buffer and set flag Deallocator deallocator = (IntPtr ptr, IntPtr len, ref DeallocatorArgs args) => { + if (args.deallocator_called) + return; // note: since the ptr given to tensorflow is just the addr of the pinned object we can not directly free it! we need to free the gcHandle instead GCHandle.FromIntPtr(args.gc_handle).Free(); args.deallocator_called = true; }; - if (shape == null || shape.Length==0) + if (shape == null || shape.Length == 0) return TF_NewTensor(dt, new long[0], 0, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref _deallocatorArgs); else return TF_NewTensor(dt, shape, shape.Length, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref _deallocatorArgs); } - private DeallocatorArgs _deallocatorArgs=new DeallocatorArgs(); + private DeallocatorArgs _deallocatorArgs = new DeallocatorArgs(); } } diff --git a/src/TensorFlowNET.Core/tf.cs b/src/TensorFlowNET.Core/tf.cs index fe709c5b..9959a3a7 100644 --- a/src/TensorFlowNET.Core/tf.cs +++ b/src/TensorFlowNET.Core/tf.cs @@ -24,7 +24,8 @@ namespace Tensorflow { public static partial class tf { - public static TF_DataType bytes = TF_DataType.TF_STRING; + public static TF_DataType @byte = TF_DataType.TF_UINT8; + public static TF_DataType @sbyte = TF_DataType.TF_INT8; public static TF_DataType int16 = TF_DataType.TF_INT16; public static TF_DataType int32 = TF_DataType.TF_INT32; public static TF_DataType int64 = TF_DataType.TF_INT64; @@ -33,6 +34,7 @@ namespace Tensorflow public static TF_DataType float64 = TF_DataType.TF_DOUBLE; public static TF_DataType @bool = TF_DataType.TF_BOOL; public static TF_DataType chars = TF_DataType.TF_STRING; + public static TF_DataType @string = TF_DataType.TF_STRING; public static Context context = new Context(new ContextOptions(), new Status()); diff --git a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs index 0e7d8961..feb34663 100644 --- a/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs +++ b/test/TensorFlowNET.Examples/ImageProcessing/RetrainImageClassifier.cs @@ -557,7 +557,7 @@ namespace TensorFlowNET.Examples.ImageProcess Tensor decoded_image_tensor, Tensor resized_input_tensor, Tensor bottleneck_tensor) { // First decode the JPEG image, resize it, and rescale the pixel values. - var resized_input_values = sess.run(decoded_image_tensor, new FeedItem(image_data_tensor, image_data)); + var resized_input_values = sess.run(decoded_image_tensor, new FeedItem(image_data_tensor, new Tensor( image_data, TF_DataType.TF_STRING))); // Then run it through the recognition network. var bottleneck_values = sess.run(bottleneck_tensor, new FeedItem(resized_input_tensor, resized_input_values)); bottleneck_values = np.squeeze(bottleneck_values); @@ -644,7 +644,7 @@ namespace TensorFlowNET.Examples.ImageProcess { // height, width, depth var input_dim = (299, 299, 3); - var jpeg_data = tf.placeholder(tf.chars, name: "DecodeJPGInput"); + var jpeg_data = tf.placeholder(tf.@string, name: "DecodeJPGInput"); var decoded_image = tf.image.decode_jpeg(jpeg_data, channels: input_dim.Item3); // Convert from full range of uint8 to range [0,1] of float32. var decoded_image_as_float = tf.image.convert_image_dtype(decoded_image, tf.float32);