LiteRT yerleşik operatör kitaplığı yalnızca sınırlı sayıda TensorFlow operatörünü desteklediğinden her model dönüştürülemez. Ayrıntılı bilgi için operatör uyumluluğu başlıklı makaleyi inceleyin.
Dönüşüme izin vermek için kullanıcılar, LiteRT'de desteklenmeyen bir TensorFlow operatörünün kendi özel uygulamasını (özel operatör olarak bilinir) sağlayabilir. 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 operatör kullanma dört adımdan oluşur.
TensorFlow modeli oluşturun. SavedModel (veya Graph Def) öğesinin, doğru adlandırılmış LiteRT operatörünü ifade ettiğinden emin olun.
LiteRT modeline dönüştürün. Modeli başarıyla dönüştürmek için doğru LiteRT dönüştürücü özelliğini ayarladığınızdan emin olun.
Operatörü oluşturun ve kaydedin. Bunun nedeni, LiteRT çalışma zamanının grafiğinizdeki operatörünüzü ve parametrelerinizi yürütülebilir C/C++ koduna nasıl eşleyeceğini bilmesidir.
Operatörünüzü test edin ve profilini oluşturun. Yalnızca özel operatörünüzü test etmek istiyorsanız yalnızca özel operatörünüzü içeren bir model oluşturmanız ve benchmark_model programını kullanmanız en iyisidir.
TensorFlow'da desteklenen ancak LiteRT'de desteklenmeyen özel operatör tf.atan
(Atan
olarak adlandırılır, TensorFlow modeli oluşturma bölümüne bakın) ile model çalıştırmanın uçtan uca bir örneğini inceleyelim.
TensorFlow Text operatörü, özel operatör örneğidir. Kod örneği için TF Metnini LiteRT'ye Dönüştürme eğitimine bakın.
Örnek: Özel Atan
operatörü
LiteRT'nin sahip olmadığı bir TensorFlow operatörünü destekleme örneğini inceleyelim. Atan
operatörünü kullandığımızı ve y = atan(x + offset)
işlevi için çok basit bir model oluşturduğumuzu varsayalım. Bu durumda offset
eğitilebilir.
TensorFlow modeli oluşturma
Aşağıdaki kod snippet'i, basit bir TensorFlow modelini eğitir. Bu model yalnızca Atan
adlı bir özel operatör içerir. Bu operatör, y = atan(x +
offset)
işlevidir ve offset
eğitilebilir.
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ü işaretleriyle bir LiteRT 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.
LiteRT modeline dönüştürme
Dönüştürücü özelliğini allow_custom_ops
aşağıda gösterildiği gibi ayarlayarak özel operatörler içeren bir LiteRT 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()
Hatayı almaya devam ediyorsanız:
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, opak bir tür (TfLiteOperator
) ve ilgili işlevlerden oluşan basit bir saf C API'si kullanılarak tanımlanır.
TfLiteOperator
opak bir türdür:
typedef struct TfLiteOperator TfLiteOperator;
TfLiteOperator
, operatörün kimliğini ve uygulamasını saklar.
(Operatörün, operatörü çağıran düğümler için LiteRT grafiği düğümlerinde depolanan işlenenlerinden farklı olduğunu unutmayın.)
Bu türün örnekleri TfLiteOperatorCreate
çağrılarıyla oluşturulur ve TfLiteOperatorDelete
çağrılarıyla yok edilebilir.
Operatörün kimliği, oluşturucu işlevine yönelik parametreler aracılığıyla ayarlanır
TfLiteOperatorCreate
:
TfLiteOperator*
TfLiteOperatorCreate(
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öntemler" tanımlayabilir.
Bu yöntemlerin tümü isteğe bağlıdır ancak bir operatörün başarıyla değerlendirilebilmesi için operatör uygulamasının en az Prepare
ve Invoke
yöntemlerini tanımlaması ve ayarlaması (setter işlevlerini 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şlemler API'si yalnızca adreslerini kullanacağından, işlem uygulamanızdaki işlev adlarının (veya C++ için ad alanı önekleri) yukarıdaki kod snippet'indeki işlev adlarıyla eşleşmesi gerekmez. Bunları anonim bir ad alanında veya statik işlevler olarak bildirmenizi öneririz.
Ancak operatör adınızı bu işlev adlarında ad alanı veya önek olarak eklemeniz önerilir:
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'si olduğundan, bu "yöntemler" TfLiteOperator
türünde C işlev işaretçileri olarak uygulanır. Bu işaretçiler, uygulama işlevlerinizin adresleri karşılık gelen ayarlayıcı işlevlere TfLiteOperatorSet
MethodName iletilerek ayarlanır:
void TfLiteOperatorSetInit(
TfLiteOperator* operator,
void* (*init)(TfLiteOpaqueContext* context, const char* buffer,
size_t length));
void TfLiteOperatorSetFree(
TfLiteOperator* operator,
void (*free)(TfLiteOpaqueContext* context, void* data));
void TfLiteOperatorSetPrepare(
TfLiteOperator* operator,
TfLiteStatus (*prepare)(TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node));
void TfLiteOperatorSetInvoke(
TfLiteOperator* operator,
TfLiteStatus (*invoke)(TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node));
void TfLiteOperatorSetAsyncKernel(
TfLiteOperator* operator,
struct TfLiteAsyncKernel* (*async_kernel)(TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node));
TfLiteContext
ve TfLiteNode
ile ilgili ayrıntılar için common.h
başlıklı makaleyi inceleyin. 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 modeli yüklediğinde, grafikteki her düğüm için Init()
yöntemini bir kez çağırır. Bir işlem grafikte birden çok kez kullanılıyorsa belirli bir Init()
birden fazla kez çağrılır. Özel işlemler için, parametre adlarını değerleriyle eşleyen bir flexbuffer içeren bir yapılandırma arabelleği sağlanır. Yorumlayıcı, işlem parametrelerini zaten ayrıştırdığı için yerleşik işlemlerin arabelleği boştur. Durum gerektiren çekirdek uygulamaları, durumu burada başlatmalı ve sahipliği arayan kullanıcıya aktarmalıdır. Her Init()
çağrısı için Free()
'e karşılık gelen bir çağrı olur. Bu sayede uygulamalar, Init()
'de ayırmış olabilecekleri arabelleği kullanımdan kaldırabilir.
Giriş tensörleri yeniden boyutlandırıldığında yorumlayıcı, grafiği inceleyerek uygulamalara değişiklik hakkında bildirim gönderir. Bu sayede, dahili arabelleklerini yeniden boyutlandırabilir, giriş şekillerinin ve türlerinin geçerliliğini kontrol edebilir ve çıkış şekillerini yeniden hesaplayabilirler. Bu işlemlerin tümü Prepare()
yöntemiyle yapılır ve uygulamalar, TfLiteOpaqueNodeGetUserData(node)
kullanarak durumlarına erişebilir.
Son olarak, her çıkarım çalıştığında yorumlayıcı, Invoke()
yöntemini çağırarak grafiği geçer ve burada da durum TfLiteOpaqueNodeGetUserData(node)
olarak kullanılabilir.
Özel işlemler, bu "yöntem" işlevleri tanımlanarak ve ardından TfLiteOperator
örneğini döndüren bir işlev tanımlanarak uygulanabilir. Bu işlev, TfLiteOperatorCreate
ve ardından ilgili ayarlayıcı yöntemler çağrılarak oluşturulur:
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 TfLiteOperator* MyCustomOperator() { // Singleton instance, intentionally never destroyed. static const TfLiteOperator* my_custom_op = ()[] { TfLiteOperator* r = TfLiteOperatorCreate( kTfLiteBuiltinCustom, "MyCustomOp", /*version=*/ 1); TfLiteOperatorSetInit(r, Init); TfLiteOperatorSetFree(r, Free); TfLiteOperatorSetPrepare(r, Prepare); TfLiteOperatorSetInvoke(r, Eval); return r; }; 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 TfLiteOperator* MyCustomOpCreate() { const TfLiteOperator* r = TfLiteOperatorCreate( kTfLiteBuiltinCustom, "MyCustomOp", /*version=*/ 1); TfLiteOperatorSetInit(r, MyCustomOpInit); TfLiteOperatorSetFree(r, MyCustomOpFree); TfLiteOperatorSetPrepare(r, MyCustomOpPrepare); TfLiteOperatorSetInvoke(r, MyCustomOpEval); return r; } const TfLiteOperator* MyCustomOperator() { // Singleton instance, intentionally never destroyed. static const TfLiteOperator* my_custom_op = MyCustomOpCreate(); return my_custom_op; }
Kaydın otomatik olmadığını ve MyCustomOperator
işlevinize açık bir çağrı yapılması gerektiğini unutmayın (ayrıntıları aşağıda bulabilirsiniz). BuiltinOpResolver
(:builtin_ops
hedefinden kullanılabilir) yerleşiklerin kaydını yaparken özel işlemlerin ayrı özel kitaplıklarda toplanması gerekir.
LiteRT çalışma zamanında çekirdeği tanımlama
LiteRT'de op'u kullanmak için yapmamız gereken tek şey iki işlev (Prepare
ve Eval
) ve bir TfLiteOperator
oluşturmak için üçüncü bir işlev tanımlamaktı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<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 TfLiteOperator* AtanOperator() { // Singleton instance, intentionally never destroyed. static const TfLiteOperator* atan_op = ()[] { auto* r = TfLiteOperatorCreate( kTfLiteBuiltinCustom, "ATAN", /*version=*/ 1); TfLiteOperatorSetPrepare(r, Prepare); TfLiteOperatorSetInvoke(r, Eval); return r; }; 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 TfLiteOperator* AtanOpCreate() { TfLiteOperator* r = TfLiteOperatorCreate( kTfLiteBuiltinCustom, "ATAN", /*version=*/ 1); TfLiteOperatorSetPrepare(r, Prepare); TfLiteOperatorSetInvoke(r, Eval); return r; } const TfLiteOperator* AtanOperator() { // Singleton instance, intentionally never destroyed. static const TfLiteOperator* atan_op = AtanOpCreate(); return atan_op; }
OpResolver
başlatılırken özel işlemi çözümleyiciye ekleyin (örnek için aşağıya bakın). Bu işlem, operatörü LiteRT'ye kaydeder. Böylece LiteRT, yeni uygulamayı kullanabilir.
İşlemi çekirdek kitaplığına kaydetme
Şimdi işletmeciyi çekirdek kitaplığına kaydetmemiz gerekiyor. Bu işlem, OpResolver
ile yapılır. Yorumlayıcı, arka planda, modeldeki her operatörü yürütmek üzere atanacak bir çekirdek kitaplığı yükler.
Varsayılan kitaplık yalnızca yerleşik çekirdekleri içerse de özel kitaplık operatörleriyle değiştirilebilir/artırılabilir.
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 TfLiteOperator
yerine eski somut tür TfLiteRegistration
kullandığını ancak TfLiteRegistration
yapısının TfLiteOperator*
türünde bir registration_external
alanı içerdiğini unutmayın.
MutableOpResolver
ve BuiltinOpResolver
sınıfları OpResolver
'den türetilir:
class MutableOpResolver : public OpResolver {
public:
MutableOpResolver(); // Constructs an initially empty op resolver.
void AddAll(const MutableOpResolver& other);
...
};
class BuiltinOpResolver : public MutableOpResolver {
public:
BuiltinOpResolver(); // Constructs an op resolver with all the builtin ops.
};
Normal kullanım (özel işlemler olmadan) için BuiltinOpResolver
kullanmanız ve şunları yazmanız gerekir:
tflite::ops::builtin::BuiltinOpResolver resolver;
Yukarıda oluşturulan özel işlemi eklemek için bunun yerine MutableOpResolver
kullanabilir ve tflite::AddOp
işlevini çağırabilirsiniz (çözücüyü InterpreterBuilder
işlevine iletmeden önce):
tflite::ops::builtin::MutableOpResolver resolver;
resolver.AddAll(tflite::ops::builtin::BuiltinOpResolver());
tflite::AddOp(&resolver, AtanOpRegistration());
Yerleşik işlemlerin çok büyük olduğu düşünülürse belirli bir işlem alt kümesine göre yeni bir OpResolver
kodu oluşturulabilir. Bu alt küme, muhtemelen yalnızca belirli bir modelde bulunan işlemleri içerir. Bu, TensorFlow'un seçici kaydına eşdeğerdir (ve basit bir sürümü tools
dizininde mevcuttur).
Özel operatörlerinizi Java'da 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 kullanılabilen 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 izlenebileceğini unutmayın. İhtiyacınız kadar AddCustom
operatörü eklemeniz yeterlidir. Ayrıca, MutableOpResolver
, AddBuiltin
kullanarak yerleşiklerin uygulamalarını geçersiz kılmanıza da olanak tanır.
Operatörünüzü test etme ve profilleme
LiteRT karşılaştırma aracıyla işleminizin profilini oluşturmak için LiteRT'ye yönelik karşılaştırma modeli aracını kullanabilirsiniz. Test amacıyla, LiteRT'nin yerel derlemenizi AddCustom
çağrısını (yukarıda gösterildiği gibi) register.cc dosyasına ekleyerek özel işleminizden haberdar edebilirsiniz.
En iyi uygulamalar
Bellek ayırma ve ayırmama işlemlerini dikkatli bir şekilde optimize edin. Belleği
Prepare
içinde ayırmakInvoke
içinde ayırmaktan daha verimlidir ve belleği döngüden önce ayırmak her yinelemede ayırmaktan daha iyidir. Kendiniz malloc yapmak yerine geçici tensör verilerini kullanın (2. maddeye bakın). Mümkün olduğunca çok sayıda kopyalama yapmak yerine işaretçiler/referanslar kullanın.Bir veri yapısı tüm işlem boyunca kalıcı olacaksa geçici tensörler kullanarak belleği önceden ayırmanızı öneririz. Diğer işlevlerde tensör dizinlerine referansta bulunmak için bir OpData yapısı kullanmanız gerekebilir. Evrişim için çekirdek bölümündeki örneğe bakın. Örnek bir kod snippet'i aşağıda 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; }
Çok fazla bellek israfına yol açmıyorsa her yürütme yinelemesinde dinamik olarak ayrılmış
std::vector
kullanmak yerine statik sabit boyutlu bir dizi (veyaResize
içinde önceden ayrılmış birstd::vector
) kullanmayı tercih edin.İkili boyutunu etkilediği için, henüz mevcut olmayan standart kitaplık kapsayıcı şablonlarını oluşturmaktan kaçının. Örneğin, işleminizde diğer çekirdeklerde bulunmayan bir
std::map
öğesine ihtiyacınız varsa doğrudan dizin oluşturma eşlemesi olan birstd::vector
kullanmak, ikili boyutunu küçük tutarken işe yarayabilir. Diğer çekirdeklerin hangi bilgileri kullandığını öğrenerek bilgi edinin (veya soru sorun).malloc
tarafından döndürülen belleğe yönelik işaretçiyi kontrol edin. Bu işaretçinullptr
ise bu işaretçi kullanılarak herhangi bir işlem yapılmamalıdır. Bir fonksiyondamalloc
ve hata çıkışı varsa çıkmadan önce belleğin tahsisini kaldırın.Belirli bir koşulu kontrol etmek için
TF_LITE_OPAQUE_ENSURE(context, condition)
uygulamasını kullanın. Kodunuz,TF_LITE_OPAQUE_ENSURE
kullanıldığında belleği askıda bırakmamalıdır. Diğer bir deyişle, bu makrolar, sızıntıya neden olacak kaynaklar ayrılmadan önce kullanılmalıdır.