마이크로컨트롤러 환경에서 프로그램은 C/C++ 언어를 사용하여 만들어진다. Caffe2 / TensorFlow Lite for MicroControllers 은 C++ 의 클래스 라이브러리를 통해 구현되어 있으므로, 각 API의 사용법을 알아두는 것은 필수적이다.
하드웨어 계층
즉, MicroController를 말함.
마이크로컨트롤러 프로그래밍 기본은 ..
MCU 레지스터 조작 !!
MPU 레지스터
연산을 수행하는데 필요한 피연산자와 연산 결과를 임시로 저장하는 임시 기억 장소 (안중요)
입출력 레지스터
MPU 외에도 주변 장치와 데이터를 교환하기 위한 장치를 제어하는 데 필요! 번지로 레지스터 조작 가능함 (중요)
쉽지 않음
하드웨어 종속적 소프트웨어 계층
마이크로컨트롤러의 종류에 따라 다르게 구현
레지스터 조작 작업을 함수로 구현
하드웨어 독립적 소프트웨어 계층 :
서로 다른 MCU의 서로 다른 레지스터를 같은 함수로 조작가능
하지만, 두 API 모두 지원하는 마이크로컨트롤러가 제약적이기 때문에(즉, 다양한 제작사마다, 다른 조합의 MCU가 있고, API는 하드웨어 종속적이기 때문에) API 선정시, 마이크로 컨트롤러의 제작사를 고려하여야 하며, 또는 같은 방법으로 제어할 수 있는 방법을 찾아봐야한다. 텐서플로 같은 경우, 지원하지 않을 경우 이식하는 과정을 추가하여 해야하며, C++11 를 지원하여야 한다.
마이크로 컨트롤러는 연산을 담당하는 마이크로프로세서(CPU)와 이를 사용하기 위한 메모리, 입출력 장치 등의 필요한 기능이 함께 직접된 장치. 마이크로컨트롤러만으로 LED 센서와 같은 장치들을 사용할 수 있기 때문에, 작은컴퓨터, 혹은 마이컴이라 불림. 마이크로 프로세서는 단순하고, 신뢰성이 있으며, 저렴하고, 저전력인 장점이 있으나, 매우 제한된 용량, 연산 성능을 가지고 있음.
MCU 제조 회사 별 특징
STMicroelectronics : ARM 기반의 고성능 마이크로프로세서로 유명함.
Texas Instruments (TI) : 세계적으로 DSP 분야에서 독보적인 선두주자임.
자사는 TI (Texas Instruments) 사의 ADS1292 칩셋 및 CC2642 칩셋을 사용하여, 동일한 제조사를 사용하여 호환성을 확보하였다. ADS1292 칩셋과 CC2642 MCU는 개발보드에 탑재하여 구동되며, ADS1292 칩셋의 연산은 사람 몸에 흐르는 전극에서 오는 복잡한 심전도 신호 (ECG) 를 센싱한 후, CC2642 MCU의 BLE5 통신 기술로 데이터를 외부 장치로 전달 및 의미있는 결과 LED로 출력함.
본 프로젝트는
[ DATA ] ADS1292센서에서 추출한 심전도 데이터를 가지고,
[ MODEL ] CC2642 마이크로컨트롤러에 경량화된 딥러닝 모델을 탑재하여
[ PREDICTION ] 유의미한 결과 (정상/비정상)를 LED 센서를 통하여 출력하여야 한다.
따라서, 환자는 심장박동이 불규칙한 부정맥을 파악하여, 다양한 심장질환, 사고를 예방할 수 있다.
시제품 개발보드 살펴보기
MCU 가 CC2642이라는 마이크로컨트롤러이며, 아래는 시제품 보드임. 시제품 보드는, CC2642와 ADS1292 를 탑재하고 있으며, CC2642를 주변 회로와의 연결을 통해, 보다 편리하게 사용할 수 있도록 제작된 마이크로컨트롤러 보드임. 개발이 되면, 소스코드를 패치형 MCU 에 임폴트해야함.
현재 개발 사항 : 실제 패치는 다시 개발을 해야하며, 하드웨어 개발자와 패치에 들어갈 MCU 제작사에 대한 논의가 필요한 것으로 보임.
MCU/BLE :
CC2642 MCU 주요 사양
코어 : Arm Cortex-M4F
동작 주파수 : 48MHz
CC2642 마이크로컨트롤러는 32bit(??)의 Cortex-M4 코어를 기반으로 하고 있으며, 최대 48 MHz의 속도로 동작할 수 있다.
메모리 :
RAM (휘발성)
SRAM (Static RAM)
8KB of Cache SRAM (Alternatively available as general-purpose RAM)
80KB of ultra-low leakage SRAM. The SRAM is protected by parity to ensure high reliability of operation.
ROM (비휘발성)
ROM : 256KB of ROM for protocols and library functions
플래시 메모리 : 352KB of in-system Programmable Flash
동작 전압 : 3.8V
Micro 5 pin connector : usb케이블과 커넥되어 (1)전원공급 (2)컴파일한 프로그램 업로드 (3)디버깅메시지 출력 등을 수행함.
Ant. : 헬스케어 기기에 탑재되는 기술
ECG signal input : 심전도 센서
그 외 : Button / Battery / External Memory / ECG signal processor
Development Workflow ( 모델 훈련부터 탑재까지 )
1.경량화된 딥러닝 모델 개발
딥러닝 모델이 기기에 탑재 가능할 수 있도록, 기기에 메모리, 프로세서 등을 고려하여 설계해야함.
# Convert the model to the TensorFlow Lite format with quantizationconverter = tf.lite.TFLiteConverter.from_keras_model(model_2)converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]tflite_model = converter.convert()# Save the model to diskopen("sine_model_quantized.tflite", "wb").write(tflite_model)
# Install xxd if it is not available!apt-get-qqinstallxxd# Save the file as a C source file!xxd-isine_model_quantized.tflite>sine_model_quantized.cc# Print the source file!catsine_model_quantized.cc
4.TensorFlow Lite for MicroControllers C++ library
준비물
마이크로 컨트롤러를 위한 개발환경 : CCSTUDIO-TMAX4 Code Composer
보드 : CC2642 (CC2652)
개발 언어 : C++
대부분 C/C++ 사용함
C++는 코드의 재사용성이 높지만, 컴파일 후 실행 파일의 크기가 커지는 것이 단점이나, 마이크로컨트롤러의 플래시 메모리가 증가함에 따라 실행 파일의 크기가 큰 문제가 되지 않다고 함..
마이크로컨트롤러 프로그래밍에서는 레지스터 조작이 기본이 됨. 하지만, 레지스터를 조작한다는 것은 그리 쉽지 않음. 따라서, 레지스터 조작을 간편하게 할 수 있도로 레지스터 조작 작업을 함수로 구현한 하드웨어 종속적인 소프트웨어 계층이 필요함.
메인함수
main.cc
#include"tensorflow/lite/experimental/micro/examples/micro_speech/audio_provider.h"#include"tensorflow/lite/experimental/micro/examples/micro_speech/command_responder.h"#include"tensorflow/lite/experimental/micro/examples/micro_speech/feature_provider.h"#include"tensorflow/lite/experimental/micro/examples/micro_speech/micro_features/micro_model_settings.h"#include "tensorflow/lite/experimental/micro/examples/micro_speech/micro_features/tiny_conv_micro_features_model_data.h"
#include"tensorflow/lite/experimental/micro/examples/micro_speech/recognize_commands.h"#include"tensorflow/lite/experimental/micro/kernels/all_ops_resolver.h"#include"tensorflow/lite/experimental/micro/micro_error_reporter.h"#include"tensorflow/lite/experimental/micro/micro_interpreter.h"#include"tensorflow/lite/schema/schema_generated.h"#include"tensorflow/lite/version.h"intmain(int argc,char* argv[]) { // Set up logging. tflite::MicroErrorReporter micro_error_reporter; tflite::ErrorReporter* error_reporter =µ_error_reporter; // Map the model into a usable data structure. This doesn't involve any // copying or parsing, it's a very lightweight operation.const tflite::Model* model = ::tflite::GetModel(g_tiny_conv_micro_features_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);return1; } // This pulls in all the operation implementations we need. tflite::ops::micro::AllOpsResolver resolver; // Create an area of memory to use for input, output, and intermediate arrays. // The size of this will depend on the model you're using, and may need to be // determined by experimentation.constint tensor_arena_size =10*1024;uint8_ttensor_arena[tensor_arena_size]; // Build an interpreter to run the model with. tflite::MicroInterpreter interpreter(model, resolver, tensor_arena, tensor_arena_size, error_reporter);interpreter.AllocateTensors(); // Get information about the memory area to use for the model's input. TfLiteTensor* model_input =interpreter.input(0);if ((model_input->dims->size !=4) || (model_input->dims->data[0] !=1) || (model_input->dims->data[1] != kFeatureSliceCount) || (model_input->dims->data[2] != kFeatureSliceSize) || (model_input->type != kTfLiteUInt8)) {error_reporter->Report("Bad input tensor parameters in model");return1; } // Prepare to access the audio spectrograms from a microphone or other source // that will provide the inputs to the neural network. FeatureProvider feature_provider(kFeatureElementCount,model_input->data.uint8); RecognizeCommands recognizer(error_reporter);int32_t previous_time =0; // Keep reading and analysing audio data in an infinite loop.while (true) { // Fetch the spectrogram for the current time.constint32_t current_time =LatestAudioTimestamp();int how_many_new_slices =0; TfLiteStatus feature_status =feature_provider.PopulateFeatureData( error_reporter, previous_time, current_time,&how_many_new_slices);if (feature_status != kTfLiteOk) {error_reporter->Report("Feature generation failed");return1; } previous_time = current_time; // If no new audio samples have been received since last time, don't bother // running the network model.if (how_many_new_slices ==0) {continue; } // Run the model on the spectrogram input and make sure it succeeds. TfLiteStatus invoke_status =interpreter.Invoke();if (invoke_status != kTfLiteOk) {error_reporter->Report("Invoke failed");return1; } // The output from the model is a vector containing the scores for each // kind of prediction, so figure out what the highest scoring category was. TfLiteTensor* output =interpreter.output(0);uint8_t top_category_score =0;for (int category_index =0; category_index < kCategoryCount;++category_index) {constuint8_t category_score =output->data.uint8[category_index];if (category_score > top_category_score) { top_category_score = category_score; } }constchar* found_command =nullptr;uint8_t score =0;bool is_new_command =false; TfLiteStatus process_status =recognizer.ProcessLatestResults( output, current_time,&found_command,&score,&is_new_command);if (process_status != kTfLiteOk) {error_reporter->Report("RecognizeCommands::ProcessLatestResults() failed");return1; } // Do something based on the recognized command. The default implementation // just prints to the error console, but you should replace this with your // own function for a real application.RespondToCommand(error_reporter, current_time, found_command, score, is_new_command); }return0;}
TensorFlow Lite for MicroControllers C++ library 헤더 파일 임폴트
const tflite::Model* model = ::tflite::GetModel(g_tiny_conv_micro_features_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);return1;}
파이썬은 컴파일 과정을 거치지 않고, 실시간 텍스트를 분석하며 실행되는 Script 언어이다. 반면, c언어는 미리 기계어로 컴파일된 실행파일을 수행하는 컴파일러 언어이며, Script 언어와 비교하여 약 171배 수행 속도가 빠르다.
따라서, 본 프로젝트<심전도 판별 인공지능 알고리즘 모듈 경량화> 에서 알고리즘 모듈 실행 속도를 최적화하기 위하여, Cython언어를 고려한다. 대부분의 Deep Learning Framework (Tensorflow, Caffe, Pytorch) 는 Cython 패키지를 지원하며, 딥러닝 프레임워크 아래 기준을 가지고 선택한다.
딥러닝 프레임워크 선택 기준
알고리즘 모듈 크기
알고리즘 실행 속도
시스템 아키텍처와의 호환성
본 문서에서는 System Architecture ( Google 의 Firebase, Android )를 고려하여 Tensorflow를 선정한다.
Cython코드 작성
HelloTensorflow.pyx
import numpy as npimport tensorflow as tfcdef floatExecuteTensorFlow(): x = tf.placeholder(tf.float32, [1, 2]) w = tf.constant([0.2, 0.3], tf.float32) b = tf.constant( -0.1, tf.float32) linearModel = tf.matmul(x, tf.reshape(w, shape=[2, 1]))+ b session = tf.Session() result = session.run(linearModel, feed_dict={x: np.reshape([-0.1, -0.4], [1, 2])} )return np.reshape(result, [])
setup.py를 활용한 Cython 빌드
setup.py
from distutils.core import setupfrom Cython.Build import cythonizesetup(name='Hello Tensorflow app', ext_modules=cythonize("helloTensorflow.pyx"))
pythonsetup.pybuild_ext--inplace
setup.py는 Cython 파일 ( helloTensorflow.pyx ) 을 cython 컴파일러를 통해 .c파일로 변환한 후, c 컴파일러는 .helloTensorflow.c 를 컴파일하여 실행파일 helloTensorflow.so (shared object) 을 생성한다.