A API LiteRT CompiledModel está disponível em C++, oferecendo aos desenvolvedores
controle refinado sobre a alocação de memória e o desenvolvimento de baixo nível. Para ver um
exemplo, consulte o app C++ de segmentação de imagens.
O guia a seguir mostra a inferência básica de CPU da API CompiledModel Kotlin. Consulte o guia sobre aceleração de GPU e aceleração de NPU para
recursos avançados de aceleração.
Adicionar dependência de build
Escolha o caminho adequado para seu projeto:
Usar biblioteca pré-criada (plataforma cruzada): use a biblioteca pré-criada do LiteRT para configuração instantânea. Saiba como usar a biblioteca C++ pré-criada do pacote LiteRT Maven no Android ou faça o download/integração do binário C++ pré-criado no Android, iOS, macOS, Linux e Windows.
Criar do código-fonte (plataforma cruzada): crie do código-fonte com o CMake para ter controle total e suporte a várias plataformas (Android, iOS, macOS, Linux e Windows). Confira detalhes neste guia.
Inferência básica
Esta seção mostra como a inferência básica é realizada.
Criar o ambiente
O objeto Environment fornece um ambiente de execução que inclui componentes
como o caminho do plug-in do compilador e contextos de GPU. O Environment é obrigatório ao criar CompiledModel e TensorBuffer. O código a seguir cria um Environment para execução de CPU e GPU sem opções:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
Criar o CompiledModel
Depois de conseguir um modelo LiteRT ou converter um modelo para o formato .tflite,
inicialize o ambiente de execução com o arquivo do modelo usando a API CompiledModel.
É possível especificar a aceleração de hardware neste ponto
(kLiteRtHwAcceleratorCpu ou kLiteRtHwAcceleratorGpu):
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));
Criar buffers de entrada e saída
Crie as estruturas de dados (buffers) necessárias para armazenar os dados de entrada que você vai inserir no modelo para inferência e os dados de saída que o modelo produz após a execução da inferência.
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
Se você estiver usando a memória da CPU, preencha as entradas gravando dados diretamente no primeiro buffer de entrada.
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, input_size));
Invocar o modelo
Forneça os buffers de entrada e saída e execute o modelo compilado com o modelo e a aceleração de hardware especificados nas etapas anteriores.
compiled_model.Run(input_buffers, output_buffers);
Recuperar saídas
Recuperar saídas lendo diretamente a saída do modelo da memória.
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
// ... process output data
Principais conceitos e componentes
Consulte as seções a seguir para informações sobre os principais conceitos e componentes
da API LiteRT CompiledModel.
Tratamento de erros
O LiteRT usa litert::Expected para retornar valores ou propagar erros de maneira semelhante a absl::StatusOr ou std::expected. Você pode verificar manualmente se há erros.
Para sua conveniência, o LiteRT fornece as seguintes macros:
LITERT_ASSIGN_OR_RETURN(lhs, expr)atribui o resultado deexpralhsse não produzir um erro. Caso contrário, retorna o erro.Ele vai ser expandido para algo como o snippet a seguir.
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)faz o mesmo queLITERT_ASSIGN_OR_RETURN, mas encerra o programa em caso de erro.LITERT_RETURN_IF_ERROR(expr)retornaexprse a avaliação dele produzir um erro.LITERT_ABORT_IF_ERROR(expr)faz o mesmo queLITERT_RETURN_IF_ERROR, mas interrompe o programa em caso de erro.
Para mais informações sobre macros LiteRT, consulte litert_macros.h.
Buffer de tensor (TensorBuffer)
O LiteRT oferece suporte integrado à interoperabilidade de buffer de E/S usando a
API Tensor Buffer (TensorBuffer) para processar o fluxo de dados de entrada e saída do
modelo compilado. A API Tensor Buffer permite gravar (Write<T>()) e ler (Read<T>()), além de bloquear a memória da CPU.
Para uma visão mais completa de como a API TensorBuffer é implementada, consulte o
código-fonte de
litert_tensor_buffer.h.
Requisitos de entrada/saída do modelo de consulta
Os requisitos para alocar um buffer de tensor (TensorBuffer) geralmente são especificados pelo acelerador de hardware. Os buffers para entradas e saídas podem ter requisitos relacionados a alinhamento, strides de buffer e tipo de memória. É possível usar funções auxiliares, como CreateInputBuffers, para processar automaticamente esses requisitos.
O snippet de código simplificado a seguir demonstra como recuperar os requisitos de buffer para dados de entrada:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
Para uma visão mais completa de como a API TensorBufferRequirements é
implementada, consulte o código-fonte de
litert_tensor_buffer_requirements.h.
Criar buffers de tensor gerenciados (TensorBuffers)
O snippet de código simplificado a seguir demonstra como criar Managed Tensor
Buffers, em que a API TensorBuffer aloca os buffers respectivos:
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));
Criar buffers de tensor com cópia zero
Para encapsular um buffer existente como um TensorBuffer (cópia zero), use o seguinte snippet de código:
// 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));
Como ler e gravar no buffer de tensor
O snippet a seguir demonstra como ler de um buffer de entrada e gravar em um buffer de saída:
// 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 */
}
Avançado: interoperabilidade de buffer de cópia zero para tipos de buffer de hardware especializados
Alguns tipos de buffer, como AHardwareBuffer, permitem a interoperabilidade com outros tipos de buffer. Por exemplo, um buffer do OpenGL pode ser criado de um
AHardwareBuffer com cópia zero. O snippet de código a seguir mostra um exemplo:
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());
Os buffers OpenCL também podem ser criados com AHardwareBuffer:
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());
Em dispositivos móveis que oferecem suporte à interoperabilidade entre OpenCL e OpenGL, os buffers CL podem ser criados com base em buffers 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());
Exemplos de implementações
Consulte as seguintes implementações do LiteRT em C++.
Inferência básica (CPU)
Confira a seguir uma versão condensada dos snippets de código da seção Começar. É a implementação mais simples da inferência com o 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));
Cópia zero com memória do host
A API LiteRT CompiledModel reduz o atrito dos pipelines de inferência,
especialmente ao lidar com vários back-ends de hardware e fluxos de cópia zero. O
snippet de código a seguir usa o método CreateFromHostMemory ao criar o
buffer de entrada, que usa cópia zero com memória do host.
// 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);