AI/ML Uç Bilişim
TEKNİK REHBER AI/ML UÇ BİLİŞİM TFLite Micro 2026

TFLite Micro —
MCU/Edge çıkarımı.

TensorFlow Lite Micro ile heap'siz, RTTI'sız MCU'larda model çıkarımı. INT8 quantization'dan arena allocator'a, STM32 X-CUBE-AI'dan Raspberry Pi deployment'a kapsamlı rehber.

00 TFLite nedir? — TF ekosistemindeki yeri

TensorFlow Lite (TFLite), TensorFlow modellerini uç cihazlarda çalıştırmak için tasarlanmış hafif bir çıkarım çerçevesidir. TFLite Micro ise bu çerçevenin heap ve işletim sistemi olmaksızın bare-metal MCU'larda bile çalışan alt kümesidir.

TF ekosisteminde konum

  TensorFlow (Eğitim)
  ┌──────────────────────────────────────────────┐
  │  tf.keras model  →  saved_model  →  .tflite  │
  │                       ↓                      │
  │            TFLite Converter                   │
  │         (quantize + flatbuffer)               │
  └──────────────────────────────────────────────┘
            ↓                    ↓
  ┌─────────────────┐   ┌─────────────────────┐
  │   TFLite Runtime│   │  TFLite Micro        │
  │  Linux / Android│   │  Cortex-M / bare     │
  │  iOS / Windows  │   │  metal / Zephyr      │
  └─────────────────┘   └─────────────────────┘
    
TFLite Runtime
Heap kullanan tam özellikli runtime. Linux, Android, iOS, Windows hedef alır. Dynamic tensor allocation destekler. ~1 MB binary footprint.
TFLite Micro (TFLM)
Statik bellek (arena allocator), no heap, no RTTI, no exceptions. Cortex-M0+ ile çalışır. ~20 KB binary. libc bağımlılığı minimumdur.
.tflite Formatı
FlatBuffer tabanlı ikili format. Hem TFLite hem TFLM aynı .tflite dosyasını okur. Ağırlıklar, grafik topolojisi ve quantization parametrelerini içerir.
Desteklenen hedefler
ARM Cortex-M (STM32, nRF52, SAMD51), ESP32-S3, RPi, Linux x86_64, Android NDK, Arduino, Zephyr RTOS.

TFLite vs TFLite Micro karşılaştırması

ÖzellikTFLite RuntimeTFLite Micro
Heap tahsisiEvet (malloc/free)Hayır (arena)
RTTI / ExceptionsEvetHayır
İşletim sistemiGerekliOpsiyonel
Binary boyutu~1 MB~20 KB
Min RAM~5 MB~16 KB
Op sayısı~150+~50 (seçici)
Flatbuffer yüklemeDinamikFlash'tan ROM
Not

TFLite Micro, TFLite'ın bir "alt kümesi" değil, ayrı bir kaynak ağacıdır. Op kernel implementasyonları optimize edilmiş CMSIS-NN rutinlerini çağırabilir. Tüm .tflite modeller TFLM ile uyumlu değildir — kullanılan op'ların TFLM kerneli olmalıdır.

01 Model quantization — INT8, INT4, float16

Quantization, kayan noktalı ağırlıkları (float32) düşük bit genişlikli tam sayılara (INT8, INT4) dönüştürür. Bu işlem model boyutunu 4x küçültür ve MCU'larda mevcut olan SIMD tam sayı birimlerini devreye sokar.

Quantization türleri

Post-Training Quantization (PTQ)
Eğitilmiş float32 modeli kalibrasyonlu veri seti ile dönüştürür. Hızlı uygulanır; küçük doğruluk kaybı (<1-2%) beklenir. INT8 ve float16 PTQ destekler.
Quantization-Aware Training (QAT)
Eğitim sırasında sahte quantization (fake-quant) düğümleri ekler. Model, quantization gürültüsünü "öğrenir". PTQ'ya kıyasla daha iyi doğruluk, ancak yeniden eğitim gerektirir.
INT8 Tam Quantization
Ağırlıklar ve aktivasyonlar INT8'e dönüştürülür. Model 4x küçülür, çıkarım 2-4x hızlanır. Cortex-M SIMD (SSAT, VMLA) ile doğrudan uyumludur.
float16 Quantization
Yalnızca ağırlıklar float16. Aktivasyonlar float32 kalır. GPU'lu cihazlarda (RPi + VideoCore) işe yarar; MCU'da avantaj sınırlıdır.
INT4 (Dynamic Range)
TF 2.13+ ile desteklenir. Ağırlıklar INT4, aktivasyonlar INT8/float. Bellek baskısı çok yüksek MCU senaryolarında kullanılır; doğruluk kaybı daha yüksektir.

PTQ — Python kodu

ptq_int8.py
import tensorflow as tf
import numpy as np

# Eğitilmiş modeli yükle
model = tf.keras.models.load_model("gesture_mobilenet.h5")

# Kalibrasyon veri seti oluştur (temsili örnekler)
def representative_dataset():
    for _ in range(200):
        data = np.random.rand(1, 96, 96, 3).astype(np.float32)
        yield [data]

# TFLite Converter
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset

# Tam INT8: hem ağırlıklar hem aktivasyonlar
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("gesture_int8.tflite", "wb") as f:
    f.write(tflite_model)

print(f"Model boyutu: {len(tflite_model) / 1024:.1f} KB")

Accuracy-Size tradeoff

QuantizationBoyutAccuracyLatency (CM4)Hedef
float32 (baseline)5.2 MB92.4%420 msLinux/Android
float16 PTQ2.6 MB92.2%380 msLinux/GPU
INT8 PTQ1.3 MB91.8%95 msRPi/MCU
INT8 QAT1.3 MB92.1%95 msRPi/MCU
INT4 Dynamic0.7 MB89.5%68 msMCU (Flash kısıtlı)
İpucu

QAT için tensorflow_model_optimization paketini kullan: tfmot.quantization.keras.quantize_model(model). QAT sonrası model PTQ pipeline'ına sokulur; çift adım zahmetli ama kritik doğruluk gerektiren uygulamalarda zorunludur.

02 TFLite Micro mimarisi — arena, op resolver, no-heap

TFLM'in tasarımı "sıfır dinamik bellek tahsisi" ilkesine dayanır. Tüm bellek, kullanıcının sağladığı sabit boyutlu bir byte dizisinden (tensor arena) karşılanır.

Mimari katmanları

  Kullanıcı Kodu
       ↓
  MicroInterpreter
  ├── MicroErrorReporter   (hata mesajları, uart/printf)
  ├── MicroMutableOpResolver  (kayıtlı op kernelleri)
  │     ├── CONV_2D     → cmsis_nn_convolve_wrapper
  │     ├── DEPTHWISE_CONV_2D
  │     ├── FULLY_CONNECTED
  │     └── SOFTMAX
  ├── Model (flatbuffer, Flash'ta)
  └── TensorArena (SRAM, statik dizi)
        ├── Input tensor  (INT8 piksel verisi)
        ├── Intermediate activations (katmanlar arası)
        └── Output tensor (sınıf skorları)
    
TensorArena
Statik uint8_t tensor_arena[ARENA_SIZE] dizisi. AllocateTensors() çağrısında TFLM, grafik analizine göre gerekli belleği bu arenadan ayırır. Arena çok küçükse hata döner.
MicroMutableOpResolver
Yalnızca kullanılan op'ları kaydeder. Tüm op'ların binary'e dahil edilmesini önler. Her AddXxx() çağrısı ilgili kernel'i listeye ekler ve binary boyutunu artırır.
Kernel Abstraction
Her op'un platform-agnostik referans implementasyonu ve optimize edilmiş implementasyonu (CMSIS-NN, Xtensa, RISC-V P-extension) vardır. Derleme zamanında seçilir.
CMSIS-NN
ARM'ın Cortex-M için optimize NN kütüphanesi. TFLM, CMSIS-NN'i otomatik olarak çağırır. SSAT ve MVE intrinsiklerini kullanarak INT8 konvolüsyonu 4-8x hızlandırır.
Arena Boyutu Hesaplama

Arena boyutu, modelin aktivasyon belleğine bağlıdır ve deneme-yanılma ile belirlenir. AllocateTensors() başarısız olursa arena'yı büyüt. Pratik yöntem: önce büyük bir arena ayır, interpreter.arena_used_bytes() ile gerçek kullanımı ölç, sonra küçült.

03 Python API — Interpreter, invoke, benchmark

TFLite Python API, Linux ve Raspberry Pi'da model doğrulama ve prototipleme için idealdir. tflite_runtime paketi TensorFlow'un tamamını kurmadan çalışır.

Kurulum

bash
# RPi OS / Debian tabanlı (arm64)
pip install tflite-runtime

# PC'de geliştirme için (tam TF)
pip install tensorflow

Temel çıkarım akışı

tflite_infer.py
import numpy as np
import tflite_runtime.interpreter as tflite
import time

# Model yükle
interpreter = tflite.Interpreter(model_path="gesture_int8.tflite",
                                  num_threads=4)
interpreter.allocate_tensors()

# Tensor bilgilerini al
input_details  = interpreter.get_input_details()
output_details = interpreter.get_output_details()

print("Input  shape:", input_details[0]['shape'])   # [1, 96, 96, 1]
print("Input  dtype:", input_details[0]['dtype'])   # int8
print("Output shape:", output_details[0]['shape'])  # [1, 5]

# INT8 giriş için quantization parametreleri
scale, zero_point = input_details[0]['quantization']
# float_input → int8: int8_val = float_val / scale + zero_point

# Örnek çıkarım
img_float = np.random.rand(1, 96, 96, 1).astype(np.float32)
img_int8  = (img_float / scale + zero_point).astype(np.int8)

interpreter.set_tensor(input_details[0]['index'], img_int8)

t0 = time.perf_counter()
interpreter.invoke()
t1 = time.perf_counter()

output = interpreter.get_tensor(output_details[0]['index'])
print(f"Çıkarım süresi: {(t1-t0)*1000:.2f} ms")
print(f"Sınıf skorları: {output}")
print(f"Tahmin: sınıf {np.argmax(output)}")

benchmark_model ile performans ölçümü

bash — RPi4
# benchmark_model binary'ini indir veya derle
wget https://storage.googleapis.com/tensorflow-nightly-public/prod/tensorflow/release/lite/tools/nightly/latest/linux_aarch64/benchmark_model

chmod +x benchmark_model

./benchmark_model \
  --graph=gesture_int8.tflite \
  --num_threads=4 \
  --num_runs=50 \
  --warmup_runs=5

# Örnek çıktı:
# Inference timings in us: Avg=28471, Min=27842, Max=31205
# INIT: 45231 us, 1st inference: 30123 us
# Warmup iterations: 5, Inferences: 50
Çoklu Thread

num_threads=4 RPi4'ün 4 çekirdeğini kullanır. Ancak küçük modellerde thread overhead, paralel faydayı geçebilir. Her model için optimal thread sayısını ölçerek belirle.

04 C++ API — MicroInterpreter ve tensor arena

TFLM C++ API, MCU'larda doğrudan kullanılan temel arayüzdür. Arduino ve Zephyr kütüphaneleri bu API üzerine sarıcı katman sağlar.

Tam MCU çıkarım örneği

main_functions.cc
// TFLM başlıkları
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/schema/schema_generated.h"

// Model (xxd ile C array'e dönüştürülmüş .tflite)
#include "gesture_model_data.h"

namespace {
  tflite::MicroErrorReporter micro_error_reporter;
  tflite::ErrorReporter*     error_reporter = µ_error_reporter;

  const tflite::Model* model = nullptr;
  tflite::MicroInterpreter* interpreter = nullptr;
  TfLiteTensor* input  = nullptr;
  TfLiteTensor* output = nullptr;

  // Tensor arena — SRAM'de statik dizi
  constexpr int kTensorArenaSize = 70 * 1024;  // 70 KB
  static uint8_t tensor_arena[kTensorArenaSize];
}

void setup() {
  // Model flatbuffer'ı yükle
  model = tflite::GetModel(g_gesture_model_data);
  if (model->version() != TFLITE_SCHEMA_VERSION) {
    TF_LITE_REPORT_ERROR(error_reporter, "Model schema uyumsuz!");
    return;
  }

  // Yalnızca kullanılan op'ları kaydet (binary boyutu küçültür)
  static tflite::MicroMutableOpResolver<5> resolver;
  resolver.AddConv2D();
  resolver.AddDepthwiseConv2D();
  resolver.AddMaxPool2D();
  resolver.AddFullyConnected();
  resolver.AddSoftmax();

  // Interpreter oluştur
  static tflite::MicroInterpreter static_interpreter(
      model, resolver, tensor_arena, kTensorArenaSize, error_reporter);
  interpreter = &static_interpreter;

  // Tensörleri tahsis et
  TfLiteStatus alloc_status = interpreter->AllocateTensors();
  if (alloc_status != kTfLiteOk) {
    TF_LITE_REPORT_ERROR(error_reporter,
        "AllocateTensors başarısız! Arena: %d KB", kTensorArenaSize/1024);
    return;
  }

  input  = interpreter->input(0);
  output = interpreter->output(0);

  // Kullanılan arena miktarını yazdır
  TF_LITE_REPORT_ERROR(error_reporter,
      "Arena kullanımı: %d bytes",
      interpreter->arena_used_bytes());
}

void loop() {
  // Giriş tensörüne kamera görüntüsü yaz (INT8 format)
  int8_t* input_data = input->data.int8;
  // ... kamera'dan piksel kopyala ...

  // Çıkarım
  if (interpreter->Invoke() != kTfLiteOk) {
    TF_LITE_REPORT_ERROR(error_reporter, "Invoke başarısız!");
    return;
  }

  // Çıkış tensöründen sınıfı al
  int8_t* scores = output->data.int8;
  int     best   = 0;
  for (int i = 1; i < output->dims->data[1]; i++) {
    if (scores[i] > scores[best]) best = i;
  }
  // best → 0=idle, 1=up, 2=down, 3=left, 4=right
}

Model binary'e gömme

bash
# .tflite dosyasını C array'e çevir
xxd -i gesture_int8.tflite > gesture_model_data.cc

# gesture_model_data.cc içeriği:
# unsigned char g_gesture_model_data[] = { 0x1c, 0x00, ... };
# unsigned int  g_gesture_model_data_len = 1348216;

05 Raspberry Pi deployment — cross-compile, benchmark_model

Raspberry Pi 4, TFLite Runtime'ı tam anlamıyla çalıştırabilir. NEON optimizasyonları ve 4 çekirdek sayesinde gerçek zamanlı kamera çıkarımı mümkündür.

TFLite shared library kurulumu

bash — RPi4 (arm64)
# Yöntem 1: pip ile Python runtime (en hızlı)
pip3 install tflite-runtime

# Yöntem 2: libtensorflow-lite.so ile C++ uygulamaları için
# TFLite'ı kaynaktan derle (aarch64 üzerinde)
git clone --depth 1 https://github.com/tensorflow/tensorflow
cd tensorflow

# Bağımlılıklar
sudo apt-get install -y cmake libabsl-dev libflatbuffers-dev

# CMake ile derle
mkdir tflite-build && cd tflite-build
cmake ../tensorflow/lite \
    -DCMAKE_BUILD_TYPE=Release \
    -DTFLITE_ENABLE_XNNPACK=ON \
    -DTFLITE_ENABLE_NNAPI=OFF
make -j4 libtensorflow-lite.a

# Host PC'den cross-compile (aarch64-linux-gnu toolchain)
cmake ../tensorflow/lite \
    -DCMAKE_TOOLCHAIN_FILE=../tensorflow/lite/tools/cmake/toolchains/aarch64_linux.cmake \
    -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

XNNPACK ile hızlandırma

xnnpack_infer.py
import tflite_runtime.interpreter as tflite

# XNNPACK delegate etkinleştir (RPi4'te varsayılan olarak açık)
interpreter = tflite.Interpreter(
    model_path="gesture_int8.tflite",
    experimental_delegates=[
        tflite.load_delegate('libxnnpack_delegate.so')
    ],
    num_threads=4
)
interpreter.allocate_tensors()

Kamera entegrasyonu — picamera2

camera_infer.py
from picamera2 import Picamera2
import numpy as np
import tflite_runtime.interpreter as tflite
import cv2, time

interpreter = tflite.Interpreter("gesture_int8.tflite", num_threads=4)
interpreter.allocate_tensors()
inp_det = interpreter.get_input_details()[0]
out_det = interpreter.get_output_details()[0]
scale, zp = inp_det['quantization']

picam2 = Picamera2()
picam2.configure(picam2.create_preview_configuration(
    main={"size": (640, 480), "format": "RGB888"}))
picam2.start()

LABELS = ["idle", "up", "down", "left", "right"]

while True:
    frame = picam2.capture_array()                    # (480,640,3) uint8
    roi   = cv2.resize(frame[:, 80:560], (96, 96))    # kare kırp, yeniden boyutlandır
    gray  = cv2.cvtColor(roi, cv2.COLOR_RGB2GRAY)
    inp   = (gray.astype(np.float32)/255.0 / scale + zp).astype(np.int8)
    inp   = inp[np.newaxis, ..., np.newaxis]          # (1,96,96,1)

    interpreter.set_tensor(inp_det['index'], inp)
    t0 = time.perf_counter()
    interpreter.invoke()
    dt = (time.perf_counter() - t0) * 1000

    scores = interpreter.get_tensor(out_det['index'])[0]
    pred   = int(np.argmax(scores))
    print(f"{LABELS[pred]}  ({dt:.1f} ms)")

06 STM32 MCU deployment — X-CUBE-AI, CubeMX

STM32Cube.AI (X-CUBE-AI), .tflite ve .onnx modellerini STM32 MCU'lar için C kaynak koduna dönüştürür. CubeMX entegrasyonu ile tek tıkla Flash/RAM analizi ve kod üretimi sağlar.

X-CUBE-AI iş akışı

  gesture_int8.tflite
         ↓
  STM32Cube.AI Converter
  (STM32CubeMX → X-CUBE-AI paketi)
         ↓
  ┌──────────────────────────────────┐
  │  network.c / network.h           │  ← üretilen C kodu
  │  network_data.c / network_data.h │  ← ağırlıklar
  │  app_x-cube-ai.c                 │  ← çıkarım başlatma
  └──────────────────────────────────┘
         ↓
  CubeMX → Keil / IAR / GCC proje
         ↓
  STM32H743 Flash programlama
    

CubeMX X-CUBE-AI konfigürasyonu

1. Paket Kurulumu
STM32CubeMX → Software Packs → X-CUBE-AI'yi etkinleştir. En güncel sürümü seç (v8.x+).
2. Model Yükleme
Middleware → X-CUBE-AI → Add Network → Model type: TFLite → gesture_int8.tflite dosyasını seç.
3. Analiz
Analyze butonu: Flash kullanımı (ağırlıklar), RAM kullanımı (aktivasyon buffer), per-layer latency tahmini gösterir.
4. Validation
Validate on Desktop: Orijinal TFLite çıktısı ile üretilen C kodunun sayısal uyumunu kontrol eder. MAE ve max delta raporlar.

Flash/RAM kısıtları ve çözümler

SorunBelirtiÇözüm
Flash dolduLink error: .rodata overflowINT4 quantization veya pruning ile model küçült
RAM yetersizAI_NETWORK_ERROR_INVALID_STATEActivation buffer'ı CCMRAM veya external RAM'e taşı
Yavaş çıkarım>100ms Cortex-M7'deCMSIS-NN etkinleştir, FPU kullan, cache aç
Doğruluk farkıPC vs MCU farklı sonuçValidate Desktop ile karşılaştır, rounding mode kontrol et
app_x-cube-ai.c (özet)
#include "app_x-cube-ai.h"
#include "network.h"
#include "network_data.h"

static ai_handle network = AI_HANDLE_NULL;
static ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE];

void MX_X_CUBE_AI_Init(void) {
    ai_network_create_and_init(&network,
        activations, AI_NETWORK_DATA_CONFIG);
}

void MX_X_CUBE_AI_Process(int8_t *input_buf, int8_t *output_buf) {
    ai_buffer ai_input[]  = AI_NETWORK_IN_1_ARGS(input_buf);
    ai_buffer ai_output[] = AI_NETWORK_OUT_1_ARGS(output_buf);
    ai_network_run(network, ai_input, ai_output);
}

07 Model optimizasyon araçları — Netron, profiler

Model dönüşümü ve deployment öncesinde model yapısını görselleştirmek, per-op latency'yi ölçmek ve darboğazları bulmak kritik önem taşır.

Netron ile görselleştirme

Netron
Tarayıcı tabanlı model görselleştirme aracı. netron.app adresinde veya pip install netron ile yerel kullanım. .tflite, .onnx, .pb, .h5 destekler. Her katmanın şekil, dtype ve quantization parametrelerini gösterir.
TFLite Model Benchmark
Resmi benchmark aracı. Per-operator profiling modu ile hangi op'ın ne kadar süre harcadığını gösterir. --enable_op_profiling=true ile aktif edilir.
Optimize.DEFAULT
TFLite Converter'ın varsayılan optimizasyon modu. Dynamic range quantization uygular — ağırlıkları INT8, aktivasyonları float tutar. Hızlı başlangıç noktası.
bash — per-op profiling
./benchmark_model \
  --graph=gesture_int8.tflite \
  --enable_op_profiling=true \
  --num_threads=4 \
  --num_runs=20

# Örnek çıktı:
# Node type              | avg (ms) | avg%  | cdf%
# CONV_2D                |   8.241  | 42.1% | 42.1%
# DEPTHWISE_CONV_2D      |   6.112  | 31.2% | 73.3%
# FULLY_CONNECTED        |   2.834  | 14.5% | 87.8%
# SOFTMAX                |   0.231  |  1.2% | 89.0%
# MAX_POOL_2D            |   2.154  | 11.0% |100.0%
model_analyzer.py
import tensorflow as tf

# Model analizi
analyzer = tf.lite.experimental.Analyzer
analyzer.analyze(
    model_path="gesture_int8.tflite",
    gpu_compatibility=True
)

# Netron ile aç (yerel)
import netron
netron.start("gesture_int8.tflite", port=8080)

08 Pratik — MobileNet INT8 ile gesture recognition

Raspberry Pi 4 üzerinde kameradan 5 sınıflı el hareketi tanıma. MobileNet-v1 INT8 modeli, picamera2 ile 30fps kamera girişi, hedef <30ms çıkarım gecikmesi.

Sistem mimarisi

  Pi Camera Module 3
       ↓ 30fps RGB888 (640×480)
  [picamera2 DMA capture]
       ↓
  [ROI kırp + resize 96×96]
       ↓
  [float→INT8 quantize]
       ↓
  [TFLite Interpreter  4-thread]
       ↓
  [INT8→float dequantize]
       ↓
  [softmax → argmax → label]
       ↓
  [GPIO LED / UART output]
    

Performans hedefleri ve sonuçlar

AdımHedefÖlçülen (RPi4)
Kamera capture<5 ms3.2 ms
Ön işleme (resize+quant)<3 ms1.8 ms
TFLite Invoke (4t)<25 ms22.4 ms
Son işleme<1 ms0.3 ms
Toplam gecikme<30 ms27.7 ms
Throughput30 FPS36 FPS

GPIO entegrasyonu

gesture_gpio.py
import RPi.GPIO as GPIO

LED_PINS = {
    "up":    17,
    "down":  18,
    "left":  27,
    "right": 22,
    "idle":  None
}

GPIO.setmode(GPIO.BCM)
for pin in LED_PINS.values():
    if pin: GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)

def set_gesture_led(label: str):
    for name, pin in LED_PINS.items():
        if pin:
            GPIO.output(pin, GPIO.HIGH if name == label else GPIO.LOW)
    print(f"Hareket: {label}")
Hedef Aşıldı

MobileNet-v1-0.25 INT8 modeli (96×96 giriş) RPi4'te 22ms çıkarım süresi ile 30ms hedefini rahatlıkla geçer. Daha büyük modeller (MobileNet-v2, EfficientNet-Lite) için XNNPACK delegate ek 20-30% hız kazandırır.