Karena library operator bawaan TensorFlow Lite hanya mendukung operator TensorFlow dalam jumlah terbatas, tidak semua model dapat dikonversi. Untuk mengetahui detailnya, lihat kompatibilitas operator.
Untuk mengizinkan konversi, pengguna dapat memberikan implementasi kustom mereka sendiri dari operator TensorFlow yang tidak didukung di TensorFlow Lite, yang dikenal sebagai operator kustom. Jika Anda ingin menggabungkan serangkaian operator TensorFlow yang tidak didukung (atau didukung) ke dalam satu operator kustom yang dioptimalkan, lihat gabungan operator.
Penggunaan operator kustom terdiri dari empat langkah.
Membuat Model TensorFlow. Pastikan Model Tersimpan (atau Graph Def) merujuk ke operator TensorFlow Lite yang diberi nama dengan benar.
Lakukan konversi ke Model TensorFlow Lite. Pastikan Anda menetapkan atribut pengonversi TensorFlow Lite yang tepat agar berhasil mengonversi model.
Buat dan daftarkan operator. Hal ini bertujuan agar runtime TensorFlow Lite mengetahui cara memetakan operator dan parameter dalam grafik Anda ke kode C/C++ yang dapat dieksekusi.
Uji dan buat profil operator Anda. Jika Anda hanya ingin menguji operator kustom, sebaiknya buat model hanya dengan operator kustom dan gunakan program benchmark_model.
Mari kita pelajari contoh menyeluruh dalam menjalankan model dengan operator kustom tf.atan
(dinamai Atan
, lihat Membuat Model TensorFlow) yang didukung di TensorFlow, tetapi tidak didukung di TensorFlow Lite.
Operator Teks TensorFlow adalah contoh operator kustom. Lihat tutorial Mengonversi Teks TF ke TF Lite untuk contoh kode.
Contoh: Operator Atan
kustom
Mari kita lihat contoh dukungan operator TensorFlow yang tidak dimiliki TensorFlow Lite. Asumsikan kita menggunakan operator Atan
dan kita sedang membangun model yang sangat sederhana untuk fungsi y = atan(x + offset)
, di mana offset
dapat dilatih.
Membuat Model TensorFlow
Cuplikan kode berikut melatih model TensorFlow sederhana. Model ini hanya berisi operator kustom bernama Atan
, yang merupakan fungsi y = atan(x +
offset)
, dengan offset
dapat dilatih.
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
Pada tahap ini, jika Anda mencoba membuat model TensorFlow Lite dengan flag konverter default, Anda akan mendapatkan pesan error berikut:
Error:
error: 'tf.Atan' op is neither a custom op nor a flex op.
Mengonversi ke Model TensorFlow Lite
Buat model TensorFlow Lite dengan operator kustom, dengan menetapkan atribut pengonversi allow_custom_ops
seperti yang ditunjukkan di bawah:
converter = tf.lite.TFLiteConverter.from_concrete_functions([atan.get_concrete_function()], atan) converter.allow_custom_ops = True tflite_model = converter.convert()
Pada tahap ini, jika Anda menjalankannya dengan penafsir default menggunakan perintah seperti berikut:
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
Anda akan tetap mendapatkan error:
Encountered unresolved custom op: Atan.
Buat dan daftarkan operator.
#include "third_party/tensorflow/lite/c/c_api.h"
#include "third_party/tensorflow/lite/c/c_api_opaque.h"
Operator kustom TensorFlow Lite ditentukan menggunakan API C murni sederhana yang
terdiri dari jenis buram (TfLiteRegistrationExternal
) dan fungsi terkait.
TfLiteRegistrationExternal
adalah jenis buram:
typedef struct TfLiteRegistrationExternal TfLiteRegistrationExternal;
TfLiteRegistrationExternal
menyimpan identitas dan implementasi operator.
(Perhatikan bahwa operator berbeda dengan operand-nya, yang disimpan dalam
node grafik TF Lite untuk node yang memanggil operator.)
Instance jenis ini dibuat dengan panggilan ke
TfLiteRegistrationExternalCreate
dan dapat dihancurkan dengan memanggil
TfLiteRegistrationExternalDelete
.
Identitas operator ditetapkan melalui parameter ke fungsi konstruktor
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.
);
Implementasi operator dapat menentukan "metode" dengan tanda tangan berikut.
Semua metode ini bersifat opsional, tetapi agar operator berhasil
dievaluasi, implementasi operator harus menentukan dan menetapkan (menggunakan fungsi
penyetel) setidaknya metode Prepare
dan Invoke
.
// 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);
names fungsi (atau awalan namespace, untuk C++) dalam penerapan operasi Anda tidak harus sama dengan nama fungsi dalam cuplikan kode di atas, karena TF Lite Custom Ops API hanya akan menggunakan alamatnya. Sebaiknya Anda mendeklarasikan dalam namespace anonim atau sebagai fungsi statis.
Namun, sebaiknya sertakan nama operator Anda sebagai namespace atau awalan pada nama fungsi ini:
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.
Karena ini adalah API C, "metode" ini diimplementasikan sebagai pointer fungsi C dalam
jenis TfLiteRegistrationExternal
, yang ditetapkan dengan meneruskan alamat
fungsi implementasi Anda ke fungsi penyetel
TfLiteRegistrationExternalSet
MethodName yang sesuai:
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));
Lihat
common.h
untuk detail tentang TfLiteContext
dan TfLiteNode
. TfLiteContext
menyediakan fasilitas
pelaporan error dan akses ke objek global, termasuk semua tensor.
TfLiteNode
memungkinkan implementasi operator mengakses input dan outputnya.
Saat penafsir memuat model, penafsir akan memanggil metode Init()
satu kali untuk setiap node dalam grafik. Init()
yang ditentukan akan dipanggil lebih dari sekali jika operasi digunakan beberapa kali dalam grafik. Untuk operasi kustom, buffer konfigurasi akan
disediakan, berisi flexbuffer yang memetakan nama parameter ke nilainya. Buffer kosong untuk operasi bawaan karena penafsir telah mengurai parameter op. Implementasi kernel yang memerlukan status harus melakukan inisialisasi di sini dan mentransfer kepemilikan ke pemanggil. Untuk setiap panggilan Init()
, akan ada
panggilan terkait ke Free()
, yang memungkinkan implementasi membuang
buffer yang mungkin telah dialokasikan di Init()
.
Setiap kali tensor input diubah ukurannya, penafsir akan melihat
grafik yang memberitahukan implementasi perubahan tersebut. Hal ini memberi mereka kesempatan untuk
mengubah ukuran buffer internal, memeriksa validitas bentuk dan jenis input, dan
menghitung ulang bentuk output. Ini semua dilakukan melalui metode Prepare()
, dan
implementasi dapat mengakses statusnya menggunakan
TfLiteOpaqueNodeGetUserData(node)
.
Terakhir, setiap kali inferensi berjalan, penafsir akan melintasi grafik yang memanggil
metode Invoke()
, dan di sini juga status tersedia sebagai
TfLiteOpaqueNodeGetUserData(node)
.
Operasi kustom dapat diterapkan dengan menentukan fungsi "metode" tersebut, lalu
menentukan fungsi yang menampilkan instance TfLiteRegistrationExternal
yang dibuat dengan memanggil TfLiteRegistrationExternalCreate
, kemudian metode
penyetel yang relevan:
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; }
Perhatikan bahwa pendaftaran tidak bersifat otomatis dan panggilan eksplisit ke fungsi MyCustomOpRegistration
harus dilakukan (lihat detailnya di bawah). Meskipun BuiltinOpResolver
standar (tersedia dari target :builtin_ops
) menangani pendaftaran bawaan, operasi kustom harus dikumpulkan dalam library kustom yang terpisah.
Menentukan kernel di runtime TensorFlow Lite
Yang perlu kita lakukan untuk menggunakan op di TensorFlow Lite adalah menentukan dua fungsi (Prepare
dan Eval
), dan fungsi ketiga untuk membuat TfLiteRegistrationExternal
:
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; }
Saat menginisialisasi OpResolver
, tambahkan op kustom ke resolver (lihat di bawah untuk contoh). Tindakan ini akan mendaftarkan operator ke Tensorflow Lite sehingga TensorFlow Lite dapat menggunakan implementasi baru. Perhatikan bahwa dua argumen terakhir di TfLiteRegistration
sesuai dengan fungsi AtanPrepare
dan AtanEval
yang Anda tentukan untuk op kustom. Jika Anda menggunakan fungsi AtanInit
dan AtanFree
untuk melakukan inisialisasi variabel yang digunakan di operasi dan mengosongkan ruang penyimpanan, masing-masing argumen tersebut akan ditambahkan ke dua argumen pertama TfLiteRegistration
; argumen tersebut ditetapkan ke nullptr
dalam contoh ini.
Mendaftarkan operator dengan library kernel
Sekarang kita perlu mendaftarkan
operator dengan library {i>kernel<i}. Hal ini dilakukan dengan
OpResolver
. Di balik layar, penafsir akan memuat library
kernel yang akan ditetapkan untuk mengeksekusi setiap operator dalam model.
Meskipun library default hanya berisi kernel bawaan, Anda dapat
mengganti/meningkatkannya dengan operator op library kustom.
Class OpResolver
, yang menerjemahkan kode dan nama operator menjadi kode
yang sebenarnya, ditentukan seperti berikut:
class OpResolver {
public:
virtual TfLiteRegistration* FindOp(tflite::BuiltinOperator op) const = 0;
virtual TfLiteRegistration* FindOp(const char* op) const = 0;
...
};
Perlu diperhatikan bahwa untuk kompatibilitas mundur, class ini menggunakan jenis konkret lama
TfLiteRegistration
, bukan jenis buram TfLiteRegistrationExternal
,
tetapi struct TfLiteRegistration
berisi kolom registration_external
dari
jenis TfLiteRegistrationExternal*
.
Class MutableOpResolver
dan BuiltinOpResolver
berasal dari
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.
};
Penggunaan reguler (tanpa operasi kustom) mengharuskan Anda menggunakan BuiltinOpResolver
dan menulis:
tflite::ops::builtin::BuiltinOpResolver resolver;
Untuk menambahkan operasi kustom yang dibuat di atas, Anda dapat menggunakan MutableOpResolver
dan memanggil AddCustom
(sebelum meneruskan resolver ke InterpreterBuilder
):
tflite::ops::builtin::MutableOpResolver resolver;
resolver.AddAll(tflite::ops::builtin::BuiltinOpResolver());
resolver.AddCustom("Atan", AtanOpRegistration());
Jika kumpulan operasi bawaan dianggap terlalu besar, OpResolver
baru dapat
dibuat dengan kode berdasarkan subset operasi tertentu, mungkin hanya yang terdapat
dalam model tertentu. Hal ini setara dengan pendaftaran selektif TensorFlow (dan versi sederhananya tersedia di direktori tools
).
Jika ingin menentukan operator kustom di Java, saat ini Anda harus membangun lapisan JNI kustom dan mengompilasi AAR Anda sendiri dalam kode jni ini. Demikian pula, jika ingin menentukan operator ini yang tersedia di Python, Anda dapat menempatkan pendaftaran dalam kode wrapper Python.
Perhatikan bahwa proses serupa seperti di atas dapat diikuti untuk mendukung sekumpulan operasi, bukan operator tunggal. Cukup tambahkan operator AddCustom
sebanyak yang Anda butuhkan. Selain itu, MutableOpResolver
juga memungkinkan Anda mengganti
implementasi bawaan menggunakan AddBuiltin
.
Uji dan buat profil operator Anda
Untuk membuat profil operasi dengan alat tolok ukur TensorFlow Lite, Anda dapat menggunakan alat model tolok ukur untuk TensorFlow Lite. Untuk tujuan pengujian, Anda dapat membuat build lokal TensorFlow Lite mengetahui pengoperasian kustom Anda dengan menambahkan panggilan AddCustom
yang sesuai (seperti yang ditunjukkan di atas) ke register.cc
Praktik terbaik
Optimalkan alokasi dan de-alokasi memori dengan hati-hati. Mengalokasikan memori di
Prepare
lebih efisien daripada diInvoke
, dan mengalokasikan memori sebelum loop lebih baik daripada di setiap iterasi. Gunakan data tensor sementara, bukan malocing diri sendiri (lihat item 2). Gunakan pointer/referensi, bukan menyalin sebanyak mungkin.Jika struktur data akan tetap ada selama operasi, sebaiknya alokasikan memori terlebih dahulu menggunakan tensor sementara. Anda mungkin perlu menggunakan struct OpData untuk mereferensikan indeks tensor dalam fungsi lain. Lihat contoh di kernel untuk konvolusi. Contoh cuplikan kode ada di bawah.
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; }
Jika tidak menghabiskan terlalu banyak memori yang terbuang, lebih baik gunakan array ukuran tetap statis (atau
std::vector
yang telah dialokasikan sebelumnya diResize
), daripada menggunakanstd::vector
yang dialokasikan secara dinamis pada setiap iterasi eksekusi.Hindari membuat instance template penampung library standar yang belum ada, karena memengaruhi ukuran biner. Misalnya, jika Anda memerlukan
std::map
dalam operasi yang tidak ada di kernel lain, penggunaanstd::vector
dengan pemetaan pengindeksan langsung dapat sangat membantu dengan menjaga agar ukuran biner tetap kecil. Lihat apa yang digunakan kernel lain untuk mendapatkan insight (atau bertanya).Periksa pointer ke memori yang ditampilkan oleh
malloc
. Jika pointer ini adalahnullptr
, tidak ada operasi yang harus dilakukan menggunakan pointer tersebut. Jika Andamalloc
dalam suatu fungsi dan mengalami error keluar, batalkan alokasi memori sebelum Anda keluar.Gunakan
TF_LITE_OPAQUE_ENSURE(context, condition)
untuk memeriksa kondisi tertentu. Kode Anda tidak boleh membiarkan memori bergantung saatTF_LITE_OPAQUE_ENSURE
digunakan, yaitu, makro ini harus digunakan sebelum resource apa pun dialokasikan yang akan bocor.