Özel operatörler

LiteRT yerleşik operatör kitaplığı yalnızca sayısı, TensorFlow operatörü sayısı, her model dönüştürülebilir değildir. Ayrıntılı bilgi için operatör uyumluluğu bölümünü inceleyin.

Dönüşüme izin vermek için, kullanıcılar bir LiteRT'de, özel operatör olarak bilinen desteklenmeyen TensorFlow operatörü. Bunun yerine, desteklenmeyen (veya desteklenen) TensorFlow operatörlerini çok kaynaklı, optimize edilmiş tek bir özel operatöre dönüştürme, operatör birleştirme.

Özel operatörleri kullanma dört adımdan oluşur.

Şimdi, özel bir model ile çalıştırmayı gösteren uçtan uca tf.atan operatörünün (Atan olarak adlandırılmıştır. TensorFlow Modeli Oluşturma bölümüne bakın.) TensorFlow'da desteklenir ancak LiteRT'te desteklenmez.

TensorFlow Metin operatörü bir özel operatör örneğidir. Bkz. Kod örneği için TF Metnini LiteRT'e dönüştürün.

Örnek: Özel Atan operatörü

Bir TensorFlow operatörünü desteklemek için bir örneği inceleyelim. LiteRT'te yoktur. Atan operatörünü kullandığımızı ve y = atan(x + offset) fonksiyonu için çok basit bir model oluşturuyoruz; offset eğitilebilir.

TensorFlow Modeli Oluşturma

Aşağıdaki kod snippet'i basit bir TensorFlow modeli eğitir. Bu model Atan adlı bir özel operatör içerir. Bu, offset işlevinin eğitilebilir olduğu y = atan(x + offset) işlevidir.

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 değer ile bir LiteRT modeli oluşturmaya çalışırsanız dönüştürücü flag'lerini kaldırı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.

LiteRT Modeline Dönüştür

Dönüştürücüyü ayarlayarak özel operatörlerle LiteRT modeli oluşturma özelliği (allow_custom_ops) aşağıda gösterildiği gibidir:

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

Bu noktada, şöyle olur:

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

Şu hatayı almaya devam edersiniz:

Encountered unresolved custom op: Atan.

Operatörü oluşturun ve kaydedin.

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

LiteRT özel operatörleri, aşağıdakileri içeren basit bir saf API kullanılarak tanımlanır: opak bir tür (TfLiteRegistrationExternal) ve ilgili işlevlerden oluşur.

TfLiteRegistrationExternal, opak bir türdür:

typedef struct TfLiteRegistrationExternal TfLiteRegistrationExternal;

TfLiteRegistrationExternal, operatörün kimliğini ve uygulamasını depolar. (Operatörün, Operatörü çağıran düğümler için LiteRT grafik düğümleri.)

Bu tür örnekler, TfLiteRegistrationExternalCreate ve çağrı yapılarak yok edilebilir TfLiteRegistrationExternalDelete.

Operatörün kimliği, kurucu işleve parametreler aracılığıyla ayarlanır TfLiteRegistrationExternalCreate:

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ı "yöntemleri" tanımlayabilir ekleyebilirsiniz. Bu yöntemlerin hepsi isteğe bağlıdır ancak operatörün başarılı bir şekilde operatör uygulamasının doğru bir şekilde tanımlanması ve ayarlanması (belirleyici işlevleri) en azından Prepare ve Invoke yöntemlerini kullanmalıdır.

// 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);

Operasyon uygulamanızdaki işlev adları (veya C++ için ad alanı önekleri) yukarıdaki kod snippet'indeki işlev adlarıyla eşleşmesi gerekmez; Lite custom ops API yalnızca bu yayıncıların adreslerini kullanacak. Size önerimiz bunları anonim bir ad alanında veya statik işlevler olarak tanımlamalıdır.

Ancak operatör adını şu işlev adlarını içerir:

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'sı olduğundan, bu "yöntemler" C fonksiyonu işaretçileri olarak aşağıdaki adresleri geçirerek ayarlanan TfLiteRegistrationExternal türünü ilgili belirleyici işlevlerine TfLiteRegistrationExternalSetMethodName:

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));

Referans common.h TfLiteContext ve TfLiteNode hakkında ayrıntılı bilgi edinin. TfLiteContext hata veriyor raporlama tesisleri ve tüm tensörler dahil olmak üzere küresel nesnelere erişim. TfLiteNode, operatör uygulamalarının giriş ve çıkışlarına erişmesine olanak tanır.

Çevirmen bir model yüklediğinde her model için Init() yöntemini bir kez çağırır düğümünü çizin. İşlem şu durumda belirli bir Init() birden fazla kez çağrılacaktır: grafikte birden çok kez kullanılmıştır. Özel işlemler için bir yapılandırma tamponu ve parametre adlarını kendi değerleriyle eşleyen bir flexbuffer içerir. İlgili içeriği oluşturmak için kullanılan çevirmen, dahili işlemler için arabellek boş işlem parametreleridir. Durum gerektiren çekirdek uygulamaları bunu başlatmalıdır arayan kişiye aktaracağım. Her Init() araması için Free() için karşılık gelen bir çağrı gönderir ve uygulamaların Init() ayında ayırmış olabileceği tampon ya da esneme payı.

Giriş tensörleri yeniden boyutlandırıldığında çevirmen, değişikliğin uygulamalarını bildiren bir grafik Böylece karşınızdaki paydaşa iç arabelleklerini yeniden boyutlandırma, giriş şekillerinin ve türlerinin geçerliliğini kontrol etme ve çıktı şekillerini yeniden hesaplayın. Bu işlemlerin hepsi Prepare() yöntemi kullanılarak yapılır ve kendi durumlarına TfLiteOpaqueNodeGetUserData(node).

Son olarak, çıkarım her çalıştığında çevirmen, çağrıyı yapan grafikte Invoke() yöntemini kullanır ve burada da durum TfLiteOpaqueNodeGetUserData(node).

Özel işlemler, bu "yöntem" tanımlanarak uygulanabilir fonksiyonlarını kullanarak TfLiteRegistrationExternal örneğini döndüren bir işlev tanımlanıyor TfLiteRegistrationExternalCreate ve ardından alakalı belirleyici yöntemler:

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;
}
      

Kayıt işleminin otomatik olmadığını ve MyCustomOpRegistration işlevi gerçekleştirilmelidir (aşağıdaki ayrıntılara bakın). Araç standart BuiltinOpResolver (:builtin_ops hedefinden kullanılabilir) sağlamak için özel işlemlerin doğru bir şekilde ayrı özel kitaplıklar.

Çekirdeği LiteRT çalışma zamanında tanımlama

LiteRT'de operasyonu kullanmak için tek yapmamız gereken iki işlevi tanımlamaktır. (Prepare ve Eval) ve üçüncüsü TfLiteRegistrationExternal oluşturmak için:

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<float*>(TfLiteOpaqueTensorData(input));
      float* output_data = static_cast<float*>(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<float*>(TfLiteOpaqueTensorData(input));
  float* output_data = static_cast<float*>(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 öğesini başlatırken özel işlemi çözümleyiciye ekleyin (bkz. bölümüne bakın). Bu işlem, operatörü LiteRT'e kaydeder. Böylece, nasıl kullanabileceğini açıkladık. Son iki seçeneğin TfLiteRegistration içindeki bağımsız değişkenler AtanPrepare ve AtanEval değerlerine karşılık gelir özel işlem için tanımladığınız işlevler. AtanInit ve AtanFree kullandıysanız operasyonda kullanılan değişkenleri ilk kullanıma hazırlamak ve yer açmak için fonksiyonlar ise bunlar, tablodaki ilk iki bağımsız değişkene TfLiteRegistration; bu bağımsız değişkenler bu örnekte nullptr olarak ayarlanmıştır.

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

Şimdi operatörü çekirdek kitaplığına kaydetmemiz gerekiyor. Bu işlem şununla yapılır: OpResolver. Kamera arkasında, çevirmen şunları içeren bir kitaplık yükler: çekirdeklerini belirler. Varsayılan kitaplık yalnızca yerleşik çekirdekleri içerse de, bunu özel kitaplık işlem operatörleriyle değiştirin/genişletin.

Operatör kodlarını ve adlarını gerçeklere çeviren OpResolver sınıfı kodu ş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 eski beton türünü kullandığını unutmayın. opak TfLiteRegistrationExternal türü yerine TfLiteRegistration ancak TfLiteRegistration struct'ı şunun registration_external alanını içeriyor: TfLiteRegistrationExternal* yazın.

MutableOpResolver ve BuiltinOpResolver sınıfları şuradan türetilir: OpResolver:

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ı gerektirir ve şunu yazın:

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

Yukarıda oluşturulan özel işlemi eklemek için bunun yerine bir MutableOpResolver kullanabilirsiniz. ve AddCustom işlevini çağırın (çözümleyiciyi InterpreterBuilder):

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

Yerleşik işlem kümesinin çok büyük olduğu kabul edilirse yeni bir OpResolver belirli bir işlem alt kümesine (muhtemelen yalnızca kapsanan) göre oluşturulan kod birlikte çalışır. Bu, TensorFlow'un seçmeli kaydına eşdeğerdir. (ve basit bir sürümü tools dizininde bulunmaktadır).

Java'da özel operatörlerinizi tanımlamak istiyorsanız, şu anda kendi özel JNI katmanınızı derleyin ve kendi AAR'nizi derleyin burada bulabilirsiniz. Benzer şekilde, Python'da kullanılabilen bu operatörleri tanımlamak isterseniz kayıtlarınızı Python sarmalayıcı kodu

Bir grup anahtar kelimeyi desteklemek için yukarıdakiyle benzer bir süreç izlenebilir. işlemleri gerçekleştirmelerini sağlar. Dilediğiniz kadar AddCustom operatörü ekleyebilirsiniz gerektiği gibidir. Buna ek olarak, MutableOpResolver, geçersiz kılma işlemlerini gerçekleştirmenize de olanak tanır. yerleşik uygulamaları AddBuiltin kullanarak daha iyi hale getirebilirsiniz.

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

LiteRT karşılaştırma aracıyla envanterinizin profilini çıkarmak için karşılaştırma modeli aracı kullanabilirsiniz. Test amacıyla yerel derlemenizi Uygun AddCustom ekleyerek özel operasyonunuzdan haberdar olun çağrısı (yukarıda gösterildiği gibi) register.cc

En iyi uygulamalar

  1. Bellek tahsislerini ve ayırmaları dikkatli bir şekilde optimize edin. Bellek ayrılıyor Prepare bölgesinde Invoke ile karşılaştırıldığında daha verimli ve bellek ayrılıyor her yinelemeden daha iyidir. Geçici tensör verilerini kullanma bir neden sunmalısınız (2. maddeye bakın). Bunun yerine işaretçiler/referanslar kullanın yapmaya çalışın.

  2. Bir veri yapısı tüm işlem boyunca devam edecekse hafızaya geçici tensörler kullanarak önceden ayırma Bir OpData struct'ı kullanır. Bkz. konvolüsyon çekirdek özellikleri hakkında daha fazla bilgi edinin. Aşağıda örnek bir kod snippet'i verilmiştir.

    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 bellek israfına mal değilse statik sabit boyut kullanmayı tercih edin yerine bir dizi (veya Resize içinde önceden ayrılmış bir std::vector) her yürütme yinelemesinde std::vector dinamik olarak ayrıldı.

  4. Henüz başlatılmayan standart kitaplık kapsayıcı şablonlarını örneklendirmek için çünkü ikili boyutu etkiler. Örneğin bir web sitesi için diğer çekirdeklerde bulunmayan işleminizdeki std::map Doğrudan dizine ekleme eşlemesiyle std::vector, küçük bir ikili programdır. Bilgi edinmek (veya soru sormak) için diğer çekirdeklerin neler kullandığını görün.

  5. malloc tarafından döndürülen belleğe işaret eden işaretçiyi kontrol edin. Bu işaretçi nullptr bu işaretçi kullanılarak hiçbir işlem yapılmamalıdır. Şu durumda: bir fonksiyonda malloc hatası görürseniz çıkış yapmadan önce belleği çıkar.

  6. Belirli bir alanı kontrol etmek için TF_LITE_OPAQUE_ENSURE(context, condition) öğesini kullanın koşul. Aşağıdaki işlemler gerçekleştirilirken kodunuz bellekte askı kalmamalıdır: TF_LITE_OPAQUE_ENSURE kullanılır, yani bu makrolar ayrılan tüm kaynakların ayrılması gerekir.