일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 초소형머신러닝
- 코테 문제
- 알고리즘
- TensorFlow Lite
- 삼성역량테스트
- MCU 딥러닝
- 삼성코딩테스트
- dfs문제
- DP문제
- 포스코 AI교육
- 자료구조
- tinyml
- 포스코 ai 교육
- sort
- 포스코 교육
- bfs문제
- 코테
- 딥러닝
- 코딩테스트
- 컴퓨팅사고
- 삼성역테
- 다이나믹프로그래밍
- 삼성코테
- BFS
- DP
- 그리디
- dfs
- 임베디드 딥러닝
- tflite
- 영상처리
- Today
- Total
코딩뚠뚠
[머신러닝 공부] Tiny ML -14 / 인체감지 어플리케이션-2 본문
Chapter14. 인체감지 어플리케이션
"
카메라로 인체를 감지해보자 (CNN)
"
목차 :
- 개요 -> 이전장
- 만들고자하는 -> 이전장
- 어플리케이션 아키텍처 -> 이전장
- 코드 기본흐름
- 핵심 함수 분석
- 마이크로컨트롤러 배포 -> 다음장
이전장 링크
참고 repo
코드 기본흐름 :
- person_detection_test.cc
- image_provider.h
- detection_responder.h
- detection_responder_test.cc
- person_detection_test.cc
▼ 모델에 적합한 크기의 tensor_arena를 정의한다.
constexpr int tensor_arena_size = 73 * 1024;
uint8_t tensor_arena[tensor_arena_size];
▼ 인터프리터를 준비한다.
▼ MicroMutableOpResolver를 사용해 필요한 Op를 등록한다.
// 로깅 설정
tflite::MicroErrorReporter micro_error_reporter;
tflite::ErrorReporter* error_reporter = µ_error_reporter;
// 모델을 사용가능한 데이터 구조에 매핑한다.
const tflite::Model* model = ::tflite::GetModel(g_person_detect_model_data);
if (model->version() != TFLITE_SCHEMA_VERSION) {
error_reporter->Report(
"Model provided is schema version %d not equal "
"to supported version %d.\n",
model->version(), TFLITE_SCHEMA_VERSION);
}
// Op구현을 가져온다 (필요한것만)
tflite::MicroMutableOpResolver micro_mutable_op_resolver;
micro_mutable_op_resolver.AddBuiltin(
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
tflite::ops::micro::Register_CONV_2D());
micro_mutable_op_resolver.AddBuiltin(
tflite::BuiltinOperator_AVERAGE_POOL_2D,
tflite::ops::micro::Register_AVERAGE_POOL_2D());
// 모델을 실행할 인터프리터를 빌드한다
tflite::MicroInterpreter interpreter(model, micro_mutable_op_resolver,
tensor_arena, tensor_arena_size,
error_reporter);
interpreter.AllocateTensors();
▼ 입력 텐서를 검사한다.
TfLiteTensor* input = interpreter.input(0);
TF_LITE_MICRO_EXPECT_NE(nullptr, input);
TF_LITE_MICRO_EXPECT_EQ(4, input->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, input->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(kNumRows, input->dims->data[1]);
TF_LITE_MICRO_EXPECT_EQ(kNumCols, input->dims->data[2]);
TF_LITE_MICRO_EXPECT_EQ(kNumChannels, input->dims->data[3]);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteUInt8, input->type);
> 위를 통해 입력이 5D Tensor 구조임을 확인할 수 있다.
▼ for문을 통해 테스트 이미지를 입력텐서에(메모리영역으로) 복사해준다.
for (int i = 0; i < input->bytes; ++i) {
input->data.uint8[i] = person_data[i];
}
▼ 입력텐서를 채운 후 추론을 실행한다.
TfLiteStatus invoke_status = interpreter.Invoke();
if (invoke_status != kTfLiteOk) {
error_reporter->Report("Invoke failed\n");
}
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, invoke_status);
▼ 출력텐서를 확인한다.
TfLiteTensor* output = interpreter.output(0);
TF_LITE_MICRO_EXPECT_EQ(4, output->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[1]);
TF_LITE_MICRO_EXPECT_EQ(1, output->dims->data[2]);
TF_LITE_MICRO_EXPECT_EQ(kCategoryCount, output->dims->data[3]);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteUInt8, output->type);
> 위를 통해 모델 출력은 4D 임을 알 수 있다.
> 처음 세 차원은 네 번째 차원을 감싸는 래퍼이다. - 사실 결과는 1D여도 되는데 이 모델은 출력이 이렇다.. 그냥 인덱싱해서 사용하면 된다.
▼ '사람' 과 '사람없음' 점수를 기록 후 '사람' 점수가 더 높은지 확인한다.
uint8_t person_score = output->data.uint8[kPersonIndex];
uint8_t no_person_score = output->data.uint8[kNotAPersonIndex];
error_reporter->Report(
"person data. person score: %d, no person score: %d\n", person_score,
no_person_score);
TF_LITE_MICRO_EXPECT_GT(person_score, no_person_score);
> 점수의 최소값은 0 최대값은 255이다.
▼ 이번에는 no_person_data 즉 사람이 없는 이미지도 테스트해본다.
const uint8_t* no_person_data = g_no_person_data;
for (int i = 0; i < input->bytes; ++i) {
input->data.uint8[i] = no_person_data[i];
}
▼ 추론을 실행한 후 '사람없음' 점수가 더 높은지 확인한다.
person_score = output->data.uint8[kPersonIndex];
no_person_score = output->data.uint8[kNotAPersonIndex];
error_reporter->Report(
"no person data. person score: %d, no person score: %d\n", person_score,
no_person_score);
TF_LITE_MICRO_EXPECT_GT(no_person_score, person_score);
테스트를 실행해본다.
make -f tensorflow/lite/micro/tools/make/Makefile test_person_detection_test
실행결과
> 맨 아래줄과 같이 테스트가 통과되었음을 알 수 있다.
- image_provider_test.cc
카메라에서 데이터를 가져와 모델의 입력 텐서에 쓰기 적합한 형식으로 변환하는 코드이다.
인터페이스는 image_provider.h 에 구현되어있다.
▼ 이미지 데이터를 보유할 배열을 생성한다.
uint8_t image_data[kMaxImageSize];
▼ 카메라에서 이미지를 캡처한다. (GetImage())
TfLiteStatus get_status =
GetImage(error_reporter, kNumCols, kNumRows, kNumChannels, image_data);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, get_status);
TF_LITE_MICRO_EXPECT_NE(image_data, nullptr);
> 전달하는 인수에는 ErrorReporter 인스턴스, 열 개수, 행 개수, 채널 개수, image_data배열 포인터가 담겨있다.
> 함수는 이미지데이터를 image_data 배열에 쓰고 반환값이 kTLiteError라면 에러, kTLiteOk라면 정상이다.
▼ 반환된 데이터를 확인한다.
uint32_t total = 0;
for (int i = 0; i < kMaxImageSize; ++i) {
total += image_data[i];
}
테스트를 실행해본다.
make -f tensorflow/lite/micro/tools/make/Makefile test_image_provider_test
실행결과
> 위와 같이 테스트 PASS를 확인할 수 있었다.
- detection_responder_test.cc
detection_responder는 추론 결과를 전달하는 코드이다.
인터페이스는 detection_responder.h에 정의되어있다.
▼ 인터페이스는 '사람' 과 '사람없음' 카테고리의 점수를 인수로 전달한다.
void RespondToDetection(tflite::ErrorReporter* error_reporter,
uint8_t person_score, uint8_t no_person_score);
▼ 테스트는 이 함수를 여러 번 호출하는 코드이다.
RespondToDetection(error_reporter, 100, 200);
RespondToDetection(error_reporter, 200, 100);
테스트를 실행해본다.
make -f tensorflow/lite/micro/tools/make/Makefile test_detection_responder_test
실행결과
역시 만들어져있는 코드라 PASS이다.ㅎㅎ
핵심 함수 분석 :
- main_functions.cc :
▼ 모델에 필요한 모든 Op를 가져온다.
생략 (#include 등등)
▼ 몇 개의 포인터 변수를 선언한다.
tflite::ErrorReporter* error_reporter = nullptr;
const tflite::Model* model = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
TfLiteTensor* input = nullptr;
▼ 메모리를 할당한다.
constexpr int kTensorArenaSize = 73 * 1024;
static uint8_t tensor_arena[kTensorArenaSize];
▼ setup() 함수 : 오류 리포터 작성, 모델 로드, 인터프리터 인스턴스 설정, 입력텐서에 대한 참조를 가져온다.
void setup() {
▽ 로깅 설정
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = µ_error_reporter;
▽ 모델을 데이터 구조에 매핑한다
model = tflite::GetModel(g_person_detect_model_data);
if (model->version() != TFLITE_SCHEMA_VERSION) {
error_reporter->Report(
"Model provided is schema version %d not equal "
"to supported version %d.",
model->version(), TFLITE_SCHEMA_VERSION);
return;
}
▽ 필요한 Op 구현을 가져온다
static tflite::MicroMutableOpResolver micro_mutable_op_resolver;
micro_mutable_op_resolver.AddBuiltin(
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
tflite::ops::micro::Register_CONV_2D());
micro_mutable_op_resolver.AddBuiltin(
tflite::BuiltinOperator_AVERAGE_POOL_2D,
tflite::ops::micro::Register_AVERAGE_POOL_2D());
▽ 모델을 실행할 인터프리터를 빌드한다
static tflite::MicroInterpreter static_interpreter(
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
error_reporter);
interpreter = &static_interpreter;
▽ 모델 텐서에 tensor_arena 메모리를 할당한다
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk) {
error_reporter->Report("AllocateTensors() failed");
return;
}
▽ 모델의 입력에 사용할 메모리 영역에 대한 정보를 가져온다.
input = interpreter->input(0);
▼ loop() 함수 : 메인루프에서 계속 호출되는 부분의 동작을 정의한다.
void loop() {
▽ 이미지 추출기로 이미지를 가져온 뒤 참조를 입력텐서로 전달한다.
if (kTfLiteOk != GetImage(error_reporter, kNumCols, kNumRows, kNumChannels,
input->data.uint8)) {
error_reporter->Report("Image capture failed.");
▽ 추론을 실행하고 출력텐서를 가져와 각각의 점수를 읽는다. ('사람' '사람없음' 의)
if (kTfLiteOk != interpreter->Invoke()) {
error_reporter->Report("Invoke failed.");
}
TfLiteTensor* output = interpreter->output(0);
// Process the inference results.
uint8_t person_score = output->data.uint8[kPersonIndex];
uint8_t no_person_score = output->data.uint8[kNotAPersonIndex];
RespondToDetection(error_reporter, person_score, no_person_score);
> 이 점수는 RespondToDetection() 으로 전달된ㄷ다.
main 문 :
#include "tensorflow/lite/micro/examples/person_detection/main_functions.h"
int main(int argc, char* argv[]) {
setup();
while (true) {
loop();
}
}
프로그램을 로컬환경에서 실행해보자.
아래 명령어로 프로그램을 빌드한다.
make -f tensorflow/lite/micro/tools/make/Makefile person_detection
예제를 실행한다.
tensorflow/lite/micro/tools/make/gen/linux_x86_64/bin person_detection
> 당연히 받은 사람 이미지가 없으니 다음과 같은 결과가 나온다.
이렇게 코드를 살펴볼 수 있었다.
정교한 머신러닝 모델을 복잡하지 않은 방법으로 임베디드 기기에 이식할 수 있는것을 알 수 있었다.
다음 장에서는 실제로 모델을 MCU에 이식해볼 예정인데..
카메라가 해외배송이라 한달후에나 온다고 한다ㅎㅎ
오기전까지 다른걸 공부하고 있어야겠다.
끝
'공부 > ML&DL' 카테고리의 다른 글
[머신러닝 공부] Tiny ML -16 / 인체감지 모델 훈련하기 (0) | 2022.03.28 |
---|---|
[머신러닝 공부] Tiny ML -15 / 인체감지 어플리케이션-3 (2) | 2022.03.22 |
[머신러닝 공부] Tiny ML -13 / 인체감지 어플리케이션-1 (0) | 2022.02.17 |
[머신러닝 공부] Tiny ML -12 / 음성인식 모델훈련하기 -3 (0) | 2022.02.13 |
[머신러닝 공부] Tiny ML -11 / 음성인식 모델훈련하기 -2 (0) | 2022.02.07 |