Inferensi TensorFlow Lite

Istilah inferensi mengacu pada proses eksekusi model TensorFlow Lite di perangkat untuk membuat prediksi berdasarkan data masukan. Untuk melakukan inferensi dengan model TensorFlow Lite, Anda harus menjalankannya melalui interpreter . Penerjemah TensorFlow Lite dirancang agar ramping dan cepat. Penerjemah menggunakan pengurutan grafik statis dan pengalokasi memori khusus (kurang dinamis) untuk memastikan latensi beban, inisialisasi, dan eksekusi minimal.

Halaman ini menjelaskan cara mengakses juru bahasa TensorFlow Lite dan melakukan inferensi menggunakan C++, Java, dan Python, serta link ke resource lain untuk setiap platform yang didukung .

Konsep penting

Inferensi TensorFlow Lite biasanya mengikuti langkah-langkah berikut:

  1. Memuat model

    Anda harus memuat model .tflite ke dalam memori, yang berisi grafik eksekusi model.

  2. Mengubah data

    Data masukan mentah untuk model umumnya tidak sesuai dengan format data masukan yang diharapkan oleh model. Misalnya, Anda mungkin perlu mengubah ukuran gambar atau mengubah format gambar agar kompatibel dengan model.

  3. Menjalankan inferensi

    Langkah ini melibatkan penggunaan TensorFlow Lite API untuk mengeksekusi model. Ini melibatkan beberapa langkah seperti membangun interpreter, dan mengalokasikan tensor, seperti yang dijelaskan di bagian berikut.

  4. Menafsirkan keluaran

    Saat Anda menerima hasil dari inferensi model, Anda harus menafsirkan tensor dengan cara yang bermakna dan berguna dalam aplikasi Anda.

    Misalnya, suatu model mungkin hanya mengembalikan daftar probabilitas. Terserah Anda untuk memetakan probabilitas ke kategori yang relevan dan menyajikannya kepada pengguna akhir Anda.

Platform yang didukung

API inferensi TensorFlow disediakan untuk sebagian besar platform seluler/tersemat seperti Android , iOS , dan Linux , dalam berbagai bahasa pemrograman.

Dalam kebanyakan kasus, desain API mencerminkan preferensi terhadap kinerja dibandingkan kemudahan penggunaan. TensorFlow Lite didesain untuk inferensi cepat pada perangkat kecil, jadi tidak mengherankan jika API mencoba menghindari salinan yang tidak perlu dengan mengorbankan kenyamanan. Demikian pula, konsistensi dengan TensorFlow API bukanlah tujuan eksplisit dan beberapa perbedaan antar bahasa mungkin terjadi.

Di semua perpustakaan, TensorFlow Lite API memungkinkan Anda memuat model, memasukkan masukan, dan mengambil keluaran inferensi.

Platform Android

Di Android, inferensi TensorFlow Lite dapat dilakukan menggunakan Java atau C++ API. Java API memberikan kemudahan dan dapat digunakan langsung dalam kelas Aktivitas Android Anda. C++ API menawarkan lebih banyak fleksibilitas dan kecepatan, namun mungkin memerlukan penulisan wrapper JNI untuk memindahkan data antara lapisan Java dan C++.

Lihat di bawah untuk detail tentang penggunaan C++ dan Java , atau ikuti mulai cepat Android untuk tutorial dan contoh kode.

Generator kode wrapper Android TensorFlow Lite

Untuk model TensorFlow Lite yang disempurnakan dengan metadata , pengembang dapat menggunakan generator kode wrapper Android TensorFlow Lite untuk membuat kode wrapper khusus platform. Kode pembungkus menghilangkan kebutuhan untuk berinteraksi langsung dengan ByteBuffer di Android. Sebagai gantinya, developer dapat berinteraksi dengan model TensorFlow Lite dengan objek yang diketik seperti Bitmap dan Rect . Untuk informasi selengkapnya, lihat generator kode wrapper Android TensorFlow Lite .

Platform iOS

Di iOS, TensorFlow Lite tersedia dengan pustaka iOS asli yang ditulis dalam Swift dan Objective-C . Anda juga dapat menggunakan C API secara langsung dalam kode Objective-C.

Lihat di bawah untuk detail tentang penggunaan Swift , Objective-C dan C API , atau ikuti mulai cepat iOS untuk tutorial dan contoh kode.

Platform Linux

Pada platform Linux (termasuk Raspberry Pi ), ​​Anda dapat menjalankan inferensi menggunakan TensorFlow Lite API yang tersedia dalam C++ dan Python , seperti yang ditunjukkan di bagian berikut.

Menjalankan model

Menjalankan model TensorFlow Lite melibatkan beberapa langkah sederhana:

  1. Muat model ke dalam memori.
  2. Bangun Interpreter berdasarkan model yang sudah ada.
  3. Tetapkan nilai tensor masukan. (Secara opsional, ubah ukuran tensor masukan jika ukuran yang telah ditentukan sebelumnya tidak diinginkan.)
  4. Panggil inferensi.
  5. Membaca nilai tensor keluaran.

Bagian berikut menjelaskan bagaimana langkah-langkah ini dapat dilakukan dalam setiap bahasa.

Memuat dan menjalankan model di Java

Peron: Android

Java API untuk menjalankan inferensi dengan TensorFlow Lite terutama didesain untuk digunakan dengan Android, sehingga tersedia sebagai dependensi pustaka Android: org.tensorflow:tensorflow-lite .

Di Java, Anda akan menggunakan kelas Interpreter untuk memuat model dan mendorong inferensi model. Dalam banyak kasus, ini mungkin satu-satunya API yang Anda perlukan.

Anda dapat menginisialisasi Interpreter menggunakan file .tflite :

public Interpreter(@NotNull File modelFile);

Atau dengan MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

Dalam kedua kasus tersebut, Anda harus menyediakan model TensorFlow Lite yang valid atau API akan menampilkan IllegalArgumentException . Jika Anda menggunakan MappedByteBuffer untuk menginisialisasi Interpreter , itu harus tetap tidak berubah selama masa pakai Interpreter .

Cara yang lebih disukai untuk menjalankan inferensi pada model adalah dengan menggunakan tanda tangan - Tersedia untuk model yang dikonversi mulai Tensorflow 2.5

try (Interpreter interpreter = new Interpreter(file_of_tensorflowlite_model)) {
  Map<String, Object> inputs = new HashMap<>();
  inputs.put("input_1", input1);
  inputs.put("input_2", input2);
  Map<String, Object> outputs = new HashMap<>();
  outputs.put("output_1", output1);
  interpreter.runSignature(inputs, outputs, "mySignature");
}

Metode runSignature membutuhkan tiga argumen:

  • Inputs : peta untuk input dari nama input di tanda tangan ke objek input.

  • Output : peta untuk pemetaan keluaran dari nama keluaran di tanda tangan ke data keluaran.

  • Nama Tanda Tangan [opsional]: Nama tanda tangan (Dapat dikosongkan jika model memiliki tanda tangan tunggal).

Cara lain untuk menjalankan inferensi ketika model tidak memiliki tanda tangan yang ditentukan. Cukup hubungi Interpreter.run() . Misalnya:

try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
  interpreter.run(input, output);
}

Metode run() hanya mengambil satu masukan dan hanya mengembalikan satu keluaran. Jadi, jika model Anda memiliki banyak masukan atau banyak keluaran, gunakan:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

Dalam hal ini, setiap entri dalam inputs berhubungan dengan tensor masukan dan map_of_indices_to_outputs memetakan indeks tensor keluaran ke data keluaran yang sesuai.

Dalam kedua kasus tersebut, indeks tensor harus sesuai dengan nilai yang Anda berikan ke TensorFlow Lite Converter saat Anda membuat model. Perlu diketahui bahwa urutan tensor dalam input harus sesuai dengan urutan yang diberikan pada TensorFlow Lite Converter.

Kelas Interpreter juga menyediakan fungsi yang mudah bagi Anda untuk mendapatkan indeks input atau output model apa pun menggunakan nama operasi:

public int getInputIndex(String opName);
public int getOutputIndex(String opName);

Jika opName bukan operasi yang valid dalam model, ia akan memunculkan IllegalArgumentException .

Berhati-hatilah juga bahwa Interpreter memiliki sumber daya. Untuk menghindari kebocoran memori, sumber daya harus dilepaskan setelah digunakan dengan cara:

interpreter.close();

Untuk contoh proyek dengan Java, lihat contoh klasifikasi gambar Android .

Tipe data yang didukung (di Java)

Untuk menggunakan TensorFlow Lite, tipe data tensor input dan output harus berupa salah satu tipe primitif berikut:

  • float
  • int
  • long
  • byte

Tipe String juga didukung, namun kodenya berbeda dari tipe primitif. Secara khusus, bentuk string Tensor menentukan jumlah dan susunan string di Tensor, dengan setiap elemen itu sendiri menjadi string dengan panjang variabel. Dalam hal ini, ukuran (byte) Tensor tidak dapat dihitung hanya dari bentuk dan tipenya saja, dan akibatnya string tidak dapat diberikan sebagai argumen ByteBuffer tunggal yang datar. Anda dapat melihat beberapa contoh di halaman ini.

Jika tipe data lain, termasuk tipe kotak seperti Integer dan Float , digunakan, IllegalArgumentException akan dilempar.

masukan

Setiap input harus berupa array atau array multidimensi dari tipe primitif yang didukung, atau ByteBuffer mentah dengan ukuran yang sesuai. Jika masukannya berupa larik atau larik multidimensi, tensor masukan terkait akan diubah ukurannya secara implisit ke dimensi larik pada waktu inferensi. Jika masukannya adalah ByteBuffer, pemanggil harus terlebih dahulu mengubah ukuran tensor masukan terkait secara manual (melalui Interpreter.resizeInput() ) sebelum menjalankan inferensi.

Saat menggunakan ByteBuffer , lebih baik menggunakan buffer byte langsung, karena ini memungkinkan Interpreter menghindari salinan yang tidak perlu. Jika ByteBuffer adalah buffer byte langsung, urutannya harus ByteOrder.nativeOrder() . Setelah digunakan untuk inferensi model, ia harus tetap tidak berubah hingga inferensi model selesai.

Keluaran

Setiap output harus berupa array atau array multidimensi dari tipe primitif yang didukung, atau ByteBuffer dengan ukuran yang sesuai. Perhatikan bahwa beberapa model memiliki keluaran dinamis, yang bentuk tensor keluarannya dapat bervariasi bergantung pada masukannya. Tidak ada cara langsung untuk menangani hal ini dengan API inferensi Java yang ada, namun ekstensi yang direncanakan akan memungkinkan hal ini.

Muat dan jalankan model di Swift

Peron: iOS

Swift API tersedia di TensorFlowLiteSwift Pod dari Cocoapods.

Pertama, Anda perlu mengimpor modul TensorFlowLite .

import TensorFlowLite
// Getting model path
guard
  let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
else {
  // Error handling...
}

do {
  // Initialize an interpreter with the model.
  let interpreter = try Interpreter(modelPath: modelPath)

  // Allocate memory for the model's input `Tensor`s.
  try interpreter.allocateTensors()

  let inputData: Data  // Should be initialized

  // input data preparation...

  // Copy the input data to the input `Tensor`.
  try self.interpreter.copy(inputData, toInputAt: 0)

  // Run inference by invoking the `Interpreter`.
  try self.interpreter.invoke()

  // Get the output `Tensor`
  let outputTensor = try self.interpreter.output(at: 0)

  // Copy output to `Data` to process the inference results.
  let outputSize = outputTensor.shape.dimensions.reduce(1, {x, y in x * y})
  let outputData =
        UnsafeMutableBufferPointer<Float32>.allocate(capacity: outputSize)
  outputTensor.data.copyBytes(to: outputData)

  if (error != nil) { /* Error handling... */ }
} catch error {
  // Error handling...
}

Memuat dan menjalankan model di Objective-C

Peron: iOS

API Objective-C tersedia di TensorFlowLiteObjC Pod dari Cocoapods.

Pertama, Anda perlu mengimpor modul TensorFlowLite .

@import TensorFlowLite;
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];
NSError *error;

// Initialize an interpreter with the model.
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
                                                                  error:&error];
if (error != nil) { /* Error handling... */ }

// Allocate memory for the model's input `TFLTensor`s.
[interpreter allocateTensorsWithError:&error];
if (error != nil) { /* Error handling... */ }

NSMutableData *inputData;  // Should be initialized
// input data preparation...

// Get the input `TFLTensor`
TFLTensor *inputTensor = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy the input data to the input `TFLTensor`.
[inputTensor copyData:inputData error:&error];
if (error != nil) { /* Error handling... */ }

// Run inference by invoking the `TFLInterpreter`.
[interpreter invokeWithError:&error];
if (error != nil) { /* Error handling... */ }

// Get the output `TFLTensor`
TFLTensor *outputTensor = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy output to `NSData` to process the inference results.
NSData *outputData = [outputTensor dataWithError:&error];
if (error != nil) { /* Error handling... */ }

Menggunakan C API dalam kode Objective-C

Saat ini API Objective-C tidak mendukung delegasi. Untuk menggunakan delegasi dengan kode Objective-C, Anda perlu langsung memanggil C API yang mendasarinya.

#include "tensorflow/lite/c/c_api.h"
TfLiteModel* model = TfLiteModelCreateFromFile([modelPath UTF8String]);
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();

// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);

// Allocate tensors and populate the input tensor data.
TfLiteInterpreterAllocateTensors(interpreter);
TfLiteTensor* input_tensor =
    TfLiteInterpreterGetInputTensor(interpreter, 0);
TfLiteTensorCopyFromBuffer(input_tensor, input.data(),
                           input.size() * sizeof(float));

// Execute inference.
TfLiteInterpreterInvoke(interpreter);

// Extract the output tensor data.
const TfLiteTensor* output_tensor =
    TfLiteInterpreterGetOutputTensor(interpreter, 0);
TfLiteTensorCopyToBuffer(output_tensor, output.data(),
                         output.size() * sizeof(float));

// Dispose of the model and interpreter objects.
TfLiteInterpreterDelete(interpreter);
TfLiteInterpreterOptionsDelete(options);
TfLiteModelDelete(model);

Memuat dan menjalankan model di C++

Platform: Android, iOS, dan Linux

Di C++, model disimpan di kelas FlatBufferModel . Ini merangkum model TensorFlow Lite dan Anda dapat membuatnya dengan beberapa cara berbeda, bergantung pada tempat model disimpan:

class FlatBufferModel {
  // Build a model based on a file. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromFile(
      const char* filename,
      ErrorReporter* error_reporter);

  // Build a model based on a pre-loaded flatbuffer. The caller retains
  // ownership of the buffer and should keep it alive until the returned object
  // is destroyed. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromBuffer(
      const char* buffer,
      size_t buffer_size,
      ErrorReporter* error_reporter);
};

Sekarang setelah Anda memiliki model sebagai objek FlatBufferModel , Anda dapat menjalankannya dengan Interpreter . Satu FlatBufferModel dapat digunakan secara bersamaan oleh lebih dari satu Interpreter .

Bagian penting dari Interpreter API ditunjukkan dalam cuplikan kode di bawah. Perlu dicatat bahwa:

  • Tensor diwakili oleh bilangan bulat, untuk menghindari perbandingan string (dan ketergantungan tetap pada pustaka string).
  • Seorang juru bahasa tidak boleh diakses dari thread bersamaan.
  • Alokasi memori untuk tensor masukan dan keluaran harus dipicu dengan memanggil AllocateTensors() tepat setelah mengubah ukuran tensor.

Penggunaan TensorFlow Lite yang paling sederhana dengan C++ terlihat seperti ini:

// Load the model
std::unique_ptr<tflite::FlatBufferModel> model =
    tflite::FlatBufferModel::BuildFromFile(filename);

// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);

// Resize input tensors, if desired.
interpreter->AllocateTensors();

float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.

interpreter->Invoke();

float* output = interpreter->typed_output_tensor<float>(0);

Untuk contoh kode selengkapnya, lihat minimal.cc dan label_image.cc .

Memuat dan menjalankan model dengan Python

Peron: Linux

API Python untuk menjalankan inferensi disediakan di modul tf.lite . Oleh karena itu, sebagian besar Anda hanya memerlukan tf.lite.Interpreter untuk memuat model dan menjalankan inferensi.

Contoh berikut menunjukkan cara menggunakan juru bahasa Python untuk memuat file .tflite dan menjalankan inferensi dengan data input acak:

Contoh ini direkomendasikan jika Anda mengonversi dari SavedModel dengan SignatureDef yang ditentukan. Tersedia mulai dari TensorFlow 2.5

class TestModel(tf.Module):
  def __init__(self):
    super(TestModel, self).__init__()

  @tf.function(input_signature=[tf.TensorSpec(shape=[1, 10], dtype=tf.float32)])
  def add(self, x):
    '''
    Simple method that accepts single input 'x' and returns 'x' + 4.
    '''
    # Name the output 'result' for convenience.
    return {'result' : x + 4}


SAVED_MODEL_PATH = 'content/saved_models/test_variable'
TFLITE_FILE_PATH = 'content/test_variable.tflite'

# Save the model
module = TestModel()
# You can omit the signatures argument and a default signature name will be
# created with name 'serving_default'.
tf.saved_model.save(
    module, SAVED_MODEL_PATH,
    signatures={'my_signature':module.add.get_concrete_function()})

# Convert the model using TFLiteConverter
converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL_PATH)
tflite_model = converter.convert()
with open(TFLITE_FILE_PATH, 'wb') as f:
  f.write(tflite_model)

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
# There is only 1 signature defined in the model,
# so it will return it by default.
# If there are multiple signatures then we can pass the name.
my_signature = interpreter.get_signature_runner()

# my_signature is callable with input as arguments.
output = my_signature(x=tf.constant([1.0], shape=(1,10), dtype=tf.float32))
# 'output' is dictionary with all outputs from the inference.
# In this case we have single output 'result'.
print(output['result'])

Contoh lain jika model tidak menentukan SignatureDefs.

import numpy as np
import tensorflow as tf

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path="converted_model.tflite")
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# Test the model on random input data.
input_shape = input_details[0]['shape']
input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data)

interpreter.invoke()

# The function `get_tensor()` returns a copy of the tensor data.
# Use `tensor()` in order to get a pointer to the tensor.
output_data = interpreter.get_tensor(output_details[0]['index'])
print(output_data)

Sebagai alternatif untuk memuat model sebagai file .tflite yang telah dikonversi sebelumnya, Anda dapat menggabungkan kode Anda dengan TensorFlow Lite Converter Python API ( tf.lite.TFLiteConverter ), yang memungkinkan Anda mengonversi model Keras ke dalam format TensorFlow Lite dan kemudian jalankan inferensi:

import numpy as np
import tensorflow as tf

img = tf.keras.Input(shape=(64, 64, 3), name="img")
const = tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
val = img + const
out = tf.identity(val, name="out")

# Convert to TF Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

# Continue to get tensors and so forth, as shown above...

Untuk contoh kode Python lainnya, lihat label_image.py .

Jalankan inferensi dengan model bentuk dinamis

Jika Anda ingin menjalankan model dengan bentuk masukan dinamis, ubah ukuran bentuk masukan sebelum menjalankan inferensi. Jika tidak, bentuk None di model Tensorflow akan digantikan oleh placeholder 1 di model TFLite.

Contoh berikut menunjukkan cara mengubah ukuran bentuk masukan sebelum menjalankan inferensi dalam berbagai bahasa. Semua contoh mengasumsikan bahwa bentuk masukan didefinisikan sebagai [1/None, 10] , dan perlu diubah ukurannya menjadi [3, 10] .

Contoh C++:

// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();

Contoh ular piton:

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(model_path=TFLITE_FILE_PATH)

# Resize input shape for dynamic shape model and allocate tensor
interpreter.resize_tensor_input(interpreter.get_input_details()[0]['index'], [3, 10])
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()