diff --git a/eng/Versions.props b/eng/Versions.props
index f7f879a844..d264ca5d0f 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -67,9 +67,9 @@
2.1.3
0.11.1
1.4.2
- 0.20.1
+ 0.70.2
2
- 2.3.1
+ 2.7.0
1.4.1
0.2.3
1.48.0
@@ -90,6 +90,7 @@
0.13.12
6.0.36
8.0.16
+ 9.0.5
8.1.0
1.1.2
9.0.0-beta.24212.4
diff --git a/global.json b/global.json
index 768272c719..7b05feb25b 100644
--- a/global.json
+++ b/global.json
@@ -4,11 +4,13 @@
"runtimes": {
"dotnet": [
"$(DotNetRuntime60Version)",
- "$(DotNetRuntime80Version)"
+ "$(DotNetRuntime80Version)",
+ "$(DotNetRuntime90Version)"
],
"dotnet/x86": [
"$(DotNetRuntime60Version)",
- "$(DotNetRuntime80Version)"
+ "$(DotNetRuntime80Version)",
+ "$(DotNetRuntime90Version)"
]
}
},
diff --git a/src/Microsoft.ML.Core/Utilities/ArrayUtils.cs b/src/Microsoft.ML.Core/Utilities/ArrayUtils.cs
index 4c23831917..5ffb15fc41 100644
--- a/src/Microsoft.ML.Core/Utilities/ArrayUtils.cs
+++ b/src/Microsoft.ML.Core/Utilities/ArrayUtils.cs
@@ -100,5 +100,26 @@ public static int EnsureSize(ref T[] array, int min, int max, bool keepOld, o
resized = true;
return newSize;
}
+
+ public static int[] CastLongArrayToIntArray(long[] source)
+ {
+ if (source == null)
+ throw new ArgumentNullException(nameof(source));
+
+ int[] result = new int[source.Length];
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ long value = source[i];
+ if (value > int.MaxValue || value < int.MinValue)
+ {
+ throw new OverflowException($"Value at index {i} ({value}) cannot be safely cast to int.");
+ }
+
+ result[i] = (int)value;
+ }
+
+ return result;
+ }
}
}
diff --git a/src/Microsoft.ML.DataView/VectorType.cs b/src/Microsoft.ML.DataView/VectorType.cs
index 574a473f1e..4423f1e8b0 100644
--- a/src/Microsoft.ML.DataView/VectorType.cs
+++ b/src/Microsoft.ML.DataView/VectorType.cs
@@ -67,6 +67,24 @@ public VectorDataViewType(PrimitiveDataViewType itemType, params int[] dimension
Size = ComputeSize(Dimensions);
}
+ ///
+ /// Constructs a potentially multi-dimensional vector type.
+ ///
+ /// The type of the items contained in the vector.
+ /// The dimensions. Note that, like , must be non-empty, with all
+ /// non-negative values. Also, because is the product of , the result of
+ /// multiplying all these values together must not overflow .
+ public VectorDataViewType(PrimitiveDataViewType itemType, params long[] dimensions)
+ : base(GetRawType(itemType))
+ {
+ Contracts.CheckParam(ArrayUtils.Size(dimensions) > 0, nameof(dimensions));
+ Contracts.CheckParam(dimensions.All(d => d >= 0), nameof(dimensions));
+
+ ItemType = itemType;
+ Dimensions = ArrayUtils.CastLongArrayToIntArray(dimensions).ToImmutableArray();
+ Size = ComputeSize(Dimensions);
+ }
+
///
/// Constructs a potentially multi-dimensional vector type.
///
diff --git a/src/Microsoft.ML.TensorFlow/TensorTypeExtensions.cs b/src/Microsoft.ML.TensorFlow/TensorTypeExtensions.cs
index 330c398133..5742a5f8cb 100644
--- a/src/Microsoft.ML.TensorFlow/TensorTypeExtensions.cs
+++ b/src/Microsoft.ML.TensorFlow/TensorTypeExtensions.cs
@@ -25,7 +25,7 @@ public static void ToScalar(this Tensor tensor, ref T dst) where T : unmanage
return;
}
- if (typeof(T).as_dtype() != tensor.dtype)
+ if (typeof(T).as_tf_dtype() != tensor.dtype)
throw new NotSupportedException();
unsafe
@@ -37,7 +37,7 @@ public static void ToScalar(this Tensor tensor, ref T dst) where T : unmanage
public static void CopyTo(this Tensor tensor, Span values) where T : unmanaged
{
- if (typeof(T).as_dtype() != tensor.dtype)
+ if (typeof(T).as_tf_dtype() != tensor.dtype)
throw new NotSupportedException();
unsafe
diff --git a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
index fd556a175f..ae22454ae9 100644
--- a/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
+++ b/src/Microsoft.ML.TensorFlow/TensorflowTransform.cs
@@ -16,8 +16,8 @@
using Microsoft.ML.Runtime;
using Microsoft.ML.TensorFlow;
using Microsoft.ML.Transforms;
-using NumSharp;
using Tensorflow;
+using Tensorflow.NumPy;
using static Microsoft.ML.TensorFlow.TensorFlowUtils;
using static Tensorflow.Binding;
using Utils = Microsoft.ML.Internal.Utilities.Utils;
@@ -51,7 +51,7 @@ public sealed class TensorFlowTransformer : RowToRowTransformerBase, IDisposable
internal readonly DataViewType[] OutputTypes;
internal readonly TF_DataType[] TFOutputTypes;
internal readonly TF_DataType[] TFInputTypes;
- internal readonly TensorShape[] TFInputShapes;
+ internal readonly Shape[] TFInputShapes;
internal readonly (Operation, int)[] TFInputOperations;
internal readonly (Operation, int)[] TFOutputOperations;
internal TF_Output[] TFInputNodes;
@@ -212,14 +212,14 @@ internal TensorFlowTransformer(IHostEnvironment env, TensorFlowEstimator.Options
env.CheckValue(options, nameof(options));
}
- private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, bool isVector, int colIndex, TensorShape tfShape)
+ private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, bool isVector, int colIndex, Shape tfShape)
{
if (isVector)
return new TensorValueGetterVec(input, colIndex, tfShape);
return new TensorValueGetter(input, colIndex, tfShape);
}
- private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, TF_DataType tfType, bool isVector, int colIndex, TensorShape tfShape)
+ private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, TF_DataType tfType, bool isVector, int colIndex, Shape tfShape)
{
var type = Tf2MlNetType(tfType);
return Utils.MarshalInvoke(CreateTensorValueGetter, type.RawType, input, isVector, colIndex, tfShape);
@@ -230,7 +230,7 @@ private static ITensorValueGetter[] GetTensorValueGetters(
int[] inputColIndices,
bool[] isInputVector,
TF_DataType[] tfInputTypes,
- TensorShape[] tfInputShapes)
+ Shape[] tfInputShapes)
{
var srcTensorGetters = new ITensorValueGetter[inputColIndices.Length];
for (int i = 0; i < inputColIndices.Length; i++)
@@ -331,10 +331,10 @@ private static (Operation, int) GetOperationFromName(string operation, Session s
return (session.graph.OperationByName(operation), 0);
}
- internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Operation, int)[]) GetInputInfo(IHost host, Session session, string[] inputs, int batchSize = 1)
+ internal static (TF_DataType[] tfInputTypes, Shape[] tfInputShapes, (Operation, int)[]) GetInputInfo(IHost host, Session session, string[] inputs, int batchSize = 1)
{
var tfInputTypes = new TF_DataType[inputs.Length];
- var tfInputShapes = new TensorShape[inputs.Length];
+ var tfInputShapes = new Shape[inputs.Length];
var tfInputOperations = new (Operation, int)[inputs.Length];
int index = 0;
@@ -351,7 +351,7 @@ internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Opera
throw host.ExceptParam(nameof(session), $"Input type '{tfInputType}' of input column '{input}' is not supported in TensorFlow");
tfInputTypes[index] = tfInputType;
- tfInputShapes[index] = ((Tensor)inputTensor).TensorShape;
+ tfInputShapes[index] = ((Tensor)inputTensor).shape;
tfInputOperations[index] = (inputTensor, inputTensorIndex);
index++;
}
@@ -359,7 +359,7 @@ internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Opera
return (tfInputTypes, tfInputShapes, tfInputOperations);
}
- internal static TensorShape GetTensorShape(TF_Output output, Graph graph, Status status = null)
+ internal static Shape GetTensorShape(TF_Output output, Graph graph, Status status = null)
{
if (graph == IntPtr.Zero)
throw new ObjectDisposedException(nameof(graph));
@@ -370,12 +370,12 @@ internal static TensorShape GetTensorShape(TF_Output output, Graph graph, Status
cstatus.Check();
if (n == -1)
- return new TensorShape(new int[0]);
+ return new Shape(new int[0]);
var dims = new long[n];
c_api.TF_GraphGetTensorShape(graph, output, dims, dims.Length, cstatus.Handle);
cstatus.Check();
- return new TensorShape(dims.Select(x => (int)x).ToArray());
+ return new Shape(dims.Select(x => (int)x).ToArray());
}
internal static (TF_DataType[] tfOutputTypes, DataViewType[] outputTypes, (Operation, int)[]) GetOutputInfo(IHost host, Session session, string[] outputs, bool treatOutputAsBatched)
@@ -404,10 +404,10 @@ internal static (TF_DataType[] tfOutputTypes, DataViewType[] outputTypes, (Opera
// This is the work around in absence of reshape transformer.
var idims = shape.dims;
- int[] dims = idims;
+ long[] dims = idims;
if (treatOutputAsBatched)
{
- dims = shape.ndim > 0 ? idims.Skip(idims[0] == -1 ? 1 : 0).ToArray() : new int[0];
+ dims = shape.ndim > 0 ? idims.Skip(idims[0] == -1 ? 1 : 0).ToArray() : new long[0];
}
for (int j = 0; j < dims.Length; j++)
dims[j] = dims[j] == -1 ? 0 : dims[j];
@@ -517,7 +517,7 @@ public void Dispose()
if (Session != null && Session != IntPtr.Zero)
{
- Session.close(); // invoked Dispose()
+ Session.Dispose();
}
}
finally
@@ -536,7 +536,7 @@ private sealed class Mapper : MapperBase
private readonly TensorFlowTransformer _parent;
private readonly int[] _inputColIndices;
private readonly bool[] _isInputVector;
- private readonly TensorShape[] _fullySpecifiedShapes;
+ private readonly Shape[] _fullySpecifiedShapes;
private readonly ConcurrentBag _runners;
public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
@@ -546,7 +546,7 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
_parent = parent;
_inputColIndices = new int[_parent.Inputs.Length];
_isInputVector = new bool[_parent.Inputs.Length];
- _fullySpecifiedShapes = new TensorShape[_parent.Inputs.Length];
+ _fullySpecifiedShapes = new Shape[_parent.Inputs.Length];
for (int i = 0; i < _parent.Inputs.Length; i++)
{
if (!inputSchema.TryGetColumnIndex(_parent.Inputs[i], out _inputColIndices[i]))
@@ -570,11 +570,11 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
{
vecType = (VectorDataViewType)type;
var colTypeDims = vecType.Dimensions.Select(dim => (int)dim).ToArray();
- _fullySpecifiedShapes[i] = new TensorShape(colTypeDims);
+ _fullySpecifiedShapes[i] = new Shape(colTypeDims);
}
else
// for primitive type use default TensorShape
- _fullySpecifiedShapes[i] = new TensorShape();
+ _fullySpecifiedShapes[i] = new Shape(Array.Empty());
}
else
{
@@ -582,7 +582,7 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
var colTypeDims = vecType.Dimensions.Select(dim => (int)dim).ToArray();
// If the column is one dimension we make sure that the total size of the TF shape matches.
// Compute the total size of the known dimensions of the shape.
- int valCount = 1;
+ long valCount = 1;
int numOfUnkDim = 0;
foreach (var s in shape)
{
@@ -592,7 +592,7 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
numOfUnkDim++;
}
// The column length should be divisible by this, so that the other dimensions can be integral.
- int typeValueCount = type.GetValueCount();
+ long typeValueCount = type.GetValueCount();
if (typeValueCount % valCount != 0)
throw Contracts.Except($"Input shape mismatch: Input '{_parent.Inputs[i]}' has shape {originalShape.ToString()}, but input data is of length {typeValueCount}.");
@@ -616,10 +616,10 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
throw Contracts.Except($"Input shape mismatch: Input '{_parent.Inputs[i]}' has shape {originalShape.ToString()}, but input data is of length {typeValueCount}.");
// Fill in the unknown dimensions.
- var l = new int[originalShapeNdim];
+ var l = new long[originalShapeNdim];
for (int ishape = 0; ishape < originalShapeNdim; ishape++)
- l[ishape] = originalShapeDims[ishape] == -1 ? (int)d : originalShapeDims[ishape];
- _fullySpecifiedShapes[i] = new TensorShape(l);
+ l[ishape] = originalShapeDims[ishape] == -1 ? (long)d : originalShapeDims[ishape];
+ _fullySpecifiedShapes[i] = new Shape(l);
}
if (_parent._addBatchDimensionInput)
@@ -627,11 +627,11 @@ public Mapper(TensorFlowTransformer parent, DataViewSchema inputSchema) :
// ndim of default TensorShape is -1, make originDim to 0 in this case.
// after addBatchDimension, input column will be changed: type -> type[]
var originDim = _fullySpecifiedShapes[i].ndim < 0 ? 0 : _fullySpecifiedShapes[i].ndim;
- var l = new int[originDim + 1];
+ var l = new long[originDim + 1];
l[0] = 1;
for (int ishape = 1; ishape < l.Length; ishape++)
l[ishape] = _fullySpecifiedShapes[i].dims[ishape - 1];
- _fullySpecifiedShapes[i] = new TensorShape(l);
+ _fullySpecifiedShapes[i] = new Shape(l);
}
}
@@ -720,7 +720,7 @@ private Delegate MakeGetter(DataViewRow input, int iinfo, ITensorValueGetter[
UpdateCacheIfNeeded(input.Position, srcTensorGetters, activeOutputColNames, outputCache);
var tensor = outputCache.Outputs[_parent.Outputs[iinfo]];
- var tensorSize = tensor.TensorShape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
+ var tensorSize = tensor.shape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
var editor = VBufferEditor.Create(ref dst, (int)tensorSize);
FetchStringData(tensor, editor.Values);
@@ -735,7 +735,7 @@ private Delegate MakeGetter(DataViewRow input, int iinfo, ITensorValueGetter[
UpdateCacheIfNeeded(input.Position, srcTensorGetters, activeOutputColNames, outputCache);
var tensor = outputCache.Outputs[_parent.Outputs[iinfo]];
- var tensorSize = tensor.TensorShape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
+ var tensorSize = tensor.shape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
var editor = VBufferEditor.Create(ref dst, (int)tensorSize);
@@ -821,10 +821,10 @@ private class TensorValueGetter : ITensorValueGetter
{
private readonly ValueGetter _srcgetter;
private readonly T[] _bufferedData;
- private readonly TensorShape _tfShape;
+ private readonly Shape _tfShape;
private int _position;
- public TensorValueGetter(DataViewRow input, int colIndex, TensorShape tfShape)
+ public TensorValueGetter(DataViewRow input, int colIndex, Shape tfShape)
{
_srcgetter = input.GetGetter(input.Schema[colIndex]);
_tfShape = tfShape;
@@ -864,7 +864,7 @@ public Tensor GetBufferedBatchTensor()
private class TensorValueGetterVec : ITensorValueGetter
{
private readonly ValueGetter> _srcgetter;
- private readonly TensorShape _tfShape;
+ private readonly Shape _tfShape;
private VBuffer _vBuffer;
private T[] _denseData;
private T[] _bufferedData;
@@ -872,7 +872,7 @@ private class TensorValueGetterVec : ITensorValueGetter
private readonly long[] _dims;
private readonly long _bufferedDataSize;
- public TensorValueGetterVec(DataViewRow input, int colIndex, TensorShape tfShape)
+ public TensorValueGetterVec(DataViewRow input, int colIndex, Shape tfShape)
{
_srcgetter = input.GetGetter>(input.Schema[colIndex]);
_tfShape = tfShape;
diff --git a/src/Microsoft.ML.TensorFlow/TensorflowUtils.cs b/src/Microsoft.ML.TensorFlow/TensorflowUtils.cs
index faec243057..18cc4ab52c 100644
--- a/src/Microsoft.ML.TensorFlow/TensorflowUtils.cs
+++ b/src/Microsoft.ML.TensorFlow/TensorflowUtils.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
@@ -12,8 +13,8 @@
using Microsoft.ML.Runtime;
using Microsoft.ML.TensorFlow;
using Microsoft.ML.Transforms;
-using NumSharp;
using Tensorflow;
+using Tensorflow.NumPy;
using static Tensorflow.Binding;
using Utils = Microsoft.ML.Internal.Utilities.Utils;
@@ -77,9 +78,9 @@ internal static DataViewSchema GetModelSchema(IExceptionContext ectx, Graph grap
}
// Construct the final ML.NET type of a Tensorflow variable.
- var tensorShape = op.output.TensorShape.dims;
+ var dimensions = op.output.shape.dims;
- if (tensorShape == null)
+ if (dimensions == null)
{
// primitive column type
schemaBuilder.AddColumn(op.name, mlType, metadataBuilder.ToAnnotations());
@@ -88,15 +89,15 @@ internal static DataViewSchema GetModelSchema(IExceptionContext ectx, Graph grap
{
// vector column type
DataViewType columnType = new VectorDataViewType(mlType);
- if (!(Utils.Size(tensorShape) == 1 && tensorShape[0] <= 0) &&
- (Utils.Size(tensorShape) > 0 && tensorShape.Skip(1).All(x => x > 0)))
+ if (!(Utils.Size(dimensions) == 1 && dimensions[0] <= 0) &&
+ (Utils.Size(dimensions) > 0 && dimensions.Skip(1).All(x => x > 0)))
// treatOutputAsBatched == true means that if the first dimension is greater
// than 0 we take the tensor shape as is. If the first value is less then 0, we treat it as the batch input so we can
// ignore it for the shape of the ML.NET vector. I.E. if the input dimensions are [-1, 5], ML.NET will read the -1 as
// batch input, and so the ML.NET data type will be a vector of length 5.
if (treatOutputAsBatched)
{
- columnType = new VectorDataViewType(mlType, tensorShape[0] > 0 ? tensorShape : tensorShape.Skip(1).ToArray());
+ columnType = new VectorDataViewType(mlType, dimensions[0] > 0 ? dimensions : dimensions.Skip(1).ToArray());
}
// When treatOutputAsBatched is false, if the first value is less than 0 we want to set it to 0. TensorFlow
// represents an unknown size as -1, but ML.NET represents it as 0 so we need to convert it.
@@ -104,9 +105,9 @@ internal static DataViewSchema GetModelSchema(IExceptionContext ectx, Graph grap
// data type will be a vector of 2 dimensions, where the first dimension is unknown and the second has a length of 5.
else
{
- if (tensorShape[0] < 0)
- tensorShape[0] = 0;
- columnType = new VectorDataViewType(mlType, tensorShape);
+ if (dimensions[0] < 0)
+ dimensions[0] = 0;
+ columnType = new VectorDataViewType(mlType, dimensions);
}
schemaBuilder.AddColumn(op.name, columnType, metadataBuilder.ToAnnotations());
@@ -441,32 +442,32 @@ internal static bool IsTypeSupported(TF_DataType tfoutput)
}
}
- internal static Tensor CastDataAndReturnAsTensor(T[] data, TensorShape tfShape)
+ internal static Tensor CastDataAndReturnAsTensor(T[] data, Shape tfShape)
{
var dims = tfShape.dims.Select(x => (long)x).ToArray();
if (typeof(T) == typeof(sbyte))
- return new Tensor((sbyte[])(object)data, dims, TF_DataType.TF_INT8);
+ return new Tensor((sbyte[])(object)data, dims);
else if (typeof(T) == typeof(long))
- return new Tensor((long[])(object)data, dims, TF_DataType.TF_INT64);
+ return new Tensor((long[])(object)data, dims);
else if (typeof(T) == typeof(Int32))
- return new Tensor((Int32[])(object)data, dims, TF_DataType.TF_INT32);
+ return new Tensor((Int32[])(object)data, dims);
else if (typeof(T) == typeof(Int16))
- return new Tensor((Int16[])(object)data, dims, TF_DataType.TF_INT16);
+ return new Tensor((Int16[])(object)data, dims);
else if (typeof(T) == typeof(byte))
- return new Tensor((byte[])(object)data, dims, TF_DataType.TF_UINT8);
+ return new Tensor((byte[])(object)data, dims);
else if (typeof(T) == typeof(ulong))
- return new Tensor((ulong[])(object)data, dims, TF_DataType.TF_UINT64);
+ return new Tensor((ulong[])(object)data, dims);
else if (typeof(T) == typeof(UInt32))
- return new Tensor((UInt32[])(object)data, dims, TF_DataType.TF_UINT32);
+ return new Tensor((UInt32[])(object)data, dims);
else if (typeof(T) == typeof(UInt16))
- return new Tensor((UInt16[])(object)data, dims, TF_DataType.TF_UINT16);
+ return new Tensor((UInt16[])(object)data, dims);
else if (typeof(T) == typeof(bool))
- return new Tensor((bool[])(object)data, dims, TF_DataType.TF_BOOL);
+ return new Tensor((bool[])(object)data, dims);
else if (typeof(T) == typeof(float))
- return new Tensor((float[])(object)data, dims, TF_DataType.TF_FLOAT);
+ return new Tensor((float[])(object)data, dims);
else if (typeof(T) == typeof(double))
- return new Tensor((double[])(object)data, dims, TF_DataType.TF_DOUBLE);
+ return new Tensor((double[])(object)data, dims);
else if (typeof(T) == typeof(ReadOnlyMemory))
{
string[] strings = new string[data.Length];
@@ -484,27 +485,30 @@ internal static Tensor CastDataAndReturnAsTensor(T[] data, TensorShape tfShap
internal static Tensor CastDataAndReturnAsTensor(T data)
{
if (typeof(T) == typeof(sbyte))
- return new Tensor((sbyte)(object)data, TF_DataType.TF_INT8);
+ return new Tensor((sbyte)(object)data);
else if (typeof(T) == typeof(long))
- return new Tensor((long)(object)data, TF_DataType.TF_INT64);
+ return new Tensor((long)(object)data);
else if (typeof(T) == typeof(Int32))
- return new Tensor((Int32)(object)data, TF_DataType.TF_INT32);
+ return new Tensor((Int32)(object)data);
else if (typeof(T) == typeof(Int16))
- return new Tensor((Int16)(object)data, TF_DataType.TF_INT16);
+ return new Tensor((Int16)(object)data);
else if (typeof(T) == typeof(byte))
- return new Tensor((byte)(object)data, TF_DataType.TF_UINT8);
+ return new Tensor((byte)(object)data);
else if (typeof(T) == typeof(ulong))
- return new Tensor((ulong)(object)data, TF_DataType.TF_UINT64);
+ return new Tensor((ulong)(object)data);
else if (typeof(T) == typeof(UInt32))
- return new Tensor((UInt32)(object)data, TF_DataType.TF_UINT32);
+ return new Tensor((UInt32)(object)data);
else if (typeof(T) == typeof(UInt16))
- return new Tensor((UInt16)(object)data, TF_DataType.TF_UINT16);
+#pragma warning disable IDE0055
+ // Tensorflow.NET v2.7 has no constructor for UInt16 so using the array version
+ return new Tensor(new UInt16[]{(UInt16)(object)data});
+#pragma warning restore IDE0055
else if (typeof(T) == typeof(bool))
- return new Tensor((bool)(object)data, TF_DataType.TF_BOOL);
+ return new Tensor((bool)(object)data);
else if (typeof(T) == typeof(float))
- return new Tensor((float)(object)data, TF_DataType.TF_FLOAT);
+ return new Tensor((float)(object)data);
else if (typeof(T) == typeof(double))
- return new Tensor((double)(object)data, TF_DataType.TF_DOUBLE);
+ return new Tensor((double)(object)data);
else if (typeof(T) == typeof(ReadOnlyMemory))
return new Tensor(data.ToString());
@@ -556,7 +560,8 @@ public Runner AddInput(Tensor value, int index)
{
_inputTensors[index]?.Dispose();
_inputTensors[index] = value;
- _inputValues[index] = value;
+ _inputValues[index] = value.Handle.DangerousGetHandle();
+
return this;
}
@@ -613,7 +618,9 @@ public Tensor[] Run()
_status.Check(true);
for (int i = 0; i < _outputs.Length; i++)
- _outputTensors[i] = new Tensor(_outputValues[i]);
+ {
+ _outputTensors[i] = new Tensor(new SafeTensorHandle(_outputValues[i]));
+ }
return _outputTensors;
}
diff --git a/src/Microsoft.ML.Vision/DnnRetrainTransform.cs b/src/Microsoft.ML.Vision/DnnRetrainTransform.cs
index d172633057..8075dd1d03 100644
--- a/src/Microsoft.ML.Vision/DnnRetrainTransform.cs
+++ b/src/Microsoft.ML.Vision/DnnRetrainTransform.cs
@@ -15,7 +15,6 @@
using Microsoft.ML.Runtime;
using Microsoft.ML.TensorFlow;
using Microsoft.ML.Transforms;
-using NumSharp;
using Tensorflow;
using static Microsoft.ML.TensorFlow.TensorFlowUtils;
using static Tensorflow.Binding;
@@ -50,7 +49,7 @@ internal sealed class DnnRetrainTransformer : RowToRowTransformerBase, IDisposab
private readonly DataViewType[] _outputTypes;
private readonly TF_DataType[] _tfOutputTypes;
private readonly TF_DataType[] _tfInputTypes;
- private readonly TensorShape[] _tfInputShapes;
+ private readonly Shape[] _tfInputShapes;
private readonly (Operation, int)[] _tfInputOperations;
private readonly (Operation, int)[] _tfOutputOperations;
private readonly TF_Output[] _tfInputNodes;
@@ -225,7 +224,7 @@ private void CheckTrainingParameters(DnnRetrainEstimator.Options options)
}
}
- private (int, bool, TF_DataType, TensorShape) GetTrainingInputInfo(DataViewSchema inputSchema, string columnName, string tfNodeName, int batchSize)
+ private (int, bool, TF_DataType, Shape) GetTrainingInputInfo(DataViewSchema inputSchema, string columnName, string tfNodeName, int batchSize)
{
if (!inputSchema.TryGetColumnIndex(columnName, out int inputColIndex))
throw Host.Except($"Column {columnName} doesn't exist");
@@ -237,7 +236,7 @@ private void CheckTrainingParameters(DnnRetrainEstimator.Options options)
var tfInput = new TF_Input(inputTensor, index);
var tfInputType = inputTensor.OpType == "Placeholder" ? inputTensor.OutputType(index) :
inputTensor.InputType(index);
- var tfInputShape = ((Tensor)inputTensor).TensorShape;
+ var tfInputShape = ((Tensor)inputTensor).shape;
var numInputDims = tfInputShape != null ? tfInputShape.ndim : -1;
if (isInputVector && (tfInputShape == null || (numInputDims == 0)))
@@ -248,17 +247,17 @@ private void CheckTrainingParameters(DnnRetrainEstimator.Options options)
for (int indexLocal = 0; indexLocal < vecType.Dimensions.Length; indexLocal += 1)
colTypeDims[indexLocal + 1] = vecType.Dimensions[indexLocal];
- tfInputShape = new TensorShape(colTypeDims);
+ tfInputShape = new Shape(colTypeDims);
}
if (numInputDims != -1)
{
- var newShape = new int[numInputDims];
+ var newShape = new long[numInputDims];
var dims = tfInputShape.dims;
newShape[0] = dims[0] == 0 || dims[0] == -1 ? batchSize : dims[0];
for (int j = 1; j < numInputDims; j++)
newShape[j] = dims[j];
- tfInputShape = new TensorShape(newShape);
+ tfInputShape = new Shape(newShape);
}
var expectedType = Tf2MlNetType(tfInputType);
@@ -278,7 +277,7 @@ private void TrainCore(DnnRetrainEstimator.Options options, IDataView input, IDa
var inputColIndices = new int[inputsForTraining.Length];
var isInputVector = new bool[inputsForTraining.Length];
var tfInputTypes = new TF_DataType[inputsForTraining.Length];
- var tfInputShapes = new TensorShape[inputsForTraining.Length];
+ var tfInputShapes = new Shape[inputsForTraining.Length];
for (int i = 0; i < _inputs.Length; i++)
inputsForTraining[i] = _idvToTfMapping[_inputs[i]];
@@ -382,13 +381,13 @@ private void TrainCore(DnnRetrainEstimator.Options options, IDataView input, IDa
runner.AddInput(srcTensorGetters[i].GetBufferedBatchTensor(), i + 1);
Tensor[] tensor = runner.Run();
- if (tensor.Length > 0 && tensor[0] != IntPtr.Zero)
+ if (tensor.Length > 0 && tensor[0].TensorDataPointer != IntPtr.Zero)
{
tensor[0].ToScalar(ref loss);
tensor[0].Dispose();
}
- if (tensor.Length > 1 && tensor[1] != IntPtr.Zero)
+ if (tensor.Length > 1 && tensor[1].TensorDataPointer != IntPtr.Zero)
{
tensor[1].ToScalar(ref metric);
tensor[1].Dispose();
@@ -460,14 +459,14 @@ private void UpdateModelOnDisk(string modelDir, DnnRetrainEstimator.Options opti
}
}
- private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, bool isVector, int colIndex, TensorShape tfShape, bool keyType = false)
+ private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, bool isVector, int colIndex, Shape tfShape, bool keyType = false)
{
if (isVector)
return new TensorValueGetterVec(input, colIndex, tfShape);
return new TensorValueGetter(input, colIndex, tfShape, keyType);
}
- private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, TF_DataType tfType, bool isVector, int colIndex, TensorShape tfShape)
+ private static ITensorValueGetter CreateTensorValueGetter(DataViewRow input, TF_DataType tfType, bool isVector, int colIndex, Shape tfShape)
{
var type = Tf2MlNetType(tfType);
if (input.Schema[colIndex].Type is KeyDataViewType && type.RawType == typeof(Int64))
@@ -481,7 +480,7 @@ private static ITensorValueGetter[] GetTensorValueGetters(
int[] inputColIndices,
bool[] isInputVector,
TF_DataType[] tfInputTypes,
- TensorShape[] tfInputShapes)
+ Shape[] tfInputShapes)
{
var srcTensorGetters = new ITensorValueGetter[inputColIndices.Length];
for (int i = 0; i < inputColIndices.Length; i++)
@@ -574,10 +573,10 @@ private static (Operation, int) GetOperationFromName(string operation, Session s
return (session.graph.OperationByName(operation), 0);
}
- internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Operation, int)[]) GetInputInfo(IHost host, Session session, string[] inputs, int batchSize = 1)
+ internal static (TF_DataType[] tfInputTypes, Shape[] tfInputShapes, (Operation, int)[]) GetInputInfo(IHost host, Session session, string[] inputs, int batchSize = 1)
{
var tfInputTypes = new TF_DataType[inputs.Length];
- var tfInputShapes = new TensorShape[inputs.Length];
+ var tfInputShapes = new Shape[inputs.Length];
var tfInputOperations = new (Operation, int)[inputs.Length];
int index = 0;
@@ -594,7 +593,7 @@ internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Opera
throw host.ExceptParam(nameof(session), $"Input type '{tfInputType}' of input column '{input}' is not supported in TensorFlow");
tfInputTypes[index] = tfInputType;
- tfInputShapes[index] = ((Tensor)inputTensor).TensorShape;
+ tfInputShapes[index] = ((Tensor)inputTensor).shape;
tfInputOperations[index] = (inputTensor, inputTensorIndex);
index++;
}
@@ -602,7 +601,7 @@ internal static (TF_DataType[] tfInputTypes, TensorShape[] tfInputShapes, (Opera
return (tfInputTypes, tfInputShapes, tfInputOperations);
}
- internal static TensorShape GetTensorShape(TF_Output output, Graph graph, Status status = null)
+ internal static Shape GetTensorShape(TF_Output output, Graph graph, Status status = null)
{
if (graph == IntPtr.Zero)
throw new ObjectDisposedException(nameof(graph));
@@ -613,12 +612,12 @@ internal static TensorShape GetTensorShape(TF_Output output, Graph graph, Status
cstatus.Check();
if (n == -1)
- return new TensorShape(new int[0]);
+ return new Shape(new int[0]);
var dims = new long[n];
c_api.TF_GraphGetTensorShape(graph, output, dims, dims.Length, cstatus.Handle);
cstatus.Check();
- return new TensorShape(dims.Select(x => (int)x).ToArray());
+ return new Shape(dims.Select(x => (int)x).ToArray());
}
internal static (TF_DataType[] tfOutputTypes, DataViewType[] outputTypes, (Operation, int)[]) GetOutputInfo(IHost host, Session session, string[] outputs)
@@ -645,12 +644,12 @@ internal static (TF_DataType[] tfOutputTypes, DataViewType[] outputTypes, (Opera
// i.e. the first dimension (if unknown) is assumed to be batch dimension.
// If there are other dimension that are unknown the transformer will return a variable length vector.
// This is the work around in absence of reshape transformer.
- int[] dims = shape.ndim > 0 ? shape.dims.Skip(shape.dims[0] == -1 ? 1 : 0).ToArray() : new[] { 0 };
+ long[] dims = shape.ndim > 0 ? shape.dims.Skip(shape.dims[0] == -1 ? 1 : 0).ToArray() : new long[] { 0 };
for (int j = 0; j < dims.Length; j++)
dims[j] = dims[j] == -1 ? 0 : dims[j];
if (dims == null || dims.Length == 0)
{
- dims = new[] { 1 };
+ dims = new long[] { 1 };
outputTypes[i] = Tf2MlNetType(tfOutputType);
}
else
@@ -741,7 +740,7 @@ public void Dispose()
{
if (_session.graph != null)
_session.graph.Dispose();
- _session.close();
+ _session.Dispose();
}
}
finally
@@ -760,7 +759,7 @@ private sealed class Mapper : MapperBase
private readonly DnnRetrainTransformer _parent;
private readonly int[] _inputColIndices;
private readonly bool[] _isInputVector;
- private readonly TensorShape[] _fullySpecifiedShapes;
+ private readonly Shape[] _fullySpecifiedShapes;
private readonly ConcurrentBag _runners;
public Mapper(DnnRetrainTransformer parent, DataViewSchema inputSchema) :
@@ -770,7 +769,7 @@ public Mapper(DnnRetrainTransformer parent, DataViewSchema inputSchema) :
_parent = parent;
_inputColIndices = new int[_parent._inputs.Length];
_isInputVector = new bool[_parent._inputs.Length];
- _fullySpecifiedShapes = new TensorShape[_parent._inputs.Length];
+ _fullySpecifiedShapes = new Shape[_parent._inputs.Length];
for (int i = 0; i < _parent._inputs.Length; i++)
{
if (!inputSchema.TryGetColumnIndex(_parent._inputs[i], out _inputColIndices[i]))
@@ -792,12 +791,12 @@ public Mapper(DnnRetrainTransformer parent, DataViewSchema inputSchema) :
var colTypeDims = vecType.Dimensions.Select(dim => (int)dim).ToArray();
if (shape == null || (shape.Length == 0))
- _fullySpecifiedShapes[i] = new TensorShape(colTypeDims);
+ _fullySpecifiedShapes[i] = new Shape(colTypeDims);
else
{
// If the column is one dimension we make sure that the total size of the TF shape matches.
// Compute the total size of the known dimensions of the shape.
- int valCount = 1;
+ long valCount = 1;
int numOfUnkDim = 0;
foreach (var s in shape)
{
@@ -821,19 +820,19 @@ public Mapper(DnnRetrainTransformer parent, DataViewSchema inputSchema) :
// Fill in the unknown dimensions.
var originalShapeDims = originalShape.dims;
var originalShapeNdim = originalShape.ndim;
- var l = new int[originalShapeNdim];
+ var l = new long[originalShapeNdim];
for (int ishape = 0; ishape < originalShapeNdim; ishape++)
l[ishape] = originalShapeDims[ishape] == -1 ? (int)d : originalShapeDims[ishape];
- _fullySpecifiedShapes[i] = new TensorShape(l);
+ _fullySpecifiedShapes[i] = new Shape(l);
}
if (_parent._addBatchDimensionInput)
{
- var l = new int[_fullySpecifiedShapes[i].ndim + 1];
+ var l = new long[_fullySpecifiedShapes[i].ndim + 1];
l[0] = 1;
for (int ishape = 1; ishape < l.Length; ishape++)
l[ishape] = _fullySpecifiedShapes[i].dims[ishape - 1];
- _fullySpecifiedShapes[i] = new TensorShape(l);
+ _fullySpecifiedShapes[i] = new Shape(l);
}
}
@@ -891,7 +890,7 @@ private Delegate MakeGetter(DataViewRow input, int iinfo, ITensorValueGetter[
UpdateCacheIfNeeded(input.Position, srcTensorGetters, activeOutputColNames, outputCache);
var tensor = outputCache.Outputs[_parent._outputs[iinfo]];
- var tensorSize = tensor.TensorShape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
+ var tensorSize = tensor.shape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
var editor = VBufferEditor.Create(ref dst, (int)tensorSize);
FetchStringData(tensor, editor.Values);
@@ -906,7 +905,7 @@ private Delegate MakeGetter(DataViewRow input, int iinfo, ITensorValueGetter[
UpdateCacheIfNeeded(input.Position, srcTensorGetters, activeOutputColNames, outputCache);
var tensor = outputCache.Outputs[_parent._outputs[iinfo]];
- var tensorSize = tensor.TensorShape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
+ var tensorSize = tensor.shape.dims.Where(x => x > 0).Aggregate((x, y) => x * y);
var editor = VBufferEditor.Create(ref dst, (int)tensorSize);
@@ -972,12 +971,12 @@ private class TensorValueGetter : ITensorValueGetter
private readonly ValueGetter _srcgetter;
private readonly T[] _bufferedData;
private readonly Int64[] _bufferedDataLong;
- private readonly TensorShape _tfShape;
+ private readonly Shape _tfShape;
private int _position;
private readonly bool _keyType;
private readonly long[] _dims;
- public TensorValueGetter(DataViewRow input, int colIndex, TensorShape tfShape, bool keyType = false)
+ public TensorValueGetter(DataViewRow input, int colIndex, Shape tfShape, bool keyType = false)
{
_srcgetter = input.GetGetter(input.Schema[colIndex]);
_tfShape = tfShape;
@@ -1035,7 +1034,7 @@ public Tensor GetBufferedBatchTensor()
{
if (_keyType)
{
- var tensor = new Tensor(_bufferedDataLong, _dims, TF_DataType.TF_INT64);
+ var tensor = new Tensor(_bufferedDataLong, _dims);
_position = 0;
return tensor;
}
@@ -1051,7 +1050,7 @@ public Tensor GetBufferedBatchTensor()
private class TensorValueGetterVec : ITensorValueGetter
{
private readonly ValueGetter> _srcgetter;
- private readonly TensorShape _tfShape;
+ private readonly Shape _tfShape;
private VBuffer _vBuffer;
private T[] _denseData;
private T[] _bufferedData;
@@ -1059,7 +1058,7 @@ private class TensorValueGetterVec : ITensorValueGetter
private readonly long[] _dims;
private readonly long _bufferedDataSize;
- public TensorValueGetterVec(DataViewRow input, int colIndex, TensorShape tfShape)
+ public TensorValueGetterVec(DataViewRow input, int colIndex, Shape tfShape)
{
_srcgetter = input.GetGetter>(input.Schema[colIndex]);
_tfShape = tfShape;
diff --git a/src/Microsoft.ML.Vision/ImageClassificationTrainer.cs b/src/Microsoft.ML.Vision/ImageClassificationTrainer.cs
index 846de00518..67381a87fe 100644
--- a/src/Microsoft.ML.Vision/ImageClassificationTrainer.cs
+++ b/src/Microsoft.ML.Vision/ImageClassificationTrainer.cs
@@ -25,7 +25,9 @@
using Tensorflow;
using Tensorflow.Summaries;
using static Microsoft.ML.Data.TextLoader;
+using static Microsoft.ML.Internal.Utilities.ArrayUtils;
using static Microsoft.ML.TensorFlow.TensorFlowUtils;
+using static Microsoft.ML.Vision.StringTensorFactory;
using static Tensorflow.Binding;
using Column = Microsoft.ML.Data.TextLoader.Column;
@@ -763,23 +765,7 @@ private void CheckTrainingParameters(Options options)
private static Tensor EncodeByteAsString(VBuffer buffer)
{
- int length = buffer.Length;
- var size = c_api.TF_StringEncodedSize((ulong)length);
- var handle = c_api.TF_AllocateTensor(TF_DataType.TF_STRING, Array.Empty(), 0, ((ulong)size + 8));
-
- IntPtr tensor = c_api.TF_TensorData(handle);
- Marshal.WriteInt64(tensor, 0);
-
- var status = new Status();
- unsafe
- {
- fixed (byte* src = buffer.GetValues())
- c_api.TF_StringEncode(src, (ulong)length, (byte*)(tensor + sizeof(Int64)), size, status.Handle);
- }
-
- status.Check(true);
- status.Dispose();
- return new Tensor(handle);
+ return StringTensorFactory.CreateStringTensor(buffer.DenseValues().ToArray());
}
internal sealed class ImageProcessor
@@ -976,8 +962,8 @@ private void TrainAndEvaluateClassificationLayer(string trainBottleneckFilePath,
metrics.Train = new TrainMetrics();
float accuracy = 0;
float crossentropy = 0;
- var labelTensorShape = _labelTensor.TensorShape.dims.Select(x => (long)x).ToArray();
- var featureTensorShape = _bottleneckInput.TensorShape.dims.Select(x => (long)x).ToArray();
+ var labelTensorShape = _labelTensor.shape.dims.Select(x => (long)x).ToArray();
+ var featureTensorShape = _bottleneckInput.shape.dims.Select(x => (long)x).ToArray();
byte[] buffer = new byte[sizeof(int)];
trainSetFeatureReader.ReadExactly(buffer, 0, 4);
int trainingExamples = BitConverter.ToInt32(buffer, 0);
@@ -1119,12 +1105,12 @@ private void TrainAndEvaluateClassificationLayerCore(int epoch, float learningRa
{
// Add learning rate as a placeholder only when learning rate scheduling is used.
metrics.Train.LearningRate = learningRateScheduler.GetLearningRate(trainState);
- runner.AddInput(new Tensor(metrics.Train.LearningRate, TF_DataType.TF_FLOAT), 2);
+ runner.AddInput(new Tensor(metrics.Train.LearningRate), 2);
}
- var outputTensors = runner.AddInput(new Tensor(featureBufferPtr, featureTensorShape, TF_DataType.TF_FLOAT, featuresFileBytesRead), 0)
- .AddInput(new Tensor(labelBufferPtr, labelTensorShape, TF_DataType.TF_INT64, labelFileBytesRead), 1)
- .Run();
+ var outputTensors = runner.AddInput(new Tensor(featureBufferPtr, featureTensorShape, TF_DataType.TF_FLOAT), 0)
+ .AddInput(new Tensor(labelBufferPtr, labelTensorShape, TF_DataType.TF_INT64), 1)
+ .Run();
metrics.Train.BatchProcessedCount += 1;
metricsAggregator(outputTensors, metrics);
@@ -1186,7 +1172,7 @@ private void TryCleanupTemporaryWorkspace()
{
tf_with(tf.name_scope("correct_prediction"), delegate
{
- _prediction = tf.argmax(resultTensor, 1);
+ _prediction = tf.math.argmax(resultTensor, 1);
correctPrediction = tf.equal(_prediction, groundTruthTensor);
});
@@ -1240,7 +1226,7 @@ private void VariableSummaries(ResourceVariable var)
string scoreColumnName, Tensor bottleneckTensor, bool isTraining, bool useLearningRateScheduler,
float learningRate)
{
- var bottleneckTensorDims = bottleneckTensor.TensorShape.dims;
+ var bottleneckTensorDims = CastLongArrayToIntArray(bottleneckTensor.shape.dims);
var (batch_size, bottleneck_tensor_size) = (bottleneckTensorDims[0], bottleneckTensorDims[1]);
tf_with(tf.name_scope("input"), scope =>
{
@@ -1254,7 +1240,7 @@ private void VariableSummaries(ResourceVariable var)
_learningRateInput = tf.placeholder(tf.float32, null, name: "learningRateInputPlaceholder");
}
- _labelTensor = tf.placeholder(tf.int64, new TensorShape(batch_size), name: labelColumn);
+ _labelTensor = tf.placeholder(tf.int64, new Shape(batch_size), name: labelColumn);
});
string layerName = "final_retrain_ops";
@@ -1274,7 +1260,7 @@ private void VariableSummaries(ResourceVariable var)
ResourceVariable layerBiases = null;
tf_with(tf.name_scope("biases"), delegate
{
- TensorShape shape = new TensorShape(classCount);
+ Shape shape = new Shape(classCount);
layerBiases = tf.Variable(tf.zeros(shape), name: "final_biases");
VariableSummaries(layerBiases);
});
@@ -1514,10 +1500,94 @@ public void Dispose()
if (_session != null && _session != IntPtr.Zero)
{
- _session.close();
+ _session.Dispose();
}
_isDisposed = true;
}
}
+
+#pragma warning disable MSML_GeneralName
+#pragma warning disable MSML_ParameterLocalVarName
+#pragma warning disable IDE0055
+ public class StringTensorFactory
+ {
+ // Define TF_TString struct
+ [StructLayout(LayoutKind.Sequential)]
+ struct TF_TString
+ {
+ public IntPtr data;
+ public UIntPtr length;
+ public UIntPtr capacity;
+ public int memory_type;
+ }
+
+ // Import TF_TString methods from TensorFlow C API
+ [DllImport("tensorflow", CallingConvention = CallingConvention.Cdecl)]
+ private static extern unsafe void TF_StringInit(TF_TString* tstring);
+
+ [DllImport("tensorflow", CallingConvention = CallingConvention.Cdecl)]
+ private static extern unsafe void TF_StringCopy(TF_TString* dst, byte* src, UIntPtr size);
+
+ [DllImport("tensorflow", CallingConvention = CallingConvention.Cdecl)]
+ private static extern unsafe void TF_StringDealloc(TF_TString* tstring);
+
+ private static readonly TF_Deallocator _deallocatorInstance = new StringTensorFactory.TF_Deallocator(Deallocator);
+
+ // Delegate for TensorFlow deallocator
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void TF_Deallocator(IntPtr data, UIntPtr length, IntPtr arg);
+
+ // Deallocator function
+ public static void Deallocator(IntPtr data, UIntPtr length, IntPtr arg)
+ {
+ unsafe
+ {
+ TF_StringDealloc((TF_TString*)data);
+ }
+ Marshal.FreeHGlobal(data);
+ }
+
+ public static Tensor CreateStringTensor(byte[] data)
+ {
+ int sizeOfTString = Marshal.SizeOf();
+
+ // Allocate memory for TF_TString
+ IntPtr tstringPtr = Marshal.AllocHGlobal(sizeOfTString);
+ unsafe
+ {
+ TF_TString* tstring = (TF_TString*)tstringPtr;
+ TF_StringInit(tstring);
+
+ fixed (byte* src = data)
+ {
+ TF_StringCopy(tstring, src, (UIntPtr)data.Length);
+ }
+ }
+
+ // Create a scalar tensor (rank 0, so no shape dims)
+ Tensor tensor = new Tensor(new SafeTensorHandle(TF_NewTensor(
+ TF_DataType.TF_STRING,
+ Array.Empty(),
+ 0,
+ tstringPtr,
+ (UIntPtr)sizeOfTString,
+ _deallocatorInstance,
+ IntPtr.Zero
+ )));
+
+ return tensor;
+ }
+
+ [DllImport("tensorflow", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr TF_NewTensor(
+ TF_DataType dtype,
+ long[] dims, int num_dims,
+ IntPtr data, UIntPtr len,
+ TF_Deallocator deallocator,
+ IntPtr deallocator_arg);
+ }
+#pragma warning restore MSML_GeneralName
+#pragma warning restore MSML_ParameterLocalVarName
+#pragma warning restore IDE0055
}
diff --git a/test/Microsoft.ML.TensorFlow.Tests/TensorflowTests.cs b/test/Microsoft.ML.TensorFlow.Tests/TensorflowTests.cs
index 16bc4a6b74..d9d362d020 100644
--- a/test/Microsoft.ML.TensorFlow.Tests/TensorflowTests.cs
+++ b/test/Microsoft.ML.TensorFlow.Tests/TensorflowTests.cs
@@ -18,6 +18,7 @@
using Microsoft.ML.Transforms;
using Microsoft.ML.Transforms.Image;
using Microsoft.ML.Vision;
+using Tensorflow;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.ML.DataOperationsCatalog;
@@ -1187,7 +1188,9 @@ public void TensorFlowSaveAndLoadSavedModel()
predictFunction.Dispose();
// Reload the model and check the output schema consistency
+#pragma warning disable IDE0055
DataViewSchema loadedInputschema;
+#pragma warning restore IDE0055
var testTransformer = _mlContext.Model.Load(mlModelLocation, out loadedInputschema);
var testOutputSchema = transformer.GetOutputSchema(data.Schema);
Assert.True(TestCommon.CheckSameSchemas(outputSchema, testOutputSchema));
@@ -2055,7 +2058,7 @@ public void TensorflowPlaceholderShapeInferenceTest()
new TextLoader.Column("name", DataKind.String, 1)
});
- Tensorflow.TensorShape[] tfInputShape;
+ Tensorflow.Shape[] tfInputShape;
using (var tfModel = _mlContext.Model.LoadTensorFlowModel(modelLocation))
{
@@ -2070,8 +2073,8 @@ public void TensorflowPlaceholderShapeInferenceTest()
transformer.Dispose();
}
- Assert.Equal(imageHeight, tfInputShape.ElementAt(0)[1].dims[0]);
- Assert.Equal(imageWidth, tfInputShape.ElementAt(0)[2].dims[0]);
+ Assert.Equal(imageHeight, tfInputShape.ElementAt(0)[Slice.Index(1)].dims[0]);
+ Assert.Equal(imageWidth, tfInputShape.ElementAt(0)[Slice.Index(2)].dims[0]);
}
}
}