CUDA graph

CUDA graph merekam satu eksekusi dari urutan kernel tetap dan memutarnya ulang sebagai satu launch. LibreYOLO menangkap inferensi pada 39 family terverifikasi dan pelatihan pada 24 family, selalu per family, selalu setelah pemeriksaan paritas bitwise, dan tidak pernah sebagai fallback diam-diam.

Family inferensi
39
Family pelatihan
24
Flag inferensi
predict(cuda_graph=True)
Flag pelatihan
train(cuda_graph=True)

Yang ditangkap

Graph merekam urutan kernel tetap dan alamat memori yang dibaca serta ditulis. Graph tidak merekam nilai, bentuk, atau control flow. Replay menjadi satu launch alih-alih ratusan, sehingga peningkatan terbesar terjadi pada jaringan kecil dan ukuran batch kecil, ketika sebuah langkah didominasi overhead launch, bukan aritmetika.

Kedua entry point menangkap jumlah pekerjaan yang berbeda.

Di dalam graphEager
InferensiForward jaringan, model._forward(x)Preprocessing, NMS, seluruh postprocessing
PelatihanForward dan backward jaringanLoss, optimizer step, gradient clipping, EMA, schedule LR

NMS maupun loss deteksi bukan kandidat. Keduanya memilih dengan boolean mask, menjalankan Hungarian matching atau assigner, dan bercabang berdasarkan hasil, yang merupakan hal persis yang tidak dapat direkam graph. Membiarkannya di luar membuat capture aman, bukan batasan yang harus diakali.

Prediksi
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9t.pt") # True menangkap saat penggunaan pertama per bentuk input.# "auto" menunggu hingga bentuk berulang sebelum membayar biaya capture.result = model(SAMPLE_IMAGE, cuda_graph=True)
Pelatihan
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt")model.train(data="my-dataset.yaml", epochs=100, cuda_graph=True)
Pelatihan dari CLI
libreyolo train model=LibreYOLO9t.pt data=my-dataset.yaml \  epochs=100 --cuda-graph

cuda_graph menerima tiga nilai saat prediksi. False adalah default. True menangkap saat pertama kali setiap bentuk input terlihat. "auto" menunggu hingga bentuk berulang, sehingga pekerjaan sekali jalan dan dengan bentuk berubah-ubah tidak membayar capture yang tidak akan digunakan kembali. capture_graph(imgsz=None, batch=1, dtype=None) memindahkan biaya dari permintaan pertama, graph_info() melaporkan graph yang ditangkap dan jumlah replay, sedangkan release_graphs() membebaskannya.

Saat pelatihan, flag berupa boolean biasa, --cuda-graph pada CLI. Lihat performa prediksi dan performa pelatihan untuk kontrol di sekitarnya.

Dukungan inferensi

Dukungan berlaku per family, dinyatakan melalui variabel kelas SUPPORTS_CUDA_GRAPH, dan family baru ditandai setelah berhasil menangkap dan melakukan replay yang identik secara bit terhadap dua input probe dari distribusi berbeda. Matriks paritas bersama tersebut mencakup 39 family dalam sembilan task.

TaskFamily
detectyolo1, yolo2, yolo3, yolo4, yolo9, yolo9_p2, yolo9_e2e, yolox, yolo7, yolonas, picodet, rtmdet, dfine, deim, deimv2, rtdetr, rtdetrv2, rtdetrv4, rfdetr, ec
segmentdfine, rtmdet, rfdetr, ec
poseec, yolonas, rfdetr
pointfomo
classifyresnet, convnext, mobilenetv4, efficientnetv2, clip, dinov2, siglip2
semanticeomt, dinov2, segformer, pidnet, lingbotvision
depthdepth_anything, depth_anything3, zipdepth
restorenafnet, realesrgan, swinir
mattebirefnet

Beberapa family muncul pada lebih dari satu task, sehingga matriks menjalankan lebih banyak baris daripada jumlah family berbeda. Tiga family tambahan melakukan capture melalui jalur khusus family dengan pengujian khusus sendiri, bukan melalui matriks bersama, dan tidak termasuk dalam 39: PP-OCR, SAM, dan SenseNova.

Verifikasi bersifat bitwise, bukan perkiraan. Versi awal protokol menilai paritas berdasarkan magnitudo relatif dan secara keliru menurunkan tiga family sehat, YOLOX, EfficientNetV2, dan YOLOv7, yang perbedaan antara eager dan graph terukur sekitar 1e-7 meski identik secara bit pada probe yang penting.

Dukungan pelatihan

Capture pelatihan bertambah dari dua family menjadi 24 pada rilis ini, dalam lima task.

TaskFamily
detectyolo9, yolo9_p2, yolo9_e2e, yolox, yolo7, yolonas, picodet, rtmdet, rfdetr, dfine, deim, deimv2, rtdetr, rtdetrv2, rtdetrv4, ec
classifyresnet, convnext, mobilenetv4, efficientnetv2
semanticsegformer, lingbotvision
pointfomo
restorenafnet

Selain yang tercantum, semuanya berlatih secara eager: task lain pada family yang sama, family yang tidak ada dalam list, run terdistribusi, dan run distillation. Capture juga dilewati selama bentuk masih baru karena jalur pelatihan menunggu bentuk input berulang tiga kali sebelum menangkap. Artinya, multi_scale=True dapat sama sekali tidak pernah melakukan capture.

Dua jawaban berbeda untuk family yang tidak didukung

Jalur inferensi memunculkan error. predict(cuda_graph=True) pada family yang belum ikut serta memunculkan NotImplementedError yang menyebutkan family, bukan menjalankan eager dan membuat pengguna mengira memperoleh percepatan. Alasannya, capture buruk tidak gagal dengan jelas: replay forward yang melakukan operasi tidak dapat ditangkap akan diam-diam mengembalikan angka salah. Karena itu, dukungan harus berupa pernyataan eksplisit per family, bukan percobaan dengan fallback.

Jalur pelatihan mencatat log. train(cuda_graph=True) selalu aman diberikan, dan family, task, atau konfigurasi yang tidak dapat ditangkap menulis satu baris lalu berlatih eager tanpa perubahan. Capture yang gagal di tengah run juga mengalihkan sisa run ke eager, bukan membatalkannya. Asimetri ini disengaja: prediksi adalah pemanggilan yang dapat diperbaiki di call site, sedangkan run pelatihan tidak seharusnya berhenti pada jam keenam akibat optimisasi opsional.

Pemisahan seam

Beberapa family tidak dapat ditangkap secara utuh karena satu tahap benar-benar melakukan operasi yang tidak dapat direkam graph. Alih-alih menghapus family, capture dibagi pada seam yang terverifikasi: bagian yang dapat ditangkap melakukan replay, sisanya berjalan eager, dan output gabungan sama dengan menjalankan semuanya secara eager.

FamilyDitangkapEager dan alasannya
Depth Anything 3JaringanTahap sky, yaitu pekerjaan yang terlihat host setelah forward
BiRefNetEncoder, forward_encDecoder, dengan deform_conv2d yang menghasilkan nilai berbeda saat replay capture
PP-OCRTahap deteksi, forward_detPengenalan, karena lebar crop berbeda per baris
SAMGambar encoderJalur prompt, yang berjalan berkali-kali per encode
SenseNovaVision towerGenerasi autoregresif dengan cache KV yang bertambah setiap langkah
Detektor encoder-decoderBackbone dan encoderDecoder dan Hungarian criterion

Pemisahan BiRefNet perlu diperhatikan: kesalahan deform_conv2d saat capture dapat direproduksi pada pemanggilan sederhana di luar model. Menggantinya dengan padanan PyTorch murni ditolak karena juga akan menggeser prediksi eager, dan angka eager adalah kontrak.

Kasus encoder-decoder mencakup D-FINE, DEIM, DEIMv2, RT-DETR, RT-DETRv2, RT-DETRv4, dan EC. Decoder membangun query contrastive-denoising dari ground truth, dan jumlah query berasal dari jumlah ground truth terbesar dalam batch, sehingga jumlah token decoder berubah antarbatch. Itulah satu hal yang tidak dapat ditoleransi graph. Backbone ditambah encoder menghabiskan sekitar seperlima hingga seperempat langkah untuk family tersebut, sehingga berada di bagian bawah tabel percepatan.

PP-OCR menangkap satu graph per bentuk input deteksi, dibatasi oleh batas cache runner, dan mengembalikan hasil eager ketika tidak ada scope capture aktif.

Numerik

Sebagian besar family identik secara bit, dan jika tidak, alasannya disebutkan secara jelas. Pada langkah nol pelatihan, loss identik secara bit untuk semua 24 family dan tidak ada buffer BatchNorm yang berbeda; perbandingan gradienlah yang memisahkan kategori.

KelasFamilyArti
ExactSebagian besar dari 24Setiap gradien identik secara bit
1 ULPfomo, lingbotvisionBit terakhir float32, sekitar 1e-7 relatif, akibat urutan penjumlahan berbeda
Eager noiseLineage DETRPerbedaan graph terhadap eager tidak lebih besar daripada perbedaan dua run eager satu sama lain
Float roundingrtmdet137 dari 139 gradien identik secara bit, dua berbeda sekitar 3e-4
Own RNG streamsegformerStochastic depth berada di dalam region yang ditangkap

Kelas eager-noise harus dibaca dengan benar. Untuk family tersebut, dua run eager dengan seed yang sama sudah berbeda, sehingga identik secara bit bukan standar yang gagal dicapai run graph, melainkan standar yang tidak dicapai apa pun. Hal ini berlaku lebih luas pada amp=False, ketika nondeterminisme relatif 3.2e-7 yang diukur dalam gradien bobot fp32 terakumulasi: dua run eager YOLOv9-t dengan seed sama berbeda 36 persen setelah 20 langkah, dan menonaktifkan TF32 tidak memperbaikinya.

Pin memory

Capture berjalan dengan capture_error_mode="thread_local". Dalam mode default PyTorch "global", thread pin-memory DataLoader yang menyiapkan batch berikutnya memanggil cudaHostAlloc, yang membatalkan capture aktif sekaligus dirusak oleh capture tersebut. Akibatnya, run berhenti saat mengambil batch berikutnya dengan error dari dalam thread pin-memory. Pasangan kegagalan ini teramati dua kali dalam campaign pelatihan nyata sebelum didiagnosis.

Mode thread-local hanya membatasi thread yang melakukan capture. Thread pin tidak pernah menyentuh stream capture, sehingga tidak ada operasinya yang termasuk graph. Pelatihan melangkah lebih jauh dan sementara mengganti subclass torch.cuda.CUDAGraph yang memaksa mode tersebut karena make_graphed_callables tidak menyediakan argumen untuknya. Penggantian dilakukan di bawah lock agar dua capture bersamaan tidak meninggalkannya terpasang.

Nilai manfaatnya

Diukur pada RTX 5070 Ti dengan AMP, satu process per kelompok, melakukan replay satu batch nyata agar dataloader tidak terlibat, menggunakan langkah tercepat dari 24 langkah setelah warm-up. Deteksi pada 640 px, classification pada 224 px.

FamilyBatchPercepatan
FOMO s163.63x
MobileNetV4 s162.74x
EfficientNetV2 b0162.44x
YOLOv9-t81.99x
YOLOv9 e2e81.76x
YOLOv9 p281.49x
Selain ituberagam1.04x hingga 1.26x

Percepatan seluruh run lebih kecil karena graph tidak dapat mempercepat dataloader atau validasi. Fine-tuning YOLOv9-t selama 20 epoch pada 406 gambar turun dari 428.4 detik menjadi 367.7 detik, peningkatan end-to-end 1.16x, dengan mAP50-95 identik sebesar 0.6394 pada kedua kelompok dan loss per epoch identik.

Batas atas ditentukan oleh porsi jaringan dalam satu langkah. Pada hardware yang sama di 640 px dan batch 8, porsinya 84 persen untuk YOLOv9-t, tetapi hanya 26 persen untuk RTMDet-t, yang menghabiskan sebagian besar langkah dalam label assigner. Overhead launch tertinggi ada di Windows, sehingga peningkatan Linux sekitar sepertiga hingga setengah tabel ini, dan run yang dibatasi dataloader tidak mengalami perubahan wall-clock. Peak memory berubah antara 5 persen lebih rendah dan 19 persen lebih tinggi.

Catatan penting

Graph merekam alamat, bukan nilai, sehingga semua tindakan yang memindahkan parameter akan menghapusnya. Mengubah device melalui predict(device=...), melakukan kuantisasi, dan membatalkan kuantisasi semuanya membatalkan graph yang telah ditangkap.

Ukuran batch lebih berpengaruh daripada family: RT-DETR-r18 meningkat 1.19x pada batch 2 dan 1.04x pada batch 8 karena batch besar dibatasi komputasi dan memiliki lebih sedikit overhead launch yang dapat dihilangkan.

Suite paritas inferensi berjalan tanpa paket kernels opsional terinstal, sehingga keamanan capture dengan kernel Hub terkompilasi aktif tidak dicakup. Tetapkan LIBREYOLO_HUB_KERNELS=0 untuk mengeluarkannya saat mengisolasi masalah capture. Lihat kernel.

List family inferensi diturunkan dari matriks CAPTURABLE dalam tests/e2e/test_cuda_graph_families.py pada v1.5.0. List family pelatihan, kelas paritas, dan timing dari docs/training_cuda_graphs.md. API dan NotImplementedError dari BaseModel._require_cuda_graph_support, cuda_graph_scope, dan capture_graph di libreyolo/models/base/model.py, dengan variabel kelas SUPPORTS_CUDA_GRAPH. Pemisahan seam dibaca dari override _get_graph_runner dalam family depth_anything3, birefnet, ppocr, sam, dan sensenova serta dari libreyolo/models/base/detr_cuda_graph.py. capture_error_mode dari libreyolo/models/base/cuda_graph.py dan libreyolo/training/cuda_graph.py. Fallback pelatihan dari libreyolo/training/trainer.py dan flag --cuda-graph dari libreyolo/cli/commands/train.py.