Xử lý dữ liệu đầu vào và đầu ra bằng Thư viện hỗ trợ LiteRT

Các nhà phát triển ứng dụng di động thường tương tác với các đối tượng đã nhập, chẳng hạn như bitmap hoặc dữ liệu gốc như số nguyên. Tuy nhiên, trình thông dịch LiteRT API chạy mô hình học máy trên thiết bị sử dụng các tensor ở dạng ByteBuffer có thể khó gỡ lỗi và thao tác. Chiến lược phát hành đĩa đơn Thư viện hỗ trợ Android LiteRT được thiết kế để giúp xử lý dữ liệu đầu vào và đầu ra của mô hình LiteRT, và giúp trình thông dịch LiteRT dễ sử dụng hơn.

Bắt đầu

Nhập phần phụ thuộc Gradle và các chế độ cài đặt khác

Sao chép tệp mô hình .tflite vào thư mục thành phần của mô-đun Android nơi mô hình sẽ chạy. Chỉ định rằng tệp không được nén và thêm thư viện LiteRT vào tệp build.gradle của mô-đun:

android {
    // Other settings

    // Specify tflite file should not be compressed for the app apk
    aaptOptions {
        noCompress "tflite"
    }

}

dependencies {
    // Other dependencies

    // Import tflite dependencies
    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'
    // The GPU delegate library is optional. Depend on it as needed.
    implementation 'com.google.ai.edge.litert:litert-gpu:0.0.0-nightly-SNAPSHOT'
    implementation 'com.google.ai.edge.litert:litert-support:0.0.0-nightly-SNAPSHOT'
}

Khám phá Thư viện hỗ trợ LiteRT AAR được lưu trữ tại MavenCentral cho các phiên bản khác nhau của Thư viện hỗ trợ.

Thao tác cơ bản và chuyển đổi hình ảnh

Thư viện hỗ trợ LiteRT có một bộ công cụ chỉnh sửa ảnh cơ bản như cắt và đổi kích thước. Để sử dụng công cụ này, hãy tạo một ImagePreprocessor và hãy thêm các toán tử bắt buộc. Để chuyển đổi hình ảnh sang định dạng tensor theo yêu cầu của trình thông dịch LiteRT, hãy tạo một TensorImage để sử dụng làm dữ liệu đầu vào:

import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.image.ImageProcessor;
import org.tensorflow.lite.support.image.TensorImage;
import org.tensorflow.lite.support.image.ops.ResizeOp;

// Initialization code
// Create an ImageProcessor with all ops required. For more ops, please
// refer to the ImageProcessor Architecture section in this README.
ImageProcessor imageProcessor =
    new ImageProcessor.Builder()
        .add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR))
        .build();

// Create a TensorImage object. This creates the tensor of the corresponding
// tensor type (uint8 in this case) that the LiteRT interpreter needs.
TensorImage tensorImage = new TensorImage(DataType.UINT8);

// Analysis code for every frame
// Preprocess the image
tensorImage.load(bitmap);
tensorImage = imageProcessor.process(tensorImage);

DataType của tensor có thể được đọc qua thư viện trình trích xuất siêu dữ liệu cũng như các thông tin khác về mô hình.

Xử lý dữ liệu âm thanh cơ bản

Thư viện hỗ trợ LiteRT cũng xác định cách gói lớp TensorAudio một số phương thức xử lý dữ liệu âm thanh cơ bản. Thông số này chủ yếu được sử dụng cùng với AudioRecord và ghi lại các mẫu âm thanh trong bộ đệm đổ chuông.

import android.media.AudioRecord;
import org.tensorflow.lite.support.audio.TensorAudio;

// Create an `AudioRecord` instance.
AudioRecord record = AudioRecord(...)

// Create a `TensorAudio` object from Android AudioFormat.
TensorAudio tensorAudio = new TensorAudio(record.getFormat(), size)

// Load all audio samples available in the AudioRecord without blocking.
tensorAudio.load(record)

// Get the `TensorBuffer` for inference.
TensorBuffer buffer = tensorAudio.getTensorBuffer()

Tạo đối tượng đầu ra và chạy mô hình

Trước khi chạy mô hình, chúng ta cần tạo các đối tượng vùng chứa lưu trữ kết quả:

import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;

// Create a container for the result and specify that this is a quantized model.
// Hence, the 'DataType' is defined as UINT8 (8-bit unsigned integer)
TensorBuffer probabilityBuffer =
    TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);

Tải mô hình và chạy suy luận:

import java.nio.MappedByteBuffer;
import org.tensorflow.lite.InterpreterFactory;
import org.tensorflow.lite.InterpreterApi;

// Initialise the model
try{
    MappedByteBuffer tfliteModel
        = FileUtil.loadMappedFile(activity,
            "mobilenet_v1_1.0_224_quant.tflite");
    InterpreterApi tflite = new InterpreterFactory().create(
        tfliteModel, new InterpreterApi.Options());
} catch (IOException e){
    Log.e("tfliteSupport", "Error reading model", e);
}

// Running inference
if(null != tflite) {
    tflite.run(tImage.getBuffer(), probabilityBuffer.getBuffer());
}

Truy cập vào kết quả

Nhà phát triển có thể trực tiếp truy cập vào kết quả thông qua probabilityBuffer.getFloatArray(). Nếu mô hình tạo ra một đầu ra lượng tử hoá, hãy nhớ chuyển đổi kết quả. Đối với mô hình lượng tử hoá MobileNet, trình phát triển cần chia mỗi giá trị đầu ra cho 255 để có được xác suất trong khoảng từ 0 (ít có khả năng nhất) đến 1 (có nhiều khả năng nhất) cho mỗi danh mục.

Không bắt buộc: Liên kết kết quả với nhãn

Nhà phát triển cũng có thể tuỳ ý liên kết kết quả với nhãn. Trước tiên, hãy sao chép văn bản chứa các nhãn vào thư mục nội dung của mô-đun. Tiếp theo, hãy tải nhãn bằng cách sử dụng mã sau:

import org.tensorflow.lite.support.common.FileUtil;

final String ASSOCIATED_AXIS_LABELS = "labels.txt";
List<String> associatedAxisLabels = null;

try {
    associatedAxisLabels = FileUtil.loadLabels(this, ASSOCIATED_AXIS_LABELS);
} catch (IOException e) {
    Log.e("tfliteSupport", "Error reading label file", e);
}

Đoạn mã sau đây trình bày cách liên kết xác suất với nhãn danh mục:

import java.util.Map;
import org.tensorflow.lite.support.common.TensorProcessor;
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.label.TensorLabel;

// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
    new TensorProcessor.Builder().add(new NormalizeOp(0, 255)).build();

if (null != associatedAxisLabels) {
    // Map of labels and their corresponding probability
    TensorLabel labels = new TensorLabel(associatedAxisLabels,
        probabilityProcessor.process(probabilityBuffer));

    // Create a map to access the result based on label
    Map<String, Float> floatMap = labels.getMapWithFloatValue();
}

Phạm vi trường hợp sử dụng hiện tại

Phiên bản hiện tại của Thư viện hỗ trợ LiteRT bao gồm:

  • các kiểu dữ liệu phổ biến (float, uint8, hình ảnh, âm thanh và mảng của các đối tượng này) làm đầu vào và đầu ra của các mô hình tflite.
  • các thao tác cơ bản đối với hình ảnh (cắt hình ảnh, đổi kích thước và xoay).
  • chuẩn hoá và lượng tử hoá
  • phần mềm tệp

Các phiên bản trong tương lai sẽ cải thiện dịch vụ hỗ trợ cho các ứng dụng liên quan đến văn bản.

Cấu trúc ImageProcessor

Thiết kế của ImageProcessor cho phép các thao tác thao tác hình ảnh được xác định trước và tối ưu hoá trong quá trình xây dựng. ImageProcessor hiện hỗ trợ ba thao tác tiền xử lý cơ bản, như được mô tả trong trong đoạn mã dưới đây:

import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.common.ops.QuantizeOp;
import org.tensorflow.lite.support.image.ops.ResizeOp;
import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;
import org.tensorflow.lite.support.image.ops.Rot90Op;

int width = bitmap.getWidth();
int height = bitmap.getHeight();

int size = height > width ? width : height;

ImageProcessor imageProcessor =
    new ImageProcessor.Builder()
        // Center crop the image to the largest square possible
        .add(new ResizeWithCropOrPadOp(size, size))
        // Resize using Bilinear or Nearest neighbour
        .add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR));
        // Rotation counter-clockwise in 90 degree increments
        .add(new Rot90Op(rotateDegrees / 90))
        .add(new NormalizeOp(127.5, 127.5))
        .add(new QuantizeOp(128.0, 1/128.0))
        .build();

Xem thêm thông tin tại đây về chuẩn hoá và lượng tử hoá.

Mục tiêu sau cùng của thư viện hỗ trợ là hỗ trợ tất cả tf.image phép biến đổi. Điều này có nghĩa là phép biến đổi sẽ giống như TensorFlow và việc triển khai sẽ độc lập với hệ điều hành.

Nhà phát triển cũng có thể tạo bộ xử lý tuỳ chỉnh. Có ý nghĩa quan trọng trong những trường hợp này cho phù hợp với quy trình đào tạo, tức là tương tự nên áp dụng cho cả huấn luyện và suy luận để tăng khả năng tái tạo.

Lượng tử hoá

Khi khởi tạo các đối tượng đầu vào hoặc đầu ra như TensorImage hoặc TensorBuffer bạn cần chỉ định loại của chúng là DataType.UINT8 hoặc DataType.FLOAT32.

TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
    TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);

Có thể dùng TensorProcessor để lượng tử hoá tensor đầu vào hoặc khử lượng tử đầu ra tensor. Ví dụ: khi xử lý đầu ra lượng tử hoá TensorBuffer, nhà phát triển có thể sử dụng DequantizeOp để khử lượng tử kết quả thành dấu phẩy động xác suất trong khoảng từ 0 đến 1:

import org.tensorflow.lite.support.common.TensorProcessor;

// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
    new TensorProcessor.Builder().add(new DequantizeOp(0, 1/255.0)).build();
TensorBuffer dequantizedBuffer = probabilityProcessor.process(probabilityBuffer);

Tham số lượng tử hoá của tensor có thể được đọc thông qua thư viện trình trích xuất siêu dữ liệu.