Google I/O הוא עטיפה! התעדכן בהפעלות של TensorFlow. צפה בהפעלות

מסקנת TensorFlow Lite

המסקנה המונח מתייחס לתהליך של ביצוע מודל לייט TensorFlow על-מכשיר כדי לבצע תחזיות המבוססות על נתוני קלט. כדי לבצע היקש עם מודל לייט TensorFlow, עליך להפעיל אותו באמצעות מתורגמן. מתורגמן TensorFlow Lite נועד להיות רזה ומהיר. המתורגמן משתמש בסידור גרפים סטטי ובמקצה זיכרון מותאם אישית (פחות דינמי) כדי להבטיח עומס מינימלי, אתחול וחביון ביצוע.

דף זה מתאר כיצד גישה למתורגמן TensorFlow לייט ולבצע מסקנה באמצעות C ++, Java, Python, ועוד קישורים למקורות אחרים עבור כל פלטפורמה נתמכת .

מושגים חשובים

מסקנות TensorFlow Lite בדרך כלל פועלות לפי השלבים הבאים:

  1. טוען דגם

    עליך לטעון את .tflite המודל לתוך זיכרון, אשר מכיל את גרף הביצוע של המודל.

  2. שינוי נתונים

    נתוני קלט גולמיים עבור המודל בדרך כלל אינם תואמים את פורמט נתוני הקלט הצפוי על ידי המודל. לדוגמה, ייתכן שיהיה עליך לשנות את גודל התמונה או לשנות את פורמט התמונה כך שתתאים לדגם.

  3. מסקנות ריצה

    שלב זה כולל שימוש ב- TensorFlow Lite API לביצוע המודל. הוא כולל מספר שלבים כגון בניית המתורגמן והקצאת טנסורים, כמתואר בסעיפים הבאים.

  4. פרשנות פלט

    כאשר אתה מקבל תוצאות מהסקת המודל, עליך לפרש את הטנזורים בצורה משמעותית שימושית ביישום שלך.

    לדוגמה, מודל עשוי להחזיר רק רשימת הסתברויות. זה תלוי בך למפות את ההסתברויות לקטגוריות רלוונטיות ולהציג אותן בפני משתמשי הקצה שלך.

פלטפורמות נתמכות

APIs היקש TensorFlow מסופקים ביותר פלטפורמות ניידות / מוטבע נפוצות כגון אנדרואיד , iOS ו- Linux , בשפות תכנות רבות.

ברוב המקרים, עיצוב ה- API משקף העדפה לביצועים על פני קלות השימוש. TensorFlow Lite מיועד להסקת מסקנות מהירה במכשירים קטנים, ולכן אין להתפלא שממשקי ה- API מנסים להימנע מהעתקים מיותרים על חשבון הנוחות. באופן דומה, עקביות עם ממשקי ה- API של TensorFlow לא הייתה מטרה מפורשת ויש לצפות לשונות מסוימת בין השפות.

בכל הספריות, ממשק ה- TensorFlow Lite מאפשר לך לטעון מודלים, להאכיל תשומות ולאחזר פלט מסקנות.

פלטפורמת אנדרואיד

ב- Android, ניתן לבצע מסקנות TensorFlow Lite באמצעות ממשקי API של Java או C ++. ממשקי ה- Java של Java מספקים נוחות וניתן להשתמש בהם ישירות בתוך שיעורי הפעילות שלך ב- Android. ממשקי ה- C ++ מציעים גמישות ומהירות רבה יותר, אך עשויים לדרוש כתיבת עטיפות JNI כדי להעביר נתונים בין שכבות Java ו- C ++.

ראה להלן פרטים אודות שימוש C ++ ו- Java , או בצע את QuickStart אנדרואיד עבור קוד הדרכה ודוגמא.

מחולל קוד עוטף אנדרואיד של TensorFlow Lite

עבור דגם לייט TensorFlow משופר עם metadata , מפתחים יכולים להשתמש במחולל קוד מעטפת TensorFlow לייט אנדרואיד כדי ליצור קוד מעטפת פלטפורמה ספציפית. קוד המעטפה מסיר את הצורך ליצור קשר ישיר עם ByteBuffer על אנדרואיד. במקום זאת, מפתחים יכולים לקיים אינטראקציה עם מודל לייט TensorFlow עם חפצים מוקלדים כגון Bitmap ו Rect . לקבלת מידע נוסף, עיין מחולל קוד מעטפת TensorFlow לייט אנדרואיד .

פלטפורמת iOS

ב- iOS, לייט TensorFlow זמין עם ספריות iOS ילידי כתוב סוויפט ו Objective-C . אתה יכול גם להשתמש ב- API C ישירות קודי Objective-C.

ראה להלן פרטים אודות השימוש סוויפט , Objective-C ואת API C , או בצע את QuickStart iOS קוד הדרכה ודוגמא.

פלטפורמת לינוקס

בפלטפורמות לינוקס (כולל פטל Pi ), אתה יכול לרוץ מסקנות שימוש בממשקי API לייט TensorFlow זמין C ++ ו Python , כמתואר בסעיפים הבאים.

הפעלת דוגמנית

הפעלת דגם TensorFlow Lite כוללת כמה צעדים פשוטים:

  1. טען את הדגם בזיכרון.
  2. בנה Interpreter המבוססת על מודל קיים.
  3. הגדר ערכי טנסור קלט. (אופציונלי לשנות את גודל טנסורי הקלט אם הגדלים שהוגדרו מראש אינם רצויים.)
  4. לעורר מסקנות.
  5. קרא ערכי טנסור פלט.

הסעיפים הבאים מתארים כיצד ניתן לבצע שלבים אלה בכל שפה.

טען והפעל מודל ב- Java

פלטפורמה: אנדרואיד

ה- API של ג'אווה עבור מפעיל היקש עם לייט TensorFlow מיועד בעיקר לשימוש עם אנדרואיד, כך שהוא זמין כמו תלות הספרייה אנדרואיד: org.tensorflow:tensorflow-lite .

ב- Java, תוכל להשתמש Interpreter בכיתה לטעון היקש מודל מודל וכונן. במקרים רבים, זה עשוי להיות ה- API היחיד שאתה צריך.

ניתן לאתחל Interpreter באמצעות .tflite קובץ:

public Interpreter(@NotNull File modelFile);

או עם MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

בשני המקרים, אתה חייב לספק מודל TensorFlow לייט תקף או ה- 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 ממפה מדדי tensors פלט נתוני התפוקה המתאימה.

בשני המקרים, המדדים המותחים צריכים להתאים את הערכים שנתתם אל הממיר לייט TensorFlow כאשר יצרת את המודל. שים לב שסדר tensors ב input חייב להתאים את הסדר שניתן הממיר לייט TensorFlow.

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 סוגים נתמכים גם, אבל הם מקודדים באופן שונה מאשר סוגים פרימיטיביים. בפרט, צורת מחרוזת טנסור מכתיבה את מספר וסידור המיתרים בטנסור, כאשר כל אלמנט עצמו הוא מחרוזת באורך משתנה. במובן זה, בגודל (בייטים) של הטנזור לא ניתן לחשב את הצורה ואת סוג לבד, וכתוצאה מכך מחרוזות לא יכול להינתן כסינגל, שטוח ByteBuffer טיעון.

אם סוגי נתונים אחרים, כולל סוגים בארגזים כמו Integer ו Float , משמשים, גידול IllegalArgumentException ייזרקו.

תשומות

כול קלט צריך להיות מערך מערך או רב-ממדי של סוגים פרימיטיווי הנתמכים, או גלם ByteBuffer בגודל המתאים. אם הקלט הוא מערך או מערך רב-ממדי, מתח הקלט המשויך ישתנה באופן מרומז למידות המערך בזמן הסקה. אם הקלט הוא ByteBuffer, המתקשר צריך קודם ידני לשנות את גודל מותח הקלט הנלווה (דרך Interpreter.resizeInput() ) לפני הפעלת היקש.

בעת שימוש ByteBuffer , מעדיף באמצעות מאגרים בייט ישירים, מכיוון שהדבר מאפשר Interpreter להימנע עותקים מיותרים. אם ByteBuffer הוא חוצץ ישיר בייט, כדי שלה חייב להיות ByteOrder.nativeOrder() . לאחר השימוש בו להסקת מודל, עליו להישאר ללא שינוי עד להשלמת מסקנת המודל.

יציאות

כל פלט צריך להיות מערך או מערך רב ממדי של הסוגים הפרימיטיביים הנתמכים, או ByteBuffer בגודל המתאים. שים לב שלחלק מהדגמים יש יציאות דינאמיות, כאשר צורת טנסורי הפלט יכולה להשתנות בהתאם לקלט. אין דרך פשוטה להתמודד עם זה עם ממשק ה- API להסקת Java, אך הרחבות מתוכננות יאפשרו זאת.

טען והפעל דגם בסוויפט

פלטפורמה: iOS

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

API 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 API אינו תומך בנציגים. כדי נציגים לשימוש עם קוד Objective-C, אתה צריך להתקשר ישירות שבבסיס API 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 ו- Linux

ב ++ 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() מייד אחרי שינוי גודל tensors.

השימוש הפשוט ביותר ב- 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

פלטפורמה: לינוקס

ה- API של Python להפעלת מסקנה מסופק 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 הקובץ, אתה יכול לשלב את הקוד שלך עם API Python TensorFlow ממיר לייט ( tf.lite.TFLiteConverter ), המאפשר לך להמיר מודל TensorFlow לפורמט לייט TensorFlow ולאחר מכן להסיק מסקנות:

import numpy as np
import tensorflow as tf

img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
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
with tf.Session() as sess:
  converter = tf.lite.TFLiteConverter.from_session(sess, [img], [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 .

טיפ: הפעלת help(tf.lite.Interpreter) בטרמינל Python כדי לקבל תיעוד מפורט על המתורגמן.

פעולות נתמכות

TensorFlow Lite תומך בקבוצת משנה של פעולות TensorFlow עם כמה מגבלות. לקבלת רשימה מלאה של פעולות ומגבלות לראות בדף Ops לייט TF .