Browse Source

Tensor: Added more constructors to create Tensors from arrays without copying (and without converting to and from NDArray).

tags/v0.10
Meinrad Recheis 6 years ago
parent
commit
21e9d9bf58
3 changed files with 309 additions and 24 deletions
  1. +283
    -18
      src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs
  2. +2
    -1
      src/TensorFlowNET.Core/Tensors/Tensor.cs
  3. +24
    -5
      src/TensorFlowNET.Core/Tensors/dtypes.cs

+ 283
- 18
src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs View File

@@ -1,5 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved.
Portions of this file have been adapted from TensorFlowSharp, authored by Miguel de Icaza (miguel@microsoft.com)


Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +20,8 @@ using NumSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using static Tensorflow.c_api; using static Tensorflow.c_api;
@@ -36,39 +40,246 @@ namespace Tensorflow
_handle = handle; _handle = handle;
} }


public Tensor(NDArray nd, TF_DataType? tensorDType = null)
#if _REGEN
%types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"]
%foreach types%
/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(#1[] data)
{ {
_handle = Allocate(nd, tensorDType: tensorDType);
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(#1)), new long[]{data.Length}, data, Marshal.SizeOf<#1>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(#1[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(#1)), shape, data, Marshal.SizeOf<#1>());
}

%
#else
/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(sbyte[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(sbyte)), new long[]{data.Length}, data, Marshal.SizeOf<sbyte>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(sbyte[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(sbyte)), shape, data, Marshal.SizeOf<sbyte>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(byte[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(byte)), new long[]{data.Length}, data, Marshal.SizeOf<byte>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(byte[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(byte)), shape, data, Marshal.SizeOf<byte>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(short[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(short)), new long[]{data.Length}, data, Marshal.SizeOf<short>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(short[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(short)), shape, data, Marshal.SizeOf<short>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(ushort[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ushort)), new long[]{data.Length}, data, Marshal.SizeOf<ushort>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(ushort[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ushort)), shape, data, Marshal.SizeOf<ushort>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(int[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(int)), new long[]{data.Length}, data, Marshal.SizeOf<int>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(int[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(int)), shape, data, Marshal.SizeOf<int>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(uint[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(uint)), new long[]{data.Length}, data, Marshal.SizeOf<uint>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(uint[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(uint)), shape, data, Marshal.SizeOf<uint>());
} }


public unsafe Tensor(byte[] buffer)
/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(long[] data)
{ {
var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length);
_handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8));
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(long)), new long[]{data.Length}, data, Marshal.SizeOf<long>());
}


IntPtr tensor = c_api.TF_TensorData(_handle);
Marshal.WriteInt64(tensor, 0);
fixed (byte* src = &buffer[0])
c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status);
/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(long[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(long)), shape, data, Marshal.SizeOf<long>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(ulong[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ulong)), new long[]{data.Length}, data, Marshal.SizeOf<ulong>());
}


status.Check(true);
/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(ulong[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(ulong)), shape, data, Marshal.SizeOf<ulong>());
} }


private IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null)
/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(float[] data)
{ {
if (tensorDType == TF_DataType.TF_STRING &&
nd.dtype.Name == "Byte")
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(float)), new long[]{data.Length}, data, Marshal.SizeOf<float>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(float[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(float)), shape, data, Marshal.SizeOf<float>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(double[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(double)), new long[]{data.Length}, data, Marshal.SizeOf<double>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(double[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(double)), shape, data, Marshal.SizeOf<double>());
}

/// <summary>
/// Create a 1d Tensor from the given linear array and shape
/// </summary>
public Tensor(Complex[] data)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(Complex)), new long[]{data.Length}, data, Marshal.SizeOf<Complex>());
}

/// <summary>
/// Create a N-dimensional Tensor from the given array
/// </summary>
public Tensor(Complex[] data, long[] shape)
{
_handle = CreateTensorWithoutCopying(dtypes.as_dtype(typeof(Complex)), shape, data, Marshal.SizeOf<Complex>());
}
#endif

public Tensor(NDArray nd, TF_DataType? tensorDType = null)
{
_handle = Allocate(nd, tensorDType: tensorDType);
}

private unsafe IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null)
{
if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte")
{ {
return new Tensor(nd.Data<byte>());
var buffer=nd.Data<byte>();
var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length);
var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8));

IntPtr tensor = c_api.TF_TensorData(handle);
Marshal.WriteInt64(tensor, 0);
fixed (byte* src = &buffer[0])
c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status);

status.Check(true);
return handle;
} }


IntPtr dotHandle = IntPtr.Zero; IntPtr dotHandle = IntPtr.Zero;
ulong size = 0;
int buffersize = 0;


if (nd.dtype.Name != "String") if (nd.dtype.Name != "String")
{ {
dotHandle = Marshal.AllocHGlobal(nd.dtypesize * nd.size);
size = (ulong)(nd.size * nd.dtypesize);
buffersize = (nd.size * nd.dtypesize);
dotHandle = Marshal.AllocHGlobal(buffersize);
} }


var dataType = ToTFDataType(nd.dtype); var dataType = ToTFDataType(nd.dtype);
@@ -116,7 +327,7 @@ namespace Tensorflow
dims, dims,
dims.Length, dims.Length,
dotHandle, dotHandle,
(UIntPtr)size,
(UIntPtr)buffersize,
deallocator, deallocator,
ref deallocator_called); ref deallocator_called);


@@ -130,5 +341,59 @@ namespace Tensorflow
_dtype = dtype; _dtype = dtype;
_id = ops.uid(); _id = ops.uid();
} }

/// <summary>
/// Creates a new tensor from the given array without copying memory. The array is pinned down and the pointer passed on.
/// </summary>
/// <param name="shape">Represents the tensor shape.</param>
/// <param name="data">The linear array of data, the data must fit in the tensor with the specified dimensions.</param>
/// <param name="element_size">The number of bytes in memory of a single array element</param>
/// <remarks>
/// Use the FromBuffer method to create a tensor that has the specified dimensions
/// and is initialized with data from the data array. The data is copied starting
/// at the start offset, for count bytes and is laid out into the tensor following the
/// specified dimensions.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size)
{
return CreateTensorWithoutCopying(dt, shape, data, 0, data.Length, element_size);
}

/// <summary>
/// Creates a new tensor from a subsection of the given array without copying memory. The array is pinned down and the pointer passed on.
/// </summary>
/// <param name="shape">Represents the tensor shape.</param>
/// <param name="data">The linear array of data, the data must fit in the tensor with the specified dimensions.</param>
/// <param name="start">The offset into the provided data array where the data resides.</param>
/// <param name="count">The number of elements to copy from data.</param>
/// <param name="element_size">The number of bytes in memory of a single array element</param>
/// <remarks>
/// Use the FromBuffer method to create a tensor that has the specified dimensions
/// and is initialized with data from the data array. The data is copied starting
/// at the start offset, for count bytes and is laid out into the tensor following the
/// specified dimensions.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int start, int count, int element_size)
{
if (start < 0 || start > data.Length - count)
throw new ArgumentException("start + count > Array size");
// get a handle to the pinned array which we will pass on to the tensor computation engine to use
var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);

// Free the original buffer and set flag
Deallocator deallocator = (IntPtr values, IntPtr len, ref bool closure) =>
{
dataHandle.Free();
closure = true;
};

if (shape == null)
return TF_NewTensor(dt, null, 0, dataHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref deallocator_called);
else
return TF_NewTensor(dt, shape, shape.Length, dataHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), deallocator, ref deallocator_called);
}
} }
} }

+ 2
- 1
src/TensorFlowNET.Core/Tensors/Tensor.cs View File

@@ -31,7 +31,7 @@ namespace Tensorflow
/// </summary> /// </summary>
public partial class Tensor : IDisposable, ITensorOrOperation public partial class Tensor : IDisposable, ITensorOrOperation
{ {
private readonly IntPtr _handle;
private IntPtr _handle;


private int _id; private int _id;
private Operation _op; private Operation _op;
@@ -351,6 +351,7 @@ namespace Tensorflow
public void Dispose() public void Dispose()
{ {
c_api.TF_DeleteTensor(_handle); c_api.TF_DeleteTensor(_handle);
_handle = IntPtr.Zero;
status.Dispose(); status.Dispose();
} }




+ 24
- 5
src/TensorFlowNET.Core/Tensors/dtypes.cs View File

@@ -52,32 +52,51 @@ namespace Tensorflow
} }
} }


// "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"
public static TF_DataType as_dtype(Type type) public static TF_DataType as_dtype(Type type)
{ {
TF_DataType dtype = TF_DataType.DtInvalid; TF_DataType dtype = TF_DataType.DtInvalid;
switch (type.Name) switch (type.Name)
{ {
case "Boolean":
dtype = TF_DataType.TF_BOOL;
case "SByte":
dtype = TF_DataType.TF_INT8;
break;
case "Byte":
dtype = TF_DataType.TF_UINT8;
break;
case "Int16":
dtype = TF_DataType.TF_INT16;
break;
case "UInt16":
dtype = TF_DataType.TF_UINT16;
break; break;
case "Int32": case "Int32":
dtype = TF_DataType.TF_INT32; dtype = TF_DataType.TF_INT32;
break; break;
case "UInt32":
dtype = TF_DataType.TF_UINT32;
break;
case "Int64": case "Int64":
dtype = TF_DataType.TF_INT64; dtype = TF_DataType.TF_INT64;
break; break;
case "UInt64":
dtype = TF_DataType.TF_UINT64;
break;
case "Single": case "Single":
dtype = TF_DataType.TF_FLOAT; dtype = TF_DataType.TF_FLOAT;
break; break;
case "Double": case "Double":
dtype = TF_DataType.TF_DOUBLE; dtype = TF_DataType.TF_DOUBLE;
break; break;
case "Complex":
dtype = TF_DataType.TF_COMPLEX128;
break;
case "String": case "String":
dtype = TF_DataType.TF_STRING; dtype = TF_DataType.TF_STRING;
break; break;
case "Byte":
dtype = TF_DataType.TF_STRING;
case "Boolean":
dtype = TF_DataType.TF_BOOL;
break; break;
default: default:
throw new Exception("as_dtype Not Implemented"); throw new Exception("as_dtype Not Implemented");


Loading…
Cancel
Save