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 vs TFLite Micro karşılaştırması
| Özellik | TFLite Runtime | TFLite Micro |
|---|---|---|
| Heap tahsisi | Evet (malloc/free) | Hayır (arena) |
| RTTI / Exceptions | Evet | Hayır |
| İşletim sistemi | Gerekli | Opsiyonel |
| Binary boyutu | ~1 MB | ~20 KB |
| Min RAM | ~5 MB | ~16 KB |
| Op sayısı | ~150+ | ~50 (seçici) |
| Flatbuffer yükleme | Dinamik | Flash'tan ROM |
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
PTQ — Python kodu
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
| Quantization | Boyut | Accuracy | Latency (CM4) | Hedef |
|---|---|---|---|---|
| float32 (baseline) | 5.2 MB | 92.4% | 420 ms | Linux/Android |
| float16 PTQ | 2.6 MB | 92.2% | 380 ms | Linux/GPU |
| INT8 PTQ | 1.3 MB | 91.8% | 95 ms | RPi/MCU |
| INT8 QAT | 1.3 MB | 92.1% | 95 ms | RPi/MCU |
| INT4 Dynamic | 0.7 MB | 89.5% | 68 ms | MCU (Flash kısıtlı) |
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ı)
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.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
# RPi OS / Debian tabanlı (arm64)
pip install tflite-runtime
# PC'de geliştirme için (tam TF)
pip install tensorflow
Temel çıkarım akışı
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ü
# 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
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
// 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
# .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
# 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
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
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
Flash/RAM kısıtları ve çözümler
| Sorun | Belirti | Çözüm |
|---|---|---|
| Flash doldu | Link error: .rodata overflow | INT4 quantization veya pruning ile model küçült |
| RAM yetersiz | AI_NETWORK_ERROR_INVALID_STATE | Activation buffer'ı CCMRAM veya external RAM'e taşı |
| Yavaş çıkarım | >100ms Cortex-M7'de | CMSIS-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 |
#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
pip install netron ile yerel kullanım. .tflite, .onnx, .pb, .h5 destekler. Her katmanın şekil, dtype ve quantization parametrelerini gösterir.--enable_op_profiling=true ile aktif edilir../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%
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ım | Hedef | Ölçülen (RPi4) |
|---|---|---|
| Kamera capture | <5 ms | 3.2 ms |
| Ön işleme (resize+quant) | <3 ms | 1.8 ms |
| TFLite Invoke (4t) | <25 ms | 22.4 ms |
| Son işleme | <1 ms | 0.3 ms |
| Toplam gecikme | <30 ms | 27.7 ms |
| Throughput | 30 FPS | 36 FPS |
GPIO entegrasyonu
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}")
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.