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