NVIDIA ile GPU Ön İşleme
Giriş
Dağıtım sırasında Ultralytics YOLO modellerini üretim ortamında devreye alırken, ön işleme genellikle darboğaz haline gelir. TensorRT , model çıkarımını sadece birkaç milisaniye içinde çalıştırabilirken, CPU ön işleme (boyutlandırma, dolgulama, normalleştirme) özellikle yüksek çözünürlüklerde görüntü başına 2-10 ms sürebilir. NVIDIA (Veri Yükleme Kütüphanesi), tüm ön işleme boru hattını GPU taşıyarak bu sorunu çözer.
Bu kılavuz, Ultralytics YOLO tam olarak taklit eden DALI iş akışlarını oluşturmanızı ve bunları model.predict(), video akışlarını işleme ve uçtan uca dağıtım Triton Çıkarım Sunucusu.
Bu kılavuz kimler içindir?
Bu kılavuz, CPU belirgin bir darboğaz oluşturduğu üretim ortamlarında YOLO devreye alan mühendisler içindir — genellikle TensorRT NVIDIA 'larda yapılan dağıtımlar, yüksek verimli video işleme akışları veya Triton Çıkarım Sunucusu kurulumlar. Eğer standart çıkarım işlemini model.predict() ve ön işleme aşamasında bir darboğaz yoksa, varsayılan CPU sorunsuz çalışır.
Hızlı Özet
- Bir DALI iş akışı mı oluşturuyorsunuz? Kullanım
fn.resize(mode="not_larger")+fn.crop(out_of_bounds_policy="pad")+fn.crop_mirror_normalizeYOLO letterbox ön işleme sürecini GPU üzerinde taklit etmek için. - Ultralytics ile entegrasyon mu? DALI çıkışını bir
torch.Tensoriçinmodel.predict()— Ultralytics , görüntü ön işlemeyi otomatik olarak Ultralytics . - Triton ile dağıtım mı yapıyorsunuz?CPU için DALI arka ucunu bir TensorRT ile birlikte kullanın.
YOLO İçin Neden DALI Kullanılmalı?
Tipik bir YOLO iş akışında, ön işleme adımları CPU üzerinde gerçekleştirilir:
- Resmi (JPEG/PNG) aç
- En boy oranını koruyarak boyutlandır
- Hedef boyuta kırp (letterbox)
- Normale döndür piksel değerleri
[0, 255]için[0, 1] - Düzeni HWC'den CHW'ye dönüştür
DALI ile tüm bu işlemler GPU üzerinde gerçekleştirilir ve böylece CPU ortadan kalkar. Bu durum özellikle şu durumlarda çok faydalıdır:
| Senaryo | DALI Neden Yardımcı Olur? |
|---|---|
| Hızlı GPU | TensorRT milisaniyenin altında çıkarım yapan TensorRT motorları, CPU maliyetini baskın hale getiriyor |
| Yüksek çözünürlüklü girişler | 1080p ve 4K video akışları, maliyetli boyut değiştirme işlemleri gerektirir |
| Büyük parti boyutları | Sunucu tarafında çok sayıda görüntünün paralel olarak işlenmesi |
| Sınırlı CPU | NVIDIA gibi uç cihazlar veya GPU başına az sayıda CPU sahip yoğun GPU |
Ön koşullar
Yalnızca Linux
NVIDIA yalnızca Linux'u destekler. Windows veya macOS'ta kullanılamaz.
Gerekli paketleri kurun:
pip install ultralytics
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda120
pip install ultralytics
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda110
Gereksinimler:
- NVIDIA GPU hesaplama kapasitesi 5.0+ / Maxwell veya daha yeni)
- CUDA .0 veya üzeri ya da 12.0 veya üzeri
- Python .10–3.14
- Linux işletim sistemi
YOLO Anlamak
Bir DALI iş akışı oluşturmadan önce, Ultralytics ön işleme sırasında tam olarak ne Ultralytics anlamak faydalı olacaktır. Anahtar sınıf şudur: LetterBox içinde ultralytics/data/augment.py:
from ultralytics.data.augment import LetterBox
letterbox = LetterBox(
new_shape=(640, 640), # Target size
center=True, # Center the image (pad equally on both sides)
stride=32, # Stride alignment
padding_value=114, # Gray padding (114, 114, 114)
)
[buradaki] tam ön işleme akışı ultralytics/engine/predictor.py şu adımları gerçekleştirir:
| Adım | Çalışma | CPU | DALI Eşdeğeri |
|---|---|---|---|
| 1 | Posta kutusunun boyutunu değiştirme | cv2.resize | fn.resize(mode="not_larger") |
| 2 | Ortalanmış dolgu | cv2.copyMakeBorder | fn.crop(out_of_bounds_policy="pad") |
| 3 | BGR → RGB | im[..., ::-1] | fn.decoders.image(output_type=types.RGB) |
| 4 | HWC → CHW + normalleştirme /255 | np.transpose + tensor / 255 | fn.crop_mirror_normalize(std=[255,255,255]) |
Posta kutusu işlemi, en-boy oranını şu şekilde korur:
- Hesaplama ölçeği:
r = min(target_h / h, target_w / w) - Boyutu şu şekilde değiştir
(round(w * r), round(h * r)) - Kalan alanı gri ile doldurmak (
114) hedef boyuta ulaşmak için - Görüntüyü ortalayarak kenar boşluklarının her iki tarafa eşit olarak dağıtılması
YOLO için DALI İş Akışı
Aşağıdaki ortalanmış akış şemasını varsayılan referans olarak kullanın. Bu şema Ultralytics uyumludur LetterBox(center=True) davranış; bu, standart YOLO kullanılan yöntemdir.
Ortalanmış Boru Hattı (Önerilen, Ultralytics ile uyumludur)
Bu sürüm, ortalanmış dolgulu ve eşleştirme içeren varsayılan Ultralytics sürecini tam olarak yansıtmaktadır LetterBox(center=True):
Ortalanmış dolgulu DALI iş akışı (önerilir)
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=8, num_threads=4, device_id=0)
def yolo_dali_pipeline_centered(image_dir, target_size=640):
"""DALI pipeline replicating YOLO preprocessing with centered padding.
Matches Ultralytics LetterBox(center=True) behavior exactly.
"""
# Read and decode images on GPU
jpegs, _ = fn.readers.file(file_root=image_dir, random_shuffle=False, name="Reader")
images = fn.decoders.image(jpegs, device="mixed", output_type=types.RGB)
# Aspect-ratio-preserving resize
resized = fn.resize(
images,
resize_x=target_size,
resize_y=target_size,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False, # Match cv2.INTER_LINEAR (no antialiasing)
)
# Centered padding using fn.crop with out_of_bounds_policy
# When crop size > image size, fn.crop centers the image and pads symmetrically
padded = fn.crop(
resized,
crop=(target_size, target_size),
out_of_bounds_policy="pad",
fill_values=114, # YOLO padding value
)
# Normalize and convert layout
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
Ne zaman fn.pad Yeter mi?
Eğer tam olarak ihtiyacınız yoksa LetterBox(center=True) eşlik, şu yöntemi kullanarak dolguyu basitleştirebilirsiniz fn.pad(...) yerine fn.crop(..., out_of_bounds_policy="pad"). Bu varyant yalnızca sağ ve alt kenarları; bu, özel dağıtım boru hatları için kabul edilebilir olabilir, ancak Ultralytics varsayılan ortalanmış letterbox davranışıyla tam olarak uyuşmayacaktır.
Neden fn.crop ortalanmış dolgu için mi?
DALI'nin fn.pad işlemci yalnızca sağ ve alt kenarlar. Ortalanmış dolgu elde etmek için ( Ultralytics uyumlu) LetterBox(center=True)), kullan fn.crop ile out_of_bounds_policy="pad". Varsayılan olarak crop_pos_x=0.5 ve crop_pos_y=0.5, resim simetrik boşluklarla otomatik olarak ortalanır.
Antialias uyumsuzluğu
DALI'nin fn.resize varsayılan olarak kenar yumuşatmayı etkinleştirir (antialias=True), OpenCV ise cv2.resize ile INTER_LINEAR yapıyor olmayan kenar yumuşatma uygulayın. Her zaman antialias=False DALI'de CPU uyacak şekilde. Bunun atlanması, sonuçları etkileyebilecek ince sayısal farklılıklara yol açar model doğruluğu.
Pipeline'ı çalıştırma
Bir DALI iş akışı oluşturun ve çalıştırın
# Build and run the pipeline
pipe = yolo_dali_pipeline_centered(image_dir="/path/to/images", target_size=640)
pipe.build()
# Get a batch of preprocessed images
(output,) = pipe.run()
# Convert to numpy or PyTorch tensors
batch_np = output.as_cpu().as_array() # Shape: (batch_size, 3, 640, 640)
print(f"Output shape: {batch_np.shape}, dtype: {batch_np.dtype}")
print(f"Value range: [{batch_np.min():.4f}, {batch_np.max():.4f}]")
Ultralytics ile DALI kullanımı
Önceden işlenmiş bir PyTorch tensor model.predict(). Bir torch.Tensor kabul edildiğinde, Ultralytics görüntü ön işlemeyi atlar (letterbox, BGR→RGB, HWC→CHW ve /255 normalizasyonu) ve verileri modele göndermeden önce yalnızca cihaz dönüşümü ve veri türü dönüştürme işlemlerini gerçekleştirir.
Ultralytics bu durumda orijinal görüntü boyutlarına Ultralytics için, algılama kutusu koordinatları 640×640 boyutundaki letterbox formatında döndürülür. Bunları orijinal görüntü koordinatlarına dönüştürmek için şunu kullanın: scale_boxes şu tarafından kullanılan tam yuvarlama mantığını işleyen LetterBox:
from ultralytics.utils.ops import scale_boxes
# boxes: tensor of shape (N, 4) in xyxy format, in 640x640 letterboxed coords
# Scale boxes from letterboxed (640, 640) back to original (orig_h, orig_w)
boxes = scale_boxes((640, 640), boxes, (orig_h, orig_w))
Bu, tüm harici ön işleme yolları için geçerlidir — doğrudan tensor , video akışları ve Triton .
DALI + Ultralytics
from nvidia.dali.plugin.pytorch import DALIGenericIterator
from ultralytics import YOLO
# Load model
model = YOLO("yolo26n.pt")
# Create DALI iterator
pipe = yolo_dali_pipeline_centered(image_dir="/path/to/images", target_size=640)
pipe.build()
dali_iter = DALIGenericIterator(pipe, ["images"], reader_name="Reader")
# Run inference with DALI-preprocessed tensors
for batch in dali_iter:
images = batch[0]["images"] # Already on GPU, shape (B, 3, 640, 640)
results = model.predict(images, verbose=False)
for result in results:
print(f"Detected {len(result.boxes)} objects")
Ön işleme yükü sıfır
Bir...'in yanından geçerken torch.Tensor için model.predict(), görüntü ön işleme adımı ~0,004 ms (neredeyse sıfır) sürerken, CPU ile CPU ~1-10 ms sürer. tensor , BCHW biçiminde, float32 (veya float16) tensor ve [0, 1]. Ultralytics , cihaz aktarımını ve veri türü dönüştürmeyi yine otomatik olarak Ultralytics .
DALI ve Video Akışları
Gerçek zamanlı video işleme için şunu kullanın fn.external_source herhangi bir kaynaktan kareleri beslemek için — OpenCV, GStreamer veya özel yakalama kütüphaneleri:
Video akışının ön işlenmesi için DALI iş akışı
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=1, num_threads=4, device_id=0)
def yolo_video_pipeline(target_size=640):
"""DALI pipeline for processing video frames from external source."""
# External source for feeding frames from OpenCV, GStreamer, etc.
frames = fn.external_source(device="cpu", name="input")
frames = fn.reshape(frames, layout="HWC")
# Move to GPU and preprocess
frames_gpu = frames.gpu()
resized = fn.resize(
frames_gpu,
resize_x=target_size,
resize_y=target_size,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False,
)
padded = fn.crop(
resized,
crop=(target_size, target_size),
out_of_bounds_policy="pad",
fill_values=114,
)
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
import cv2
import numpy as np
import torch
from ultralytics import YOLO
model = YOLO("yolo26n.engine") # TensorRT model
pipe = yolo_video_pipeline(target_size=640)
pipe.build()
cap = cv2.VideoCapture("video.mp4")
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Feed BGR frame (convert to RGB for DALI)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pipe.feed_input("input", [np.array(frame_rgb)])
(output,) = pipe.run()
# Convert DALI output to torch tensor for inference.
# This is a simple fallback path: using feed_input() with pipe.run() keeps a GPU->CPU->GPU copy.
# For high-throughput deployments, prefer a reader-based pipeline plus DALIGenericIterator to keep data on GPU.
tensor = torch.tensor(output.as_cpu().as_array()).to("cuda")
results = model.predict(tensor, verbose=False)
DALI özellikli Triton Sunucusu
Üretim ortamına dağıtım için DALI ön işlemeyi TensorRT çıkarımını Triton Bu, CPU tamamen ortadan kaldırır — ham JPEG baytları girer, algılamalar çıkar ve her şey GPU üzerinde işlenir.
Model Deposu Yapısı
model_repository/
├── dali_preprocessing/
│ ├── 1/
│ │ └── model.dali
│ └── config.pbtxt
├── yolo_trt/
│ ├── 1/
│ │ └── model.plan
│ └── config.pbtxt
└── ensemble_dali_yolo/
├── 1/ # Empty directory (required by Triton)
└── config.pbtxt
1. Adım: DALI İş Akışını Oluşturun
Triton arka ucu için DALI iş akışını serileştirin:
Triton için DALI iş akışını serileştir
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=8, num_threads=4, device_id=0)
def triton_dali_pipeline():
"""DALI preprocessing pipeline for Triton deployment."""
# Input: raw encoded image bytes from Triton
images = fn.external_source(device="cpu", name="DALI_INPUT_0")
images = fn.decoders.image(images, device="mixed", output_type=types.RGB)
resized = fn.resize(
images,
resize_x=640,
resize_y=640,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False,
)
padded = fn.crop(
resized,
crop=(640, 640),
out_of_bounds_policy="pad",
fill_values=114,
)
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
# Serialize pipeline to model repository
pipe = triton_dali_pipeline()
pipe.serialize(filename="model_repository/dali_preprocessing/1/model.dali")
2. Adım: YOLO TensorRT YOLO aktarın
YOLO TensorRT aktarın
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.export(format="engine", imgsz=640, half=True, batch=8)
# Copy the .engine file to model_repository/yolo_trt/1/model.plan
3. Adım: Triton yapılandırın
dali_preprocessing/config.pbtxt:
name: "dali_preprocessing"
backend: "dali"
max_batch_size: 8
input [
{
name: "DALI_INPUT_0"
data_type: TYPE_UINT8
dims: [ -1 ]
}
]
output [
{
name: "DALI_OUTPUT_0"
data_type: TYPE_FP32
dims: [ 3, 640, 640 ]
}
]
yolo.pbtxt:
name: "yolo_trt"
platform: "tensorrt_plan"
max_batch_size: 8
input [
{
name: "images"
data_type: TYPE_FP32
dims: [ 3, 640, 640 ]
}
]
output [
{
name: "output0"
data_type: TYPE_FP32
dims: [ 300, 6 ]
}
]
ensemble_dali_yolo/config.pbtxt:
name: "ensemble_dali_yolo"
platform: "ensemble"
max_batch_size: 8
input [
{
name: "INPUT"
data_type: TYPE_UINT8
dims: [ -1 ]
}
]
output [
{
name: "OUTPUT"
data_type: TYPE_FP32
dims: [ 300, 6 ]
}
]
ensemble_scheduling {
step [
{
model_name: "dali_preprocessing"
model_version: -1
input_map {
key: "DALI_INPUT_0"
value: "INPUT"
}
output_map {
key: "DALI_OUTPUT_0"
value: "preprocessed_image"
}
},
{
model_name: "yolo_trt"
model_version: -1
input_map {
key: "images"
value: "preprocessed_image"
}
output_map {
key: "output0"
value: "OUTPUT"
}
}
]
}
Ensemble Haritalama Nasıl Çalışır?
Bu topluluk, modelleri şu şekilde birbirine bağlar: sanal tensor. Şunu output_map değer "preprocessed_image" DALI adımındaki değer, input_map değer "preprocessed_image" TensorRT . Bunlar, bir adımın çıktısını bir sonraki adımın girdisine bağlayan rastgele isimlerdir; herhangi bir modelin dahili tensor eşleşmeleri gerekmez.
4. Adım: Çıkarım İsteklerini Gönder
Neden tritonclient yerine YOLO(\"http://...\")?
Ultralytics yerleşik Triton ön/son işlemeyi otomatik olarak gerçekleştiren bir sistem. Ancak, DALI ensemble ile çalışmayacaktır çünkü YOLO() ön işleme tabi tutulmuş bir float32 tensor topluluk ham JPEG baytlarını bekler. Şunu kullanın tritonclient doğrudan DALI setleri için ve yerleşik entegrasyon DALI'siz standart kurulumlar için.
Görüntüleri Triton gönder
import numpy as np
import tritonclient.http as httpclient
client = httpclient.InferenceServerClient(url="localhost:8000")
# Load image as raw bytes (JPEG/PNG encoded)
image_data = np.fromfile("image.jpg", dtype="uint8")
image_data = np.expand_dims(image_data, axis=0) # Add batch dimension
# Create input
input_tensor = httpclient.InferInput("INPUT", image_data.shape, "UINT8")
input_tensor.set_data_from_numpy(image_data)
# Run inference through the ensemble
result = client.infer(model_name="ensemble_dali_yolo", inputs=[input_tensor])
detections = result.as_numpy("OUTPUT") # Shape: (1, 300, 6) -> [x1, y1, x2, y2, conf, class_id]
# Filter by confidence (no NMS needed — YOLO26 is end-to-end)
detections = detections[0] # First image
detections = detections[detections[:, 4] > 0.25] # Confidence threshold
print(f"Detected {len(detections)} objects")
JPEG Görüntülerini Toplu İşleme
Triton'a bir grup JPEG görüntüsü gönderirken, kodlanmış tüm bayt dizilerini aynı uzunluğa (gruptaki maksimum bayt sayısı) tamamlayın. Triton , giriş tensor için homojen grup yapıları Triton .
Desteklenen Görevler
DALI ön işleme, standartı kullanan tüm YOLO çalışır LetterBox boru hattı:
| Görev | Destekleniyor | Notlar |
|---|---|---|
| Algılama | ✅ | Standart posta kutusu ön işleme |
| Segmentasyon | ✅ | Algılama ile aynı ön işleme |
| Poz Tahmini | ✅ | Algılama ile aynı ön işleme |
| Yönlü Algılama (OBB) | ✅ | Algılama ile aynı ön işleme |
| Sınıflandırma | ❌ | Letterbox değil, Torchvision dönüşümlerini (ortadan kırpma) kullanır |
Sınırlamalar
- Yalnızca Linux: DALI, Windows veya macOS'u desteklemez
- NVIDIA GPU : CPU çalışmaz
- Statik iş akışı: İş akışı yapısı derleme sırasında tanımlanır ve dinamik olarak değiştirilemez
fn.padsadece sağ/alt: Kullanınfn.cropileout_of_bounds_policy="pad"ortalanmış dolgu için- Rekt modu yok: DALI iş akışları sabit boyutlu çıktılar üretir (ör. 640×640).
auto=TrueDeğişken boyutlu çıktılar (ör. 384×640) üreten rect modu desteklenmemektedir. Şunu unutmayın ki TensorRT Dinamik giriş biçimlerini destekler; sabit boyutlu bir DALI iş akışı, maksimum verim elde etmek için doğal olarak sabit boyutlu bir motorla eşleştirilir - Birden çok örneği olan bellek: Kullanarak
instance_groupilecount> Triton 'daki 1, yüksek bellek kullanımına neden Triton . DALI modeli için varsayılan örnek grubunu kullanın
SSS
DALI ön işleme, CPU hızıyla karşılaştırıldığında nasıl bir performans sergiliyor?
Bu avantaj, kullandığınız iş akışına bağlıdır. GPU ile GPU zaten hızlıysa TensorRTile zaten hızlıysa, 2-10 ms süren CPU en büyük maliyet unsuru haline gelebilir. DALI, ön işlemeyi GPU üzerinde çalıştırarak bu darboğazı ortadan kaldırır. En büyük kazançlar, yüksek çözünürlüklü girdilerde (1080p, 4K), büyük parti boyutlarında ve GPU başına sınırlı sayıda CPU sahip sistemlerde görülür.
DALI'yi PyTorch (sadece TensorRT değil) kullanabilir miyim?
Evet. Kullan DALIGenericIterator ön işleme tabi tutulmak için torch.Tensor çıktıları, ardından bunları model.predict(). Ancak, performans artışı en fazla TensorRT çıkarım işleminin zaten çok hızlı olduğu ve CPU darboğaz haline geldiği modeller.
Şunlar arasındaki fark nedir fn.pad ve fn.crop dolgu olarak mı?
fn.pad sadece şuraya boşluk ekler sağ ve alt kenarlar. fn.crop ile out_of_bounds_policy="pad" görüntüyü ortalar ve Ultralytics ile uyumlu olacak şekilde her tarafa simetrik olarak boşluk ekler LetterBox(center=True) davranış.
DALI, CPU tamamen aynı sonuçlar veriyor mu?
Neredeyse aynı. Set antialias=False içinde fn.resize OpenCV cv2.INTER_LINEAR. Minor floating-point differences (< 0.001) may occur due to GPU vs CPU arithmetic, but these have no measurable impact on detection doğruluğu.
DALI'ye alternatifCUDA nasıl olur?
CUDA GPU görüntü işleme için bir başka NVIDIA . Operatör bazında kontrol sağlar (örneğin OpenCV DALI'nin ardışık işlem yaklaşımı yerine (ancak GPU üzerinde).CUDA cvcuda.copymakeborder() her iki kenar için ayrı ayrı dolguyu destekler, böylece ortalanmış letterbox formatı kolayca elde edilebilir. Boru hattı tabanlı iş akışları için (özellikle Triton) ve özel çıkarım kodlarında ayrıntılı operatör düzeyinde kontrolCUDA .