استنتاج TensorFlow Lite

اصطلاح استنتاج به فرآیند اجرای یک مدل TensorFlow Lite بر روی دستگاه به منظور پیش‌بینی بر اساس داده‌های ورودی اشاره دارد. برای انجام استنتاج با یک مدل TensorFlow Lite، باید آن را از طریق یک مفسر اجرا کنید. مفسر TensorFlow Lite طوری طراحی شده است که ناب و سریع باشد. مفسر از یک ترتیب گراف استاتیک و یک تخصیص دهنده حافظه سفارشی (کمتر پویا) برای اطمینان از حداقل بارگذاری، مقداردهی اولیه و تأخیر اجرا استفاده می کند.

این صفحه نحوه دسترسی به مفسر TensorFlow Lite و استنتاج با استفاده از C++، جاوا و پایتون، به علاوه پیوندهایی به منابع دیگر برای هر پلتفرم پشتیبانی شده را شرح می‌دهد.

مفاهیم مهم

استنتاج TensorFlow Lite معمولاً مراحل زیر را دنبال می کند:

  1. در حال بارگذاری یک مدل

    شما باید مدل .tflite را در حافظه بارگذاری کنید که شامل گراف اجرای مدل است.

  2. تبدیل داده ها

    داده‌های ورودی خام برای مدل معمولاً با قالب داده‌های ورودی مورد انتظار مدل مطابقت ندارد. برای مثال، ممکن است لازم باشد اندازه یک تصویر را تغییر دهید یا فرمت تصویر را تغییر دهید تا با مدل سازگار باشد.

  3. استنتاج در حال اجرا

    این مرحله شامل استفاده از TensorFlow Lite API برای اجرای مدل است. این شامل چند مرحله مانند ساخت مفسر و تخصیص تانسور است که در بخش‌های زیر توضیح داده شده است.

  4. تفسیر خروجی

    وقتی نتایجی را از استنتاج مدل دریافت می‌کنید، باید تانسورها را به روشی معنی‌دار تفسیر کنید که در کاربرد شما مفید باشد.

    برای مثال، یک مدل ممکن است فقط فهرستی از احتمالات را برگرداند. این به شما بستگی دارد که احتمالات را به دسته های مربوطه ترسیم کنید و آن را به کاربر نهایی خود ارائه دهید.

پلتفرم های پشتیبانی شده

APIهای استنتاج TensorFlow برای اکثر پلتفرم‌های رایج موبایل/جاسازی‌شده مانند اندروید ، iOS و لینوکس در چندین زبان برنامه‌نویسی ارائه شده‌اند.

در بیشتر موارد، طراحی API نشان دهنده ترجیح عملکرد نسبت به سهولت استفاده است. TensorFlow Lite برای استنتاج سریع در دستگاه‌های کوچک طراحی شده است، بنابراین جای تعجب نیست که APIها سعی می‌کنند از کپی‌های غیرضروری به قیمت راحتی جلوگیری کنند. به طور مشابه، سازگاری با API های TensorFlow یک هدف صریح نبود و باید انتظار داشت که تفاوتی بین زبان ها وجود داشته باشد.

در تمام کتابخانه‌ها، TensorFlow Lite API شما را قادر می‌سازد مدل‌ها را بارگیری کنید، ورودی‌های تغذیه و خروجی‌های استنتاج را بازیابی کنید.

پلتفرم اندروید

در اندروید، استنتاج TensorFlow Lite را می توان با استفاده از جاوا یا C++ API انجام داد. API های جاوا راحتی را فراهم می کنند و می توانند مستقیماً در کلاس های Android Activity شما استفاده شوند. APIهای C++ انعطاف‌پذیری و سرعت بیشتری را ارائه می‌دهند، اما ممکن است برای جابجایی داده‌ها بین لایه‌های جاوا و سی پلاس پلاس نیاز به نوشتن Wrapper‌های JNI داشته باشند.

برای جزئیات بیشتر در مورد استفاده از C++ و جاوا به زیر مراجعه کنید یا برای آموزش و کد نمونه ، راه اندازی سریع Android را دنبال کنید.

TensorFlow Lite مولد کد بسته بندی اندروید

برای مدل TensorFlow Lite که با ابرداده بهبود یافته است، توسعه‌دهندگان می‌توانند از تولیدکننده کد بسته‌بندی Android TensorFlow Lite برای ایجاد کد بسته‌بندی خاص پلتفرم استفاده کنند. کد Wrapper نیاز به تعامل مستقیم با ByteBuffer در اندروید را از بین می برد. در عوض، توسعه دهندگان می توانند با مدل TensorFlow Lite با اشیاء تایپ شده مانند Bitmap و Rect تعامل داشته باشند. برای اطلاعات بیشتر، لطفاً به تولیدکننده کد لفاف دار اندروید TensorFlow Lite مراجعه کنید.

پلتفرم iOS

در iOS، TensorFlow Lite با کتابخانه‌های بومی iOS نوشته شده در Swift و Objective-C در دسترس است. همچنین می توانید از C API به طور مستقیم در کدهای Objective-C استفاده کنید.

برای جزئیات بیشتر در مورد استفاده از Swift ، Objective-C و C API به زیر مراجعه کنید یا برای آموزش و کد نمونه ، راه اندازی سریع iOS را دنبال کنید.

پلتفرم لینوکس

در پلتفرم‌های لینوکس (از جمله Raspberry Pi )، می‌توانید استنتاج‌ها را با استفاده از APIهای TensorFlow Lite موجود در C++ و Python اجرا کنید، همانطور که در بخش‌های زیر نشان داده شده است.

اجرای یک مدل

اجرای یک مدل TensorFlow Lite شامل چند مرحله ساده است:

  1. مدل را در حافظه بارگذاری کنید.
  2. یک Interpreter بر اساس مدل موجود بسازید.
  3. مقادیر تانسور ورودی را تنظیم کنید. (اگر اندازه های از پیش تعریف شده دلخواه نباشند، به صورت اختیاری، اندازه تانسورهای ورودی را تغییر دهید.)
  4. استنباط را احضار کنید.
  5. مقادیر تانسور خروجی را بخوانید.

بخش های بعدی نحوه انجام این مراحل را در هر زبان توضیح می دهد.

یک مدل را در جاوا بارگیری و اجرا کنید

پلتفرم: اندروید

Java API برای اجرای استنتاج با TensorFlow Lite در اصل برای استفاده با Android طراحی شده است، بنابراین به عنوان یک وابستگی کتابخانه Android در دسترس است: org.tensorflow:tensorflow-lite .

در جاوا، از کلاس Interpreter برای بارگذاری مدل و استنتاج مدل استفاده می‌کنید. در بسیاری از موارد، این ممکن است تنها API مورد نیاز شما باشد.

شما می توانید یک Interpreter با استفاده از یک فایل .tflite مقداردهی اولیه کنید:

public Interpreter(@NotNull File modelFile);

یا با MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

در هر دو مورد، باید یک مدل معتبر TensorFlow Lite ارائه دهید یا API IllegalArgumentException را پرتاب کند. اگر از MappedByteBuffer برای مقداردهی اولیه یک Interpreter استفاده می کنید، باید در تمام طول عمر Interpreter بدون تغییر باقی بماند.

روش ترجیحی برای اجرای استنتاج بر روی یک مدل، استفاده از امضاها است - موجود برای مدل‌هایی که با شروع 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");
}

متد runSignature سه آرگومان می گیرد:

  • ورودی ها : برای ورودی ها از نام ورودی در امضا به یک شی ورودی نگاشت.

  • خروجی ها : نقشه برای نگاشت خروجی از نام خروجی در امضا تا داده های خروجی.

  • نام امضا [اختیاری]: نام امضا (اگر مدل دارای امضای واحد باشد می‌توان آن را خالی گذاشت).

راه دیگری برای اجرای استنتاج زمانی که مدل دارای امضاهای تعریف شده نیست. به سادگی Interpreter.run() را فراخوانی کنید. مثلا:

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

متد run() تنها یک ورودی می گیرد و تنها یک خروجی را برمی گرداند. بنابراین اگر مدل شما چندین ورودی یا چند خروجی دارد، در عوض از:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

در این حالت، هر ورودی در inputs مربوط به یک تانسور ورودی است و map_of_indices_to_outputs شاخص‌های تانسورهای خروجی را به داده‌های خروجی مربوطه نگاشت می‌کند.

در هر دو مورد، شاخص‌های تانسور باید با مقادیری مطابقت داشته باشند که هنگام ایجاد مدل به مبدل TensorFlow Lite داده‌اید. توجه داشته باشید که ترتیب تانسورها در input باید با ترتیب داده شده به مبدل TensorFlow Lite مطابقت داشته باشد.

کلاس Interpreter همچنین توابع مناسبی را برای شما فراهم می کند تا شاخص هر ورودی یا خروجی مدل را با استفاده از نام عملیات بدست آورید:

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

اگر opName یک عملیات معتبر در مدل نباشد، یک IllegalArgumentException ایجاد می‌کند.

همچنین مراقب باشید که Interpreter صاحب منابع است. برای جلوگیری از نشت حافظه، منابع باید پس از استفاده توسط:

interpreter.close();

برای نمونه پروژه با جاوا، نمونه طبقه بندی تصاویر اندروید را ببینید.

انواع داده های پشتیبانی شده (در جاوا)

برای استفاده از TensorFlow Lite، نوع داده تانسورهای ورودی و خروجی باید یکی از انواع اولیه زیر باشد:

  • float
  • int
  • long
  • byte

انواع String ها نیز پشتیبانی می شوند، اما آنها متفاوت از انواع ابتدایی کدگذاری می شوند. به طور خاص، شکل یک تانسور رشته‌ای، تعداد و ترتیب رشته‌ها را در تنسور تعیین می‌کند، به طوری که هر عنصر خود یک رشته با طول متغیر است. به این معنا، اندازه (بایت) Tensor را نمی توان به تنهایی از روی شکل و نوع محاسبه کرد، و در نتیجه رشته ها را نمی توان به عنوان یک آرگومان واحد و مسطح ByteBuffer ارائه کرد. می توانید چند نمونه را در این صفحه مشاهده کنید.

اگر از انواع داده های دیگر، از جمله انواع جعبه مانند Integer و Float استفاده شود، یک IllegalArgumentException پرتاب می شود.

ورودی ها

هر ورودی باید یک آرایه یا آرایه چند بعدی از انواع اولیه پشتیبانی شده یا یک ByteBuffer خام با اندازه مناسب باشد. اگر ورودی یک آرایه یا آرایه چند بعدی باشد، اندازه تانسور ورودی مرتبط به طور ضمنی به ابعاد آرایه در زمان استنتاج تغییر خواهد کرد. اگر ورودی یک ByteBuffer باشد، تماس‌گیرنده ابتدا باید اندازه تانسور ورودی مرتبط را (از طریق Interpreter.resizeInput() ) قبل از اجرای استنتاج تغییر دهد.

هنگام استفاده از ByteBuffer ، استفاده از بافرهای مستقیم بایت را ترجیح دهید، زیرا این کار به Interpreter اجازه می‌دهد از کپی‌های غیرضروری اجتناب کند. اگر ByteBuffer یک بافر مستقیم بایت باشد، ترتیب آن باید ByteOrder.nativeOrder() باشد. پس از استفاده از آن برای استنتاج مدل، باید تا زمانی که استنتاج مدل به پایان برسد، بدون تغییر باقی بماند.

خروجی ها

هر خروجی باید یک آرایه یا آرایه چند بعدی از انواع اولیه پشتیبانی شده یا یک ByteBuffer با اندازه مناسب باشد. توجه داشته باشید که برخی از مدل ها دارای خروجی پویا هستند که شکل تانسورهای خروجی می تواند بسته به ورودی متفاوت باشد. هیچ راه ساده ای برای مدیریت این مورد با API استنتاج جاوا وجود ندارد، اما برنامه های افزودنی برنامه ریزی شده این امکان را فراهم می کند.

یک مدل را در سوئیفت بارگیری و اجرا کنید

پلتفرم: iOS

Swift API در TensorFlowLiteSwift Pod از Cocoapods موجود است.

ابتدا باید ماژول 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...
}

یک مدل را در Objective-C بارگذاری و اجرا کنید

پلتفرم: iOS

Objective-C API در TensorFlowLiteObjC Pod از Cocoapods موجود است.

ابتدا باید ماژول 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... */ }

استفاده از C API در کد Objective-C

در حال حاضر Objective-C API از نمایندگان پشتیبانی نمی کند. برای استفاده از نمایندگان با کد Objective-C، باید مستقیماً C API زیربنایی را فراخوانی کنید.

#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++ بارگیری و اجرا کنید

پلتفرم ها: اندروید، iOS و لینوکس

در C++، مدل در کلاس FlatBufferModel ذخیره می شود. این یک مدل TensorFlow Lite را کپسوله می کند و بسته به جایی که مدل ذخیره می شود، می توانید آن را به چند روش مختلف بسازید:

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

اکنون که مدل را به عنوان یک شی FlatBufferModel دارید، می توانید آن را با یک Interpreter اجرا کنید. یک FlatBufferModel می تواند به طور همزمان توسط بیش از یک Interpreter استفاده شود.

بخش های مهم Interpreter API در قطعه کد زیر نشان داده شده است. لازم به ذکر است که:

  • تانسورها با اعداد صحیح نشان داده می شوند تا از مقایسه رشته ها (و هر گونه وابستگی ثابت به کتابخانه های رشته ای) جلوگیری شود.
  • یک مفسر نباید از رشته های همزمان قابل دسترسی باشد.
  • تخصیص حافظه برای تانسورهای ورودی و خروجی باید با فراخوانی AllocateTensors() درست پس از تغییر اندازه تانسورها فعال شود.

ساده ترین استفاده از TensorFlow Lite با C++ به صورت زیر است:

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

برای نمونه کد بیشتر، minimal.cc و label_image.cc را ببینید.

یک مدل را در پایتون بارگیری و اجرا کنید

پلتفرم: لینوکس

API پایتون برای اجرای استنتاج در ماژول tf.lite ارائه شده است. از آن، برای بارگذاری یک مدل و اجرای یک استنتاج، بیشتر به tf.lite.Interpreter نیاز دارید.

مثال زیر نحوه استفاده از مفسر پایتون برای بارگذاری فایل .tflite و اجرای استنتاج با داده های ورودی تصادفی را نشان می دهد:

اگر از SavedModel با SignatureDef تعریف شده تبدیل می کنید، این مثال توصیه می شود. از 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'])

مثال دیگر اگر مدل دارای 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)

به‌عنوان جایگزینی برای بارگیری مدل به‌عنوان یک فایل .tflite از پیش تبدیل شده، می‌توانید کد خود را با TensorFlow Lite Converter Python API ( tf.lite.TFLiteConverter ) ترکیب کنید، که به شما امکان می‌دهد مدل Keras خود را به قالب TensorFlow Lite تبدیل کنید و سپس اجرای استنتاج:

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

برای کد نمونه بیشتر پایتون، label_image.py را ببینید.

استنتاج را با مدل شکل پویا اجرا کنید

اگر می خواهید مدلی با شکل ورودی پویا اجرا کنید، قبل از اجرای استنتاج ، اندازه شکل ورودی را تغییر دهید . در غیر این صورت، شکل None در مدل های Tensorflow با یک مکان نگهدارنده 1 در مدل های TFLite جایگزین می شود.

مثال‌های زیر نحوه تغییر اندازه شکل ورودی را قبل از اجرای استنتاج در زبان‌های مختلف نشان می‌دهند. همه مثال ها فرض می کنند که شکل ورودی به صورت [1/None, 10] تعریف شده است و باید اندازه آن به [3, 10] تغییر داده شود.

مثال C++:

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

مثال پایتون:

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