TinyML로 엣지 디바이스 추론 자원 최적화하기
TinyML 환경에서 양자화·프루닝·경량 아키텍처·커널 최적화를 조합해 엣지 디바이스의 메모리, 지연, 전력 제약을 다루는 방법
2026-08-14 · 최초 발행 2024-04-29
MCU 제약 안에서 추론을 설계하는 방법
엣지 IoT 장비는 수십 KB수 MB 메모리와 수 mW수백 mW 전력 예산 안에서 동작해야 한다. 이 환경에서는 학습된 모델을 그대로 배포하는 대신, 연산량·메모리·전력·정확도 사이의 균형을 맞추는 경량화가 필요하다.
Lightweight Algorithms for Edge Devices는 이런 제약을 전제로 한 알고리즘 설계 방식이다. 정수 양자화, 구조적·비구조적 프루닝, Depthwise 연산 치환, MobileNet이나 DS-CNN 같은 경량 아키텍처가 여기에 포함된다.
TinyML Optimization은 모델 변환만을 뜻하지 않는다. 학습부터 변환, 배포, 운영까지를 엣지 조건에 맞춰 다루는 MLOps 과정이며, 모델 용량(Byte), 지연 시간(ms), 에너지(µJ/추론), 정확도(%)를 다목적으로 최적화한다.
모델과 연산을 가볍게 만드는 선택지
양자화는 float32 모델을 int8 또는 uint8로 변환하는 방법이다. 메모리를 4× 줄일 수 있고 SIMD 가속을 활용할 수 있지만, 스케일과 제로포인트 보정을 위해 대표데이터가 필요하다. PTQ와 QAT는 모두 이 흐름에 속한다.
프루닝은 가중치를 희소화해 MAC을 줄인다. 구조적 프루닝은 임베디드 커널 효율 측면에서 유리하지만, 비구조적 프루닝은 런타임이 스파스를 지원해야 한다. 지식 증류는 대형 스승 모델을 통해 소형 학생 모델을 학습시키며, NAS는 탐색으로 아키텍처를 자동 최적화한다.
연산 계층에서는 Depthwise/Pointwise와 Grouped Conv를 활용해 계산량을 낮출 수 있다. Operator fusion과 INT8 GEMM 최적화도 함께 고려한다. CMSIS-NN, TFLite Micro, uTVM처럼 타깃에 맞는 최적화 커널을 선택하고, 캐시 및 메모리 배치(Locality)를 반영해야 한다.
입력 데이터도 자원 예산의 일부다. 저해상도·저샘플링, 고정소수점 전처리, 슬라이딩 윈도우 설계를 검토한다. MFCC, FFT, Δ-Feature 같은 전처리 피처는 정밀도를 줄이거나 LUT를 적용할 수 있다.
배포 이후까지 포함한 운영 설계
모델 배포는 A/B 슬롯을 사용한 원자적 모델 스왑으로 구성할 수 있다. 유효성 검증이 실패했을 때 이전 모델로 되돌릴 수 있어 롤백 안전성을 확보한다.
운영 중에는 Confidence와 Drift를 온디바이스에서 확인하고, 저전력 모드 전환이나 네트워크 불안정 시 지연전송 큐 처리를 함께 설계한다. Confidence < τ이거나 Drift가 감지되면 클라우드 폴백 업로드를 수행한다. 배터리 임계치가 낮아지면 샘플링과 추론 주기를 낮추고, OTA 유효성 검증이 실패하면 A/B 슬롯과 원자적 커밋을 이용해 자동 롤백한다.
제약 조건이 다른 엣지 적용 장면
제조·설비 예지보전에서는 Cortex-M4F@80MHz, RAM 256KB를 갖춘 배터리 IoT 노드에서 1D 가속도계 입력을 FFT/MFCC로 변환하고, INT8 DS-CNN으로 이상치를 알릴 수 있다. 이 파이프라인의 결과 예시는 윈도우당 지연 815ms, 모델 크기 4080KB, 월간 배터리 수명 10~20% 연장이다.
스마트 홈 키워드 스포팅은 16kHz 오디오와 MFCC 20차, 1초 윈도우를 사용한다. QAT와 CMSIS-NN 커널을 적용하면 INT8 KWS 모델을 2060KB로 만들 수 있으며, 온라인 인퍼런스는 10ms 이하로 유지하고 오탐률은 12%p 내에서 유지한다.
엣지 비전에서는 96×96 Grayscale 입력에 MobileNetV1-0.25 또는 TinyConv를 사용할 수 있다. 프루닝 50%와 PTQ를 조합하면 모델 크기 200400KB, MCU/DSP에서 FPS 510을 목표로 한다. 이미지 외부 전송이 필요하지 않아 프라이버시 보호와 네트워크 비용 절감에도 연결된다.
자원 절감 효과와 운영상 차이
INT8 양자화는 메모리와 스토리지를 4× 줄일 수 있으며, 프루닝을 함께 적용하면 610×가 가능하다. INT8 커널은 28× 속도 향상에 기여하고, 작업 및 부하에 따라 배터리 소모를 3060% 줄일 수 있다. 엣지 추론으로 전환하면 업링크 데이터는 7095% 감소한다.
온디바이스 처리는 프라이버시와 규제 준수를 강화하며, 오프라인 상황에서도 동작할 수 있다. 실시간성을 확보하고 현장 설치의 선택 폭도 넓어진다.
| 기법 | 성능(정확도/지연) | 확장성(기기·도메인) | 일관성(재현성) | 안정성(런타임 리스크) | 운영 편의(툴·배포) |
|---|---|---|---|---|---|
| 양자화(PTQ/QAT) | 정확도 손실 0~2%p(PTQ), QAT로 보정, 지연↓ | 범용 커널 광범위 지원 | 대표데이터 품질에 좌우 | SNR 낮을 때 과양자화 리스크 | TFLM/TVM 지원 풍부 |
| 프루닝(구조적) | 지연↓, 정확도 손실 0~3%p | 하드웨어/커널 의존 | 재훈련 필요 시 편차 | 과도 프루닝 시 불안정 | 프레임워크 지원 중간 |
| 지식 증류 | 소형 모델 정확도↑ | 다양한 도메인 이식 | 스승 모델 품질 영향 | 데이터 시프트 민감 | 파이프라인 복잡도↑ |
| 경량 아키텍처 | 지연·메모리 우수 | 다양한 MCU 적용 용이 | 학습 안정성 보통 | 과적합 관리 필요 | 배포 용이 |
입력에서 OTA 패키지까지의 흐름
입력은 오디오·진동·이미지 같은 센서 스트림과 디바이스의 RAM, Flash, 주파수, 전력 예산이다. 데이터 프로파일링 후 피처와 윈도우를 설계하고, 베이스라인 학습을 진행한다. 이어 PTQ/QAT, 프루닝, 지식 증류를 적용하고 타깃별 커널로 매핑한다.
마지막으로 지연·메모리·에너지를 프로파일링하고 온디바이스에서 검증한다. 결과물은 모델과 메타데이터를 묶은 OTA 패키지, 알람이나 명령 같은 추론 결과, 성능과 품질 로그를 담은 텔레메트리다.
INT8 변환과 MCU 추론 예시
전제조건은 Python 3.10, TensorFlow 2.12+ (CPU)이며, 타깃은 Cortex-M4F와 TFLite Micro(INT8)다.
# pip install tensorflow==2.12 numpy
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# 1) 간단한 1D CNN (예: KWS/진동)
def build_model(input_len=16000, n_classes=2):
inp = keras.Input(shape=(input_len, 1))
x = layers.Conv1D(16, 3, strides=2, padding="same", activation="relu")(inp)
x = layers.DepthwiseConv1D(3, padding="same")(x)
x = layers.Conv1D(32, 1, activation="relu")(x)
x = layers.GlobalAveragePooling1D()(x)
out = layers.Dense(n_classes, activation="softmax")(x)
return keras.Model(inp, out)
model = build_model()
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
# 더미 데이터 (실전에서는 MFCC 등 전처리 후 입력)
X = np.random.randn(256, 16000, 1).astype(np.float32)
y = np.random.randint(0, 2, size=(256,))
model.fit(X, y, epochs=2, batch_size=16, verbose=0)
# 2) 대표 데이터셋 함수 (PTQ 교정용)
def representative_dataset():
for i in range(100):
sample = np.random.randn(1, 16000, 1).astype(np.float32)
yield [sample]
# 3) TFLite INT8 변환
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
tflite_model = converter.convert()
with open("model_int8.tflite", "wb") as f:
f.write(tflite_model)
print("INT8 TFLite size (bytes):", len(tflite_model))
// Arduino/MCU 환경, TFLite Micro 라이브러리 필요
#include "model_int8.h" // 툴로 바이트 배열 변환
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
constexpr int kArenaSize = 80 * 1024;
static uint8_t tensor_arena[kArenaSize];
tflite::AllOpsResolver resolver;
const tflite::Model* model = tflite::GetModel(g_model_int8);
tflite::MicroInterpreter interpreter(model, resolver, tensor_arena, kArenaSize);
interpreter.AllocateTensors();
TfLiteTensor* input = interpreter.input(0);
// 입력 스케일/제로포인트에 맞춰 int8로 양자화
// for (...) input->data.int8[i] = QuantizeFloatToInt8(x, input->params.scale, input->params.zero_point);
TfLiteStatus invoke_status = interpreter.Invoke();
// 출력 디퀀타이즈 후 의사결정 수행
대표데이터에는 운영 분포와 같은 센서·노이즈·온도 조건을 반영한다. PTQ에서 정확도 손실이 2%p 이상이면 QAT를 권장한다. 모델은 Flash, 텐서 아레나는 RAM으로 나누어 측정해 Arena 오버런을 방지한다. 프로파일링에서는 지연(ms), 에너지(µJ/inf), 캐시 미스, 음성·진동 윈도우 경계 효과를 확인한다. 운영 단계에서는 A/B OTA, 버전과 입력 스키마의 일관성 검증, Confidence/Drift 트리거 기반 클라우드 폴백을 함께 둔다.