Tham dự Hội nghị chuyên đề Women in ML vào ngày 7 tháng 12 Đăng ký ngay

Suy luận của TensorFlow Lite

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Thuật ngữ suy luận đề cập đến quá trình thực thi mô hình TensorFlow Lite trên thiết bị để đưa ra dự đoán dựa trên dữ liệu đầu vào. Để thực hiện một suy luận với mô hình TensorFlow Lite, bạn phải chạy nó thông qua một trình thông dịch . Trình thông dịch TensorFlow Lite được thiết kế gọn nhẹ và nhanh chóng. Trình thông dịch sử dụng thứ tự đồ thị tĩnh và trình cấp phát bộ nhớ tùy chỉnh (ít động hơn) để đảm bảo độ trễ tải, khởi tạo và thực thi tối thiểu.

Trang này mô tả cách truy cập vào trình thông dịch TensorFlow Lite và thực hiện suy luận bằng C ++, Java và Python, cùng với các liên kết đến các tài nguyên khác cho mỗi nền tảng được hỗ trợ .

Các khái niệm quan trọng

Suy luận của TensorFlow Lite thường tuân theo các bước sau:

  1. Đang tải một mô hình

    Bạn phải tải mô hình .tflite vào bộ nhớ, nơi chứa đồ thị thực thi của mô hình.

  2. Chuyển đổi dữ liệu

    Dữ liệu đầu vào thô cho mô hình thường không khớp với định dạng dữ liệu đầu vào mà mô hình mong đợi. Ví dụ: bạn có thể cần thay đổi kích thước hình ảnh hoặc thay đổi định dạng hình ảnh để tương thích với kiểu máy.

  3. Chạy suy luận

    Bước này liên quan đến việc sử dụng API TensorFlow Lite để thực thi mô hình. Nó bao gồm một số bước như xây dựng trình thông dịch và phân bổ các bộ căng, như được mô tả trong các phần sau.

  4. Phiên dịch đầu ra

    Khi bạn nhận được kết quả từ suy luận mô hình, bạn phải giải thích các tensors theo cách có ý nghĩa và hữu ích trong ứng dụng của bạn.

    Ví dụ, một mô hình có thể chỉ trả về một danh sách các xác suất. Bạn tùy thuộc vào việc lập bản đồ xác suất cho các danh mục có liên quan và trình bày nó cho người dùng cuối của bạn.

Nền tảng được hỗ trợ

Các API suy luận TensorFlow được cung cấp cho hầu hết các nền tảng di động / nhúng phổ biến như Android , iOSLinux , bằng nhiều ngôn ngữ lập trình.

Trong hầu hết các trường hợp, thiết kế API phản ánh sở thích về hiệu suất hơn là tính dễ sử dụng. TensorFlow Lite được thiết kế để suy luận nhanh trên các thiết bị nhỏ, vì vậy không có gì ngạc nhiên khi các API cố gắng tránh các bản sao không cần thiết với chi phí thuận tiện. Tương tự, tính nhất quán với các API TensorFlow không phải là một mục tiêu rõ ràng và dự kiến ​​sẽ có một số khác biệt giữa các ngôn ngữ.

Trên tất cả các thư viện, API TensorFlow Lite cho phép bạn tải mô hình, đầu vào nguồn cấp dữ liệu và truy xuất đầu ra suy luận.

Nền tảng Android

Trên Android, suy luận TensorFlow Lite có thể được thực hiện bằng cách sử dụng API Java hoặc C ++. Các API Java mang lại sự tiện lợi và có thể được sử dụng trực tiếp trong các lớp Hoạt động Android của bạn. Các API C ++ cung cấp tính linh hoạt và tốc độ cao hơn, nhưng có thể yêu cầu viết trình bao bọc JNI để di chuyển dữ liệu giữa các lớp Java và C ++.

Xem bên dưới để biết chi tiết về cách sử dụng C ++Java hoặc làm theo phần bắt đầu nhanh của Android để biết hướng dẫn và mã ví dụ.

Trình tạo mã trình bao bọc Android TensorFlow Lite

Đối với mô hình TensorFlow Lite được tăng cường siêu dữ liệu , các nhà phát triển có thể sử dụng trình tạo mã trình bao bọc TensorFlow Lite Android để tạo mã trình bao bọc dành riêng cho nền tảng. Mã trình bao bọc loại bỏ nhu cầu tương tác trực tiếp với ByteBuffer trên Android. Thay vào đó, các nhà phát triển có thể tương tác với mô hình TensorFlow Lite với các đối tượng được nhập như BitmapRect . Để biết thêm thông tin, vui lòng tham khảo trình tạo mã trình bao bọc Android TensorFlow Lite .

Nền tảng iOS

Trên iOS, TensorFlow Lite có sẵn với các thư viện iOS gốc được viết bằng SwiftObjective-C . Bạn cũng có thể sử dụng C API trực tiếp trong mã Objective-C.

Xem bên dưới để biết chi tiết về cách sử dụng Swift , Objective-CAPI C hoặc làm theo phần bắt đầu nhanh trên iOS để biết hướng dẫn và mã ví dụ.

Nền tảng Linux

Trên nền tảng Linux (bao gồm Raspberry Pi ), bạn có thể chạy các suy luận bằng cách sử dụng các API TensorFlow Lite có sẵn trong C ++Python , như được hiển thị trong các phần sau.

Chạy một mô hình

Chạy mô hình TensorFlow Lite bao gồm một vài bước đơn giản:

  1. Tải mô hình vào bộ nhớ.
  2. Xây dựng Interpreter dựa trên mô hình hiện có.
  3. Đặt giá trị tensor đầu vào. (Tùy chọn thay đổi kích thước bộ căng đầu vào nếu không muốn kích thước xác định trước.)
  4. Gọi ra suy luận.
  5. Đọc giá trị tensor đầu ra.

Các phần sau đây mô tả cách thực hiện các bước này trong mỗi ngôn ngữ.

Tải và chạy một mô hình trong Java

Nền tảng: Android

API Java để chạy suy luận với TensorFlow Lite chủ yếu được thiết kế để sử dụng với Android, vì vậy nó có sẵn dưới dạng phụ thuộc thư viện Android: org.tensorflow:tensorflow-lite .

Trong Java, bạn sẽ sử dụng lớp Interpreter để tải mô hình và suy luận mô hình ổ đĩa. Trong nhiều trường hợp, đây có thể là API duy nhất bạn cần.

Bạn có thể khởi tạo Interpreter bằng tệp .tflite :

public Interpreter(@NotNull File modelFile);

Hoặc với MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

Trong cả hai trường hợp, bạn phải cung cấp mô hình TensorFlow Lite hợp lệ hoặc API ném IllegalArgumentException . Nếu bạn sử dụng MappedByteBuffer để khởi tạo Interpreter , nó phải không thay đổi trong suốt thời gian tồn tại của Interpreter .

Cách ưa thích để chạy suy luận trên một mô hình là sử dụng chữ ký - Có sẵn cho các mô hình được chuyển đổi bắt đầu từ 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");
}

Phương thức runSignature có ba đối số:

  • Đầu vào : ánh xạ cho các đầu vào từ tên đầu vào trong chữ ký đến một đối tượng đầu vào.

  • Đầu ra : bản đồ để ánh xạ đầu ra từ tên đầu ra trong chữ ký đến dữ liệu đầu ra.

  • Signature Name [tùy chọn]: Tên chữ ký (Có thể để trống nếu kiểu máy có một chữ ký).

Một cách khác để chạy suy luận khi mô hình không có chữ ký xác định. Đơn giản chỉ cần gọi Interpreter.run() . Ví dụ:

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

Phương thức run() chỉ nhận một đầu vào và chỉ trả về một đầu ra. Vì vậy, nếu mô hình của bạn có nhiều đầu vào hoặc nhiều đầu ra, thay vào đó hãy sử dụng:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

Trong trường hợp này, mỗi mục nhập trong các inputs tương ứng với một tensor đầu vào và map_of_indices_to_outputs ánh xạ các chỉ số của tensor đầu ra với dữ liệu đầu ra tương ứng.

Trong cả hai trường hợp, chỉ số tensor phải tương ứng với các giá trị bạn đã cung cấp cho Công cụ chuyển đổi TensorFlow Lite khi bạn tạo mô hình. Lưu ý rằng thứ tự của tensor input phải khớp với thứ tự được cung cấp cho TensorFlow Lite Converter.

Lớp Interpreter cũng cung cấp các chức năng thuận tiện để bạn lấy chỉ mục của bất kỳ đầu vào hoặc đầu ra của mô hình nào bằng cách sử dụng tên hoạt động:

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

Nếu opName không phải là một hoạt động hợp lệ trong mô hình, nó sẽ ném ra một IllegalArgumentException .

Cũng cần lưu ý rằng Interpreter sở hữu tài nguyên. Để tránh rò rỉ bộ nhớ, tài nguyên phải được giải phóng sau khi sử dụng bằng cách:

interpreter.close();

Đối với một dự án ví dụ với Java, hãy xem mẫu phân loại hình ảnh Android .

Các kiểu dữ liệu được hỗ trợ (trong Java)

Để sử dụng TensorFlow Lite, kiểu dữ liệu của các tenxơ đầu vào và đầu ra phải là một trong các kiểu nguyên thủy sau:

  • float
  • int
  • long
  • byte

Các kiểu String cũng được hỗ trợ, nhưng chúng được mã hóa khác với kiểu nguyên thủy. Đặc biệt, hình dạng của một chuỗi Tensor quy định số lượng và sự sắp xếp của các chuỗi trong Tensor, với bản thân mỗi phần tử là một chuỗi có độ dài thay đổi. Theo nghĩa này, kích thước (byte) của Tensor không thể được tính toán chỉ từ hình dạng và kiểu, và do đó, các chuỗi không thể được cung cấp dưới dạng một đối số ByteBuffer đơn, phẳng.

Nếu các kiểu dữ liệu khác, bao gồm các kiểu đóng hộp như IntegerFloat , được sử dụng, thì một IllegalArgumentException sẽ được ném ra.

Đầu vào

Mỗi đầu vào phải là một mảng hoặc mảng đa chiều của các kiểu nguyên thủy được hỗ trợ hoặc một ByteBuffer thô có kích thước thích hợp. Nếu đầu vào là một mảng hoặc mảng nhiều chiều, thì tenxơ đầu vào được liên kết sẽ được thay đổi kích thước hoàn toàn thành kích thước của mảng tại thời điểm suy luận. Nếu đầu vào là ByteBuffer, trước tiên người gọi phải thay đổi kích thước của tensor đầu vào được liên kết theo cách thủ công (thông qua Interpreter.resizeInput() ) trước khi chạy suy luận.

Khi sử dụng ByteBuffer , hãy ưu tiên sử dụng bộ đệm byte trực tiếp, vì điều này cho phép Interpreter tránh các bản sao không cần thiết. Nếu ByteBuffer là bộ đệm byte trực tiếp, thứ tự của nó phải là ByteOrder.nativeOrder() . Sau khi nó được sử dụng cho một suy luận mô hình, nó phải không thay đổi cho đến khi kết thúc suy luận mô hình.

Kết quả đầu ra

Mỗi đầu ra phải là một mảng hoặc mảng đa chiều của các kiểu nguyên thủy được hỗ trợ hoặc một ByteBuffer có kích thước thích hợp. Lưu ý rằng một số mô hình có đầu ra động, trong đó hình dạng của bộ căng đầu ra có thể khác nhau tùy thuộc vào đầu vào. Không có cách nào đơn giản để xử lý vấn đề này với API suy luận Java hiện có, nhưng các phần mở rộng được lên kế hoạch sẽ giúp điều này trở nên khả thi.

Tải và chạy một mô hình trong Swift

Nền tảng: iOS

API Swift có sẵn trong TensorFlowLiteSwift Pod từ Cocoapods.

Đầu tiên, bạn cần nhập mô-đun 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...
}

Tải và chạy một mô hình trong Objective-C

Nền tảng: iOS

API Objective-C có sẵn trong TensorFlowLiteObjC Pod từ Cocoapods.

Đầu tiên, bạn cần nhập mô-đun 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... */ }

Sử dụng API C trong mã Objective-C

Hiện tại Objective-C API không hỗ trợ các đại biểu. Để sử dụng đại biểu với mã Objective-C, bạn cần gọi trực tiếp API C bên dưới.

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

Tải và chạy một mô hình trong C ++

Nền tảng: Android, iOS và Linux

Trong C ++, mô hình được lưu trữ trong lớp FlatBufferModel . Nó đóng gói một mô hình TensorFlow Lite và bạn có thể xây dựng nó theo một vài cách khác nhau, tùy thuộc vào nơi mô hình được lưu trữ:

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

Bây giờ bạn đã có mô hình dưới dạng đối tượng FlatBufferModel , bạn có thể thực thi nó với một Interpreter . Một FlatBufferModel duy nhất có thể được sử dụng đồng thời bởi nhiều hơn một Interpreter .

Các phần quan trọng của API Interpreter được hiển thị trong đoạn mã bên dưới. Cần lưu ý rằng:

  • Hàng chục được biểu diễn bằng số nguyên, để tránh so sánh chuỗi (và bất kỳ sự phụ thuộc cố định nào vào thư viện chuỗi).
  • Một trình thông dịch không được truy cập từ các luồng đồng thời.
  • Việc phân bổ bộ nhớ cho các tensor đầu vào và đầu ra phải được kích hoạt bằng cách gọi AllocateTensors() ngay sau khi thay đổi kích thước tensors.

Cách sử dụng đơn giản nhất của TensorFlow Lite với C ++ trông như sau:

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

Để biết thêm mã mẫu, hãy xem minimal.cclabel_image.cc .

Tải và chạy một mô hình bằng Python

Nền tảng: Linux

API Python để chạy một suy luận được cung cấp trong mô-đun tf.lite . Từ đó, bạn chủ yếu chỉ cần tf.lite.Interpreter để tải một mô hình và chạy một suy luận.

Ví dụ sau cho thấy cách sử dụng trình thông dịch Python để tải tệp .tflite và chạy suy luận với dữ liệu đầu vào ngẫu nhiên:

Ví dụ này được khuyến nghị nếu bạn đang chuyển đổi từ SavedModel với một SignatureDef đã xác định. Có sẵn bắt đầu từ 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'])

Một ví dụ khác nếu mô hình không có SignatureDefs được xác định.

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)

Để thay thế cho việc tải mô hình dưới dạng tệp .tflite được chuyển đổi trước, bạn có thể kết hợp mã của mình với API Python của TensorFlow Lite Converter ( tf.lite.TFLiteConverter ), cho phép bạn chuyển đổi mô hình TensorFlow của mình sang định dạng TensorFlow Lite và sau đó chạy suy luận:

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

Để biết thêm mã mẫu Python, hãy xem label_image.py .

Các hoạt động được hỗ trợ

TensorFlow Lite hỗ trợ một tập hợp con các hoạt động TensorFlow với một số hạn chế. Để biết danh sách đầy đủ các thao tác và giới hạn, hãy xem trang TF Lite Ops .