| @@ -4,6 +4,8 @@ using Tensorflow.Keras.ArgsDefinition; | |||
| using Tensorflow.Keras.Engine.DataAdapters; | |||
| using static Tensorflow.Binding; | |||
| using static Tensorflow.KerasApi; | |||
| using System.Linq; | |||
| using System.Collections.Generic; | |||
| namespace Tensorflow | |||
| { | |||
| @@ -35,13 +37,15 @@ namespace Tensorflow | |||
| public Action<int, int> ConstantString | |||
| => (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", | |||
| "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." | |||
| }); | |||
| var data = tensor.numpy(); | |||
| }; | |||
| var tensor = tf.constant(strList, TF_DataType.TF_STRING); | |||
| var data = tensor.StringData(); | |||
| }; | |||
| public Action<int, int> Variable | |||
| @@ -47,7 +47,7 @@ namespace Tensorflow | |||
| // explaination of constant | |||
| mm.Execute(10, 100 * batchSize, basic.Constant2x3); | |||
| mm.Execute(10, 100 * batchSize, basic.ConstantString); | |||
| mm.Execute(10, batchSize, basic.ConstantString); | |||
| // 100K float variable. | |||
| mm.Execute(10, batchSize, basic.Variable); | |||
| @@ -43,7 +43,7 @@ namespace Tensorflow | |||
| /// </summary> | |||
| 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) | |||
| { | |||
| @@ -5,7 +5,7 @@ | |||
| <AssemblyName>TensorFlow.NET</AssemblyName> | |||
| <RootNamespace>Tensorflow</RootNamespace> | |||
| <TargetTensorFlow>2.2.0</TargetTensorFlow> | |||
| <Version>0.33.0</Version> | |||
| <Version>0.40.0</Version> | |||
| <LangVersion>8.0</LangVersion> | |||
| <Authors>Haiping Chen, Meinrad Recheis, Eli Belash</Authors> | |||
| <Company>SciSharp STACK</Company> | |||
| @@ -19,7 +19,7 @@ | |||
| <Description>Google's TensorFlow full binding in .NET Standard. | |||
| Building, training and infering deep learning models. | |||
| 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. | |||
| * Eager Mode is added finally. | |||
| @@ -29,8 +29,10 @@ https://tensorflownet.readthedocs.io</Description> | |||
| * Improve memory usage. | |||
| 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> | |||
| <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | |||
| <SignAssembly>true</SignAssembly> | |||
| @@ -8,27 +8,7 @@ namespace Tensorflow | |||
| { | |||
| 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) | |||
| { | |||
| @@ -40,69 +20,28 @@ namespace Tensorflow | |||
| 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, | |||
| shape.dims.Select(x => (long)x).ToArray(), | |||
| shape.ndim == 0 ? null : shape.dims.Select(x => (long)x).ToArray(), | |||
| 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++) | |||
| { | |||
| 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; | |||
| } | |||
| 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() | |||
| { | |||
| var buffer = StringBytes(); | |||
| @@ -114,7 +53,7 @@ namespace Tensorflow | |||
| return _str; | |||
| } | |||
| public unsafe byte[][] StringBytes() | |||
| public byte[][] StringBytes() | |||
| { | |||
| if (dtype != TF_DataType.TF_STRING) | |||
| 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. | |||
| // [offset1, offset2,...,offsetn, s1size, s1bytes, s2size, s2bytes,...,snsize,snbytes] | |||
| // | |||
| long size = 1; | |||
| int size = 1; | |||
| foreach (var s in TensorShape.dims) | |||
| size *= s; | |||
| 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++) | |||
| { | |||
| 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; | |||
| } | |||
| } | |||
| @@ -15,7 +15,6 @@ | |||
| ******************************************************************************/ | |||
| using NumSharp; | |||
| using NumSharp.Backends.Unmanaged; | |||
| using System; | |||
| using System.Diagnostics.CodeAnalysis; | |||
| using System.Globalization; | |||
| @@ -24,7 +23,6 @@ using System.Runtime.InteropServices; | |||
| using Tensorflow.Eager; | |||
| using Tensorflow.Framework; | |||
| using Tensorflow.Keras.Engine; | |||
| using Tensorflow.Variables; | |||
| using static Tensorflow.Binding; | |||
| 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})."); | |||
| } | |||
| 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); | |||
| } | |||
| @@ -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); | |||
| [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)] | |||
| 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 | |||
| /// `TEST_F(CApiAttributesTest, StringTensor)` | |||
| /// </summary> | |||
| [TestMethod, Ignore("Waiting for PR https://github.com/tensorflow/tensorflow/pull/46804")] | |||
| [TestMethod] | |||
| public void StringTensor() | |||
| { | |||
| string text = "Hello world!."; | |||
| @@ -120,13 +120,14 @@ namespace Tensorflow.Native.UnitTest.Tensors | |||
| null, | |||
| 0, | |||
| 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); | |||
| var data = c_api.TF_StringGetDataPointer(tstr); | |||
| Assert.AreEqual((ulong)text.Length, c_api.TF_StringGetSize(tstr)); | |||
| 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)); | |||
| TF_DeleteTensor(tensor); | |||