Ultralytics YOLO27: Bekleme listesine katıl.
Get Started
Üretimde Ultralytics YOLO·Çalışma Zamanı Seç·Ders 2/9
Dersorta düzey

ONNX'e Dışa Aktar (ve Eşdeğerliği Doğrula)

Taşınabilir bir ONNX modeli dışa aktar, ardından dağıtıma almadan önce kutular, güven değerleri ve mAP bakımından PyTorch ile eşleştiğini doğrula.

Güven değerlerini 0.1 kaydıran bir dışa aktarım hata vermez. Dağıttığın her cihazda tespitleri sessizce conf eşiğinin öte yanına taşır; sayılar yanlış çıkana kadar da kimse fark etmez. ONNX, dağıtım için taşınabilir bir başlangıç noktasıdır; bu kursta beş dakikalık eşdeğerlik kontrolü en düşük maliyetli güvencedir.

Sonuç

ONNX'e dışa aktar, sayısal verilerle kutular, güven değerleri ve mAP bakımından PyTorch ile eşleştiğini kanıtla ve bulduğun farkları açıkla.

Hızlı Başlangıç
Nasıl ilerleyeceğini zaten biliyorsan, işte kısa sürüm.
  1. yolo export model=yolo26n.pt format=onnx dynamic=True.

  2. Her iki dosyadan da bir görsel geçir; yalnızca sayıyı değil, kutu sayısını, sınıfları, güven değerlerini ve koordinatları karşılaştır.

  3. Her ikisinde de aynı batch ile yolo val komutunu çalıştır; mAP50-95 farkı 0.5 puan içinde olmalı.

  4. Fark mı var? Girdi şeklini (sabit veya dinamik), kendi ön işleme adımlarını ve hangi başlığı (nms) dışa aktardığını kontrol et.

Uygulamalı

Dışa aktarma#

from ultralytics import YOLO

model = YOLO("yolo26n.pt")  # veya runs/detect/pills_v1/weights/best.pt
model.export(format="onnx", dynamic=True)  # ağırlıkların yanına yolo26n.onnx yazar

Günlük, seçilen opset değerini (opset 18, onnx 1.23 ile) ve onnxslim'in grafiği basitleştirdiğini gösteriyor. Dışa Aktarma modu içindeki davranışı değiştiren seçenekler:

SeçenekVarsayılanNe zaman değiştirilmeli
imgsz640Üretim farklı bir boyutta çalışıyor; sabit dışa aktarım bu boyuta kilitlenir
dynamicFalseÇıkarım sırasında toplu iş boyutu veya görsel şekli değişiyor
batch1Çıkarım sırasında toplu işleme yapıyorsun (dinamik dışa aktarımda en büyük toplu iş)
opsetdesteklenen en güncel değerDaha eski bir çalışma zamanı modeli reddediyor; değeri düşür
quantizeNone (FP32)FP16 için 16; kalibrasyon data ile INT8 için 8
nmsNoneTrue, grafiğe NMS ekler; False, YOLO26'nın NMS kullanmayan başlığını seçer

Varsayılan nms=None ile dışa aktarım ham tahminleri döndürür ve Ultralytics sarmalayıcısı tıpkı PyTorch'ta olduğu gibi NMS çalıştırır.

Bilinen Bir Görüntüde Eşdeğerliği Kontrol Et#

Aynı görüntüyü iki modelden geçir ve her şeyi karşılaştır:

from ultralytics import YOLO

pt = YOLO("yolo26n.pt")
onnx = YOLO(pt.export(format="onnx", dynamic=True))

img = "https://ultralytics.com/images/bus.jpg"
a = pt(img, verbose=False)[0].boxes
b = onnx(img, verbose=False)[0].boxes

ia, ib = a.conf.argsort(descending=True), b.conf.argsort(descending=True)
print(f"boxes:         PyTorch {len(a)}  ONNX {len(b)}")
print(f"same classes:  {a.cls[ia].tolist() == b.cls[ib].tolist()}")
print(f"max conf diff: {(a.conf[ia] - b.conf[ib]).abs().max():.4f}")
print(f"max xyxy diff: {(a.xyxy[ia] - b.xyxy[ib]).abs().max():.2f} px")

Her iki sonucu da güven değerine göre sıralamak aynı nesneleri eşleştirir; böylece son iki satır her tespiti eşleştiği tespitle karşılaştırır. Şimdi bunu biri gösterildiği gibi, diğeri dynamic=True kaldırılmış olarak iki kez çalıştır:

Dışa aktarmaKutularAynı sınıflarMaks. güven farkıMaks. kutu farkı
dynamic=True5'e karşı 5evet0.00000.00 px
sabit (varsayılan)5'e karşı 5evet0.097710.11 px

Sabit dışa aktarım bozuk değil. PyTorch, dikey bus.jpg görselini en küçük, adım boyutuna hizalanmış 640×480 (yükseklik × genişlik) şekline göre mektup kutusu yöntemiyle boyutlandırırken sabit 640×640 grafiğin görseli gri şeritlerle kareye tamamlaması gerekir. Farklı dolgu, tahminleri biraz değiştirir. Aynı koşullarda karşılaştırmak için ya dynamic=True ile dışa aktar ya da PyTorch tarafında rect=False kullanarak sabit dışa aktarım gibi tam kareye dolgu yap (bkz. sabit şekil ve minimum dikdörtgen); rect=False ile sabit dışa aktarım da 0.0000 farkla eşleşir.

Her İki Dosyayı da Doğrula#

Tek bir görsel büyük hataları yakalar; doğrulama ise ince farkları ortaya çıkarır. Ön işleme eşleşmesi için her ikisinde de aynı batch değerini kullan:

yolo val model=yolo26n.pt data=coco8.yaml batch=1
yolo val model=yolo26n.onnx data=coco8.yaml batch=1

Her ikisi de burada mAP50-95 0.654 değerini bildiriyor. COCO8 modelden çok iş hattını doğrular; kendi doğrulama kümen üzerinde 0.5 puanı aşan farkları incele. Tüm kontrol:

ONNX Runtime'ı Doğrudan Çağır#

Üretim hizmetleri çoğu zaman Ultralytics sarmalayıcısını kullanmak yerine çıkarım motorunu doğrudan çağırır. Bu durumda ön işleme senin sorumluluğundadır ve Ultralytics ile tam olarak eşleşmelidir. nms=False ile YOLO26 dışa aktarımı son tespitleri döndürür; dolayısıyla NMS koduna gerek yoktur:

import cv2
import numpy as np
import onnxruntime as ort

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

path = YOLO("yolo26n.pt").export(format="onnx", nms=False)  # NMS-free head: output (1, 300, 6)
session = ort.InferenceSession(path, providers=["CPUExecutionProvider"])  # put CUDAExecutionProvider first on GPU
name = session.get_inputs()[0].name

# Preprocess like Ultralytics: letterbox to 640x640 with gray (114) padding, BGR->RGB, 0-1, NCHW
im = cv2.imread(safe_download("https://ultralytics.com/images/bus.jpg"))
h, w = im.shape[:2]
r = 640 / max(h, w)
nh, nw = round(h * r), round(w * r)
top, left = (640 - nh) // 2, (640 - nw) // 2
canvas = np.full((640, 640, 3), 114, np.uint8)
canvas[top : top + nh, left : left + nw] = cv2.resize(im, (nw, nh), interpolation=cv2.INTER_LINEAR)
x = canvas[..., ::-1].transpose(2, 0, 1)[None].astype(np.float32) / 255

(out,) = session.run(None, {name: x})
det = out[0][out[0][:, 4] > 0.25]  # rows: x1, y1, x2, y2, score, class
det[:, [0, 2]] = ((det[:, [0, 2]] - left) / r).clip(0, w)  # undo padding and scale
det[:, [1, 3]] = ((det[:, [1, 3]] - top) / r).clip(0, h)
for x1, y1, x2, y2, score, cls in det:
    print(f"class {int(cls):2d}  score {score:.2f}  box [{x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}]")
class  5  score 0.93  box [6, 229, 807, 749]
class  0  score 0.92  box [47, 399, 237, 902]
class  0  score 0.90  box [223, 405, 345, 863]
class  0  score 0.86  box [669, 391, 810, 880]
class  0  score 0.52  box [0, 554, 65, 874]

Bir otobüs (sınıf 5) ve dört kişi (sınıf 0); YOLO(path)(img) ile piksel düzeyinde eşleşiyor. Otobüsün güven değeri burada 0.93, varsayılan dışa aktarımla ise 0.88; çünkü nms=False farklı bir başlık seçiyor. Aynı nms değeriyle dışa aktarılan dosyaları karşılaştır.

Yaygın Hatalar#

Statik dışa aktarımı dikdörtgen PyTorch çıkarımıyla karşılaştırma

Belirti: Kutular aynı, ancak güven değerleri 0.1'e kadar farklı; fark kare olmayan görüntülerde en büyük. Çözüm: Pariteyi dynamic=True ile veya PyTorch tarafında rect=False ile kontrol et ya da statik dışa aktarımı doğrulama mAP değerine göre değerlendir.

Ultralytics'ten farklı, elle yazılmış ön işleme

Belirti: Kendi ONNX Runtime kodun, YOLO("model.onnx") değerine kıyasla daha az veya yeri kaymış kutu buluyor. Çözüm: ort_raw.py ile birebir aynı şekilde letterbox uygula (görüntüyü esnetme), 114 ile kenar boşluğu ekle, BGR'yi RGB'ye dönüştür, 255'e böl ve kutulardaki kenar boşluğu ile ölçeklemeyi geri al.

Başlıkları karıştırma

Belirti: Şekiller eşleşse bile tüm güven değerleri biraz farklı. Çözüm: Yalnızca bir nms=False (NMS içermeyen) dışa aktarımını başka bir nms=False dışa aktarımıyla karşılaştır. nms=True kullanırken grafiğe gömülü conf ve iou değerlerinin başka yerlerde kullandıklarınla eşleştiğini de kontrol et.

Dene

Modelini (veya yolo26n.pt değerini) ONNX'e biri sabit, diğeri dynamic=True olacak şekilde iki kez dışa aktar ve her birine karşı parity.py komutunu çalıştır. Ardından aynı batch ile .pt ve dinamik .onnx üzerinde yolo val komutunu çalıştır ve üç satırlık bir eşdeğerlik raporu yaz: her dışa aktarım için maksimum güven farkı ve mAP50-95 farkı.

Tamamlanma ölçütü
Aşağıdakilerin tümü doğru olduğunda dersi tamamlamış olursun.
  • .pt dosyanın yanında bir .onnx dosyan ve hem sabit hem de dinamik dışa aktarım için eşdeğerlik çıktın var.

  • Sabit dışa aktarımdaki güven değeri farkını açıklayabilir ve bu farkı gideren dynamic=True veya rect=False düzeltmesini gösterebilirsin.

  • ONNX dosyasındaki doğrulama mAP50-95 değeri PyTorch modelinden en fazla 0,5 puan farklıdır ya da fark açıklanmıştır.

Çözümü göster
from ultralytics import YOLO

if __name__ == "__main__":  # validation starts worker processes on macOS and Windows
    weights = "yolo26n.pt"  # or runs/detect/pills_v1/weights/best.pt
    onnx_path = YOLO(weights).export(format="onnx", dynamic=True)

    maps = {}
    for w in (weights, onnx_path):
        maps[w] = YOLO(w).val(data="coco8.yaml", batch=1, plots=False).box.map  # or my_dataset/data.yaml
        print(f"{w}: mAP50-95 = {maps[w]:.4f}")
    print(f"gap = {abs(maps[weights] - maps[onnx_path]) * 100:.2f} points")
yolo26n.pt: mAP50-95 = 0.6540
yolo26n.onnx: mAP50-95 = 0.6540
gap = 0.00 points

yolo26n için bus.jpg üzerinde hazırlanan eşdeğerlik raporunda şunlar yazıyor: statik dışa aktarmada en yüksek güven farkı 0,0977 (letterbox şekli, rect=False ile giderildi), dinamik dışa aktarmada 0,0000, mAP50-95 farkı 0,00 puan. Dinamik dışa aktarımı kullanıma al.

Sırada ne var

NVIDIA donanımında sonraki adım TensorRT; burada FP16 ve INT8, biraz doğruluktan vazgeçerek çok daha yüksek hız sağlar.

İlerlemeni kaydetmek için giriş yap