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.
TensorFlow Modeli oluşturun. Kaydedilen Model (veya Graph Def), doğru şekilde adlandırılmış LiteRT operatörünü belirtir.
LiRT modeline dönüştürün. Aşağıdaki işlemler için doğru LiteRT dönüştürücü özelliğini ayarladığınızdan emin olun: dönüştürebilirsiniz.
Operatörü oluşturun ve kaydedin. Bu Böylece LiteRT çalışma zamanı operatörünüzü nasıl eşleyeceğinizi ve parametrelerini yürütülebilir C/C++ koduna dönüştürebilirsiniz.
Operatörünüzü test edin ve profilini belirleyin. Şu durumda: özel operatörünüzü test etmek istiyorsanız en iyi seçenek, operatörünü kullanabilir ve benchmark_model çok önemli.
Ş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
TfLiteRegistrationExternalSet
MethodName:
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
Bellek tahsislerini ve ayırmaları dikkatli bir şekilde optimize edin. Bellek ayrılıyor
Prepare
bölgesindeInvoke
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.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; }
Çok fazla bellek israfına mal değilse statik sabit boyut kullanmayı tercih edin yerine bir dizi (veya
Resize
içinde önceden ayrılmış birstd::vector
) her yürütme yinelemesindestd::vector
dinamik olarak ayrıldı.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şlemesiylestd::vector
, küçük bir ikili programdır. Bilgi edinmek (veya soru sormak) için diğer çekirdeklerin neler kullandığını görün.malloc
tarafından döndürülen belleğe işaret eden işaretçiyi kontrol edin. Bu işaretçinullptr
bu işaretçi kullanılarak hiçbir işlem yapılmamalıdır. Şu durumda: bir fonksiyondamalloc
hatası görürseniz çıkış yapmadan önce belleği çıkar.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.