| @@ -4,6 +4,8 @@ using Tensorflow.Keras.ArgsDefinition; | |||||
| using Tensorflow.Keras.Engine.DataAdapters; | using Tensorflow.Keras.Engine.DataAdapters; | ||||
| using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
| using static Tensorflow.KerasApi; | using static Tensorflow.KerasApi; | ||||
| using System.Linq; | |||||
| using System.Collections.Generic; | |||||
| namespace Tensorflow | namespace Tensorflow | ||||
| { | { | ||||
| @@ -35,13 +37,15 @@ namespace Tensorflow | |||||
| public Action<int, int> ConstantString | public Action<int, int> ConstantString | ||||
| => (epoch, iterate) => | => (epoch, iterate) => | ||||
| { | { | ||||
| var tensor = tf.constant(new string[] | |||||
| var strList = new string[] | |||||
| { | { | ||||
| "Biden immigration bill would put millions of illegal immigrants on 8-year fast-track to citizenship", | "Biden immigration bill would put millions of illegal immigrants on 8-year fast-track to citizenship", | ||||
| "The Associated Press, which also reported that the eight-year path is in the bill.", | "The Associated Press, which also reported that the eight-year path is in the bill.", | ||||
| "The bill would also include provisions to stem the flow of migration by addressing root causes of migration from south of the border." | "The bill would also include provisions to stem the flow of migration by addressing root causes of migration from south of the border." | ||||
| }); | |||||
| var data = tensor.numpy(); | |||||
| }; | |||||
| var tensor = tf.constant(strList, TF_DataType.TF_STRING); | |||||
| var data = tensor.StringData(); | |||||
| }; | }; | ||||
| public Action<int, int> Variable | public Action<int, int> Variable | ||||
| @@ -47,7 +47,7 @@ namespace Tensorflow | |||||
| // explaination of constant | // explaination of constant | ||||
| mm.Execute(10, 100 * batchSize, basic.Constant2x3); | mm.Execute(10, 100 * batchSize, basic.Constant2x3); | ||||
| mm.Execute(10, 100 * batchSize, basic.ConstantString); | |||||
| mm.Execute(10, batchSize, basic.ConstantString); | |||||
| // 100K float variable. | // 100K float variable. | ||||
| mm.Execute(10, batchSize, basic.Variable); | mm.Execute(10, batchSize, basic.Variable); | ||||
| @@ -43,7 +43,7 @@ namespace Tensorflow | |||||
| /// </summary> | /// </summary> | ||||
| public partial class c_api | public partial class c_api | ||||
| { | { | ||||
| public const string TensorFlowLibName = "tensorflow"; | |||||
| public const string TensorFlowLibName = @"D:\Projects\tensorflow-haiping\bazel-bin\tensorflow\tensorflow"; | |||||
| public static string StringPiece(IntPtr handle) | public static string StringPiece(IntPtr handle) | ||||
| { | { | ||||
| @@ -5,7 +5,7 @@ | |||||
| <AssemblyName>TensorFlow.NET</AssemblyName> | <AssemblyName>TensorFlow.NET</AssemblyName> | ||||
| <RootNamespace>Tensorflow</RootNamespace> | <RootNamespace>Tensorflow</RootNamespace> | ||||
| <TargetTensorFlow>2.2.0</TargetTensorFlow> | <TargetTensorFlow>2.2.0</TargetTensorFlow> | ||||
| <Version>0.33.0</Version> | |||||
| <Version>0.40.0</Version> | |||||
| <LangVersion>8.0</LangVersion> | <LangVersion>8.0</LangVersion> | ||||
| <Authors>Haiping Chen, Meinrad Recheis, Eli Belash</Authors> | <Authors>Haiping Chen, Meinrad Recheis, Eli Belash</Authors> | ||||
| <Company>SciSharp STACK</Company> | <Company>SciSharp STACK</Company> | ||||
| @@ -19,7 +19,7 @@ | |||||
| <Description>Google's TensorFlow full binding in .NET Standard. | <Description>Google's TensorFlow full binding in .NET Standard. | ||||
| Building, training and infering deep learning models. | Building, training and infering deep learning models. | ||||
| https://tensorflownet.readthedocs.io</Description> | https://tensorflownet.readthedocs.io</Description> | ||||
| <AssemblyVersion>0.33.0.0</AssemblyVersion> | |||||
| <AssemblyVersion>0.40.0.0</AssemblyVersion> | |||||
| <PackageReleaseNotes>tf.net 0.20.x and above are based on tensorflow native 2.x. | <PackageReleaseNotes>tf.net 0.20.x and above are based on tensorflow native 2.x. | ||||
| * Eager Mode is added finally. | * Eager Mode is added finally. | ||||
| @@ -29,8 +29,10 @@ https://tensorflownet.readthedocs.io</Description> | |||||
| * Improve memory usage. | * Improve memory usage. | ||||
| TensorFlow .NET v0.3x is focused on making more Keras API works. | TensorFlow .NET v0.3x is focused on making more Keras API works. | ||||
| Keras API is a separate package released as TensorFlow.Keras.</PackageReleaseNotes> | |||||
| <FileVersion>0.33.0.0</FileVersion> | |||||
| Keras API is a separate package released as TensorFlow.Keras. | |||||
| tf.net 0.4x.x aligns with TensorFlow v2.4.1 native library.</PackageReleaseNotes> | |||||
| <FileVersion>0.40.0.0</FileVersion> | |||||
| <PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
| <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
| <SignAssembly>true</SignAssembly> | <SignAssembly>true</SignAssembly> | ||||
| @@ -8,27 +8,7 @@ namespace Tensorflow | |||||
| { | { | ||||
| public partial class Tensor | public partial class Tensor | ||||
| { | { | ||||
| const ulong TF_TSRING_SIZE = 24; | |||||
| public IntPtr StringTensor25(string[] strings, TensorShape shape) | |||||
| { | |||||
| var handle = c_api.TF_AllocateTensor(TF_DataType.TF_STRING, | |||||
| shape.dims.Select(x => (long)x).ToArray(), | |||||
| shape.ndim, | |||||
| (ulong)shape.size * TF_TSRING_SIZE); | |||||
| var data = c_api.TF_TensorData(handle); | |||||
| var tstr = c_api.TF_StringInit(handle); | |||||
| // AllocationHandle = tstr; | |||||
| // AllocationType = AllocationType.Tensorflow; | |||||
| for (int i = 0; i< strings.Length; i++) | |||||
| { | |||||
| c_api.TF_StringCopy(tstr, strings[i], strings[i].Length); | |||||
| tstr += (int)TF_TSRING_SIZE; | |||||
| } | |||||
| // c_api.TF_StringDealloc(tstr); | |||||
| return handle; | |||||
| } | |||||
| const int TF_TSRING_SIZE = 24; | |||||
| public IntPtr StringTensor(string[] strings, TensorShape shape) | public IntPtr StringTensor(string[] strings, TensorShape shape) | ||||
| { | { | ||||
| @@ -40,69 +20,28 @@ namespace Tensorflow | |||||
| return StringTensor(buffer, shape); | return StringTensor(buffer, shape); | ||||
| } | } | ||||
| public unsafe IntPtr StringTensor(byte[][] buffer, TensorShape shape) | |||||
| public IntPtr StringTensor(byte[][] buffer, TensorShape shape) | |||||
| { | { | ||||
| ulong size = 0; | |||||
| foreach (var b in buffer) | |||||
| size += c_api.TF_StringEncodedSize((ulong)b.Length); | |||||
| var src_size = size + (ulong)buffer.Length * sizeof(ulong); | |||||
| var handle = c_api.TF_AllocateTensor(TF_DataType.TF_STRING, | var handle = c_api.TF_AllocateTensor(TF_DataType.TF_STRING, | ||||
| shape.dims.Select(x => (long)x).ToArray(), | |||||
| shape.ndim == 0 ? null : shape.dims.Select(x => (long)x).ToArray(), | |||||
| shape.ndim, | shape.ndim, | ||||
| src_size); | |||||
| AllocationType = AllocationType.Tensorflow; | |||||
| (ulong)shape.size * TF_TSRING_SIZE); | |||||
| IntPtr data_start = c_api.TF_TensorData(handle); | |||||
| IntPtr string_start = data_start + buffer.Length * sizeof(ulong); | |||||
| IntPtr limit = data_start + (int)src_size; | |||||
| ulong offset = 0; | |||||
| var tstr = c_api.TF_TensorData(handle); | |||||
| #if TRACK_TENSOR_LIFE | |||||
| print($"New TString 0x{handle.ToString("x16")} {AllocationType} Data: 0x{tstr.ToString("x16")}"); | |||||
| #endif | |||||
| for (int i = 0; i < buffer.Length; i++) | for (int i = 0; i < buffer.Length; i++) | ||||
| { | { | ||||
| Marshal.WriteInt64(data_start, i * sizeof(ulong), (long)offset); | |||||
| if (buffer[i].Length == 0) | |||||
| { | |||||
| Marshal.WriteByte(string_start, 0); | |||||
| break; | |||||
| } | |||||
| fixed (byte* src = &buffer[i][0]) | |||||
| { | |||||
| /*Marshal.WriteByte(string_start, Convert.ToByte(buffer[i].Length)); | |||||
| tf.memcpy((string_start + 1).ToPointer(), src, (ulong)buffer[i].Length); | |||||
| string_start += buffer[i].Length + 1; | |||||
| offset += buffer[i].Length + 1;*/ | |||||
| var written = c_api.TF_StringEncode(src, (ulong)buffer[i].Length, (byte*)string_start, (ulong)(limit.ToInt64() - string_start.ToInt64()), tf.Status.Handle); | |||||
| tf.Status.Check(true); | |||||
| string_start += (int)written; | |||||
| offset += written; | |||||
| } | |||||
| c_api.TF_StringInit(tstr); | |||||
| c_api.TF_StringCopy(tstr, buffer[i], buffer[i].Length); | |||||
| var data = c_api.TF_StringGetDataPointer(tstr); | |||||
| tstr += TF_TSRING_SIZE; | |||||
| } | } | ||||
| return handle; | return handle; | ||||
| } | } | ||||
| public string[] StringData25() | |||||
| { | |||||
| string[] strings = new string[c_api.TF_Dim(_handle, 0)]; | |||||
| var tstrings = TensorDataPointer; | |||||
| for (int i = 0; i< strings.Length; i++) | |||||
| { | |||||
| var tstringData = c_api.TF_StringGetDataPointer(tstrings); | |||||
| /*var size = c_api.TF_StringGetSize(tstrings); | |||||
| var capacity = c_api.TF_StringGetCapacity(tstrings); | |||||
| var type = c_api.TF_StringGetType(tstrings);*/ | |||||
| strings[i] = c_api.StringPiece(tstringData); | |||||
| tstrings += (int)TF_TSRING_SIZE; | |||||
| } | |||||
| return strings; | |||||
| } | |||||
| /// <summary> | |||||
| /// Extracts string array from current Tensor. | |||||
| /// </summary> | |||||
| /// <exception cref="InvalidOperationException">When <see cref="dtype"/> != TF_DataType.TF_STRING</exception> | |||||
| public string[] StringData() | public string[] StringData() | ||||
| { | { | ||||
| var buffer = StringBytes(); | var buffer = StringBytes(); | ||||
| @@ -114,7 +53,7 @@ namespace Tensorflow | |||||
| return _str; | return _str; | ||||
| } | } | ||||
| public unsafe byte[][] StringBytes() | |||||
| public byte[][] StringBytes() | |||||
| { | { | ||||
| if (dtype != TF_DataType.TF_STRING) | if (dtype != TF_DataType.TF_STRING) | ||||
| throw new InvalidOperationException($"Unable to call StringData when dtype != TF_DataType.TF_STRING (dtype is {dtype})"); | throw new InvalidOperationException($"Unable to call StringData when dtype != TF_DataType.TF_STRING (dtype is {dtype})"); | ||||
| @@ -123,24 +62,22 @@ namespace Tensorflow | |||||
| // TF_STRING tensors are encoded with a table of 8-byte offsets followed by TF_StringEncode-encoded bytes. | // TF_STRING tensors are encoded with a table of 8-byte offsets followed by TF_StringEncode-encoded bytes. | ||||
| // [offset1, offset2,...,offsetn, s1size, s1bytes, s2size, s2bytes,...,snsize,snbytes] | // [offset1, offset2,...,offsetn, s1size, s1bytes, s2size, s2bytes,...,snsize,snbytes] | ||||
| // | // | ||||
| long size = 1; | |||||
| int size = 1; | |||||
| foreach (var s in TensorShape.dims) | foreach (var s in TensorShape.dims) | ||||
| size *= s; | size *= s; | ||||
| var buffer = new byte[size][]; | var buffer = new byte[size][]; | ||||
| var data_start = c_api.TF_TensorData(_handle); | |||||
| data_start += (int)(size * sizeof(ulong)); | |||||
| var tstrings = TensorDataPointer; | |||||
| for (int i = 0; i < buffer.Length; i++) | for (int i = 0; i < buffer.Length; i++) | ||||
| { | { | ||||
| IntPtr dst = IntPtr.Zero; | |||||
| ulong dstLen = 0; | |||||
| var read = c_api.TF_StringDecode((byte*)data_start, bytesize, (byte**)&dst, ref dstLen, tf.Status.Handle); | |||||
| tf.Status.Check(true); | |||||
| buffer[i] = new byte[(int)dstLen]; | |||||
| Marshal.Copy(dst, buffer[i], 0, buffer[i].Length); | |||||
| data_start += (int)read; | |||||
| var data = c_api.TF_StringGetDataPointer(tstrings); | |||||
| var len = c_api.TF_StringGetSize(tstrings); | |||||
| buffer[i] = new byte[len]; | |||||
| // var capacity = c_api.TF_StringGetCapacity(tstrings); | |||||
| // var type = c_api.TF_StringGetType(tstrings); | |||||
| Marshal.Copy(data, buffer[i], 0, Convert.ToInt32(len)); | |||||
| tstrings += TF_TSRING_SIZE; | |||||
| } | } | ||||
| return buffer; | return buffer; | ||||
| } | } | ||||
| } | } | ||||
| @@ -15,7 +15,6 @@ | |||||
| ******************************************************************************/ | ******************************************************************************/ | ||||
| using NumSharp; | using NumSharp; | ||||
| using NumSharp.Backends.Unmanaged; | |||||
| using System; | using System; | ||||
| using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||
| using System.Globalization; | using System.Globalization; | ||||
| @@ -24,7 +23,6 @@ using System.Runtime.InteropServices; | |||||
| using Tensorflow.Eager; | using Tensorflow.Eager; | ||||
| using Tensorflow.Framework; | using Tensorflow.Framework; | ||||
| using Tensorflow.Keras.Engine; | using Tensorflow.Keras.Engine; | ||||
| using Tensorflow.Variables; | |||||
| using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
| namespace Tensorflow | namespace Tensorflow | ||||
| @@ -287,6 +285,22 @@ namespace Tensorflow | |||||
| throw new InvalidOperationException($"Tensor.AllocationHandle is not null ({AllocationHandle}) but AllocationType is not matched to a C# allocation type ({AllocationType})."); | throw new InvalidOperationException($"Tensor.AllocationHandle is not null ({AllocationHandle}) but AllocationType is not matched to a C# allocation type ({AllocationType})."); | ||||
| } | } | ||||
| if (dtype == TF_DataType.TF_STRING) | |||||
| { | |||||
| int size = 1; | |||||
| foreach (var s in TensorShape.dims) | |||||
| size *= s; | |||||
| var tstr = TensorDataPointer; | |||||
| #if TRACK_TENSOR_LIFE | |||||
| print($"Delete TString 0x{handle.ToString("x16")} {AllocationType} Data: 0x{tstrings.ToString("x16")}"); | |||||
| #endif | |||||
| for (int i = 0; i < size; i++) | |||||
| { | |||||
| c_api.TF_StringDealloc(tstr); | |||||
| tstr += TF_TSRING_SIZE; | |||||
| } | |||||
| } | |||||
| c_api.TF_DeleteTensor(handle); | c_api.TF_DeleteTensor(handle); | ||||
| } | } | ||||
| @@ -182,7 +182,10 @@ namespace Tensorflow | |||||
| public static extern unsafe ulong TF_StringEncode(byte* src, ulong src_len, byte* dst, ulong dst_len, SafeStatusHandle status); | public static extern unsafe ulong TF_StringEncode(byte* src, ulong src_len, byte* dst, ulong dst_len, SafeStatusHandle status); | ||||
| [DllImport(TensorFlowLibName)] | [DllImport(TensorFlowLibName)] | ||||
| public static extern IntPtr TF_StringInit(IntPtr t); | |||||
| public static extern void TF_StringInit(IntPtr t); | |||||
| [DllImport(TensorFlowLibName)] | |||||
| public static extern void TF_StringCopy(IntPtr dst, byte[] text, long size); | |||||
| [DllImport(TensorFlowLibName)] | [DllImport(TensorFlowLibName)] | ||||
| public static extern void TF_StringCopy(IntPtr dst, string text, long size); | public static extern void TF_StringCopy(IntPtr dst, string text, long size); | ||||
| @@ -111,7 +111,7 @@ namespace Tensorflow.Native.UnitTest.Tensors | |||||
| /// Port from c_api_test.cc | /// Port from c_api_test.cc | ||||
| /// `TEST_F(CApiAttributesTest, StringTensor)` | /// `TEST_F(CApiAttributesTest, StringTensor)` | ||||
| /// </summary> | /// </summary> | ||||
| [TestMethod, Ignore("Waiting for PR https://github.com/tensorflow/tensorflow/pull/46804")] | |||||
| [TestMethod] | |||||
| public void StringTensor() | public void StringTensor() | ||||
| { | { | ||||
| string text = "Hello world!."; | string text = "Hello world!."; | ||||
| @@ -120,13 +120,14 @@ namespace Tensorflow.Native.UnitTest.Tensors | |||||
| null, | null, | ||||
| 0, | 0, | ||||
| 1 * 24); | 1 * 24); | ||||
| var tstr = c_api.TF_StringInit(tensor); | |||||
| var data = c_api.TF_StringGetDataPointer(tstr); | |||||
| var tstr = c_api.TF_TensorData(tensor); | |||||
| c_api.TF_StringInit(tstr); | |||||
| c_api.TF_StringCopy(tstr, text, text.Length); | c_api.TF_StringCopy(tstr, text, text.Length); | ||||
| var data = c_api.TF_StringGetDataPointer(tstr); | |||||
| Assert.AreEqual((ulong)text.Length, c_api.TF_StringGetSize(tstr)); | Assert.AreEqual((ulong)text.Length, c_api.TF_StringGetSize(tstr)); | ||||
| Assert.AreEqual(text, c_api.StringPiece(data)); | Assert.AreEqual(text, c_api.StringPiece(data)); | ||||
| Assert.AreEqual((ulong)text.Length, c_api.TF_TensorByteSize(tensor)); | |||||
| Assert.AreEqual(TF_TString_Type.TF_TSTR_SMALL, c_api.TF_StringGetType(tensor)); | |||||
| Assert.AreEqual(0, c_api.TF_NumDims(tensor)); | Assert.AreEqual(0, c_api.TF_NumDims(tensor)); | ||||
| TF_DeleteTensor(tensor); | TF_DeleteTensor(tensor); | ||||