LiteRT 시작하기

이 가이드에서는 기기에서 LiteRT 모델을 실행하여 예측을 수행하는 방법을 알아봅니다 이는 LiteRT를 사용하면 인터프리터는 정적 그래프 순서 지정 및 커스텀 (덜 동적) 로드, 초기화, 실행 지연 시간을 최소화하기 위한 메모리 할당자

LiteRT 추론은 일반적으로 다음 단계를 따릅니다.

  1. 모델 로드: .tflite 모델을 메모리에 로드합니다. 메모리에는 모델의 실행 그래프입니다.

  2. 데이터 변환: 입력 데이터를 예상 형식으로 변환합니다. 측정기준에 따라 달라집니다. 모델의 원시 입력 데이터가 일반적으로 입력과 일치하지 않음 모델에서 예상하는 데이터 형식입니다. 예를 들어 모델과 호환되도록 이미지 형식을 변경하세요.

  3. 추론 실행: LiteRT 모델을 실행하여 예측합니다. 이 LiteRT API를 사용하여 모델을 실행하는 작업이 포함됩니다. 이를 위해서는 인터프리터 빌드, 텐서 할당 등의 단계가 포함됩니다.

  4. 출력 해석: 출력 텐서를 의미 있는 방식으로 해석 애플리케이션에서 유용하게 활용할 수 있습니다 예를 들어 모델은 확률 목록입니다. 각 사용자의 몫으로 해당 확률을 출력 형식을 지정합니다.

이 가이드에서는 LiteRT 인터프리터에 액세스하고 C++, Java, Python을 이용한 추론을 살펴보겠습니다.

지원되는 플랫폼

가장 일반적인 모바일 및 임베디드를 위해 제공되는 TensorFlow 추론 API Android, iOS, Linux여러 프로그래밍 언어를 지원합니다.

대부분의 경우 API 설계는 편의성보다 성능에 대한 선호를 반영합니다. 사용합니다 LiteRT는 소형 기기에서 빠른 추론을 위해 설계되었으므로 API는 편리함을 희생하여 불필요한 사본을 복제하지 않도록 합니다.

모든 라이브러리에서 LiteRT API를 사용하면 모델을 로드하고 입력을 피드하고 추론 출력을 검색할 수 있습니다.

Android 플랫폼

Android에서 LiteRT 추론은 Java 또는 C++ API를 사용하여 수행할 수 있습니다. 이 Java API는 편의성을 제공하며 Android UI 내에서 직접 액티비티 클래스. C++ API는 더 많은 유연성과 속도를 제공하지만 JNI 래퍼를 작성하여 Java 레이어와 C++ 레이어 간에 데이터를 이동하는 방법

자세한 내용은 C++Java 섹션을 참고하세요. 또는 Android 빠른 시작을 따르세요.

iOS 플랫폼

iOS에서 LiteRT는 SwiftObjective-C iOS 라이브러리에 적합합니다. 또한 C API 직접 사용할 수 있습니다.

Swift, Objective-C, C API를 참조하세요. 섹션을 참조하거나 iOS 빠른 시작을 따르세요.

Linux 플랫폼

Linux 플랫폼에서 사용할 수 있는 LiteRT API를 사용하여 추론을 실행할 수 있습니다. C++:

모델 로드 및 실행

LiteRT 모델을 로드하고 실행하려면 다음 단계를 따르세요.

  1. 모델을 메모리에 로드하기
  2. 기존 모델을 기반으로 Interpreter 빌드
  3. 입력 텐서 값 설정
  4. 추론 호출
  5. 텐서 값 출력

Android (자바)

LiteRT로 추론을 실행하기 위한 Java API는 기본적으로 따라서 Android 라이브러리 종속 항목으로 사용할 수 있습니다. com.google.ai.edge.litert

Java에서는 Interpreter 클래스를 사용하여 모델을 로드하고 모델을 구동합니다. 제공합니다. 대부분의 경우 이 API만 필요할 수 있습니다.

FlatBuffers (.tflite) 파일을 사용하여 Interpreter를 초기화할 수 있습니다.

public Interpreter(@NotNull File modelFile);

또는 MappedByteBuffer 사용:

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

두 경우 모두 유효한 LiteRT 모델을 제공해야 합니다. 그렇지 않으면 API에서 발생합니다. IllegalArgumentException MappedByteBuffer를 사용하여 Interpreter의 경우 Interpreter

모델에서 추론을 실행하는 방법은 서명을 사용하는 것입니다. - 사용 가능 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");
}

runSignature 메서드는 다음과 같은 세 가지 인수를 사용합니다.

  • 입력 : 서명의 입력 이름의 입력을 입력에 매핑합니다. 객체를 지정합니다.

  • 출력 : 서명의 출력 이름에서 출력으로의 출력 매핑에 매핑합니다. 데이터입니다.

  • 서명 이름 (선택사항): 서명 이름( 단일 서명이 있음).

모델에 정의된 서명이 없는 경우 추론을 실행하는 또 다른 방법입니다. Interpreter.run()를 호출하기만 하면 됩니다. 예를 들면 다음과 같습니다.

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

run() 메서드는 입력을 하나만 사용하고 출력을 하나만 반환합니다. 만약 모델에 여러 개의 입력 또는 출력이 있습니다. 대신 다음을 사용하세요.

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

이 경우 inputs의 각 항목은 입력 텐서에 해당하며 map_of_indices_to_outputs는 출력 텐서의 색인을 상응하는 출력 데이터입니다.

두 경우 모두 텐서 색인은 LiteRT 변환기를 만듭니다. 유의사항 input의 텐서 순서가 LiteRT에 지정된 순서와 일치해야 함 변환기.

Interpreter 클래스는 연산 이름을 사용한 모든 모델 입력 또는 출력의 색인:

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

opName가 모델에서 유효한 연산이 아니면 IllegalArgumentException

또한 Interpreter가 리소스를 소유한다는 점에 유의하세요. 메모리 누수를 방지하기 위해 리소스는 다음에서 사용한 후에 해제되어야 합니다.

interpreter.close();

Java를 사용한 프로젝트 예는 Android 객체 감지 예 앱을 엽니다.

지원되는 데이터 유형

LiteRT를 사용하려면 입력 및 출력 텐서의 데이터 유형이 다음과 같은 기본 유형을 지원합니다.

  • float
  • int
  • long
  • byte

String 유형도 지원되지만 기본형입니다. 특히 문자열 텐서의 모양은 숫자를 결정합니다. 텐서의 문자열 정렬, 즉 각 요소 자체는 가변 길이 문자열입니다. 이런 의미에서 텐서의 (바이트) 크기는 셰이프와 유형에서만 계산되며, 따라서 문자열은 단일 플랫 ByteBuffer 인수로 제공됩니다.

IntegerFloat와 같은 박스형 유형을 비롯한 다른 데이터 유형이 사용되는 경우 IllegalArgumentException이 발생합니다.

입력

각 입력은 지원되는 원시 유형 또는 적절한 크기의 원시 ByteBuffer를 반환합니다. 입력이 다차원 배열인 경우 연결된 입력 텐서는 추론 시 배열의 크기로 암시적으로 크기가 조정됩니다. 입력이 ByteBuffer, 호출자는 먼저 연결된 입력의 크기를 수동으로 조절해야 함 텐서 (Interpreter.resizeInput()를 통해)를 전송합니다.

ByteBuffer를 사용할 때는 직접 바이트 버퍼를 사용하는 것이 좋습니다. 이렇게 하면 Interpreter: 불필요한 복사를 방지합니다. ByteBuffer가 직접 바이트인 경우 버퍼의 경우 순서는 ByteOrder.nativeOrder()여야 합니다. 포드가 모델 추론의 경우 모델 추론이 완료될 때까지 변경되지 않아야 합니다.

출력

각 출력은 지원되는 기본형 또는 적절한 크기의 ByteBuffer 중에서 선택할 수 있습니다. 일부 모델에서는 출력 텐서의 형태가 모델에 따라 달라질 수 있는 입력입니다. 기존 Java 추론 API를 지원하지만 계획된 확장 프로그램을 사용하면 가능합니다.

iOS(Swift)

스위프트 API 는 Cocoapods의 TensorFlowLiteSwift개 포드에서 사용할 수 있습니다.

먼저 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...
}

iOS (Objective-C)

Objective-C는 API 는 Cocoapods의 LiteRTObjC개 포드에서 사용할 수 있습니다.

먼저 TensorFlowLiteObjC 모듈을 가져와야 합니다.

@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... */ }

Objective-C 코드의 C API

Objective-C API는 대리자를 지원하지 않습니다. 델리게이트를 사용하려면 Objective-C 코드인 경우 기본 C를 직접 호출해야 함 API를 참고하세요.

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

C++

LiteRT로 추론을 실행하기 위한 C++ API는 Android, iOS, Linux 플랫폼에서 사용할 수 있습니다 iOS의 C++ API는 bazel을 사용할 때만 사용할 수 있습니다.

C++에서 모델은 FlatBufferModel 클래스 이는 LiteRT 모델을 캡슐화하며, 여러 가지 방법이 있습니다.

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

이제 모델을 FlatBufferModel 객체로 만들었으므로 실행할 수 있습니다. 다음 코드로 교체합니다. Interpreter 둘 이상의 사용자가 동시에 단일 FlatBufferModel를 사용할 수 있음 Interpreter

Interpreter API의 중요한 부분이 코드 스니펫에 나와 있습니다. 참조하세요. 다음 사항에 유의하세요.

  • 문자열 비교를 피하기 위해 텐서는 정수로 표시됩니다. (문자열 라이브러리에 대한 고정 종속 항목 포함)
  • 동시 스레드에서 인터프리터에 액세스하면 안 됩니다.
  • 다음을 호출하여 입력 및 출력 텐서의 메모리 할당을 트리거해야 합니다. 텐서의 크기를 조절한 직후의 AllocateTensors()

C++에서 LiteRT를 가장 간단하게 사용하는 방법은 다음과 같습니다.

// 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 needed.
interpreter->AllocateTensors();

float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.

interpreter->Invoke();

float* output = interpreter->typed_output_tensor<float>(0);

추가 코드 예는 다음을 참조하세요. minimal.cc 드림 및 label_image.cc

Python

추론 실행을 위해 Python API는 Interpreter: 모델을 로드하고 추론을 실행합니다.

LiteRT 패키지를 설치합니다.

$ python3 -m pip install ai-edge-litert

LiteRT 인터프리터 가져오기

from ai_edge_litert.interpreter import Interpreter
Interpreter = Interpreter(model_path=args.model.file)

다음 예는 Python 인터프리터를 사용하여 FlatBuffers (.tflite) 파일을 만들고 임의 입력 데이터로 추론을 실행합니다.

이 예시는 SignatureDef로 변경할 수 있습니다.

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 LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = 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'])

모델에 SignatureDefs가 정의되지 않은 경우의 또 다른 예입니다.

import numpy as np
import tensorflow as tf

# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(TFLITE_FILE_PATH)
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)

모델을 사전 변환된 .tflite 파일로 로드하는 대신 다음 작업을 수행할 수 있습니다. 코드를 LiteRT와 결합하여 컴파일러 를 사용하여 Keras 모델을 LiteRT 형식으로 변환한 다음 추론합니다.

import numpy as np
import tensorflow as tf

img = tf.keras.Input(shape=(64, 64, 3), name="img")
const = tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
val = img + const
out = tf.identity(val, name="out")

# Convert to LiteRT format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()

# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

# Continue to get tensors and so forth, as shown above...

더 많은 Python 샘플 코드는 다음을 참조하세요. label_image.py

동적 형태 모델로 추론 실행

동적 입력 형태를 사용하여 모델을 실행하려면 입력 형태 크기를 조절하세요. 살펴봤습니다 그렇지 않으면 TensorFlow 모델의 None 형태가 LiteRT 모델에서 1의 자리표시자로 대체됩니다.

다음 예는 실행 전에 입력 도형의 크기를 조절하는 방법을 보여줍니다. 추론할 수 있습니다. 모든 예에서는 입력 형태가 [1/None, 10]로 정의되며 [3, 10]로 크기를 조절해야 합니다.

C++ 예시:

// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();

Python 예시:

# Load the LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(model_path=TFLITE_FILE_PATH)

# Resize input shape for dynamic shape model and allocate tensor
interpreter.resize_tensor_input(interpreter.get_input_details()[0]['index'], [3, 10])
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()