
Edge Cihazlarda ONNX Modellerini CPU’ya Düşüren 5 Kritik Operatör Hatası ve Çözümleri

Giriş
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.
İçindekiler
- Hata 1: Dinamik Boyutlu Operatörler (NonMaxSuppression, Where, NonZero)
- Hata 2: Desteklenmeyen Veri Tipleri ve Gizli Cast Düğümleri
- Hata 3: Graph Parçalanmasına Neden Olan Egzotik Aktivasyonlar (GELU, HardSwish)
- Hata 4: Yüksek Boyutlu Tensor Permütasyonları (5D/6D Transpose ve Reshape)
- Hata 5: Kontrol Akışı (Control Flow) Operatörlerinin Kullanımı (Loop, If)
- Pratik Öneriler / Production Notları
- Sık Sorulan Sorular
- Sonuç
Hata 1: Dinamik Boyutlu Operatörler (NonMaxSuppression, Where, NonZero)
Neden Olur?
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.
Nasıl Tespit Edilir?
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.
Nasıl Çözülür?
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.
Gerçek Olay Örneği
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.
Hata 2: Desteklenmeyen Veri Tipleri ve Gizli Cast Düğümleri
Neden Olur?
Ç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.
Nasıl Tespit Edilir?
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.
Nasıl Çözülür?
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.
Gerçek Olay Örneği
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ı.
Hata 3: Graph Parçalanmasına Neden Olan Egzotik Aktivasyonlar (GELU, HardSwish)
Neden Olur?
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.
Nasıl Tespit Edilir?
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.
Nasıl Çözülür?
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.
Gerçek Olay Örneği
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.
Hata 4: Yüksek Boyutlu Tensor Permütasyonları (5D/6D Transpose ve Reshape)
Neden Olur?
Ö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.
Nasıl Tespit Edilir?
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.
Nasıl Çözülür?
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).
Gerçek Olay Örneği
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.
Hata 5: Kontrol Akışı (Control Flow) Operatörlerinin Kullanımı (Loop, If)
Neden Olur?
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.
Nasıl Tespit Edilir?
Modelinizin ONNX grafiğinde Loop, If veya Scan operatörleri bulunuyorsa, bu kesinlikle dinamik dallanma olduğunu gösterir.
Nasıl Çözülür?
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.
Gerçek Olay Örneği
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ı.
Pratik Öneriler / Production Notları
Production ortamında NPU kullanımı sırasında aşağıdaki kontrol listesi ve kod yapılandırmaları size zaman kazandıracaktır:
1. ONNX Runtime Provider Önceliklendirmesi
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)
2. Trade-Off Analizi: NPU Fallback vs CPU Post-Processing
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. |
Sık Sorulan Sorular
ONNX Simplifier (onnx-sim) kullanmak fallback sorunlarını çözer mi?
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.
NPU derleyicisi (Örn: RKNN, rknn-toolkit2) ONNX versiyonuna duyarlı mıdır?
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.
Cihazımda CPU kullanımı düşük ama inference yavaş, sebebi ne olabilir?
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.
Quantization (INT8) işlemi fallback’e neden olur mu?
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.
Sonuç
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.
Bunları da beğenebilirsiniz

Mikroservis Ödeme Akışlarında Çift Çekim (Double-Charge) Sızıntılarını Önlemek: Kafka Outbox ve Redis Idempotency
Dağıtık ödeme mimarilerinde çift çekim hatalarını sıfıra indirmek için PostgreSQL 15, Kafka Outbox Pattern ve Redis 7.2 ile idempotent tasarım stratejileri.

