@@ -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); | ||||