

Edge cihazlarda (Rockchip RK3588, NXP i.MX8M, Hailo-8) yapay zeka modellerini production ortamına alırken karşılaşılan en büyük travma, test ortamında 15ms süren inference işleminin cihaz üzerinde 450ms’ye çıkmasıdır. Bu durumun temel sorumlusu, modelin donanımsal hızlandırıcı (NPU) yerine işlemciye (CPU) “fallback” yapmasıdır. CPU üzerindeki her bir fallback, sadece işlem süresini uzatmakla kalmaz; aynı zamanda NPU ve CPU arasındaki SRAM/DRAM veri transferi (memory copy) nedeniyle PCIe veya sistem veriyolu bant genişliğini felç eder.
ONNX Runtime veya özel NPU derleyicileri (RKNN, TensorRT, SNPE), model grafiğini (graph) analiz ederken donanımın desteklemediği tek bir operatörle karşılaştığında o düğümü ve genellikle ona bağlı alt grafikleri CPU’ya atar. Bir otonom drone veya endüstriyel kalite kontrol hattında p99 latency değerinin 20ms altında kalması kritikken, %80 oranında NPU kullanımına sahip bir model bile bağlam geçişleri (context switch) nedeniyle projenin iptaline yol açabilir. 15 yıllık donanım ve yazılım entegrasyonu tecrübeme dayanarak, ONNX modellerini NPU’dan düşüren 5 spesifik operatör hatasını, debug yöntemlerini ve architectural çözümlerini inceleyeceğiz.
NPU’lar deterministik donanımlardır. Derleme (AOT – Ahead of Time) aşamasında, her bir tensörün bellek (SRAM) üzerinde ne kadar yer kaplayacağı bayt düzeyinde hesaplanır. NonMaxSuppression (NMS), Where veya NonZero gibi çıktı boyutu input’a veya threshold değerlerine göre çalışma zamanında (runtime) değişen operatörler, NPU’nun statik bellek tahsis stratejisini bozar. Derleyici bu durumu fark ettiğinde, tahsis hatasını önlemek için ilgili operatörü ve sonrasını CPU Execution Provider’a sevk eder.
Netron arayüzünde modelinizi açtığınızda tensor boyutlarında [1, 8400, ?] veya [None, 256] gibi belirsiz karakterler görüyorsanız dinamik şekil (dynamic shape) sorunu yaşıyorsunuz demektir. ONNX Runtime loglarında Shape inference failed uyarıları belirir.
Dinamik operatörleri modelin dışına, yani post-processing aşamasına taşıyın. Model sadece ham bounding box ve confidence skorlarını (statik boyutlu tensörler) döndürmeli, NMS işlemi C++ veya Rust ile yazılmış bir CPU thread’i üzerinde çalıştırılmalıdır.
Rockchip RK3588 üzerinde YoloV8 tabanlı bir nesne tespiti projesinde, ONNX modeli NMS içerdiği için 340ms latency üretiyordu. NMS düğümünü onnx.helper ile grafikten kestik ve [1, 84, 8400] boyutundaki ham tensörü dışarı aldık. NPU derlemesi sonrası model latency’si 18ms’ye, CPU post-processing ise sadece 3ms’ye düştü. Toplam sistem kazancı %93 oldu.
Çoğu edge NPU IP’si (örneğin ARM Ethos-U65, Snapdragon Hexagon) FP16, INT8 ve bazen INT16 için özelleşmiş MAC (Multiply-Accumulate) ünitelerine sahiptir. PyTorch veya TensorFlow’dan ONNX’e dönüşüm sırasında Float64 (Double) veya Int64 tipleri yanlışlıkla grafiğe sızar. NPU bu tipleri hesaplayamayacağı için ONNX Runtime otomatik olarak Cast düğümleri ekler ve bu verileri işlemek için CPU’ya döner.
Graph üzerinde peş peşe Cast (örneğin Int64 -> Int32 veya Float32 -> Float16) düğümlerinin yığılması en belirgin işarettir. Ayrıca cihazda top komutu çalıştırıldığında CPU kullanımının %100’e vurması veri tipi dönüşümü yükünü gösterir.
Modeli ONNX formatına export etmeden önce PyTorch’ta model.half() kullanarak veya dummy input’ları spesifik olarak torch.float32 / torch.int32 formatında vererek dönüşümü zorlayın. Sensörden gelen veriler Float64 ise, ONNX’e girmeden önce yazılım katmanında Float32’ye downcast edin.
Snapdragon 8 Gen 2 üzerinde çalışan bir sinyal işleme modelinde, giriş tensörü Int64 olarak tanımlanmıştı. Hexagon DSP bu tipi reddettiği için işlem CPU’ya kaydı ve throughput 14 FPS’de kaldı. Sadece giriş tensor tanımını np.int32 olarak değiştirerek modelin tamamen DSP üzerinde çalışmasını sağladık; throughput 82 FPS’ye çıktı.
Akademik çalışmalarda isabet oranını artıran GELU veya Mish gibi aktivasyon fonksiyonları, edge donanım satıcılarının sürücülerinde genellikle native olarak bulunmaz. NPU, ReLU ve Sigmoid gibi standart fonksiyonları tanır. Tanınmayan bir aktivasyon fonksiyonu araya girdiğinde; grafik NPU -> CPU -> NPU şeklinde parçalanır (Sub-graph partitioning). Bu bağlam geçişleri, donanımın paralelleştirme avantajını sıfırlar.
ONNX Runtime’da profiling aktif edildiğinde (sess_options.enable_profiling = True), üretilen JSON dosyasında NPUExecutionProvider segmentlerinin arasında CPUExecutionProvider blokları görünür. İdeal bir senaryoda tüm ağırlıkların tek bir NPU bloğunda ardışık çalışması gerekir.
Ağınızı edge deployment için tasarlıyorsanız, aktivasyon fonksiyonlarını donanım dostu versiyonlarla değiştirin. GELU yerine ReLU6 veya LeakyReLU kullanıp modeli birkaç epoch fine-tune edin. İsabet oranı (accuracy) sadece %0.5 düşerken latency’deki düşüş asimetrik olacaktır.
NXP i.MX8M Plus SoC üzerinde MobileViT tabanlı bir modelin inference süresi 850ms idi. İncelemede GELU aktivasyonları yüzünden grafiğin 14 parçaya bölündüğünü ve her seferinde 4MB’lık tensörlerin DRAM ile NPU SRAM’i arasında kopyalandığını tespit ettik. Modeldeki GELU’ları ReLU6 ile değiştirip 20 epoch yeniden eğittik. Bağlam geçişleri sıfırlandı ve p99 latency 42ms seviyesine indi.
Özellikle Transformer tabanlı modellerde (Vision Transformers vb.) Multi-Head Attention mekanizmaları sırasında boyutlar 4D’den 5D veya 6D’ye (örneğin [Batch, Heads, Sequence, Seq_len, Dim]) çıkar ve karmaşık Transpose operatörleri ile boyutların yeri değiştirilir. Çoğu edge NPU DMA (Direct Memory Access) motoru, sadece 3D veya maksimum 4D (NCHW / NHWC) tensör permütasyonlarını donanımsal olarak destekler. 5D bir tensörün bellek dizilimi NPU için okunamayacak kadar parçalıdır ve mecburen CPU’ya atılır.
ONNX optimizatör scriptleri çalıştırdıktan sonra bile model içinde peş peşe Reshape -> Transpose -> Reshape silsilesi kalıyorsa risk altındasınız. Boyut argümanlarında 5 veya daha fazla eksen (axis) listelenir.
Mümkünse boyutları birleştirin (Flatten). Örneğin, [B, H, S, D] yerine head ve sequence boyutlarını tek bir 3D tensöre indirgeyip batch matris çarpımı (BMM) yapın. ONNX optimizasyon araçlarıyla (örn: onnx-simplifier) gereksiz transpose düğümlerini katlayın (constant folding).
Hailo-8 NPU üzerinde custom bir time-series transformer modelini denerken, 5 boyutlu transpose işlemleri yüzünden NPU derleyicisi modeli reddetti. Modeldeki attention katmanını 3 boyutlu çalışacak şekilde PyTorch seviyesinde yeniden yazdık (view fonksiyonları kullanarak). Bellek bant genişliği tüketimi %40 azaldı ve model NPU’ya başarılı şekilde map edildi.
Derin öğrenme donanımları derin pipelinelar üzerine kuruludur. Bir If veya Loop operatörü (özellikle condition’ı tensör değerlerine bağlıysa), pipeline’ın boşaltılmasına (pipeline flush) ve dallanma tahmin hatasına (branch misprediction) yol açar. Edge NPU’lar komut seti mimarisinde esnek dallanma yeteneklerine sahip olmadığı için ONNX derleyicileri kontrol akışını doğrudan host CPU’ya paslar.
Modelinizin ONNX grafiğinde Loop, If veya Scan operatörleri bulunuyorsa, bu kesinlikle dinamik dallanma olduğunu gösterir.
Döngüleri açın (Loop Unrolling). Eğer modeliniz maksimum 10 iterasyon yapacaksa, döngü yerine o katmanı 10 kez art arda kopyalayın. Koşullu (If) ifadeler için maskeleme (Masking) yöntemini kullanın; yani her iki dalı da hesaplatıp, istenmeyen dalı 0 ile çarparak eleyin. Bu, NPU’nun daha fazla matematiksel işlem yapmasına neden olsa da CPU’ya geçiş yapmaktan çok daha düşük bir maliyete sahiptir.
Apple Neural Engine (ANE) üzerinde CoreML/ONNX üzerinden koşan bir metin kod çözücü (decoder) ağında Loop operatörü vardı. Inference süresi 120ms bandındaydı ve SoC 14W TDP sınırına dayanıyordu. Döngüyü statik 8 adım olacak şekilde unroll ettik. Ağ boyutu 12MB’tan 45MB’a çıktı (Trade-off) ancak işlem tamamen ANE’de kaldı, inference süresi 15ms’ye düştü ve batarya tüketimi dramatik şekilde azaldı.
Production ortamında NPU kullanımı sırasında aşağıdaki kontrol listesi ve kod yapılandırmaları size zaman kazandıracaktır:
Python veya C++ tarafında session oluştururken, her zaman NPU provider’ını ilk sıraya koyun, fallback olarak CPU’yu ekleyin. Ancak hata ayıklama (debug) yaparken CPU’yu tamamen listeden çıkarıp modelin doğrudan hata fırlatmasını sağlamak sorunlu düğümleri bulmanın en kesin yoludur.
import onnxruntime as ort
# Profiling aktif ederek sorunlu provider'ları loglama
so = ort.SessionOptions()
so.enable_profiling = True
so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
# Sadece NPU (Örn: TensorRT veya özel NPU) zorlaması. CPU listede yoksa,
# desteklenmeyen düğüm varsa uygulama crash olur ve hatayı net görürsünüz.
providers = ['TensorrtExecutionProvider']
try:
sess = ort.InferenceSession("model_optimized.onnx", sess_options=so, providers=providers)
print("Graph tamamen NPU üzerinde!")
except Exception as e:
print("NPU haritalama hatası. Desteklenmeyen operatör:", e)
Bazen mimari bir kararla bazı işlemleri CPU’da bırakmak zorundayız. Hangi durumda ne yapmalı?
| Senaryo | Önerilen Yaklaşım | Açıklama |
|---|---|---|
| Graph ortasında kompleks bir aktivasyon (Örn: Mish) | Modeli ReLU6 ile değiştir ve yeniden eğit. | Ara katmanlarda bağlam geçişi (context switch) PCI-e/Memory hatlarını tıkar. %1 accuracy kaybını göze alın. |
| Modelin en sonunda yer alan NMS veya ArgMax | İlgili düğümü ONNX’ten sil, CPU’da C++ ile yaz. | Model sonu olduğu için SRAM’den DRAM’e tek bir kopyalama olur. CPU bu işlemi SIMD komutlarıyla 2-3ms’de halleder. |
| Giriş verisinin FP64 olarak gelmesi | Yazılım katmanında FP32/INT8 cast işlemi yap. | ONNX Cast düğümleri eklemek yerine, veri hazırlığını ONNX Runtime dışına çıkarın. |
Büyük ölçüde evet. Constant folding ve gereksiz Cast/Identity düğümlerini temizlediği için modelin statik bir hale gelmesine yardımcı olur. Ancak modelin temelinde bir Loop veya NonMaxSuppression varsa, simplifier bunları tek başına yok edemez.
Kesinlikle. Çoğu edge NPU aracı Opset 18 veya 19 gibi versiyonları desteklemez. Endüstri standardı olan Opset 13 veya 14’ü hedefleyerek export almak, bilinmeyen operatör hatalarını %80 oranında önler.
Bağlam geçişleri sırasındaki bellek kilitlenmesi (Memory Bottleneck). Modeliniz NPU ile CPU arasında pinpon oynuyorsa, veri kopyalama işlemi sırasında işlemciler bekler (Idle IO). Profiling aracınızda MemcpyToHost operasyonlarının süresini kontrol edin.
Evet, eğer asymmetric ve symmetric quantization bir arada, kuralsız şekilde kullanılırsa veya bazı katmanlar INT8’i desteklemediği için FP16 bırakılırsa (Mixed Precision), araya otomatik QuantizeLinear/DequantizeLinear düğümleri girer. Bu düğümler IP üzerinde desteklenmiyorsa operasyon mecburen CPU’ya iletilir.
Edge cihazlarda AI donanımlarının vadettiği spesifikasyonları yakalayabilmek, salt model eğitiminden öte, donanımın derleme mantığını bayt düzeyinde anlamayı gerektirir. Dinamik operatörler, yanlış veri tipleri, desteklenmeyen aktivasyon fonksiyonları, aşırı boyutlu tensörler ve kontrol akış düğümleri; modelinizi 40 TOPS kapasiteli bir NPU’dan söküp ARM Cortex A55 çekirdeğine mahkum eden temel beş hatadır. Bu hatalar x86 tabanlı geliştirme bilgisayarlarında görünmez, sadece hedeflenen silikonda ortaya çıkar.
Donanım bağımsız (hardware-agnostic) model tasarımı production için geçerli değildir. Çalışan modeller, çalışacağı çipin kısıtlamalarına göre yoğrulmuş modellerdir.
Aksiyon planı olarak: Mevcut modelinizi Netron ile açıp uçtan uca inceleyin. ONNX export scriptlerinizde Opset sürümünüzü 14’e sabitleyin, dinamik şekil üreten operatörleri post-processing aşamasına ayırın ve modelinizi onnx-simplifier süzgecinden geçirmeden cihaza deploy etmeyin.

Üretim ortamındaki kritik iş yükleri için kesintisiz performans ve sistem kararlılığı sağlamak amacıyla geri basınç destekli kuyruk mimarilerinin tasarım prensiplerini ve uygulama stratejilerini keşfedin. Bu makale, sistemlerin aşırı yüklenmesini önleyen ve kaynak kullanımını optimize eden sağlam kuyruk mimarileri oluşturmak için pratik bilgiler sunar.