Nhà phát triển ứng dụng di động thường tương tác với các đối tượng được nhập, chẳng hạn như bitmap hoặc các kiểu dữ liệu nguyên gốc như số nguyên. Tuy nhiên, API trình thông dịch LiteRT chạy mô hình học máy trên thiết bị sẽ sử dụng các tensor ở dạng ByteBuffer, có thể gây khó khăn cho việc gỡ lỗi và thao tác. 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 các mô hình LiteRT, đồng thời 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 vào Gradle và các chế độ cài đặt khác
Sao chép tệp mô hình .tflite vào thư mục tài sả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á AAR của Thư viện hỗ trợ LiteRT đượ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 và chuyển đổi hình ảnh cơ bản
Thư viện hỗ trợ LiteRT có một bộ phương thức cơ bản để thao tác với hình ảnh, chẳng hạn như cắt và đổi kích thước. Để sử dụng, hãy tạo một ImagePreprocessor và thêm các thao tác bắt buộc. Để chuyển đổi hình ảnh thành định dạng tensor mà trình thông dịch LiteRT yêu cầu, hãy tạo một TensorImage để 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 một tensor có thể được đọc thông qua thư viện trình trích xuất siêu dữ liệu cũng như 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 một lớp TensorAudio bao bọc một số phương thức xử lý dữ liệu âm thanh cơ bản. Lớp này thường được dùng cùng với AudioRecord và thu thập các mẫu âm thanh trong bộ nhớ đệm vò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 các đố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 sẽ 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 quy trình 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ể truy cập trực tiếp vào đầu ra thông qua probabilityBuffer.getFloatArray(). Nếu mô hình tạo ra một đầu ra được lượng tử hoá, hãy nhớ chuyển đổi kết quả. Đối với mô hình được lượng tử hoá MobileNet, nhà phát triển cần chia mỗi giá trị đầu ra cho 255 để nhận được xác suất trong khoảng từ 0 (ít có khả năng nhất) đến 1 (có 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 tệp văn bản chứa nhãn vào thư mục tài sản của mô-đun. Tiếp theo, hãy tải tệp nhãn bằ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 minh hoạ cách liên kết các 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 của 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 loại 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 về hình ảnh (cắt, đổi kích thước và xoay hình ảnh).
- chuẩn hoá và lượng tử hoá
- tiện ích tệp
Các phiên bản sau này sẽ cải thiện khả năng 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 xác định trước các thao tác xử lý hình ảnh và tối ưu hoá trong quá trình tạo bản dựng. ImageProcessor hiện hỗ trợ 3 thao tác tiền xử lý cơ bản, như mô tả trong 3 bình luận trong đoạn mã bên dưới:
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 chi tiết tại đây về việc chuẩn hoá và lượng tử hoá.
Mục tiêu cuối cùng của thư viện hỗ trợ là hỗ trợ tất cả các phép biến đổi tf.image. Điều này có nghĩa là quá trình chuyển đổi sẽ giống như TensorFlow và quá trình 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. Trong những trường hợp này, điều quan trọng là phải phù hợp với quy trình huấn luyện – tức là cùng một quy trình tiền xử lý sẽ á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 các loại đối tượng đó là DataType.UINT8 hoặc DataType.FLOAT32.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
Bạn có thể dùng TensorProcessor để lượng tử hoá các tensor đầu vào hoặc giải lượng tử hoá các tensor đầu ra. Ví dụ: khi xử lý một đầu ra được lượng tử hoá TensorBuffer, nhà phát triển có thể dùng DequantizeOp để huỷ lượng tử hoá kết quả thành một xác suất dấu phẩy độ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);
Bạn có thể đọc các tham số định lượng của một tensor thông qua thư viện trình trích xuất siêu dữ liệu.