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.
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");
you may not use this file except in compliance with the License.
@@ -18,6 +20,8 @@ using NumSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using static Tensorflow.c_api;
@@ -36,39 +40,246 @@ namespace Tensorflow
_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;
ulong size = 0;
int buffersize = 0;

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);
@@ -116,7 +327,7 @@ namespace Tensorflow
dims,
dims.Length,
dotHandle,
(UIntPtr)size,
(UIntPtr)buffersize,
deallocator,
ref deallocator_called);

@@ -130,5 +341,59 @@ namespace Tensorflow
_dtype = dtype;
_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>
public partial class Tensor : IDisposable, ITensorOrOperation
{
private readonly IntPtr _handle;
private IntPtr _handle;

private int _id;
private Operation _op;
@@ -351,6 +351,7 @@ namespace Tensorflow
public void Dispose()
{
c_api.TF_DeleteTensor(_handle);
_handle = IntPtr.Zero;
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)
{
TF_DataType dtype = TF_DataType.DtInvalid;
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;
case "Int32":
dtype = TF_DataType.TF_INT32;
break;
case "UInt32":
dtype = TF_DataType.TF_UINT32;
break;
case "Int64":
dtype = TF_DataType.TF_INT64;
break;
case "UInt64":
dtype = TF_DataType.TF_UINT64;
break;
case "Single":
dtype = TF_DataType.TF_FLOAT;
break;
case "Double":
dtype = TF_DataType.TF_DOUBLE;
break;
case "Complex":
dtype = TF_DataType.TF_COMPLEX128;
break;
case "String":
dtype = TF_DataType.TF_STRING;
break;
case "Byte":
dtype = TF_DataType.TF_STRING;
case "Boolean":
dtype = TF_DataType.TF_BOOL;
break;
default:
throw new Exception("as_dtype Not Implemented");


Loading…
Cancel
Save