يعرّفك هذا الدليل على عملية تشغيل نموذج LiteRT على الجهاز فقط وإجراء التنبؤات استنادًا إلى البيانات المدخلة. يمكن تحقيق ذلك باستخدام LiteRT برنامج الترجمة الفورية، الذي يستخدم ترتيب الرسم البياني الثابت ومخطط الترجمة المخصص (أقل ديناميكية) مخصص للذاكرة لضمان الحد الأدنى من وقت التحميل والتهيئة والتنفيذ.
عادةً ما يتبع استنتاج LiteRT الخطوات التالية:
تحميل نموذج: يؤدي إلى تحميل نموذج "
.tflite
" في الذاكرة، الذي يحتوي على الرسم البياني لتنفيذ النموذج.تحويل البيانات: تحويل بيانات الإدخال إلى التنسيق المتوقع الأبعاد. لا تتطابق البيانات الأولية للنموذج بشكل عام مع الإدخال تنسيق البيانات المتوقع من قبل النموذج. على سبيل المثال، قد تحتاج إلى تغيير حجم الصورة أو تغيير تنسيق الصورة لتتوافق مع النموذج.
الاستنتاج الجاري: نفِّذ نموذج LiteRT لإجراء التوقّعات. هذا النمط استخدام واجهة برمجة تطبيقات LiteRT لتنفيذ النموذج. تتضمن بعض مثل بناء أداة الترجمة الفورية وتخصيص موتّرات.
تفسير الناتج: تفسير متوترات المخرجات بطريقة مجدية يكون مفيدًا في تطبيقك. فعلى سبيل المثال، قد يُرجع أحد النماذج قائمة الاحتمالات. الأمر متروك لك لتعيين الاحتمالات إلى الفئات وتنسيق الإخراج.
يصف هذا الدليل كيفية الوصول إلى مترجم LiteRT وإجراء الاستنتاج باستخدام C++ وJava وPython.
المنصّات المعتمدة
يتم توفير واجهات برمجة التطبيقات للاستنتاج TensorFlow للتطبيقات الأكثر شيوعًا على الأجهزة الجوّالة والمضمنة. أنظمة أساسية مثل Android، وiOS، وLinux، في لغات برمجة متعددة.
وفي معظم الحالات، يعكس تصميم واجهة برمجة التطبيقات تفضيلاً للأداء عن سهولة استخدامها. وقد تم تصميم LiteRT للاستنتاج السريع على الأجهزة الصغيرة، وبالتالي تتجنب واجهات برمجة التطبيقات النسخ غير الضرورية على حساب الراحة.
وتتيح لك واجهة LiteRT API عبر جميع المكتبات تحميل النماذج وإدخالات الخلاصات واسترجاع مخرجات الاستنتاج.
النظام الأساسي Android
على نظام التشغيل Android، يمكن تنفيذ الاستنتاج LiteRT باستخدام واجهات برمجة تطبيقات Java أو C++. تشير رسالة الأشكال البيانية توفّر واجهات برمجة تطبيقات Java سهولة الاستخدام ويمكن استخدامها مباشرةً في أجهزة Android صفوف الأنشطة. واجهات برمجة تطبيقات C++ توفر المزيد من المرونة والسرعة، ولكنها قد تتطلب كتابة برامج تضمين JNI لنقل البيانات بين طبقات Java وC++.
راجع قسمي C++ وJava للحصول على مزيد من المعلومات أو اتّبِع خطوات التشغيل السريع على Android.
النظام الأساسي iOS
على iOS، يتوفر LiteRT في Swift أو الهدف-ج مكتبات iOS. يمكنك أيضًا استخدام C واجهة برمجة التطبيقات مباشرةً في رمز Objective-C.
يمكنك الاطّلاع على واجهات Swift وObjective-C وC API. أو اتبع البدء السريع لنظام iOS.
نظام Linux الأساسي
على أنظمة التشغيل Linux، يمكنك تنفيذ الاستنتاجات باستخدام واجهات برمجة التطبيقات LiteRT API المتاحة في 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 (الهدف-ج)
يحدد الهدف-ج
واجهة برمجة التطبيقات
يتوفّر في 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 API في رمز 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)
يوضح المثال التالي كيفية استخدام مترجم بايثون لتحميل
ملف 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()