Özel operatörler

TensorFlow Lite yerleşik operatör kitaplığı yalnızca sınırlı sayıda TensorFlow operatörünü desteklediğinden, her model dönüştürülebilir değildir. Ayrıntılar için operatör uyumluluğuna bakın.

Dönüşüme izin vermek için kullanıcılar, özel operatör olarak bilinen, TensorFlow Lite'ta desteklenmeyen bir TensorFlow operatörünün kendi özel uygulamasını sağlayabilirler. Bunun yerine, bir dizi desteklenmeyen (veya desteklenen) TensorFlow operatörünü tek bir birleştirilmiş optimize edilmiş özel operatörde birleştirmek istiyorsanız, operatör birleştirme bölümüne bakın.

Özel işleçlerin kullanılması dört adımdan oluşur.

TensorFlow'da desteklenen ancak TensorFlow Lite'ta desteklenmeyen özel bir operatör olan tf.atan ( Atan olarak adlandırılır, #create_a_tensorflow_model'e bakın) ile bir model çalıştırmanın uçtan uca örneğini inceleyelim.

TensorFlow Text operatörü özel operatöre bir örnektir. Bir kod örneği için TF Metnini TF Lite'a Dönüştürme eğitimine bakın.

Örnek: Özel Atan operatörü

TensorFlow Lite'ın sahip olmadığı bir TensorFlow operatörünü destekleme örneğini inceleyelim. Atan operatörünü kullandığımızı ve offset eğitilebilir olduğu y = atan(x + offset) fonksiyonu için çok basit bir model oluşturduğumuzu varsayalım.

TensorFlow Modeli Oluşturun

Aşağıdaki kod parçacığı basit bir TensorFlow modelini eğitiyor. Bu model yalnızca, offset eğitilebilir olduğu y = atan(x + offset) işlevi olan Atan adında özel bir operatör içerir.

import tensorflow as tf

# Define training dataset and variables
x = [-8, 0.5, 2, 2.2, 201]
y = [-1.4288993, 0.98279375, 1.2490457, 1.2679114, 1.5658458]
offset = tf.Variable(0.0)

# Define a simple model which just contains a custom operator named `Atan`
@tf.function(input_signature=[tf.TensorSpec.from_tensor(tf.constant(x))])
def atan(x):
  return tf.atan(x + offset, name="Atan")

# Train model
optimizer = tf.optimizers.Adam(0.01)
def train(x, y):
    with tf.GradientTape() as t:
      predicted_y = atan(x)
      loss = tf.reduce_sum(tf.square(predicted_y - y))
    grads = t.gradient(loss, [offset])
    optimizer.apply_gradients(zip(grads, [offset]))

for i in range(1000):
    train(x, y)

print("The actual offset is: 1.0")
print("The predicted offset is:", offset.numpy())
The actual offset is: 1.0
The predicted offset is: 0.99999905

Bu noktada varsayılan dönüştürücü bayraklarıyla TensorFlow Lite modeli oluşturmaya çalışırsanız aşağıdaki hata mesajını alırsınız:

Error:
error: 'tf.Atan' op is neither a custom op nor a flex op.

TensorFlow Lite Modeline Dönüştürme

allow_custom_ops dönüştürücü özelliğini aşağıda gösterildiği gibi ayarlayarak özel operatörlerle bir TensorFlow Lite modeli oluşturun:

converter = tf.lite.TFLiteConverter.from_concrete_functions([atan.get_concrete_function()], atan)
converter.allow_custom_ops = True
tflite_model = converter.convert()

Bu noktada aşağıdaki gibi komutları kullanarak varsayılan yorumlayıcıyla çalıştırırsanız:

interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

Hala hatayı alacaksınız:

Encountered unresolved custom op: Atan.

Operatörü oluşturun ve kaydedin.

#include "tensorflow/lite/c/c_api.h"
#include "tensorflow/lite/c/c_api_opaque.h"

TensorFlow Lite özel operatörleri, opak bir tür ( TfLiteRegistrationExternal ) ve ilgili işlevlerden oluşan basit bir saf C API kullanılarak tanımlanır.

TfLiteRegistrationExternal opak bir türdür:

typedef struct TfLiteRegistrationExternal TfLiteRegistrationExternal;

TfLiteRegistrationExternal operatörün kimliğini ve uygulamasını saklar. (Operatörün, operatörü çağıran düğümler için TF Lite grafik düğümlerinde saklanan işlenenlerinden farklı olduğunu unutmayın.)

Bu türdeki örnekler, TfLiteRegistrationExternalCreate çağrılarıyla oluşturulur ve TfLiteRegistrationExternalDelete çağrılarak yok edilebilir.

Operatörün kimliği, parametreler aracılığıyla TfLiteRegistrationExternalCreate yapıcı işlevine ayarlanır:

TfLiteRegistrationExternal*
TfLiteRegistrationExternalCreate(
    TfLiteBuiltinOperator builtin_code,  // Normally `TfLiteBuiltinCustom`.
    const char* custom_name,  // The name of the custom op.
    int version  // Normally `1` for the first version of a custom op.
);

Operatör uygulaması aşağıdaki imzalarla "yöntemleri" tanımlayabilir. Bu yöntemlerin tümü isteğe bağlıdır, ancak bir operatörün başarılı bir şekilde değerlendirilmesi için, operatör uygulamasının en azından Prepare ve Invoke yöntemlerini tanımlaması ve ayarlaması (ayarlayıcı işlevleri kullanarak) gerekir.

// Initializes the op from serialized data.
void* Init(TfLiteOpaqueContext* context, const char* buffer, size_t length);

// Deallocates the op.
// The pointer `buffer` is the data previously returned by an Init invocation.
void Free(TfLiteOpaqueContext* context, void* buffer);

// Called when the inputs that this node depends on have been resized.
TfLiteStatus Prepare(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node);

// Called when the node is executed. (Should read node inputs and write to
// node outputs).
TfLiteStatus Invoke(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node);

// Retrieves the async kernel.
TfLiteAsyncKernel AsyncKernel(TfLiteOpaqueContext* context,
                              TfLiteOpaqueNode* node);

TF Lite özel işlem API'si yalnızca bunların adreslerini kullanacağından, işlem uygulamanızdaki işlev adlarının (veya C++ için ad alanı öneklerinin) yukarıdaki kod parçacığındaki işlev adlarıyla eşleşmesi gerekmez. Aslında bunları anonim bir ad alanında veya statik işlevler olarak bildirmenizi öneririz.

Ancak operatör adınızı bu işlev adlarına ad alanı veya önek olarak eklemek iyi bir fikirdir:

C++

namespace my_namespace::my_custom_op {
  void* Init(TfLiteOpaqueContext* context,
             const char* buffer, size_t length) { ... }
  // ... plus definitions of Free, Prepare, and Invoke ...
}
      

C

void* MyCustomOpInit(TfLiteOpaqueContext* context,
                     const char* buffer, size_t length) { ... }
// ... plus definitions of MyCustomOpFree, MyCustomOpPrepare, and
// MyCustomOpInvoke.
      

Bu bir C API olduğundan, bu "yöntemler", uygulama işlevlerinizin adreslerini ilgili ayarlayıcı işlevlere TfLiteRegistrationExternalSet MethodName ileterek ayarlanan TfLiteRegistrationExternal türünde C işlev işaretçileri olarak uygulanır:

void TfLiteRegistrationExternalSetInit(
    TfLiteRegistrationExternal* registration,
    void* (*init)(TfLiteOpaqueContext* context, const char* buffer,
                  size_t length));
void TfLiteRegistrationExternalSetFree(
    TfLiteRegistrationExternal* registration,
    void (*free)(TfLiteOpaqueContext* context, void* data));
void TfLiteRegistrationExternalSetPrepare(
    TfLiteRegistrationExternal* registration,
    TfLiteStatus (*prepare)(TfLiteOpaqueContext* context,
                            TfLiteOpaqueNode* node));
void TfLiteRegistrationExternalSetInvoke(
    TfLiteRegistrationExternal* registration,
    TfLiteStatus (*invoke)(TfLiteOpaqueContext* context,
                           TfLiteOpaqueNode* node));
void TfLiteRegistrationExternalSetAsyncKernel(
    TfLiteRegistrationExternal* registration,
    struct TfLiteAsyncKernel* (*async_kernel)(TfLiteOpaqueContext* context,
                                              TfLiteOpaqueNode* node));

TfLiteContext ve TfLiteNode ile ilgili ayrıntılar için common.h bakın. TfLiteContext hata raporlama olanakları ve tüm tensörler dahil olmak üzere genel nesnelere erişim sağlar. TfLiteNode operatör uygulamalarının giriş ve çıkışlarına erişmesine olanak tanır.

Yorumlayıcı bir model yüklediğinde grafikteki her düğüm için Init() yöntemini bir kez çağırır. Belirli bir Init() eğer op grafikte birden fazla kez kullanılırsa birden fazla kez çağrılacaktır. Özel operasyonlar için, parametre adlarını değerleriyle eşleyen bir esnek arabellek içeren bir yapılandırma arabelleği sağlanacaktır. Yorumlayıcı zaten op parametrelerini ayrıştırdığından yerleşik op'lar için arabellek boştur. Durum gerektiren çekirdek uygulamaları, onu burada başlatmalı ve sahipliği arayana aktarmalıdır. Her Init() çağrısı için, Free() 'ye karşılık gelen bir çağrı olacak ve bu, uygulamaların Init() te tahsis etmiş olabilecekleri arabelleği atmasına olanak tanıyacaktır.

Giriş tensörleri yeniden boyutlandırıldığında, yorumlayıcı, değişikliğin uygulamalarını bildiren grafiği inceleyecektir. Bu onlara dahili arabelleklerini yeniden boyutlandırma, giriş şekillerinin ve türlerinin geçerliliğini kontrol etme ve çıktı şekillerini yeniden hesaplama şansı verir. Bunların tümü, Prepare() yöntemi aracılığıyla yapılır ve uygulamalar, TfLiteOpaqueNodeGetUserData(node) kullanarak durumlarına erişebilir.

Son olarak, her çıkarım çalıştırıldığında, yorumlayıcı Invoke() yöntemini çağırarak grafiğin üzerinden geçer ve burada da durum TfLiteOpaqueNodeGetUserData(node) olarak mevcuttur.

Özel işlemler, bu "yöntem" işlevlerini tanımlayarak ve ardından TfLiteRegistrationExternalCreate ve ardından ilgili ayarlayıcı yöntemleri çağırarak oluşturulan TfLiteRegistrationExternal örneğini döndüren bir işlev tanımlayarak uygulanabilir:

C++

namespace my_namespace::my_custom_op {
  namespace {
    void* Init(TfLiteOpaqueContext* context,
               const char* buffer, size_t length) { ... }
    void Free(TfLiteOpaqueContext* context, void* buffer) { ... }
    TfLiteStatus Prepare(TfLiteOpaqueContext* context,
                         TfLiteOpaqueNode* node) { ... }
    TfLiteStatus Invoke(TfLiteOpaqueContext* context,
                        TfLiteOpaqueNode* node) {... }
  };

  const TfLiteRegistrationExternal* MyCustomOpRegistrationExternal() {
    // Singleton instance, intentionally never destroyed.
    static const TfLiteRegistrationExternal* my_custom_op = ()[] {
        TfLiteRegistrationExternal* r =
            TfLiteRegistrationExternalCreate(
                kTfLiteBuiltinCustom, "MyCustomOp", /*version=*/ 1);
        TfLiteRegistrationExternalSetInit(r, Init);
        TfLiteRegistrationExternalSetFree(r, Free);
        TfLiteRegistrationExternalSetPrepare(r, Prepare);
        TfLiteRegistrationExternalSetInvoke(r, Eval);
        return r;
      };
    return my_custom_op;
  }

  const TfLiteRegistration* MyCustomOpRegistration() {
    static const TfLiteRegistration my_custom_op {
      .registration_external = MyCustomOpRegistrationExternal();
    };
    return my_custom_op;
  }
}  // namespace my_namespace
      

C

static void* MyCustomOpInit(TfLiteOpaqueContext* context, const char* buffer,
                     size_t length) { ... }
static void MyCustomOpFree(TfLiteOpaqueContext* context, void* buffer) { ... }
static TfLiteStatus MyCustomOpPrepare(TfLiteOpaqueContext* context,
                                      TfLiteOpaqueNode* node) { ... }
static TfLiteStatus MyCustomOpInvoke(TfLiteOpaqueContext* context,
                                     TfLiteOpaqueNode* node) {... }

static TfLiteRegistrationExternal* MyCustomOpCreate() {
  const TfLiteRegistrationExternal* r =
      TfLiteRegistrationExternalCreate(
          kTfLiteBuiltinCustom, "MyCustomOp", /*version=*/ 1);
  TfLiteRegistrationExternalSetInit(r, MyCustomOpInit);
  TfLiteRegistrationExternalSetFree(r, MyCustomOpFree);
  TfLiteRegistrationExternalSetPrepare(r, MyCustomOpPrepare);
  TfLiteRegistrationExternalSetInvoke(r, MyCustomOpEval);
  return r;
}

const TfLiteRegistrationExternal* MyCustomOpRegistrationExternal() {
  // Singleton instance, intentionally never destroyed.
  static const TfLiteRegistrationExternal* my_custom_op = MyCustomOpCreate();
  return my_custom_op;
}

const TfLiteRegistration MyCustomOpRegistration() {
  static const TfLiteRegistration my_custom_op {
    .registration_external = MyCustomOpRegistrationExternal();
  };
  return my_custom_op;
}
      

Kaydın otomatik olmadığını ve MyCustomOpRegistration işlevinize açık bir çağrı yapılması gerektiğini unutmayın (aşağıdaki ayrıntılara bakın). Standart BuiltinOpResolver ( :builtin_ops hedefinden edinilebilir) yerleşiklerin kaydıyla ilgilenirken, özel operasyonların ayrı özel kitaplıklarda toplanması gerekecektir.

TensorFlow Lite çalışma zamanında çekirdeği tanımlama

TensorFlow Lite'ta op'u kullanmak için yapmamız gereken tek şey iki işlevi tanımlamak ( Prepare ve Eval ) ve üçüncüsünü bir TfLiteRegistrationExternal oluşturmaktır:

C++

namespace atan_op {
  namespace {
    TfLiteStatus AtanPrepare(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node) {
      TF_LITE_OPAQUE_ENSURE_EQ(context, TfLiteOpaqueNodeNumInputs(node), 1);
      TF_LITE_OPAQUE_ENSURE_EQ(context, TfLiteOpaqueNodeNumOutputs(node), 1);

      const TfLiteOpaqueTensor* input = TfLiteOpaqueNodeGetInput(context, node, 0);
      TfLiteOpaqueTensor* output = TfLiteOpaqueNodeGetOutput(context, node, 0);

      int num_dims = TfLiteOpaqueTensorNumDimensions(input);

      TfLiteIntArray* output_size = TfLiteIntArrayCreate(num_dims);
      for (int i=0; i < num_dims; ++i) {
        output_size->data[i] = input->dims->data[i];
      }

      return TfLiteOpaqueContextResizeTensor(context, output, output_size);
    }

    TfLiteStatus AtanEval(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node) {
      const TfLiteOpaqueTensor* input = TfLiteOpaqueNodeGetInput(context, node, 0);
      TfLiteOpaqueTensor* output = TfLiteOpaqueNodeGetOutput(context, node, 0);

      float* input_data = static_cast(TfLiteOpaqueTensorData(input));
      float* output_data = static_cast(TfLiteOpaqueTensorData(output));

      size_t count = 1;
      int num_dims = TfLiteOpaqueTensorNumDimensions(input);
      for (int i = 0; i < num_dims; ++i) {
        count *= input->dims->data[i];
      }

      for (size_t i = 0; i < count; ++i) {
        output_data[i] = atan(input_data[i]);
      }
      return kTfLiteOk;
    }
  }  // anonymous namespace

  const TfLiteRegistrationExternal* AtanOpRegistrationExternal() {
    // Singleton instance, intentionally never destroyed.
    static const TfLiteRegistrationExternal* atan_op = ()[] {
        auto* r = TfLiteRegistrationExternalCreate(
            kTfLiteBuiltinCustom, "ATAN", /*version=*/ 1);
        TfLiteRegistrationExternalSetPrepare(r, Prepare);
        TfLiteRegistrationExternalSetInvoke(r, Eval);
        return r;
      };
    return atan_op;
  }

  const TfLiteRegistration AtanOpRegistration() {
    static const TfLiteRegistration atan_op {
      .registration_external = AtanOpRegistrationExternal();
    };
    return atan_op;
  }
}  // namespace atan_op
      

C

static TfLiteStatus AtanPrepare(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node) {
  TF_LITE_OPAQUE_ENSURE_EQ(context, TfLiteOpaqueNodeNumInputs(node), 1);
  TF_LITE_OPAQUE_ENSURE_EQ(context, TfLiteOpaqueNodeNumOutputs(node), 1);

  const TfLiteOpaqueTensor* input = TfLiteOpaqueNodeGetInput(context, node, 0);
  TfLiteOpaqueTensor* output = TfLiteOpaqueNodeGetOutput(context, node, 0);

  int num_dims = TfLiteOpaqueTensorNumDimensions(input);

  TfLiteIntArray* output_size = TfLiteIntArrayCreate(num_dims);
  for (int i = 0; i < num_dims; ++i) {
    output_size->data[i] = input->dims->data[i];
  }

  return TfLiteOpaqueContextResizeTensor(context, output, output_size);
}

static TfLiteStatus AtanEval(TfLiteOpaqueContext* context, TfLiteOpaqueNode* node) {
  const TfLiteOpaqueTensor* input = TfLiteOpaqueNodeGetInput(context, node, 0);
  TfLiteOpaqueTensor* output = TfLiteOpaqueNodeGetOutput(context, node, 0);

  float* input_data = static_cast(TfLiteOpaqueTensorData(input));
  float* output_data = static_cast(TfLiteOpaqueTensorData(output));

  size_t count = 1;
  int num_dims = TfLiteOpaqueTensorNumDimensions(input);
  for (int i = 0; i < num_dims; ++i) {
    count *= input->dims->data[i];
  }

  for (size_t i = 0; i < count; ++i) {
    output_data[i] = atan(input_data[i]);
  }
  return kTfLiteOk;
}

static const TfLiteRegistrationExternal* AtanOpCreate() {
  TfLiteRegistrationExternal* r = TfLiteRegistrationExternalCreate(
          kTfLiteBuiltinCustom, "ATAN", /*version=*/ 1);
  TfLiteRegistrationExternalSetPrepare(r, Prepare);
  TfLiteRegistrationExternalSetInvoke(r, Eval);
  return r;
}

const TfLiteRegistrationExternal* AtanOpRegistrationExternal() {
  // Singleton instance, intentionally never destroyed.
  static const TfLiteRegistrationExternal* atan_op = AtanOpCreate();
  return atan_op;
}

const TfLiteRegistration AtanOpRegistration() {
  static const TfLiteRegistration atan_op {
    .registration_external = AtanOpRegistrationExternal();
  };
  return atan_op;
}
      

OpResolver başlatırken özel işlemi çözümleyiciye ekleyin (örnek için aşağıya bakın). Bu, TensorFlow Lite'ın yeni uygulamayı kullanabilmesi için operatörü Tensorflow Lite'a kaydedecektir. TfLiteRegistration son iki bağımsız değişkenin, özel işlem için tanımladığınız AtanPrepare ve AtanEval işlevlerine karşılık geldiğini unutmayın. Sırasıyla işlemde kullanılan değişkenleri başlatmak ve yer açmak için AtanInit ve AtanFree işlevlerini kullandıysanız, bunlar TfLiteRegistration ilk iki bağımsız değişkenine eklenir; bu argümanlar bu örnekte nullptr olarak ayarlanmıştır.

Operatörü çekirdek kitaplığına kaydedin

Şimdi operatörü çekirdek kütüphanesine kaydetmemiz gerekiyor. Bu bir OpResolver ile yapılır. Perde arkasında yorumlayıcı, modeldeki operatörlerin her birini yürütmek için atanacak olan çekirdek kitaplığını yükleyecektir. Varsayılan kitaplık yalnızca yerleşik çekirdekler içerse de, onu özel bir kitaplık op operatörleriyle değiştirmek/arttırmak mümkündür.

Operatör kodlarını ve adlarını gerçek koda çeviren OpResolver sınıfı şu şekilde tanımlanır:

class OpResolver {
 public:
  virtual TfLiteRegistration* FindOp(tflite::BuiltinOperator op) const = 0;
  virtual TfLiteRegistration* FindOp(const char* op) const = 0;
  ...
};

Geriye dönük uyumluluk için, bu sınıfın opak tür TfLiteRegistrationExternal yerine daha eski somut tür olan TfLiteRegistration kullandığını, ancak TfLiteRegistration yapısının TfLiteRegistrationExternal* türünde bir registration_external alanı içerdiğini unutmayın.

MutableOpResolver ve BuiltinOpResolver sınıfları OpResolver türetilmiştir:

class MutableOpResolver : public OpResolver {
 public:
  MutableOpResolver();  // Constructs an initially empty op resolver.
  void AddBuiltin(tflite::BuiltinOperator op, const TfLiteRegistration* registration) = 0;
  void AddCustom(const char* op, const TfLiteRegistration* registration) = 0;
  void AddAll(const MutableOpResolver& other);
  ...
};

class BuiltinOpResolver : public MutableOpResolver {
 public:
  BuiltinOpResolver();  // Constructs an op resolver with all the builtin ops.
};

Düzenli kullanım (özel işlemler olmadan), BuiltinOpResolver kullanmanızı ve şunu yazmanızı gerektirir:

tflite::ops::builtin::BuiltinOpResolver resolver;

Yukarıda oluşturulan özel operasyonu eklemek için bunun yerine MutableOpResolver kullanabilir ve AddCustom çağırabilirsiniz (çözümleyiciyi InterpreterBuilder iletmeden önce):

tflite::ops::builtin::MutableOpResolver resolver;
resolver.AddAll(tflite::ops::builtin::BuiltinOpResolver());
resolver.AddCustom("Atan", AtanOpRegistration());

Yerleşik ops kümesinin çok büyük olduğu kabul edilirse, yeni bir OpResolver belirli bir ops alt kümesine, muhtemelen yalnızca belirli bir modelde yer alanlara dayalı olarak kodla oluşturulabilir. Bu, TensorFlow'un seçici kaydına eşdeğerdir (ve bunun basit bir sürümü, tools dizininde mevcuttur).

Java'da özel operatörlerinizi tanımlamak istiyorsanız, şu anda kendi özel JNI katmanınızı oluşturmanız ve kendi AAR'ınızı bu jni kodunda derlemeniz gerekir. Benzer şekilde, Python'da bulunan bu operatörleri tanımlamak isterseniz kayıtlarınızı Python sarmalayıcı koduna yerleştirebilirsiniz.

Tek bir operatör yerine bir dizi işlemi desteklemek için yukarıdakine benzer bir sürecin takip edilebileceğini unutmayın. İhtiyaç duyduğunuz kadar AddCustom operatörü eklemeniz yeterli. Ayrıca MutableOpResolver , AddBuiltin kullanarak yerleşiklerin uygulamalarını geçersiz kılmanıza da olanak tanır.

Operatörünüzü test edin ve profilini çıkarın

Operasyonunuzun profilini TensorFlow Lite kıyaslama aracıyla çıkarmak için TensorFlow Lite için kıyaslama modeli aracını kullanabilirsiniz. Test amacıyla, uygun AddCustom çağrısını (yukarıda gösterildiği gibi) Register.cc'ye ekleyerek yerel TensorFlow Lite yapınızın özel operasyonunuzdan haberdar olmasını sağlayabilirsiniz.

En iyi uygulamalar

  1. Bellek ayırmalarını ve ayırmaları kaldırma işlemlerini dikkatli bir şekilde optimize edin. Prepare bellek ayırmak Invoke daha verimlidir ve bir döngüden önce bellek ayırmak her yinelemeden daha iyidir. Kendinizi mallokasyona tabi tutmak yerine geçici tensör verilerini kullanın (bkz. madde 2). Mümkün olduğunca kopyalamak yerine işaretçileri/referansları kullanın.

  2. Tüm işlem boyunca bir veri yapısı devam edecekse, geçici tensörler kullanılarak belleğin önceden tahsis edilmesini öneririz. Diğer işlevlerdeki tensör endekslerine başvurmak için bir OpData yapısı kullanmanız gerekebilir. Evrişim için çekirdekteki örneğe bakın. Örnek kod pasajı aşağıdadır.

    struct MyOpData {
      int temp_tensor_index;
      ...
    };
    
    void* Init(TfLiteOpaqueContext* context,
        const char* buffer, size_t length) {
      auto* op_data = new MyOpData{};
      ...
      return op_data;
    }
    void Free(TfLiteOpaqueContext* context, void* buffer) {
      ...
      delete reinterpret_cast<MyOpData*>(buffer);
    }
    TfLiteStatus Prepare(TfLiteOpaqueContext* context,
                         TfLiteOpaqueNode* node) {
      ...
      auto* op_data =
          reinterpret_cast<MyOpData*>(TfLiteOpaqueNodeGetUserData(node));
      const int num_temporaries = 1;
      int temporary_tensor_indices[num_temporaries];
      TfLiteOpaqueTensorBuilder* builder = TfLiteOpaqueTensorBuilderCreate();
      TfLiteOpaqueTensorBuilderSetType(builder, kTfLiteFloat32);
      TfLiteOpaqueTensorBuilderSetAllocationType(builder, kTfLiteArenaRw);
      TfLiteOpaqueContextAddTensor(context, builder,
          &temporary_tensor_indices[0]);
      TfLiteOpaqueTensorBuilderDelete(builder);
      TfLiteOpaqueNodeSetTemporaries(node, temporary_tensor_indices,
          num_temporaries);
      op_data->temp_tensor_index = temporary_tensor_indices[0];
      ...
      return kTfLiteOk;
    }
    TfLiteStatus Invoke(TfLiteOpaqueContext* context,
                        TfLiteOpaqueNode* node) {
      ...
      auto* op_data = reinterpret_cast<MyOpData*>(
          TfLiteOpaqueNodeGetUserData(node));
      TfLiteOpaqueTensor* temp_tensor =
          TfLiteOpaqueContextGetOpaqueTensor(context,
              op_data->temp_tensor_index);
      TF_LITE_OPAQUE_ENSURE(context,
          TfLiteTensorType(temp_tensor) == kTfLiteFloat32);
      TF_LITE_OPAQUE_ENSURE(context,
          TfLiteTensorGetAllocationType(temp_Tensor) == kTfLiteArenaRw);
      void *temp_data = TfLiteTensorData(temp_tensor);
      TF_LITE_OPAQUE_ENSURE(context, temp_data != nullptr);
      ...
      return kTfLiteOk;
    }
    
  3. Çok fazla boşa harcanan belleğe mal olmazsa, her yürütme yinelemesinde dinamik olarak tahsis edilmiş bir std::vector std::vector kullanmak yerine statik sabit boyutlu bir dizi (veya Resize içinde önceden tahsis edilmiş bir std::vector) kullanmayı tercih edin.

  4. İkili boyutu etkilediğinden, halihazırda mevcut olmayan standart kitaplık kapsayıcı şablonlarını başlatmaktan kaçının. Örneğin, işleminizde diğer çekirdeklerde bulunmayan bir std::map ihtiyacınız varsa, doğrudan indeksleme eşlemeli bir std::vector kullanmak, ikili boyutu küçük tutarken işe yarayabilir. Diğer çekirdeklerin fikir edinmek (veya sormak) için neler kullandığını görün.

  5. malloc tarafından döndürülen belleğin işaretçisini kontrol edin. Bu işaretçi nullptr ise, bu işaretçiyi kullanarak hiçbir işlem yapılmamalıdır. Bir işlevde malloc ve bir hata çıkışıyla karşılaşırsanız, çıkmadan önce belleğin yerini ayırın.

  6. Belirli bir koşulu kontrol etmek için TF_LITE_OPAQUE_ENSURE(context, condition) kullanın. TF_LITE_OPAQUE_ENSURE kullanıldığında kodunuz hafızayı askıda bırakmamalı, yani sızıntı yapacak herhangi bir kaynak tahsis edilmeden önce bu makrolar kullanılmalıdır.