라즈베리 파이 피코를 이용한 음악 장르 인식

이 글은 제 책 TinyML Cookbook, Second Edition의 일부입니다. 이 글에서 사용된 코드는 여기에서 확인하실 수 있습니다.

준비하기

이 글에서 설계할 애플리케이션은 1초 오디오 클립을 연속적으로 녹음하고 모델 추론을 실행하는 것입니다. 다음 이미지에서 설명하듯이:

그림 1: 순차적으로 실행되는 녹음 및 처리 작업

앞의 이미지에 표시된 작업 실행 타임라인에서 볼 수 있듯이, 특징 추출과 모델 추론은 항상 오디오 녹음 후에 수행되며 동시에 실행되지 않습니다. 따라서 라이브 오디오 스트림의 일부 세그먼트를 처리하지 않는다는 것이 분명합니다.

실시간 키워드 스팟팅(KWS) 애플리케이션과 달리, 이 애플리케이션은 모든 오디오 스트림을 캡처하고 처리하여 말하는 내용을 놓치지 않아야 합니다. 그러나 여기서는 이러한 요구 사항을 완화할 수 있으며, 이는 애플리케이션의 효과에 영향을 미치지 않습니다.

우리가 알다시피, MFCCs 특징 추출의 입력은 Q15 형식의 1초 원본 오디오입니다. 그러나 마이크로폰으로 얻은 샘플은 16비트 정수 값으로 표현됩니다. 따라서 16비트 정수 값을 Q15로 어떻게 변환할까요? 해결책은 당신이 생각하는 것보다 훨씬 간단합니다: 오디오 샘플을 변환할 필요가 없습니다.

이해를 돕기 위해 Q15 고정소수점 형식을 고려해보세요. 이 형식은 [-1, 1] 범위 내의 부동소수점 값을 표현할 수 있습니다. 부동소수점에서 Q15로 변환하려면 부동소수점 값에 32,768 (2^15)을 곱해야 합니다. 그러나 부동소수점 표현이 16비트 정수 샘플을 32,768 (2^15)로 나누어 시작하기 때문에, 16비트 정수 값이 Q15 형식으로 본질적임을 의미합니다.

이렇게 해보세요…

마이크가 장착된 브레드보드를 라즈베리 파이 피코에 연결합니다. 마이크로컨트롤러에서 데이터 케이블을 분리하고, 이 레시피에 필요하지 않으므로 브레드보드에서 푸시 버튼과 연결된 점퍼 케이블을 제거하세요. 그림 2는 브레드보드에 있어야 할 것을 보여줍니다:

그림 2: 브레드보드에 구축된 전자 회로

푸시 버튼을 브레드보드에서 제거한 후, Arduino IDE를 열고 새 스케치를 만드세요.

이제 라즈베리 파이 피코에서 음악 장르 인식 애플리케이션을 개발하기 위해 다음 단계를 따르세요:

1 단계

Arduino TensorFlow Lite 라이브러리를 TinyML-Cookbook_2E GitHub 저장소에서 다운로드하세요.

ZIP 파일을 다운로드한 후 Arduino IDE에 가져옵니다.

2단계

MFCCs 특징 추출 알고리즘에 필요한 모든 생성된 C 헤더 파일을 Arduino IDE에 가져오되, test_src.htest_dst.h를 제외하세요.

3단계

제6장,라즈베리 파이 피코에서 MFCCs 특징 추출 알고리즘 배포에서 개발된 스케치를 복사하여 MFCCs 특징 추출을 구현하되, setup()loop() 함수를 제외하세요.

test_src.htest_dst.h 헤더 파일의 포함을 제거하고, dst 배열의 할당을 제거하세요. MFCCs는 모델의 입력에 직접 저장됩니다.

4단계

제5장, 텐서플로우와 라즈베리 파이 피코로 음악 장르 인식 – 1부 Pi Pico에서 개발된 스케치를 복사하여 마이크로 오디오 샘플을 녹음하되, setup()loop() 함수를 제외하세요.

코드를 가져온 후 LED와 푸시 버튼에 대한 어떤 참조도 제거하세요. 이제 필요하지 않습니다. 그런 다음, AUDIO_LENGTH_SEC의 정의를 1초 동안 오디오를 녹음하도록 변경하세요:

C++

 

#define AUDIO_LENGTH_SEC 1

5단계

텐서플로 라이트 모델을 포함하는 헤더 파일(model.h)을 Arduino 프로젝트에 가져오세요.

파일을 가져온 후 스케치에 model.h 헤더 파일을 포함시키세요:

C++

 

#include "model.h"

C++

 

#include 
#include  
#include 
#include 
#include 
#include 

6단계

tflite-micro 모델과 인터프리터에 대한 글로벌 변수를 선언하세요:

C++

 

const tflite::Model* tflu_model            = nullptr;
tflite::MicroInterpreter* tflu_interpreter = nullptr;

그런 다음, 모델의 입력 및 출력 텐서에 접근하기 위한 TensorFlow Lite 텐서 객체(TfLiteTensor)를 선언하세요:

C++

 

TfLiteTensor* tflu_i_tensor = nullptr;
TfLiteTensor* tflu_o_tensor = nullptr;

7단계

모델 실행 중에 사용되는 중간 텐서를 저장하기 위한 버퍼(텐서 아레나)를 선언하세요:

C++

 

constexpr int tensor_arena_size = 16384;
uint8_t tensor_arena[tensor_arena_size] __attribute__((aligned(16)));

텐서 아레나의 크기는 LSTM 연산자가 어떻게 구현되는지에 따라 달라지는 중간 텐서의 메모리 요구량에 따라 경험적으로 결정되었습니다. 라즈베리 파이 피코에 대한 실험을 통해 모델이 추론에 16KB의 RAM만을 요구한다는 것을 발견했습니다.

8단계

setup() 함수에서 115200 보드레이트로 직렬 펌웨어를 초기화하세요:

C++

 

Serial.begin(115200);
while (!Serial);

직렬 펌웨어는 인식된 음악 장르를 직렬 통신으로 전송하는 데 사용됩니다.

9단계

setup() 함수에서 model.h 헤더 파일에 저장된 TensorFlow Lite 모델을 로드하세요:

C++

 

tflu_model = tflite::GetModel(model_tflite);

그런 다음, tflite-micro가 지원하는 모든 DNN 연산을 등록하고 tflite-micro 인터프리터를 초기화하세요:

C++

 

tflite::AllOpsResolver tflu_ops_resolver;

static tflite::MicroInterpreter static_interpreter(
      tflu_model,
      tflu_ops_resolver,
      tensor_arena,
      tensor_arena_size);
tflu_interpreter = &static_interpreter;

10단계

setup() 함수에서 모델이 필요로 하는 메모리를 할당하고 입력 및 출력 텐서의 메모리 포인터를 가져오세요:

C++

 

tflu_interpreter->AllocateTensors();
tflu_i_tensor = tflu_interpreter->input(0);
tflu_o_tensor = tflu_interpreter->output(0);

11단계

setup() 함수에서 Raspberry Pi Pico SDK를 사용하여 ADC 펜션을 초기화하세요:

C++

 

adc_init();
adc_gpio_init(26);
adc_select_input(0);

12단계

loop() 함수에서 모델의 입력을 준비하세요. 이를 위해 1초 동안 오디오 클립을 녹음하세요:

C++

 

// 오디오 버퍼 리셋
buffer.cur_idx = 0;
buffer.is_ready = false;

constexpr uint32_t sr_us = 1000000 / SAMPLE_RATE;
timer.attach_us(&timer_ISR, sr_us);

while(!buffer.is_ready);

timer.detach();

오디오 녹음 후 MFCC를 추출하세요:

C++

 

mfccs.run((const q15_t*)&buffer.data[0],
          (float *)&tflu_i_tensor->data.f[0]);

앞서 제시된 코드 스니펫에서 볼 수 있듯이 MFCC는 모델의 입력에 직접 저장됩니다.

13단계

모델 추론을 실행하고 직렬 통신을 통해 분류 결과를 반환하세요:

C++

 

tflu_interpreter->Invoke();

size_t ix_max = 0;
float pb_max = 0;
for (size_t ix = 0; ix < 3; ix++) {
  if(tflu_o_tensor->data.f[ix] > pb_max) {
    ix_max = ix;
    pb_max = tflu_o_tensor->data.f[ix];
  }
}

const char *label[] = {"disco", "jazz", "metal"};

Serial.println(label[ix_max]);

이제 마이크로 USB 데이터 케이블을 Raspberry Pi Pico에 꽂으세요. 연결하고 나면 마이크로 컨트롤러에 스케치를 컴파일하고 업로드하세요.

그 후 Arduino IDE에서 직렬 모니터를 열고 스마트폰을 마이크 근처에 놓고 디스코, 재즈 또는 메탈 노래를 재생하세요. 애플리케이션이 이제 노래의 음악 장르를 인식하고 직렬 모니터에 분류 결과를 표시해야 합니다!

결론

이 글에서는 tflite-micro를 사용하여 Raspberry Pi Pico에 음악 장르 분류를 위한 훈련된 모델을 배포하는 방법을 배웠습니다.

Source:
https://dzone.com/articles/recognizing-music-genres-with-the-raspberry-pi-pic