Entw.: Tensorflow mit Delphi (1)

Möchte man TensorFlow von Delphi aus nutzen, so kann dies über die Verwendung der DLL tensorflow.dll geschehen. Siehe dazu den vorhergehenden Artikel.
Die in der TensorFlow-DLL vorhandene C-API ist von Delphi aufrufbar. Dazu wurde eine Delphi-Unit erstellt, die diese C-API verwendet.

Lizenz

Die Bestimmungen zur Nutzung von TensorFlow und dadurch auch der API erfolgt nach den Apache-Lizenzbedingungen.

{ Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Source:  https://github.com/tensorflow/tensorflow
Docu:    https://www.tensorflow.org

Versions:
  1.0:  2017/06/16 - Orginal Delphi 10.2 port by hadv.de

==============================================================================}

C-API

Die C-API wird definiert durch die Header-Datei c_api.h im Verzeichnis tensorflow/c der GIT-Repository.
Die folgende C-Source zeigt ein kleinen Ausschnitt dieser Header-Datei.

// --------------------------------------------------------------------------
// TF_Version returns a string describing version information of the
// TensorFlow library. TensorFlow using semantic versioning.
TF_CAPI_EXPORT extern const char* TF_Version();

// --------------------------------------------------------------------------
// TF_DataType holds the type for a scalar value.  E.g., one slot in a tensor.
// The enum values here are identical to corresponding values in types.proto.
typedef enum TF_DataType {
  TF_FLOAT = 1,
  TF_DOUBLE = 2,
  TF_INT32 = 3,  // Int32 tensors are always in 'host' memory.
  TF_UINT8 = 4,
  TF_INT16 = 5,
  TF_INT8 = 6,
  TF_STRING = 7,
  TF_COMPLEX64 = 8,  // Single-precision complex
  TF_COMPLEX = 8,    // Old identifier kept for API backwards compatibility
  TF_INT64 = 9,
  TF_BOOL = 10,
  TF_QINT8 = 11,     // Quantized int8
  TF_QUINT8 = 12,    // Quantized uint8
  TF_QINT32 = 13,    // Quantized int32
  TF_BFLOAT16 = 14,  // Float32 truncated to 16 bits.  Only for cast ops.
  TF_QINT16 = 15,    // Quantized int16
  TF_QUINT16 = 16,   // Quantized uint16
  TF_UINT16 = 17,
  TF_COMPLEX128 = 18,  // Double-precision complex
  TF_HALF = 19,
  TF_RESOURCE = 20,
} TF_DataType;

// TF_DataTypeSize returns the sizeof() for the underlying type corresponding
// to the given TF_DataType enum value. Returns 0 for variable length types
// (eg. TF_STRING) or on failure.
TF_CAPI_EXPORT extern size_t TF_DataTypeSize(TF_DataType dt);

Verwendung der C-API mit Delphi

Die Umsetzung des obigen C-Source-Codes nach Delphi zeigt folgender Abschnitt:

unit uTensorFlowAPI;

interface

uses System.Types, Winapi.Windows;

type

TF_DataType = (
  TF_DATATYPE_UNKNOWN = 0,
  TF_FLOAT = 1,
  TF_DOUBLE = 2,
  TF_INT32 = 3,  // Int32 tensors are always in 'host' memory.
  TF_UINT8 = 4,
  TF_INT16 = 5,
  TF_INT8 = 6,
  TF_STRING = 7,
  TF_COMPLEX64 = 8,  // Single-precision complex
  TF_COMPLEX = 8,    // Old identifier kept for API backwards compatibility
  TF_INT64 = 9,
  TF_BOOL = 10,
  TF_QINT8 = 11,     // Quantized int8
  TF_QUINT8 = 12,    // Quantized uint8
  TF_QINT32 = 13,    // Quantized int32
  TF_BFLOAT16 = 14,  // Float32 truncated to 16 bits.  Only for cast ops.
  TF_QINT16 = 15,    // Quantized int16
  TF_QUINT16 = 16,   // Quantized uint16
  TF_UINT16 = 17,
  TF_COMPLEX128 = 18,  // Double-precision complex
  TF_HALF = 19,
  TF_RESOURCE = 20,
  TF_FLOAT_REF = 101,
  TF_DOUBLE_REF = 102,
  TF_INT32_REF = 103,
  TF_UINT8_REF = 104,
  TF_INT16_REF = 105,
  TF_INT8_REF = 106,
  TF_STRING_REF = 107,
  TF_COMPLEX64_REF = 108,
  TF_INT64_REF = 109,
  TF_BOOL_REF = 110,
  TF_QINT8_REF = 111,
  TF_QUINT8_REF = 112,
  TF_QINT32_REF = 113,
  TF_BFLOAT16_REF = 114,
  TF_QINT16_REF = 115,
  TF_QUINT16_REF = 116,
  TF_UINT16_REF = 117,
  TF_COMPLEX128_REF = 118,
  TF_HALF_REF = 119,
  TF_RESOURCE_REF = 120
 );
 PTF_DataType = ^TF_DataType;

const
  c_sNameOfTensorflowLib = 'tensorflow.dll';

// TF_Version returns a string describing version information of the
// TensorFlow library. TensorFlow using semantic versioning.
function TF_Version(): PAnsiChar;
{$EXTERNALSYM TF_Version}

// TF_DataTypeSize returns the sizeof() for the underlying type corresponding
// to the given TF_DataType enum value. Returns 0 for variable length types
// (eg. TF_STRING) or on failure.
function TF_DataTypeSize(dt: Int32): TF_size_t;
{$EXTERNALSYM TF_DataTypeSize}

// ...

implementation


function  TF_Version;         external c_sNameOfTensorflowLib name 'TF_Version';
function  TF_DataTypeSize;    external c_sNameOfTensorflowLib name 'TF_DataTypeSize';


// ...


end.

Die Delphi-API liefert zum grossen Teil nur Pointer auf Tensorflow-Datenstrukturen. Die Interpretation dieser Daten sollte immer nur über API-Aufrufe erfolgen. Nur so bleiben die Abhängigkeiten zwischen Delphi- und C-API übersichtlich transparent. Auch bleibt der Plegeaufwand dieser Delphi-API gering und fehlerunanfällig.
Die vollständige Delphi-Tensorflow-API für die Version 1.2.1 kann hier als Zip-Datei herunter geladen werden.