استنتاج TensorFlow لايت

يشير مصطلح الاستدلال إلى عملية تنفيذ نموذج TensorFlow Lite على الجهاز من أجل عمل تنبؤات بناءً على بيانات الإدخال. لإجراء الاستدلال باستخدام نموذج TensorFlow Lite، يجب عليك تشغيله من خلال مترجم . تم تصميم مترجم TensorFlow Lite ليكون بسيطًا وسريعًا. يستخدم المترجم ترتيب رسم بياني ثابت ومخصص ذاكرة مخصص (أقل ديناميكية) لضمان الحد الأدنى من التحميل والتهيئة وزمن الوصول للتنفيذ.

تصف هذه الصفحة كيفية الوصول إلى مترجم TensorFlow Lite وإجراء الاستدلال باستخدام C++ وJava وPython، بالإضافة إلى روابط إلى موارد أخرى لكل نظام أساسي مدعوم .

مفاهيم مهمة

عادةً ما يتبع استنتاج TensorFlow Lite الخطوات التالية:

  1. تحميل نموذج

    يجب عليك تحميل نموذج .tflite في الذاكرة، التي تحتوي على الرسم البياني لتنفيذ النموذج.

  2. تحويل البيانات

    بشكل عام، لا تتطابق بيانات الإدخال الأولية للنموذج مع تنسيق بيانات الإدخال المتوقع بواسطة النموذج. على سبيل المثال، قد تحتاج إلى تغيير حجم الصورة أو تغيير تنسيق الصورة لتكون متوافقة مع النموذج.

  3. تشغيل الاستدلال

    تتضمن هذه الخطوة استخدام TensorFlow Lite API لتنفيذ النموذج. يتضمن ذلك بضع خطوات مثل بناء المترجم وتخصيص الموترات، كما هو موضح في الأقسام التالية.

  4. تفسير الإخراج

    عندما تتلقى نتائج من استنتاج النموذج، يجب عليك تفسير الموترات بطريقة مفيدة في تطبيقك.

    على سبيل المثال، قد يُرجع النموذج قائمة الاحتمالات فقط. الأمر متروك لك لتخطيط الاحتمالات للفئات ذات الصلة وتقديمها للمستخدم النهائي.

المنصات المدعومة

يتم توفير واجهات برمجة تطبيقات الاستدلال TensorFlow لمعظم الأنظمة الأساسية المحمولة/المضمنة الشائعة مثل Android و iOS و Linux ، بلغات برمجة متعددة.

في معظم الحالات، يعكس تصميم واجهة برمجة التطبيقات (API) تفضيل الأداء على سهولة الاستخدام. تم تصميم TensorFlow Lite للاستدلال السريع على الأجهزة الصغيرة، لذلك ليس من المستغرب أن تحاول واجهات برمجة التطبيقات تجنب النسخ غير الضرورية على حساب الراحة. وبالمثل، لم يكن الاتساق مع واجهات برمجة تطبيقات TensorFlow هدفًا واضحًا ومن المتوقع حدوث بعض التباين بين اللغات.

عبر جميع المكتبات، تمكنك واجهة برمجة تطبيقات TensorFlow Lite من تحميل النماذج ومدخلات التغذية واسترداد مخرجات الاستدلال.

منصة أندرويد

على Android، يمكن إجراء الاستدلال TensorFlow Lite باستخدام Java أو C++ APIs. توفر واجهات برمجة تطبيقات Java الراحة ويمكن استخدامها مباشرة ضمن فئات نشاط Android لديك. توفر واجهات برمجة تطبيقات C++ مزيدًا من المرونة والسرعة، ولكنها قد تتطلب كتابة أغلفة JNI لنقل البيانات بين طبقات Java وC++.

انظر أدناه للحصول على تفاصيل حول استخدام C++ و Java ، أو اتبع البدء السريع لنظام Android للحصول على برنامج تعليمي ورمز نموذجي.

TensorFlow Lite مولد كود غلاف Android

بالنسبة لنموذج TensorFlow Lite المعزز بالبيانات الوصفية ، يمكن للمطورين استخدام منشئ كود غلاف TensorFlow Lite Android لإنشاء كود غلاف خاص بالنظام الأساسي. يزيل رمز المجمع الحاجة إلى التفاعل مباشرة مع ByteBuffer على Android. وبدلاً من ذلك، يمكن للمطورين التفاعل مع نموذج TensorFlow Lite باستخدام الكائنات المكتوبة مثل Bitmap و Rect . لمزيد من المعلومات، يرجى الرجوع إلى منشئ أكواد غلاف Android TensorFlow Lite .

منصة آي أو إس

على نظام التشغيل iOS، يتوفر TensorFlow Lite مع مكتبات iOS الأصلية المكتوبة بلغة Swift و Objective-C . يمكنك أيضًا استخدام C API مباشرةً في أكواد Objective-C.

انظر أدناه للحصول على تفاصيل حول استخدام Swift و Objective-C و C API ، أو اتبع التشغيل السريع لنظام التشغيل iOS للحصول على برنامج تعليمي ورمز نموذجي.

منصة لينكس

على منصات Linux (بما في ذلك Raspberry Pi )، يمكنك تشغيل الاستدلالات باستخدام واجهات برمجة تطبيقات TensorFlow Lite المتوفرة في C++ و Python ، كما هو موضح في الأقسام التالية.

تشغيل نموذج

يتضمن تشغيل نموذج TensorFlow Lite بضع خطوات بسيطة:

  1. تحميل النموذج في الذاكرة.
  2. قم ببناء Interpreter بناءً على نموذج موجود.
  3. تعيين قيم موتر الإدخال. (يمكنك تغيير حجم موترات الإدخال بشكل اختياري إذا كانت الأحجام المحددة مسبقًا غير مرغوب فيها.)
  4. استدعاء الاستدلال.
  5. قراءة قيم موتر الإخراج.

تصف الأقسام التالية كيفية تنفيذ هذه الخطوات بكل لغة.

تحميل وتشغيل نموذج في جافا

المنصة: أندرويد

تم تصميم Java API لتشغيل الاستدلال باستخدام TensorFlow Lite في المقام الأول للاستخدام مع Android، لذا فهو متاح كاعتماد على مكتبة Android: org.tensorflow:tensorflow-lite .

في Java، ستستخدم فئة Interpreter لتحميل نموذج واستدلال النموذج. في كثير من الحالات، قد تكون هذه هي واجهة برمجة التطبيقات الوحيدة التي تحتاجها.

يمكنك تهيئة Interpreter باستخدام ملف .tflite :

public Interpreter(@NotNull File modelFile);

أو باستخدام MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

في كلتا الحالتين، يجب عليك تقديم نموذج TensorFlow Lite صالح أو تقوم واجهة برمجة التطبيقات بطرح 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();

للحصول على مثال لمشروع باستخدام Java، راجع نموذج تصنيف صور Android .

أنواع البيانات المدعومة (في Java)

لاستخدام TensorFlow Lite، يجب أن تكون أنواع البيانات الخاصة بموترات الإدخال والإخراج أحد الأنواع البدائية التالية:

  • float
  • int
  • long
  • byte

أنواع String مدعومة أيضًا، لكن يتم ترميزها بشكل مختلف عن الأنواع البدائية. على وجه الخصوص، يحدد شكل سلسلة Tensor عدد السلاسل وترتيبها في Tensor، حيث يكون كل عنصر بحد ذاته سلسلة متغيرة الطول. وبهذا المعنى، لا يمكن حساب حجم (البايت) للموتر من الشكل والنوع فقط، وبالتالي لا يمكن توفير السلاسل كوسيطة ByteBuffer مسطحة واحدة. يمكنك رؤية بعض الأمثلة في هذه الصفحة .

إذا تم استخدام أنواع بيانات أخرى، بما في ذلك الأنواع المعبأة مثل Integer و Float ، فسيتم طرح IllegalArgumentException .

المدخلات

يجب أن يكون كل إدخال عبارة عن مصفوفة أو مصفوفة متعددة الأبعاد من الأنواع الأولية المدعومة، أو ByteBuffer خام بالحجم المناسب. إذا كان الإدخال عبارة عن مصفوفة أو مصفوفة متعددة الأبعاد، فسيتم تغيير حجم موتر الإدخال المرتبط ضمنيًا إلى أبعاد المصفوفة في وقت الاستدلال. إذا كان الإدخال ByteBuffer، فيجب على المتصل أولاً تغيير حجم موتر الإدخال المرتبط يدويًا (عبر Interpreter.resizeInput() ) قبل تشغيل الاستدلال.

عند استخدام ByteBuffer ، يفضل استخدام مخازن البايت المباشرة، حيث يسمح ذلك Interpreter بتجنب النسخ غير الضرورية. إذا كان ByteBuffer عبارة عن مخزن مؤقت مباشر للبايت، فيجب أن يكون ترتيبه ByteOrder.nativeOrder() . بعد استخدامه لاستدلال النموذج، يجب أن يظل دون تغيير حتى يتم الانتهاء من استدلال النموذج.

النواتج

يجب أن يكون كل مخرج عبارة عن مصفوفة أو مصفوفة متعددة الأبعاد من الأنواع الأولية المدعومة، أو ByteBuffer بالحجم المناسب. لاحظ أن بعض النماذج لها مخرجات ديناميكية، حيث يمكن أن يختلف شكل موترات المخرجات اعتمادًا على الإدخال. لا توجد طريقة مباشرة للتعامل مع هذا الأمر باستخدام واجهة برمجة تطبيقات Java الاستدلالية الحالية، ولكن الإضافات المخطط لها ستجعل ذلك ممكنًا.

قم بتحميل النموذج وتشغيله في Swift

المنصة: 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 في 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 المفوضين. من أجل استخدام المفوضين مع رمز Objective-C، تحتاج إلى الاتصال مباشرة بواجهة برمجة تطبيقات C الأساسية.

#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 في مقتطف الشفرة أدناه. تجدر الإشارة إلى أن:

  • يتم تمثيل Tensors بأعداد صحيحة، وذلك لتجنب مقارنات السلسلة (وأي تبعية ثابتة على مكتبات السلسلة).
  • لا يجب الوصول إلى المترجم من سلاسل الرسائل المتزامنة.
  • يجب تشغيل تخصيص الذاكرة لموترات الإدخال والإخراج عن طريق استدعاء 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 .

تحميل وتشغيل نموذج في بايثون

المنصة: لينكس

يتم توفير واجهة برمجة تطبيقات Python لتشغيل الاستدلال في وحدة tf.lite . ومن هنا، تحتاج في الغالب إلى tf.lite.Interpreter فقط لتحميل نموذج وتشغيل الاستدلال.

يوضح المثال التالي كيفية استخدام مترجم Python لتحميل ملف .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...

لمزيد من نماذج التعليمات البرمجية لـ Python، راجع 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()