마이크로컨트롤러 시작하기

이 문서에서는 마이크로 컨트롤러를 사용하여 모델을 학습시키고 추론을 실행하는 방법을 설명합니다.

Hello World 예시

Hello World 예는 마이크로컨트롤러용 TensorFlow Lite 사용에 관한 절대적인 기본사항을 보여주기 위해 설계되었습니다. 사인 함수를 복제하는 모델을 학습시키고 실행합니다. 이 함수는 단일 숫자를 입력으로 사용하고 숫자의 사인 값을 출력합니다. 마이크로 컨트롤러에 배포되면 예측을 사용하여 LED를 깜박이거나 애니메이션을 제어합니다.

엔드 투 엔드 워크플로에는 다음 단계가 포함됩니다.

  1. 모델 학습 (Python): 기기 내 사용을 위해 모델을 학습, 변환, 최적화하는 Python 파일입니다.
  2. 추론 실행 (C++ 17): C++ 라이브러리를 사용하여 모델에서 추론을 실행하는 엔드 투 엔드 단위 테스트입니다.

지원되는 기기 받기

예시 애플리케이션은 다음 기기에서 테스트되었습니다.

TensorFlow Lite for Microcontrollers에서 지원되는 플랫폼에 대해 자세히 알아보세요.

모델 학습

사인파 인식을 위한 Hello World 모델 학습에는 train.py를 사용합니다.

다음을 실행합니다. bazel build tensorflow/lite/micro/examples/hello_world:train bazel-bin/tensorflow/lite/micro/examples/hello_world/train --save_tf_model --save_dir=/tmp/model_created/

추론 실행

기기에서 모델을 실행하려면 README.md의 안내를 확인하세요.

Hello World README.md

다음 섹션에서는 마이크로컨트롤러용 TensorFlow Lite를 사용하여 추론을 실행하는 방법을 보여주는 단위 테스트 예의 evaluate_test.cc를 살펴봅니다. 모델을 로드하고 추론을 여러 번 실행합니다.

1. 라이브러리 헤더 포함

TensorFlow Lite for Microcontrollers 라이브러리를 사용하려면 다음 헤더 파일을 포함해야 합니다.

#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/version.h"

2. 모델 헤더 포함

TensorFlow Lite for Microcontrollers 인터프리터는 모델이 C++ 배열로 제공될 것으로 예상합니다. 모델은 model.hmodel.cc 파일에 정의되어 있습니다. 헤더는 다음 줄에 포함되어 있습니다.

#include "tensorflow/lite/micro/examples/hello_world/model.h"

3. 단위 테스트 프레임워크 헤더 포함

단위 테스트를 만들기 위해 다음 행을 포함하여 마이크로컨트롤러용 TensorFlow Lite 단위 테스트 프레임워크를 포함했습니다.

#include "tensorflow/lite/micro/testing/micro_test.h"

테스트는 다음 매크로를 사용하여 정의합니다.

TF_LITE_MICRO_TESTS_BEGIN

TF_LITE_MICRO_TEST(LoadModelAndPerformInference) {
  . // add code here
  .
}

TF_LITE_MICRO_TESTS_END

이제 위의 매크로에 포함된 코드를 살펴보겠습니다.

4. 로깅 설정

로깅을 설정하기 위해 tflite::MicroErrorReporter 인스턴스를 가리키는 포인터를 사용하여 tflite::ErrorReporter 포인터를 만듭니다.

tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = &micro_error_reporter;

이 변수는 인터프리터로 전달되어 로그를 작성할 수 있습니다. 마이크로 컨트롤러에는 종종 다양한 로깅 메커니즘이 있으므로 tflite::MicroErrorReporter 구현은 특정 기기에 맞춤설정되도록 설계되었습니다.

5. 모델 로드

다음 코드에서 모델은 model.h에 선언된 char 배열인 g_model의 데이터를 사용하여 인스턴스화됩니다. 그런 다음 모델의 스키마 버전이 사용 중인 버전과 호환되는지 확인합니다.

const tflite::Model* model = ::tflite::GetModel(g_model);
if (model->version() != TFLITE_SCHEMA_VERSION) {
  TF_LITE_REPORT_ERROR(error_reporter,
      "Model provided is schema version %d not equal "
      "to supported version %d.\n",
      model->version(), TFLITE_SCHEMA_VERSION);
}

6. 작업 리졸버 인스턴스화

MicroMutableOpResolver 인스턴스가 선언됩니다. 이는 인터프리터에서 모델에서 사용하는 작업을 등록하고 액세스하는 데 사용됩니다.

using HelloWorldOpResolver = tflite::MicroMutableOpResolver<1>;

TfLiteStatus RegisterOps(HelloWorldOpResolver& op_resolver) {
  TF_LITE_ENSURE_STATUS(op_resolver.AddFullyConnected());
  return kTfLiteOk;

MicroMutableOpResolver에는 등록될 작업 수를 나타내는 템플릿 매개변수가 필요합니다. RegisterOps 함수는 리졸버에 작업을 등록합니다.

HelloWorldOpResolver op_resolver;
TF_LITE_ENSURE_STATUS(RegisterOps(op_resolver));

7. 메모리 할당

입력, 출력, 중간 배열에 일정량의 메모리를 미리 할당해야 합니다. 다음과 같이 크기가 tensor_arena_sizeuint8_t 배열로 제공됩니다.

const int tensor_arena_size = 2 * 1024;
uint8_t tensor_arena[tensor_arena_size];

필요한 크기는 사용 중인 모델에 따라 다르며 실험을 통해 결정되어야 할 수 있습니다.

8. 인터프리터 인스턴스화

tflite::MicroInterpreter 인스턴스를 만들고 앞에서 만든 변수를 전달합니다.

tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
                                     tensor_arena_size, error_reporter);

9. 텐서 할당

모델의 텐서에 사용할 tensor_arena에서 메모리를 할당하도록 인터프리터에 지시합니다.

interpreter.AllocateTensors();

10. 입력 형태 유효성 검사

MicroInterpreter 인스턴스는 .input(0)를 호출하여 모델의 입력 텐서를 가리키는 포인터를 제공할 수 있습니다. 여기서 0는 첫 번째 (유일한) 입력 텐서를 나타냅니다.

  // Obtain a pointer to the model's input tensor
  TfLiteTensor* input = interpreter.input(0);

그런 다음 이 텐서를 검사하여 예상 모양과 유형이 맞는지 확인합니다.

// Make sure the input has the properties we expect
TF_LITE_MICRO_EXPECT_NE(nullptr, input);
// The property "dims" tells us the tensor's shape. It has one element for
// each dimension. Our input is a 2D tensor containing 1 element, so "dims"
// should have size 2.
TF_LITE_MICRO_EXPECT_EQ(2, input->dims->size);
// The value of each element gives the length of the corresponding tensor.
// We should expect two single element tensors (one is contained within the
// other).
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
// The input is a 32 bit floating point value
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, input->type);

열거형 값 kTfLiteFloat32는 TensorFlow Lite 데이터 유형 중 하나의 참조이며 common.h에 정의됩니다.

11. 입력 값 제공

모델에 입력을 제공하기 위해 다음과 같이 입력 텐서의 콘텐츠를 설정합니다.

input->data.f[0] = 0.;

여기서는 0를 나타내는 부동 소수점 값을 입력합니다.

12. 모델 실행

모델을 실행하려면 tflite::MicroInterpreter 인스턴스에서 Invoke()를 호출하면 됩니다.

TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
  TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed\n");
}

반환 값 TfLiteStatus를 확인하여 실행이 성공했는지 확인할 수 있습니다. common.h에 정의된 TfLiteStatus의 가능한 값은 kTfLiteOkkTfLiteError입니다.

다음 코드는 값이 kTfLiteOk임을 어설션합니다. 즉, 추론이 성공적으로 실행되었음을 의미합니다.

TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);

13. 출력 가져오기

모델의 출력 텐서는 tflite::MicroInterpreter에서 output(0)를 호출하여 가져올 수 있습니다. 여기서 0는 첫 번째 (유일한) 출력 텐서를 나타냅니다.

이 예시에서 모델의 출력은 2D 텐서에 포함된 단일 부동 소수점 값입니다.

TfLiteTensor* output = interpreter.output(0);
TF_LITE_MICRO_EXPECT_EQ(2, output->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[1]);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteFloat32, output->type);

출력 텐서에서 직접 값을 읽고 예상대로 어설션할 수 있습니다.

// Obtain the output value from the tensor
float value = output->data.f[0];
// Check that the output value is within 0.05 of the expected value
TF_LITE_MICRO_EXPECT_NEAR(0., value, 0.05);

14. 추론 다시 실행

나머지 코드는 추론을 여러 번 더 실행합니다. 각 인스턴스에서 값을 입력 텐서에 할당하고, 인터프리터를 호출하고, 출력 텐서에서 결과를 읽습니다.

input->data.f[0] = 1.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.841, value, 0.05);

input->data.f[0] = 3.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(0.141, value, 0.05);

input->data.f[0] = 5.;
interpreter.Invoke();
value = output->data.f[0];
TF_LITE_MICRO_EXPECT_NEAR(-0.959, value, 0.05);