LiteRT'i kullanmaya başlama

Bu kılavuzda, giriş verilerine dayalı tahminler yapmak için cihazda LiteRT (Lite Runtime'ın kısaltması) modeli çalıştırma süreci tanıtılmaktadır. Bu, LiteRT yorumlayıcısı ile sağlanır. LiteRT yorumlayıcısı, minimum yükleme, başlatma ve yürütme gecikmesi sağlamak için statik bir grafik sıralaması ve özel (daha az dinamik) bir bellek ayırıcı kullanır.

LiteRT çıkarımı genellikle aşağıdaki adımları izler:

  1. Model yükleme: Modelin yürütme grafiğini içeren .tflite modelini belleğe yükleyin.

  2. Verileri dönüştürme: Giriş verilerini beklenen biçime ve boyutlara dönüştürün. Model için ham giriş verileri genellikle modelin beklediği giriş verileri biçimiyle eşleşmez. Örneğin, modelle uyumlu olması için bir resmi yeniden boyutlandırmanız veya resim biçimini değiştirmeniz gerekebilir.

  3. Çıkarım çalıştırma: Tahminlerde bulunmak için LiteRT modelini yürütün. Bu adımda, modeli yürütmek için LiteRT API'si kullanılır. Yorumlayıcıyı oluşturma ve tensörleri ayırma gibi birkaç adım içerir.

  4. Çıktıyı yorumlama: Çıktı tensörlerini, uygulamanızda faydalı olacak şekilde anlamlı bir şekilde yorumlayın. Örneğin, bir model yalnızca olasılık listesi döndürebilir. Olasılıkları alakalı kategorilerle eşlemek ve çıkışı biçimlendirmek sizin sorumluluğunuzdadır.

Bu kılavuzda, LiteRT yorumlayıcısına nasıl erişileceği ve C++, Java ve Python kullanılarak çıkarım işleminin nasıl yapılacağı açıklanmaktadır.

Desteklenen platformlar

TensorFlow çıkarım API'leri, Android, iOS ve Linux gibi en yaygın mobil ve yerleşik platformlar için birden fazla programlama dilinde sağlanır.

Çoğu durumda, API tasarımı kullanım kolaylığı yerine performansı tercih eder. LiteRT, küçük cihazlarda hızlı çıkarım için tasarlanmıştır. Bu nedenle, API'ler kolaylık pahasına gereksiz kopyalardan kaçınır.

LiteRT API, tüm kitaplıklarda modelleri yüklemenize, girişleri beslemenize ve çıkarım çıkışlarını almanıza olanak tanır.

Android Platformu

Android'de LiteRT çıkarımı, Java veya C++ API'leri kullanılarak gerçekleştirilebilir. Java API'leri kolaylık sağlar ve doğrudan Android Activity sınıflarınızda kullanılabilir. C++ API'leri daha fazla esneklik ve hız sunar ancak Java ile C++ katmanları arasında veri taşımak için JNI sarmalayıcıları yazmayı gerektirebilir.

Daha fazla bilgi için C++ ve Java bölümlerine bakın veya Android hızlı başlangıç kılavuzunu inceleyin.

iOS Platformu

iOS'te LiteRT, Swift ve Objective-C iOS kitaplıklarında kullanılabilir. C API'yi doğrudan Objective-C kodunda da kullanabilirsiniz.

Swift, Objective-C ve C API bölümlerine bakın veya iOS hızlı başlangıç kılavuzunu inceleyin.

Linux Platformu

Linux platformlarında, C++'da bulunan LiteRT API'lerini kullanarak çıkarımlar çalıştırabilirsiniz.

Model yükleme ve çalıştırma

LiteRT modelini yükleme ve çalıştırma aşağıdaki adımları içerir:

  1. Modeli belleğe yükleme
  2. Mevcut bir modele dayalı Interpreter oluşturma.
  3. Giriş tensör değerlerini ayarlama.
  4. Çıkarımlar çağrılıyor.
  5. Tensör değerlerini çıkış olarak verme.

Android (Java)

LiteRT ile çıkarım çalıştırmak için kullanılan Java API'si öncelikle Android ile kullanılmak üzere tasarlanmıştır. Bu nedenle, Android kitaplığı bağımlılığı olarak kullanılabilir: com.google.ai.edge.litert.

Java'da, bir modeli yüklemek ve model çıkarımını yönlendirmek için Interpreter sınıfını kullanırsınız. Çoğu durumda, ihtiyacınız olan tek API bu olabilir.

FlatBuffers (.tflite) dosyası kullanarak Interpreter başlatabilirsiniz:

public Interpreter(@NotNull File modelFile);

Alternatif olarak MappedByteBuffer ile:

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

Her iki durumda da geçerli bir LiteRT modeli sağlamanız gerekir. Aksi takdirde API, IllegalArgumentException hatası verir. MappedByteBuffer kullanarak bir Interpreter başlatırsanız bu değer, Interpreter'nin kullanım ömrü boyunca değişmeden kalmalıdır.

Bir modelde çıkarım çalıştırmanın tercih edilen yolu imzaları kullanmaktır. Bu özellik, Tensorflow 2.5'ten itibaren dönüştürülen modellerde kullanılabilir.

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

runSignature yöntemi üç bağımsız değişken alır:

  • Girişler : İmzadaki giriş adından giriş nesnesine kadar olan girişlerin eşlemesi.

  • Çıkışlar : İmzadaki çıkış adından çıkış verilerine yönelik çıkış eşlemesi için harita.

  • İmza Adı (isteğe bağlı): İmza adı (Modelde tek imza varsa boş bırakılabilir).

Modelin tanımlanmış imzaları olmadığında çıkarım çalıştırmanın başka bir yolu. Interpreter.run() numarasını aramanız yeterlidir. Örneğin:

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

run() yöntemi yalnızca bir giriş alır ve yalnızca bir çıkış döndürür. Bu nedenle, modelinizde birden fazla giriş veya çıkış varsa bunun yerine şunları kullanın:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

Bu durumda, inputs içindeki her giriş bir giriş tensörüne karşılık gelir ve map_of_indices_to_outputs, çıkış tensörlerinin dizinlerini ilgili çıkış verileriyle eşler.

Her iki durumda da tensör dizinleri, modeli oluştururken LiteRT Converter'a verdiğiniz değerlere karşılık gelmelidir. input içindeki tensörlerin sırasının, LiteRT dönüştürücüye verilen sırayla eşleşmesi gerektiğini unutmayın.

Interpreter sınıfı, işlem adını kullanarak herhangi bir model girişinin veya çıkışının dizinini almanızı sağlayan kullanışlı işlevler de sunar:

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

opName, modelde geçerli bir işlem değilse IllegalArgumentException hatası verilir.

Ayrıca Interpreter'nın kaynaklara sahip olduğunu unutmayın. Bellek sızıntısını önlemek için kaynaklar kullanıldıktan sonra aşağıdaki yöntemlerle serbest bırakılmalıdır:

interpreter.close();

Java ile örnek bir proje için Android nesne algılama örneği uygulamasına bakın.

Desteklenen veri türleri

LiteRT'yi kullanmak için giriş ve çıkış tensörlerinin veri türleri aşağıdaki temel türlerden biri olmalıdır:

  • float
  • int
  • long
  • byte

String türleri de desteklenir ancak bunlar, temel türlerden farklı şekilde kodlanır. Özellikle, bir dize tensörünün şekli, tensördeki dizelerin sayısını ve düzenini belirler. Her öğe, değişken uzunlukta bir dizedir. Bu anlamda, Tensor'un (bayt) boyutu yalnızca şekil ve türden hesaplanamaz ve sonuç olarak dizeler tek bir düz ByteBuffer bağımsız değişken olarak sağlanamaz.

Integer ve Float gibi kutulu türler de dahil olmak üzere başka veri türleri kullanılıyorsa IllegalArgumentException hatası verilir.

Girişler

Her giriş, desteklenen temel türlerin bir dizisi veya çok boyutlu dizisi ya da uygun boyutta bir ham ByteBuffer olmalıdır. Giriş bir dizi veya çok boyutlu dizi ise ilişkili giriş tensörü, çıkarım sırasında dizinin boyutlarına göre örtülü olarak yeniden boyutlandırılır. Giriş bir ByteBuffer ise arayan, çıkarım çalıştırmadan önce ilişkili giriş tensörünü (Interpreter.resizeInput() aracılığıyla) manuel olarak yeniden boyutlandırmalıdır.

ByteBuffer kullanırken doğrudan bayt arabelleklerini kullanmayı tercih edin. Bu, ByteBuffer'nın gereksiz kopyalardan kaçınmasını sağlar.Interpreter ByteBuffer doğrudan bir bayt arabelleği ise sırası ByteOrder.nativeOrder() olmalıdır. Model çıkarımı için kullanıldıktan sonra, model çıkarımı tamamlanana kadar değiştirilmemelidir.

Çıkışlar

Her çıkış, desteklenen temel türlerin bir dizisi veya çok boyutlu dizisi ya da uygun boyutta bir ByteBuffer olmalıdır. Bazı modellerin, girişlere bağlı olarak çıkış tensörlerinin şeklinin değişebildiği dinamik çıkışlara sahip olduğunu unutmayın. Mevcut Java çıkarım API'si ile bu durumu doğrudan ele almanın bir yolu yoktur ancak planlanan uzantılar bu durumu mümkün kılacaktır.

iOS (Swift)

Swift API, Cocoapods'taki TensorFlowLiteSwift Pod'da kullanılabilir.

Öncelikle TensorFlowLite modülünü içe aktarmanız gerekir.

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

iOS (Objective-C)

Objective-C API Cocoapods'taki LiteRTObjC Pod'da kullanılabilir.

Öncelikle TensorFlowLiteObjC modülünü içe aktarmanız gerekir.

@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... */ }

Objective-C kodunda C API'si

Objective-C API, temsilcileri desteklemez. Objective-C koduyla temsilcileri kullanmak için temel C API'yi doğrudan çağırmanız gerekir.

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

C++

LiteRT ile çıkarım çalıştırmak için kullanılan C++ API, Android, iOS ve Linux platformlarıyla uyumludur. iOS'teki C++ API yalnızca Bazel kullanılırken kullanılabilir.

C++'ta model, FlatBufferModel sınıfında depolanır. LiteRT modelini kapsar ve modelin depolandığı yere bağlı olarak birkaç farklı şekilde oluşturulabilir:

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

Modeli FlatBufferModel nesnesi olarak aldığınıza göre artık Interpreter ile yürütebilirsiniz. Tek bir FlatBufferModel aynı anda birden fazla Interpreter tarafından kullanılabilir.

Interpreter API'sinin önemli kısımları aşağıdaki kod snippet'inde gösterilmektedir. Şunları unutmayın:

  • Dize karşılaştırmalarını (ve dize kitaplıklarına yönelik sabit bağımlılıkları) önlemek için tensörler tam sayılarla gösterilir.
  • Çevirmenlere eşzamanlı iş parçacıklarından erişilmemelidir.
  • Giriş ve çıkış tensörleri için bellek ayırma işlemi, tensörler yeniden boyutlandırıldıktan hemen sonra AllocateTensors() çağrılarak tetiklenmelidir.

C++ ile LiteRT'nin en basit kullanımı şu şekildedir:

// 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 needed.
interpreter->AllocateTensors();

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

interpreter->Invoke();

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

Daha fazla örnek kod için minimal.cc ve label_image.cc sayfalarına bakın.

Python

Çıkarım çalıştırmak için kullanılan Python API, modeli yüklemek ve çıkarım çalıştırmak için Interpreter kullanır.

LiteRT paketini yükleyin:

$ python3 -m pip install ai-edge-litert

LiteRT Interpreter'ı içe aktarma

from ai_edge_litert.interpreter import Interpreter
Interpreter = Interpreter(model_path=args.model.file)

Aşağıdaki örnekte, Python yorumlayıcısını kullanarak bir FlatBuffers (.tflite) dosyasının nasıl yükleneceği ve rastgele giriş verileriyle çıkarımın nasıl çalıştırılacağı gösterilmektedir:

Bu örnek, tanımlanmış bir SignatureDef ile SavedModel'den dönüştürme yapıyorsanız önerilir.

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 LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = 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'])

Modelde SignatureDefs tanımlanmamışsa başka bir örnek.

import numpy as np
import tensorflow as tf

# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(TFLITE_FILE_PATH)
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)

Modeli önceden dönüştürülmüş bir .tflite dosyası olarak yüklemek yerine kodunuzu LiteRT Compiler ile birleştirebilirsiniz. Bu sayede Keras modelinizi LiteRT biçimine dönüştürüp çıkarım çalıştırabilirsiniz:

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 LiteRT format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()

# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

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

Daha fazla Python örnek kodu için label_image.py bölümüne bakın.

Dinamik şekil modeliyle çıkarım çalıştırma

Dinamik giriş şekline sahip bir model çalıştırmak istiyorsanız çıkarım çalıştırmadan önce giriş şeklini yeniden boyutlandırın. Aksi takdirde, Tensorflow modellerindeki None şekli, LiteRT modellerinde 1 yer tutucusuyla değiştirilir.

Aşağıdaki örneklerde, farklı dillerde çıkarım çalıştırmadan önce giriş şeklinin nasıl yeniden boyutlandırılacağı gösterilmektedir. Tüm örneklerde giriş şeklinin [1/None, 10] olarak tanımlandığı varsayılır ve [3, 10] olarak yeniden boyutlandırılması gerekir.

C++ örneği:

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

Python örneği:

# Load the LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = 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()