API LiteRT CompiledModel có trong C++, giúp nhà phát triển kiểm soát chi tiết việc phân bổ bộ nhớ và quá trình phát triển cấp thấp. Để xem ví dụ, hãy xem Ứng dụng C++ phân đoạn hình ảnh.
Hướng dẫn sau đây trình bày suy luận cơ bản về CPU của CompiledModel Kotlin API. Hãy xem hướng dẫn về tính năng tăng tốc GPU và tính năng tăng tốc NPU để biết các tính năng tăng tốc nâng cao.
Thêm phần phụ thuộc của bản dựng
Chọn đường dẫn phù hợp với dự án của bạn:
Sử dụng thư viện tạo sẵn (đa nền tảng): Sử dụng thư viện tạo sẵn LiteRT để thiết lập ngay lập tức. Xem cách sử dụng thư viện C++ dựng sẵn từ gói LiteRT Maven trên Android hoặc tải/tích hợp tệp nhị phân C++ dựng sẵn trên Android, iOS, macOS, Linux và Windows.
Tạo từ nguồn (đa nền tảng): Tạo từ nguồn bằng CMake để có toàn quyền kiểm soát và hỗ trợ đa nền tảng (Android, iOS, macOS, Linux, Windows). Xem thông tin chi tiết trong hướng dẫn này.
Suy luận cơ bản
Phần này cho biết cách thực hiện suy luận cơ bản.
Tạo môi trường
Đối tượng Environment cung cấp một môi trường thời gian chạy bao gồm các thành phần như đường dẫn của trình bổ trợ trình biên dịch và ngữ cảnh GPU. Bạn phải có Environment khi tạo CompiledModel và TensorBuffer. Đoạn mã sau đây tạo một Environment để thực thi CPU và GPU mà không có lựa chọn nào:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
Tạo CompiledModel
Sau khi nhận được một mô hình LiteRT hoặc chuyển đổi một mô hình sang định dạng .tflite, hãy khởi động thời gian chạy bằng tệp mô hình bằng cách sử dụng API CompiledModel.
Bạn có thể chỉ định tính năng tăng tốc phần cứng tại thời điểm này (kLiteRtHwAcceleratorCpu hoặc kLiteRtHwAcceleratorGpu):
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));
Tạo vùng đệm đầu vào và đầu ra
Tạo các cấu trúc dữ liệu (vùng đệm) cần thiết để lưu trữ dữ liệu đầu vào mà bạn sẽ đưa vào mô hình để suy luận, cũng như dữ liệu đầu ra mà mô hình tạo ra sau khi chạy suy luận.
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
Nếu bạn đang sử dụng bộ nhớ CPU, hãy điền dữ liệu đầu vào bằng cách ghi dữ liệu trực tiếp vào vùng đệm đầu vào đầu tiên.
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, input_size));
Gọi mô hình
Cung cấp các vùng đệm đầu vào và đầu ra, chạy Mô hình đã biên dịch với mô hình và tính năng tăng tốc phần cứng được chỉ định ở các bước trước.
compiled_model.Run(input_buffers, output_buffers);
Truy xuất dữ liệu đầu ra
Truy xuất đầu ra bằng cách đọc trực tiếp đầu ra của mô hình từ bộ nhớ.
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
// ... process output data
Các khái niệm và thành phần chính
Hãy tham khảo các phần sau để biết thông tin về các khái niệm và thành phần chính của API LiteRT CompiledModel.
Xử lý lỗi
LiteRT sử dụng litert::Expected để trả về giá trị hoặc truyền lỗi theo cách tương tự như absl::StatusOr hoặc std::expected. Bạn có thể tự kiểm tra lỗi theo cách thủ công.
Để thuận tiện, LiteRT cung cấp các macro sau:
LITERT_ASSIGN_OR_RETURN(lhs, expr)chỉ định kết quả củaexprcholhsnếu không tạo ra lỗi, nếu không, hàm này sẽ trả về lỗi.Thẻ này sẽ mở rộng thành một đoạn mã tương tự như sau.
auto maybe_model = CompiledModel::Create(env, "mymodel.tflite", HwAccelerators::kCpu); if (!maybe_model) { return maybe_model.Error(); } auto model = std::move(maybe_model.Value());LITERT_ASSIGN_OR_ABORT(lhs, expr)hoạt động giống nhưLITERT_ASSIGN_OR_RETURNnhưng sẽ huỷ chương trình trong trường hợp xảy ra lỗi.LITERT_RETURN_IF_ERROR(expr)sẽ trả vềexprnếu quá trình đánh giá của hàm này tạo ra lỗi.LITERT_ABORT_IF_ERROR(expr)hoạt động giống nhưLITERT_RETURN_IF_ERRORnhưng sẽ huỷ chương trình trong trường hợp xảy ra lỗi.
Để biết thêm thông tin về macro LiteRT, hãy xem litert_macros.h.
Vùng đệm tensor (TensorBuffer)
LiteRT cung cấp khả năng hỗ trợ tích hợp cho khả năng tương tác của bộ đệm I/O, sử dụng Tensor Buffer API (TensorBuffer) để xử lý luồng dữ liệu vào và ra khỏi mô hình đã biên dịch. Tensor Buffer API cung cấp khả năng ghi (Write<T>()) và đọc (Read<T>()), đồng thời khoá bộ nhớ CPU.
Để biết thông tin đầy đủ hơn về cách triển khai API TensorBuffer, hãy xem mã nguồn cho litert_tensor_buffer.h.
Yêu cầu về đầu vào/đầu ra của mô hình truy vấn
Các yêu cầu để phân bổ một Tensor Buffer (TensorBuffer) thường do bộ tăng tốc phần cứng chỉ định. Vùng đệm cho dữ liệu đầu vào và đầu ra có thể có các yêu cầu về việc căn chỉnh, bước vùng đệm và loại bộ nhớ. Bạn có thể dùng các hàm trợ giúp như CreateInputBuffers để tự động xử lý các yêu cầu này.
Đoạn mã đơn giản sau đây minh hoạ cách bạn có thể truy xuất các yêu cầu về vùng đệm cho dữ liệu đầu vào:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
Để biết thông tin đầy đủ hơn về cách triển khai API TensorBufferRequirements, hãy xem mã nguồn cho litert_tensor_buffer_requirements.h.
Tạo TensorBuffer được quản lý (TensorBuffer)
Đoạn mã đơn giản sau đây minh hoạ cách tạo Managed Tensor Buffer, trong đó API TensorBuffer phân bổ các vùng đệm tương ứng:
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_cpu,
TensorBuffer::CreateManaged(env, /*buffer_type=*/kLiteRtTensorBufferTypeHostMemory,
ranked_tensor_type, buffer_size));
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_gl, TensorBuffer::CreateManaged(env,
/*buffer_type=*/kLiteRtTensorBufferTypeGlBuffer, ranked_tensor_type, buffer_size));
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb, TensorBuffer::CreateManaged(env,
/*buffer_type=*/kLiteRtTensorBufferTypeAhwb, ranked_tensor_type, buffer_size));
Tạo Tensor Buffer bằng tính năng sao chép bằng không
Để bao bọc một vùng đệm hiện có dưới dạng Vùng đệm Tensor (sao chép bằng 0), hãy sử dụng đoạn mã sau:
// Create a TensorBuffer from host memory
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_host,
TensorBuffer::CreateFromHostMemory(env, ranked_tensor_type,
ptr_to_host_memory, buffer_size));
// Create a TensorBuffer from GlBuffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
size_bytes, offset));
// Create a TensorBuffer from AHardware Buffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_ahwb,
TensorBuffer::CreateFromAhwb(env, ranked_tensor_type, ahardware_buffer, offset));
Đọc và ghi từ vùng đệm Tensor
Đoạn mã sau đây minh hoạ cách bạn có thể đọc từ vùng đệm đầu vào và ghi vào vùng đệm đầu ra:
// Example of reading to input buffer:
std::vector<float> input_tensor_data = {1,2};
LITERT_ASSIGN_OR_RETURN(auto write_success,
input_tensor_buffer.Write<float>(absl::MakeConstSpan(input_tensor_data)));
if(write_success){
/* Continue after successful write... */
}
// Example of writing to output buffer:
std::vector<float> data(total_elements);
LITERT_ASSIGN_OR_RETURN(auto read_success,
output_tensor_buffer.Read<float>(absl::MakeSpan(data)));
if(read_success){
/* Continue after successful read */
}
Nâng cao: Khả năng tương tác với vùng đệm sao chép bằng 0 cho các loại vùng đệm phần cứng chuyên dụng
Một số loại vùng đệm, chẳng hạn như AHardwareBuffer, cho phép khả năng tương tác với các loại vùng đệm khác. Ví dụ: bạn có thể tạo một vùng đệm OpenGL từ AHardwareBuffer mà không cần sao chép. Đoạn mã sau đây cho thấy một ví dụ:
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb,
TensorBuffer::CreateManaged(env, kLiteRtTensorBufferTypeAhwb,
ranked_tensor_type, buffer_size));
// Buffer interop: Get OpenGL buffer from AHWB,
// internally creating an OpenGL buffer backed by AHWB memory.
LITERT_ASSIGN_OR_RETURN(auto gl_buffer, tensor_buffer_ahwb.GetGlBuffer());
Bạn cũng có thể tạo vùng đệm OpenCL từ AHardwareBuffer:
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());
Trên các thiết bị di động hỗ trợ khả năng tương tác giữa OpenCL và OpenGL, bạn có thể tạo các vùng đệm CL từ các vùng đệm GL:
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
size_bytes, offset));
// Creates an OpenCL buffer from the OpenGL buffer, zero-copy.
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_from_gl.GetOpenClMemory());
Ví dụ về cách triển khai
Hãy tham khảo các cách triển khai LiteRT sau đây trong C++.
Suy luận cơ bản (CPU)
Sau đây là phiên bản rút gọn của các đoạn mã trong phần Bắt đầu. Đây là cách triển khai suy luận đơn giản nhất bằng LiteRT.
// Load model and initialize runtime
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, "mymodel.tflite",
kLiteRtHwAcceleratorCpu));
// Preallocate input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// Fill the first input
float input_values[] = { /* your data */ };
input_buffers[0].Write<float>(absl::MakeConstSpan(input_values, /*size*/));
// Invoke
compiled_model.Run(input_buffers, output_buffers);
// Read the output
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
Sao chép không cần bộ nhớ máy chủ
API CompiledModel LiteRT giúp giảm ma sát của các quy trình suy luận, đặc biệt là khi xử lý nhiều phần phụ trợ phần cứng và các luồng không sao chép. Đoạn mã sau đây sử dụng phương thức CreateFromHostMemory khi tạo vùng đệm đầu vào, sử dụng tính năng sao chép bằng 0 với bộ nhớ máy chủ.
// Define an LiteRT environment to use existing EGL display and context.
const std::vector<Environment::Option> environment_options = {
{OptionTag::EglDisplay, user_egl_display},
{OptionTag::EglContext, user_egl_context}};
LITERT_ASSIGN_OR_RETURN(auto env,
Environment::Create(absl::MakeConstSpan(environment_options)));
// Load model1 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto compiled_model1, CompiledModel::Create(env, "model1.tflite", kLiteRtHwAcceleratorGpu));
// Prepare I/O buffers. opengl_buffer is given outside from the producer.
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_name0"));
// Create an input TensorBuffer based on tensor_type that wraps the given OpenGL Buffer.
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_opengl,
litert::TensorBuffer::CreateFromGlBuffer(env, tensor_type, opengl_buffer));
// Create an input event and attach it to the input buffer. Internally, it creates
// and inserts a fence sync object into the current EGL command queue.
LITERT_ASSIGN_OR_RETURN(auto input_event, Event::CreateManaged(env, LiteRtEventTypeEglSyncFence));
tensor_buffer_from_opengl.SetEvent(std::move(input_event));
std::vector<TensorBuffer> input_buffers;
input_buffers.push_back(std::move(tensor_buffer_from_opengl));
// Create an output TensorBuffer of the model1. It's also used as an input of the model2.
LITERT_ASSIGN_OR_RETURN(auto intermedidate_buffers, compiled_model1.CreateOutputBuffers());
// Load model2 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto compiled_model2, CompiledModel::Create(env, "model2.tflite", kLiteRtHwAcceleratorGpu));
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model2.CreateOutputBuffers());
compiled_model1.RunAsync(input_buffers, intermedidate_buffers);
compiled_model2.RunAsync(intermedidate_buffers, output_buffers);