7. ünite / 8
Modeli mikrodenetleyiciye taşı: ağırlıklardan C koduna
Eğitilmiş doğrusal modelden C fonksiyonu üret: FPU'lu ESP32 için float, FPU'su olmayan Uno için Q-biçiminde sabit noktalı tamsayı. Nicemleme hatasını, taşma payını, bellek ve süreyi ölç.
Bu ünitenin sonunda
- Eğitilmiş ağırlıklardan derlenebilir bir C fonksiyonu üretmek
- Q-biçiminde sabit noktalı aritmetiği ve santi-birim girdileri açıklamak
- Nicemleme hatasını ve int32 taşma payını hesaplamak
- Float ve sabit noktalı modelin bellek ve süre maliyetini karşılaştırmak
Ders notları
Model bulutta da çalışabilir; ama ağ koptuğunda serayı korumak, pili korumak ve gecikmeyi sıfıra indirmek için tahmini cihazın üstünde yapmak isteriz. Doğrusal model bunun için idealdir: on özellikli bir model on çarpma ve on toplamadır, 44 baytlık ağırlık tablosudur.
ESP32: float yeterli
ESP32'nin tek duyarlıklı kayan nokta birimi (FPU) vardır. Python'daki ağırlıkları doğrudan float sabitleri olarak yazmak yeterlidir; bir tahmin mikrosaniyenin altında biter. Üretilen kodun tek şartı, girdilerin eğitimdeki ölçekte gelmesidir (ışık 0..1, saat sin/cos).
Uno: sabit noktalı tamsayı
ATmega328P'nin FPU'su yoktur; her float çarpması yazılımla, yüzlerce çevrimde yapılır. Çözüm sabit noktadır:
- Girdiler santi-birim
int16_t: 23,45 °C → 2345. Işık 0..1 → 0..100. - Ağırlıklar Q-biçiminde
int32_t: wq = round(w · 2q). q = 12 için 0,7955 → 3258. - Toplam
int32_tbiriktiricide yapılır, sonundaacc >> qile geri ölçeklenir.
İki risk: nicemleme ve taşma
Nicemleme hatası: her ağırlık en çok 2−q−1 kadar yuvarlanır. Sera modelinde q = 12 ile en büyük fark yaklaşık 0,01–0,04 °C'dir, sensörün kendi gürültüsünden çok küçük. q'yu 4'e indirirsen hata derecelere çıkar. Taşma: q büyüdükçe ağırlıklar büyür; en kötü girdide |B| + Σ |Wi| · max|xi| toplamı 2³¹ − 1'i aşarsa sonuç sessizce eksiye döner. Bu sınırı üretim anında hesaplayıp kodun yanına yaz.
Daha büyük modeller: TinyML
Doğrusal model yetmezse küçük bir sinir ağı da cihaza sığar. TensorFlow Lite Micro gibi çatılar ağırlıkları int8'e nicemler ve ESP32-S3'ün vektör komutlarını kullanır. İlke aynıdır: eğitim bilgisayarda, çıkarım cihazda; nicemlemenin bedeli ölçülür, tahmin edilmez.
Simülasyon
Modeli mikrodenetleyiciye taşı: C kodu üreteci
Eğitilmiş ağırlıklardan C fonksiyonu üret: ESP32 için float, FPU'su olmayan Uno için sabit noktalı tamsayı (Q-biçimi). Q bit sayısını değiştir; nicemleme hatasını, int32 taşma payını, bellek ve süre tahminini gör.
Kod
// export.py çıktısı: ağırlıklar ham ölçekte, ölçekleme gerekmez
float predict_temp(float temp, float lag1, float lag6,
float hour_sin, float hour_cos, float light) {
return 2.498785f
+ 0.795491f * temp + 0.233786f * lag1 - 0.190635f * lag6
+ 0.592475f * hour_sin + 0.569771f * hour_cos + 3.463065f * light;
}
void setup() {
Serial.begin(115200);
uint32_t t0 = micros();
volatile float p = 0;
for (int i = 0; i < 1000; i++) p = predict_temp(24.1f, 24.0f, 23.2f, 0.5f, -0.87f, 0.62f);
uint32_t us = micros() - t0; // 1000 tahmin
Serial.printf("forecast %.2f C, %.3f us each\n", p, us / 1000.0f);
}
void loop() {}#include <stdint.h>
#define Q 12 // ağırlık = round(w x 4096)
static const int32_t W[6] = { 3258, 958, -781, 2427, 2334, 14185 };
static const int32_t B = 1023503; // sabit terim, santi-birim x 4096
// girdiler santi-birim int16: 23,45 °C -> 2345, ışık 0..1 -> 0..100
int16_t predict_temp_c(const int16_t x[6]) {
int32_t acc = B;
for (uint8_t i = 0; i < 6; i++) acc += W[i] * (int32_t)x[i]; // en kötü durum int32'ye sığmalı
return (int16_t)(acc >> Q); // aritmetik kaydırma = aşağı yuvarlama
}
void setup() {
Serial.begin(9600);
int16_t x[6] = { 2410, 2400, 2320, 50, -87, 62 };
int16_t c = predict_temp_c(x);
Serial.print(c / 100); Serial.print('.'); Serial.println(abs(c % 100));
}
void loop() {}Kodu tarayıcıda çalıştırPython · NumPy · matplotlib · Pyodide
Python ve NumPy bu sayfada, tarayıcında çalışır (Pyodide). Kodu değiştirip yeniden çalıştırabilirsin; dersin veri setleri çalışma klasöründe hazır. Kod matplotlib kullanırsa grafikler çıktının altında görünür, yazdığı dosyalar (ör. egitim_gecmisi.csv) indirilebilir.
Çıktı
Çıktı burada görünecek.Kendin dene
Simülasyonda önce float hedefini (ESP32) seç ve üretilen C fonksiyonunu oku. Sonra sabit noktalı hedefe (Uno) geç ve Q bit sayısını 12'den 4'e, sonra 20'ye taşı: en büyük nicemleme hatası ve int32 taşma payı nasıl değişti? Hatanın 0,05 °C altında, taşma payının güvenli olduğu en küçük q'yu bul. Python kodunu çalıştır, kendi ağırlıklarınla üretilen C kodunu Wokwi'deki bir Uno ya da ESP32 projesine yapıştırıp seri çıktıyı karşılaştır.
Sık yapılan hatalar
- Cihazda girdiyi eğitimdekinden farklı ölçekte vermek (ışığı 0..1023 olarak): model tutarlı ama yanlış tahmin üretir.
- Uno'da float kullanıp sürenin önemsiz olduğunu varsaymak: özellik hesabıyla birlikte milisaniyelere çıkabilir.
- Taşma payını hesaplamadan q'yu büyütmek: uç bir girdide toplam int32 sınırını aşar ve tahmin eksiye döner.
- Ağırlıkları elle kopyalamak: model yeniden eğitilince C dosyası eski kalır; kodu betikle üret.
Yazılım mühendisliği bağlantısı
Üretilen kod, elle yazılan koddan daha güvenilir olabilir; yeter ki üreticinin kendisi test edilsin.
- Üreticiyle birlikte aynı tamsayı hesabını yapan bir referans uygulama yaz ve ikisinin bin rastgele girdide bit bit aynı sonucu verdiğini test et.
- Üretilen dosyaya model sürümü, eğitim tarihi ve q değerini yorum olarak göm; sahadaki cihazın hangi modeli çalıştırdığı bilinmeli.
- Model değişince C dosyasını CI üretsin; elle düzenleme yasak olsun.
Kendini sına
q = 12 ile 0,75 ağırlığı Q-biçiminde hangi tamsayıdır?
Arduino Uno'da sabit noktalı model neden tercih edilir?
q değerini çok büyütmenin riski nedir?
Santi-birim girdide 23,45 °C hangi int16 değeridir?
Bu üniteden sonra bitirme projesinde
- Cihaz kodu: Float ve sabit noktalı C kodu, nicemleme hatası ve taşma denetimi, bellek ve süre hesabı (G6, G7).
