Back to Developer Wiki
Edge AI

TensorRT INT8 Kalibrasyonu: Doğruluk Kaybını Yöneterek Kuantizasyon

TensorRT ile INT8 kuantizasyonuna geçerken doğruluk kaybını kontrol altında tutmanın pratik rehberi. FP16/INT8 kararı, kalibrasyon dataseti seçimi, entropy calibrator implementasyonu, doğruluk regresyon testi ve engine serileştirme pratikleri üretim perspektifinden ele alınıyor.

Spikedge MühendislikJuly 26, 20266 dk okuma

Bir modeli TensorRT ile FP16'ya düşürmek çoğu platformda tek bir builder bayrağıdır ve doğruluk kaybı genellikle ihmal edilebilir düzeydedir. INT8 ise farklı bir sözleşmedir: throughput ve güç tüketiminde ciddi kazanç vaat eder, ama karşılığında sizden aktivasyon dağılımlarını temsil eden bir kalibrasyon süreci ve disiplinli bir doğruluk regresyon testi ister. Bu yazıda FP16/INT8 kararını, kalibrasyon datasetinin nasıl seçileceğini, entropy calibrator implementasyonunu ve engine serileştirme pratiğini üretim perspektifinden ele alıyoruz.

FP16 mi, INT8 mi?

FP16, modern Jetson modüllerinde (Orin NX/AGX) Tensor Core'lar sayesinde FP32'ye göre tipik olarak 2-3x hızlanma sağlar ve dinamik aralığı hâlâ geniş olduğu için çoğu CNN'de doğruluk kaybı ihmal edilebilir bantta kalır. Kendi ölçümlerimizden bir referans: Jetson Orin NX üzerinde YOLOv8'i TensorRT FP16 ile 31 fps'ten 127 fps'e çıkardık; güç tüketimi 8.4W'ta kaldı (JetPack 6). Bu çalışmanın ayrıntıları edge AI inference vaka analizinde mevcut.

INT8'in ek kazancı platforma göre değişir. Ampere sınıfı GPU'larda INT8 Tensor Core throughput'u FP16'nın iki katıdır; asıl fark DLA'da ortaya çıkar: Orin'in DLA çekirdekleri INT8'de çok daha verimlidir ve GPU'yu başka işlere bırakmanıza izin verir. Bellek bant genişliği ihtiyacı da yarıya iner — memory-bound katmanlarda bu tek başına belirleyici olabilir.

Kriter FP16 INT8
Builder maliyeti Tek bayrak (--fp16) Kalibrasyon dataseti + calibrator
Tipik doğruluk kaybı <%0.5 %0.5–3 (modele ve kalibrasyona bağlı)
Bellek bant genişliği FP32'nin yarısı FP32'nin dörtte biri
DLA desteği Kısıtlı verim Birincil hedef format
Regresyon riski Düşük Yüksek — test şart

Pratik kural: önce FP16 ile hedef fps ve güç bütçesini tutturmaya çalışın. INT8'e ancak FP16 yetmiyorsa, DLA offload gerekiyorsa veya termal bütçe daralıyorsa geçin — kalibrasyon ve regresyon altyapısının bakım maliyeti sıfır değildir.

Kalibrasyon Datasetinin Seçimi

Post-training quantization'da TensorRT, her tensörün aktivasyon dağılımını gözlemleyip bir dinamik aralık (scale) atar. Bu dağılımı kalibrasyon dataseti belirler; dolayısıyla dataset üretim dağılımını temsil etmiyorsa scale'ler yanlış hesaplanır ve doğruluk sessizce çöker.

Uyduğumuz kurallar:

  • 500–1000 örnek genellikle yeterli. Daha fazlası kalibrasyonu yavaşlatır ama histogramı anlamlı ölçüde değiştirmez. Kritik olan sayı değil, çeşitliliktir.
  • Deployment dağılımından örnekleyin. Gündüz/gece, farklı pozlama, farklı sahne yoğunluğu — sahada ne görüyorsanız o. Validation setinden rastgele alt küme almak iyi bir başlangıçtır; ama kalibrasyonda kullandığınız örnekleri doğruluk ölçümünden çıkarın.
  • Preprocessing birebir aynı olmalı. En sık görülen hata bu: kalibrasyonda farklı normalize/letterbox uygulanırsa aktivasyon dağılımları kayar. Calibrator, inference'taki preprocessing fonksiyonunun aynısını çağırmalı.
  • Augmentasyon yok. Kalibrasyon eğitim değildir; yapay olarak genişletilmiş dağılım, gerçek dağılımın scale'lerini bozar.

Entropy Calibrator

TensorRT'nin IInt8EntropyCalibrator2 arayüzü, her tensör için KL-divergence'ı minimize eden eşiği seçer ve çoğu CNN için MinMax'ten daha iyi sonuç verir. Python tarafında implementasyon dört metottan ibarettir:

import numpy as np
import tensorrt as trt
from cuda import cudart  # cuda-python

class EntropyCalibrator(trt.IInt8EntropyCalibrator2):
    def __init__(self, image_paths, batch_size, preprocess_fn,
                 cache_file="calib.cache"):
        super().__init__()
        self.batch_size = batch_size
        self.cache_file = cache_file
        self.preprocess = preprocess_fn      # inference ile AYNI fonksiyon
        self.batches = self._chunk(image_paths, batch_size)
        self.index = 0
        nbytes = batch_size * 3 * 640 * 640 * np.float32().nbytes
        _, self.d_input = cudart.cudaMalloc(nbytes)

    def get_batch_size(self):
        return self.batch_size

    def get_batch(self, names):
        if self.index >= len(self.batches):
            return None                      # kalibrasyon bitti
        batch = np.ascontiguousarray(
            np.stack([self.preprocess(p) for p in self.batches[self.index]])
        )
        cudart.cudaMemcpy(self.d_input, batch.ctypes.data, batch.nbytes,
                          cudart.cudaMemcpyKind.cudaMemcpyHostToDevice)
        self.index += 1
        return [int(self.d_input)]

    def read_calibration_cache(self):
        try:
            with open(self.cache_file, "rb") as f:
                return f.read()
        except FileNotFoundError:
            return None

    def write_calibration_cache(self, cache):
        with open(self.cache_file, "wb") as f:
            f.write(cache)

read_calibration_cache kritik: cache varsa TensorRT kalibrasyonu tamamen atlar. Bu, cache dosyasını versiyon kontrolüne alıp CI'da deterministik build yapmanızı sağlar — ama modeli veya preprocessing'i değiştirdiğinizde cache'i invalidate etmeyi unutursanız eski scale'lerle sessizce yanlış engine üretirsiniz. Cache dosyasını model checkpoint hash'iyle birlikte adlandırmak bu hatayı yapısal olarak engeller.

Builder tarafında config.set_flag(trt.BuilderFlag.INT8) ve config.int8_calibrator = calibrator yeterli. trtexec ile hızlı deneme için --int8 --calib=calib.cache bayrakları kullanılabilir.

Doğruluk Regresyon Testi

INT8 engine'i asla göz kararı kabul etmeyin. Asgari test seti şu üç ölçümü içermeli:

  1. Uçtan uca metrik: Aynı validation seti üzerinde FP32 (referans), FP16 ve INT8 için mAP@0.5:0.95. Makul bir kabul eşiği "FP16'ya göre ≤1 mAP puanı kayıp"tır; uygulamanın toleransına göre sıkılaştırılır.
  2. Sınıf bazlı delta: Toplam mAP korunurken tek bir kritik sınıf (özellikle küçük nesneler) çökebilir. Per-class AP tablosu regresyon raporunun parçası olmalı.
  3. Katman hassasiyeti: Kayıp eşiği aşıyorsa Polygraphy ile (polygraphy debug precision) hangi katmanların INT8'e duyarlı olduğunu bulun ve o katmanları layer.precision = trt.float16 ile mixed-precision'a sabitleyin. Tipik şüpheliler: ilk konvolüsyon, detection head ve normalizasyon sonrası dar aktivasyonlar.

Bu testin CI'da koşması gerekir: engine build, mAP ölçümü ve eşik kontrolü tek pipeline'da. Aksi halde bir JetPack güncellemesinde regresyon fark edilmeden sahaya gider.

Engine Serileştirme

TensorRT engine'i taşınabilir bir artifact değildir: GPU mimarisine (SM sürümü), TensorRT sürümüne ve DLA konfigürasyonuna bağlıdır. Orin NX'te build edilen plan dosyası Xavier'de yüklenmez; JetPack major güncellemesi de rebuild gerektirir. Buradan çıkan pratikler:

  • Plan dosyasını (engine.serialize()) hedef donanımda, tercihen CI'a bağlı gerçek cihaz üzerinde build edin; x86 host'ta build edilen engine hedefte geçersizdir.
  • Engine'in yanında bir manifest tutun: model hash'i, TensorRT/JetPack sürümü, kalibrasyon cache hash'i, build bayrakları. Sahada "hangi engine koşuyor" sorusunun cevabı dosya adından okunabilmeli.
  • --timingCacheFile ile timing cache'i saklayın; rebuild süresini dakikalardan saniyelere indirir.
  • Uygulama açılışında engine sürüm uyumsuzluğunu yakalayıp kontrollü şekilde rebuild'e düşün veya açılışı reddedin — deserialize hatasını üretimde segfault olarak öğrenmek istemezsiniz.

Kuantizasyon tek seferlik bir optimizasyon değil, model yaşam döngüsünün parçasıdır: her yeni checkpoint kalibrasyon, regresyon ve serileştirme zincirinden geçmelidir. Bu zinciri edge AI pipeline altyapınızın içine bir kez kurduğunuzda INT8'in kazanımı öngörülebilir hale gelir. Mevcut inference hattınızda hassasiyet/performans dengesini ve deployment zincirini ikinci bir gözle birlikte değerlendirmek isterseniz, gömülü sistem mimari denetimi tam olarak bu tür kararların doğrulanması için tasarlandı.

#TensorRT#INT8 kuantizasyon#Jetson Orin#Edge AI#model optimizasyonu

Frequently Asked Questions

Why is NVMM zero-copy important in GStreamer?+
NVMM (NVIDIA Memory Manager) transfers data between the Jetson SoC's ISP, NVENC, and GPU units without copying to CPU memory. Without NVMM, each conversion creates a GPU→CPU→GPU cycle adding ~18ms latency. With zero-copy, this drops to ~2ms and CPU load goes from 30-40% to under 5%.
How do you measure GStreamer pipeline latency on Jetson?+
Use the GstShark plugin or GST_DEBUG environment variable. For hardware-level measurement, compare nvarguscamerasrc source timestamps against nvoverlaysink output timestamps. Alternatively, GPIO toggle + oscilloscope measures frame capture→display time directly.
Why does 'Could not negotiate' error occur in NVMM pipelines?+
This error occurs when caps filters between elements don't specify 'memory:NVMM'. A single CPU-side element in the chain eliminates the zero-copy benefit. Explicit 'video/x-raw(memory:NVMM)' caps must be added after nvvidconv.

Using this technology in your project?

Schedule an architecture audit with Spikedge engineers.

Schedule Architecture Audit