يعرّفك هذا الدليل على عملية تشغيل نموذج LiteRT (اختصارًا لـ Lite Runtime) على الجهاز لإجراء توقّعات استنادًا إلى بيانات الإدخال. هذا هو باستخدام مترجم LiteRT، والذي يستخدم ترتيب رسم بياني ثابت مخصص (أقل ديناميكية) للذاكرة لضمان الحد الأدنى من الحمل والتهيئة ووقت استجابة التنفيذ.
عادةً ما يتبع استنتاج LiteRT الخطوات التالية:
تحميل نموذج: يؤدي إلى تحميل نموذج "
.tflite
" في الذاكرة، الذي يحتوي على الرسم البياني لتنفيذ النموذج.تحويل البيانات: حوِّل بيانات الإدخال إلى التنسيق والقياسات المتوقّعة. لا تتطابق بيانات الإدخال الأوّلية للنموذج بشكل عام مع تنسيق data الإدخال المتوقّع من النموذج. على سبيل المثال، قد تحتاج إلى تغيير حجم صورة أو تغيير تنسيقها ليكون متوافقًا مع النموذج.
الاستنتاج الجاري: نفِّذ نموذج LiteRT لإجراء التوقّعات. هذا النمط استخدام واجهة برمجة تطبيقات LiteRT لتنفيذ النموذج. تتضمن بعض مثل بناء أداة الترجمة الفورية وتخصيص موتّرات.
تفسير النتائج: يمكنك تفسير مصفوفات النتائج بطريقة مفيدة ومفيدة في تطبيقك. على سبيل المثال، قد يعرض النموذج قائمة بالاحتمالات فقط. الأمر متروك لك لتعيين الاحتمالات إلى الفئات وتنسيق الإخراج.
يصف هذا الدليل كيفية الوصول إلى مترجم LiteRT وإجراء الاستنتاج باستخدام C++ وJava وPython.
المنصّات المعتمدة
تتوفر واجهات برمجة التطبيقات لميزة الاستنتاج في TensorFlow لمعظم منصّات التطوير المتوافقة مع الأجهزة الجوّالة والمضمّنة، مثل Android وiOS وLinux، وذلك بلغات برمجة متعدّدة.
في معظم الحالات، يعكس تصميم واجهة برمجة التطبيقات تفضيلًا للأداء على سهولة الاستخدام. تم تصميم LiteRT لإجراء الاستنتاجات بسرعة على الأجهزة الصغيرة، لذا تتجنّب واجهات برمجة التطبيقات النُسخ غير الضرورية على حساب الراحة.
وتتيح لك واجهة LiteRT API عبر جميع المكتبات تحميل النماذج وإدخالات الخلاصات واسترجاع مخرجات الاستنتاج.
النظام الأساسي Android
على نظام التشغيل Android، يمكن تنفيذ الاستنتاج LiteRT باستخدام واجهات برمجة تطبيقات Java أو C++. توفّر مكتبات برمجة تطبيقات Java سهولة الاستخدام ويمكن استخدامها مباشرةً في فئات Android Activity. توفّر واجهات برمجة التطبيقات C++ مرونة وسرعة أكبر، ولكن قد تتطلّب كتابة أدوات تغليف JNI لنقل البيانات بين طبقتَي Java وC++.
راجع قسمي C++ وJava للحصول على مزيد من المعلومات أو اتّبِع خطوات التشغيل السريع على Android.
نظام التشغيل iOS
على iOS، يتوفر LiteRT في Swift أو Objective-C مكتبات iOS. يمكنك أيضًا استخدام واجهة برمجة التطبيقات C مباشرةً في رمز Objective-C.
يمكنك الاطّلاع على واجهات Swift وObjective-C وC API. أو اتبع البدء السريع لنظام iOS.
نظام Linux الأساسي
على منصات Linux، يمكنك إجراء الاستنتاجات باستخدام واجهات برمجة تطبيقات LiteRT المتاحة في C++.
تحميل نموذج وتشغيله
يتضمن تحميل نموذج LiteRT وتشغيله الخطوات التالية:
- يتم تحميل النموذج إلى الذاكرة.
- يمكنك إنشاء
Interpreter
استنادًا إلى نموذج حالي. - ضبط قيم مصفوفة السواء للدخل
- الاستنتاجات الاستدعاء
- إخراج قيم مصفوفة متجاوبة
Android (Java)
تم تصميم واجهة برمجة تطبيقات Java لتشغيل الاستنتاجات باستخدام LiteRT في المقام الأول للاستخدام
في Android، لذلك يتوفّر كملحق بمكتبة Android:
com.google.ai.edge.litert
في Java، ستستخدم الفئة Interpreter
لتحميل نموذج ونموذج محرك أقراص.
الاستنتاج. في كثير من الحالات، قد تكون هذه هي واجهة برمجة التطبيقات الوحيدة التي تحتاج إليها.
يمكنك إعداد Interpreter
باستخدام ملف FlatBuffers (.tflite
):
public Interpreter(@NotNull File modelFile);
أو باستخدام MappedByteBuffer
:
public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);
في كلتا الحالتين، يجب توفير نموذج LiteRT صالح أو عمليات طرح واجهة برمجة التطبيقات
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
على ربط فهارس مصفوفات تهكمية للمخرجات ببيانات المخرجات المقابلة.
في كلتا الحالتين، يجب أن تتوافق فهارس الموتور مع القيم التي أعطيتها
محول LiteRT عند إنشاء النموذج. تنبيه
يجب أن يتطابق ترتيب موتّرات الدالة input
مع الترتيب المحدّد في LiteRT
محوّل.
وتوفر الفئة Interpreter
أيضًا الدوال المناسبة للحصول على
فهرس أي إدخال أو إخراج لنموذج باستخدام اسم عملية:
public int getInputIndex(String opName);
public int getOutputIndex(String opName);
إذا لم تكن opName
عملية صالحة في النموذج، فستطرح
IllegalArgumentException
يُرجى الانتباه أيضًا إلى أنّ Interpreter
يملك الموارد. لتجنُّب تسرُّب الذاكرة، يجب تحرير موارد المعالجة بعد استخدامها من خلال:
interpreter.close();
للاطّلاع على نموذج لمشروع باستخدام Java، يمكنك الاطّلاع على مثال على رصد الكائنات على Android. التطبيق.
أنواع البيانات المتوافقة
لاستخدام LiteRT، يجب أن تكون أنواع بيانات مصفوفات الخلاصات للدخل والإخراج أحد الأنواع الأساسية التالية:
float
int
long
byte
تتوفّر أيضًا أنواع String
، ولكن يتم ترميزها بشكل مختلف عن الترميز المستخدَم في
الأولية. على وجه الخصوص، يحدد شكل السلسلة Tensor عدد
وترتيب السلاسل في Tensor، بحيث يكون كل عنصر نفسه
سلسلة متغيرة الطول. بهذا المعنى، لا يمكن أن يكون حجم (بايت) من Tensor
حسابه من الشكل والنوع وحده، وبالتالي لا يمكن
المقدمة كوسيطة ByteBuffer
واحدة مسطحة.
في حال استخدام أنواع بيانات أخرى، بما في ذلك الأنواع المعبّأة مثل Integer
وFloat
،
سيتم طرح IllegalArgumentException
.
مدخلات
يجب أن يكون كل إدخال مصفوفة أو مصفوفة متعددة الأبعاد من الأنواع الأساسية المتوافقة، أو ByteBuffer
أولي بالحجم المناسب. إذا كان الإدخال هو
مصفوفة أو صفيف متعدد الأبعاد، سيتم
تغيير حجم مصفوفة الإدخال المرتبطة تلقائيًا لتتطابق مع أبعاد المصفوفة في وقت الاستنتاج. إذا كان المدخل
وحدة ByteBuffer، يجب أولاً أن يغير المتصل حجم الإدخال المرتبط يدويًا
تنسور (عبر Interpreter.resizeInput()
) قبل تنفيذ الاستنتاج.
عند استخدام ByteBuffer
، يُفضَّل استخدام وحدات تخزين ثنائية مباشرة، لأنّ ذلك يسمح لمحاولة
Interpreter
بتجنُّب النُسخ غير الضرورية. إذا كانت ByteBuffer
وحدة بايت مباشرة
مخزن مؤقت، يجب أن يكون الترتيب ByteOrder.nativeOrder()
. بعد استخدامها في عملية
استنتاج النموذج، يجب أن تظلّ بدون تغيير إلى أن تنتهي عملية استنتاج النموذج.
النتائج
يجب أن تكون كل ناتج صفيفًا أو مصفوفة متعددة الأبعاد من جدول البيانات أو ByteBuffer بالحجم المناسب. تجدر الإشارة إلى أنّ بعض النماذج لها نتائج ديناميكية، حيث يمكن أن يختلف شكل مصفوفات الإخراج استنادًا إلى المدخلات. لا توجد طريقة مباشرة للتعامل مع هذا الأمر مع واجهة برمجة تطبيقات استنتاج Java، لكنّ الإضافات المُخطَّط لها ستجعل هذا ممكنًا.
iOS (Swift)
نظام سويفت
واجهة برمجة التطبيقات
يتوفّر في 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...
}
iOS (Objective-C)
يتوفّر Objective-C
API
في LiteRTObjC
Pod من Cocoapods.
أولاً، عليك استيراد وحدة TensorFlowLiteObjC
.
@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 في رمز Objective-C
لا تتيح واجهة برمجة التطبيقات Objective-C API استخدام المفوَّضين. من أجل استخدام المفوَّضين مع ويجب استدعاء الرمز 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++
تتوافق واجهة برمجة التطبيقات C++ لتشغيل الاستنتاج باستخدام LiteRT مع أنظمة Android وiOS وLinux الأساسية. لا تتوفّر واجهة برمجة التطبيقات C++ على نظام التشغيل iOS إلا عند استخدام bazel.
في C++، يتم تخزين النموذج في فئة
FlatBufferModel
.
ويُحاط نموذج LiteRT بإطار، ويمكنك إنشاؤه بطريقتين مختلفتين
حسب مكان تخزين النموذج:
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
معروضة في مقتطف الرمز
أدناه. تجدر الإشارة إلى ما يلي:
- يتم تمثيل مصفوفات السلاسل بأرقام صحيحة، وذلك لتجنُّب مقارنات السلاسل (وأيّ اعتماد ثابت على مكتبات السلاسل).
- يجب عدم الوصول إلى مترجم من سلاسل محادثات متزامنة.
- يجب بدء تخصيص الذاكرة لمصفّفات الإدخال والإخراج من خلال استدعاء
AllocateTensors()
مباشرةً بعد تغيير حجم المصفوفات.
إليك أبسط استخدام لـ LiteRT مع 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 needed.
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
تستخدم واجهة Python API لتنفيذ الاستنتاجات
Interpreter
لتحميل نموذج
إجراء الاستنتاجات.
ثبِّت حزمة LiteRT:
$ python3 -m pip install ai-edge-litert
استيراد مترجم LiteRT
from ai_edge_litert.interpreter import Interpreter
Interpreter = Interpreter(model_path=args.model.file)
يوضّح المثال التالي كيفية استخدام مترجم Python لتحميل ملف
FlatBuffers (.tflite
) وتنفيذ الاستنتاج باستخدام بيانات إدخال عشوائية:
ويُنصح باستخدام هذا المثال إذا كنت بصدد التحويل من SaveModel مع نموذج SignatureDef.
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'])
مثال آخر إذا لم يتم تحديد SignatureDefs
في التصميم
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)
بدلاً من تحميل النموذج كملف .tflite
تم إجراء إحالة ناجحة له مسبقًا، يمكنك:
يمكنك دمج التعليمات البرمجية الخاصة بك مع الإصدار LiteRT
جامع المحتوى
، ما يسمح لك بتحويل نموذج Keras إلى تنسيق LiteRT ثم تشغيله
الاستنتاج:
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...
لمزيد من نماذج التعليمات البرمجية للغة بايثون، راجع
label_image.py
تنفيذ الاستنتاج باستخدام نموذج الشكل الديناميكي
إذا أردت تشغيل نموذج يحتوي على شكل إدخال ديناميكي، يمكنك تغيير حجم شكل الإدخال.
قبل إجراء الاستنتاج. بخلاف ذلك، سيتم استبدال شكل None
في نماذج Tensorflow
بعنصر نائب 1
في نماذج LiteRT.
توضِّح الأمثلة التالية كيفية تغيير حجم شكل الإدخال قبل تنفيذ عملية المعالجة المتعلّقة بالاستنتاج بلغات مختلفة. تفترض جميع الأمثلة أن شكل الإدخال
وهي عبارة عن [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 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()