NVIDIA DeepStream

NVIDIA DeepStream chạy suy luận (inference) qua phần tử nvinfer của nó, thứ cần một đồ thị ONNX, một tệp cấu hình tương ứng và một parser bounding box. Đặt deepstream=True khi xuất sang ONNX sẽ ghi ra hai thành phần đầu và nối chúng với thành phần thứ ba.

Tham số
export(format="onnx", deepstream=True)
Kết quả ghi ra
Một đồ thị ONNX, config_infer_primary_<stem>.txt và <stem>_labels.txt
Phạm vi
43 tổ hợp họ mô hình và tác vụ trải trên chín tác vụ
Parser
NvDsInferParseYolo, từ dự án DeepStream-Yolo của Marcos Luciano, giấy phép MIT. Build một lần cho mỗi thiết bị. github.com/marcoslucianops/DeepStream-Yolo
Khả dụng
Có trong v1.5.0. Đã merge vào dev ngày 2026-08-08 trong pull request 728. pull request 728, issue 648
Đã kiểm chứng khi chạy
DeepStream 8.0.0 trên một RTX 5070 Ti, chỉ phát hiện đối tượng, 2026-08-08

Khả dụng

Xuất sang DeepStream có trong v1.5.0. Nó đã merge vào dev ngày 2026-08-08 trong pull request 728, nên một bản cài đặt hiện tại đã có sẵn và không cần ghim vào branch nào.

Cài đặt
pip install "libreyolo[onnx]"

Nếu bạn đã clone branch deepstream-export trước ngày 2026-08-08, hãy thay nó bằng bản mới. Branch đó đã bị rebase và force-push, còn phần lịch sử cũ thì thiếu bản sửa lỗi giúp các lần xuất này chạy được trên máy CUDA.

Những gì lệnh xuất ghi ra

model.export(format="onnx", deepstream=True) ghi ra ba tệp cạnh nhau. Với libreyolo9s.pt:

  • libreyolo9s.onnx, đồ thị phát hiện đối tượng, một tensor đầu ra có shape (batch, num_detections, 6), mỗi hàng là [x1, y1, x2, y2, score, class_id] theo tọa độ pixel của đầu vào mạng.
  • config_infer_primary_libreyolo9s.txt, một cấu hình nvinfer mang theo các hằng số tiền xử lý của họ mô hình, số lớp đối tượng, các ngưỡng và phần nối tới parser.
  • libreyolo9s_labels.txt, mỗi dòng một tên lớp đối tượng.

Tệp nhãn xuất hiện mỗi khi checkpoint mang theo tên các lớp đối tượng. Mô hình độ sâu thì không có, nên chúng không nhận được tệp đó lẫn khóa labelfile-path.

LibreYOLO không sinh ra tệp .so nào. Tệp .so mà DeepStream nạp là parser bounding box từ marcoslucianops/DeepStream-Yolo, được build một lần cho mỗi thiết bị, và nó vẫn là cùng một binary dù bạn trỏ nó vào bộ phát hiện LibreYOLO nào. Mô hình chính là tệp ONNX. Phân loại và phân đoạn ngữ nghĩa không cần parser nào cả, vì nvinfer tự hậu xử lý những tác vụ đó.

Xuất mô hình

Python
from libreyolo import LibreYOLO9, LibreDFINE # Ghi libreyolo9s.onnx, config_infer_primary_libreyolo9s.txt# và libreyolo9s_labels.txt vào thư mục làm việcLibreYOLO9("libreyolo9s.pt", size="s").export(format="onnx", deepstream=True) # Giữ mỗi mô hình phát hiện đối tượng trong thư mục riêng: mọi config# phát hiện đều đặt cùng một tên tệp cache engine. Xem "Các bẫy đã biết"LibreDFINE("LibreDFINEs.pt", size="s").export(format="onnx", deepstream=True)
Đối số
model.export(    format="onnx",     # deepstream=True bị từ chối với mọi format khác    deepstream=True,    conf=0.25,         # đặt giá trị cho pre-cluster-threshold (và classifier-threshold,                       # segmentation-threshold ở những tác vụ đó)    iou=0.45,          # đặt giá trị cho nms-iou-threshold, bỏ qua khi cluster-mode=4    batch=1,           # đặt giá trị cho batch-size và tên tệp cache engine    half=False,        # True đánh dấu config network-mode=2 (build fp16)    int8=False,        # True đánh dấu config network-mode=1    dynamic=True,      # trục batch động trong đồ thị ONNX    imgsz=640,         # đặt giá trị cho infer-dims=3;H;W) # deepstream=True và nms=True loại trừ lẫn nhau: DeepStream chạy bước# suppression trong giai đoạn clustering, nên không có gì được nhúng vào đồ thị
Tải trọng số D-FINE trước
curl -L -o LibreDFINEs.pt \  https://huggingface.co/LibreYOLO/LibreDFINEs/resolve/main/LibreDFINEs.pt

LibreDFINE._load_weights ném FileNotFoundError khi tệp chưa có sẵn trên đĩa, mà không thử tải về, nên hãy tự tải LibreDFINEs.pt trước. Khoảng trống đó đang được theo dõi ở issue #727. Trọng số YOLO9 được tải về ở lần dùng đầu tiên.

Tham số này chỉ có ở Python. libreyolo export trên branch này không có tùy chọn deepstream, và CLI dựng các đối số xuất từ một danh sách cố định thay vì chuyển tiếp những khóa lạ.

Build parser bounding box

Phát hiện đối tượng cần thư viện parser, phân đoạn thực thể cần một thư viện khác, còn các tác vụ còn lại thì không cần. Có hai thứ trên image DeepStream 8.0 làm hỏng lệnh build được ghi trong tài liệu, và cả hai đều là vấn đề môi trường chứ không phải vấn đề của LibreYOLO.

Image có sẵn cuda, cuda-12, cuda-12.5, cuda-12.8cuda-12.9 dưới /usr/local. Chỉ cuda-12.5 có một toolkit đầy đủ. Nó cũng có libcublas.so.12libcublas.so.12.8.4.1 nhưng không có libcublas.so không phiên bản mà -lcublas dùng để resolve. Script bên dưới đi vòng qua cả hai.

build_parser.sh, chạy bên trong container DeepStream
set -egit clone --depth 1 https://github.com/marcoslucianops/DeepStream-Yolo.git # /usr/local/cuda-12 trên image này chỉ là bản stub và bước build chết vì nó với# "fatal error: crt/host_defines.h: No such file or directory". Hãy tìm một# toolkit thực sự có header đó; trên image 8.0 thì đó là cuda-12.5CUDA_DIR=$(readlink -f /usr/local/cuda)[ -f "$CUDA_DIR/include/crt/host_defines.h" ] || \  CUDA_DIR=$(ls -d /usr/local/cuda-*.* | sort -Vr | \             while read d; do [ -f "$d/include/crt/host_defines.h" ] && echo "$d" && break; done) # Image có sẵn libcublas.so.12 và libcublas.so.12.8.4.1 nhưng không có# libcublas.so không phiên bản mà -lcublas cần, nên bước link hỏng với# "/usr/bin/ld: cannot find -lcublas". Hãy đưa cho linker đúng các tên nó muốnmkdir -p /tmp/cudalibsfor lib in cublas cublasLt cudart; do  real=$(find /usr/local -name "lib${lib}.so.1*" | grep -v stubs | sort -V | tail -1)  ln -sf "$real" "/tmp/cudalibs/lib${lib}.so"doneexport LIBRARY_PATH="/tmp/cudalibs:$LIBRARY_PATH" make -C DeepStream-Yolo/nvdsinfer_custom_impl_Yolo CUDA_VER="${CUDA_DIR##*/cuda-}"
Phân đoạn thực thể dùng một parser khác
git clone --depth 1 https://github.com/marcoslucianops/DeepStream-Yolo-Seg.gitmake -C DeepStream-Yolo-Seg/nvdsinfer_custom_impl_Yolo_seg \  CUDA_VER="${CUDA_DIR##*/cuda-}"

Sau đó hãy trỏ custom-lib-path trong config được sinh ra tới tệp libnvdsinfer_custom_impl_Yolo.so vừa build. Giá trị được sinh ra là đường dẫn tương đối nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so, đường dẫn này đúng khi deepstream-app chạy từ thư mục checkout của DeepStream-Yolo và cần sửa lại trong các trường hợp còn lại.

Chạy pipeline

Hãy kiểm tra xem container có thấy GPU không trước khi mất thời gian vào bất cứ thứ gì khác. Đây là bước kiểm tra đầu tiên mà lần chạy kiểm chứng đã làm, trên một card Blackwell dưới WSL2.

Xác nhận GPU passthrough trước mọi thứ khác
docker run --rm --gpus all nvcr.io/nvidia/tritonserver:26.04-py3 \  nvidia-smi --query-gpu=name,driver_version,compute_cap --format=csv

Output

name, driver_version, compute_cap
NVIDIA GeForce RTX 5070 Ti, 591.86, 12.0

Lần chạy kiểm chứng điều khiển deepstream-app với một nguồn là tệp, không có display sink, bật on-screen display, và đặt gie-kitti-output-dir để kết quả phát hiện của từng frame rơi xuống đĩa dưới dạng văn bản KITTI. Một config với những thiết lập đó:

deepstream_app_config.txt
[application]enable-perf-measurement=1perf-measurement-interval-sec=5gie-kitti-output-dir=kitti [tiled-display]enable=0 [source0]enable=1type=3uri=file:///opt/nvidia/deepstream/deepstream/samples/streams/sample_1080p_h264.mp4num-sources=1gpu-id=0 [streammux]gpu-id=0batch-size=1batched-push-timeout=40000width=1920height=1080live-source=0 [primary-gie]enable=1gpu-id=0gie-unique-id=1config-file=config_infer_primary_libreyolo9s.txt [osd]enable=1border-width=2text-size=15 [sink0]enable=1type=1sync=0 [tests]file-loop=0
Chạy thử
deepstream-app -c deepstream_app_config.txt

Output

App run successful
Cả hai bước trong một container
docker run --rm --gpus all -v "$PWD:/work" -w /work \  nvcr.io/nvidia/deepstream:8.0-samples-multiarch \  bash -c "bash build_parser.sh && deepstream-app -c deepstream_app_config.txt"

nvinfer build engine TensorRT từ tệp ONNX ở lần chạy đầu tiên rồi cache nó ngay cạnh mô hình, nên lần chạy đầu phải trả giá cho việc build engine còn những lần sau chỉ nạp cache.

Config được sinh ra

Cả hai config bên dưới đều do bộ xuất ghi ra cho lần chạy kiểm chứng, không hề sửa lại sau đó.

KhóaYOLO9-sD-FINE-s
net-scale-factor0.0039215686270.003921568627
model-color-format00
infer-dims3;640;6403;640;640
maintain-aspect-ratio10
symmetric-padding00
network-type00
num-detected-classes8080
cluster-mode24
parse-bbox-func-nameNvDsInferParseYoloNvDsInferParseYolo
pre-cluster-threshold0.250.25
nms-iou-threshold0.45
topk300300

Hai config khác nhau ở ba chỗ: maintain-aspect-ratio, cluster-mode, và việc nms-iou-threshold có xuất hiện hay không. Config của D-FINE bỏ hẳn khóa đó, đúng như cluster-mode=4 đòi hỏi.

Những head sinh ra nhiều nhất một dự đoán cho mỗi vật thể sẽ nhận cluster-mode=4, nên DeepStream không chạy clustering trên chúng; clustering sẽ gộp nhầm những kết quả phát hiện thực sự khác nhau. Nhóm đó gồm rfdetr, dfine, deim, deimv2, ec, rtdetr, rtdetrv2, rtdetrv4yolo9_e2e. Các head dạng grid và anchor nhận cluster-mode=2 cùng nms-iou-threshold.

Config phát hiện đối tượng còn mang theo engine-create-func-name=NvDsInferYoloCudaEngineGet, khóa này giao việc build engine cho thư viện parser. Đó chính là thứ cố định tên tệp cache engine, và cũng là nguồn gốc của xung đột được mô tả ở phần các bẫy đã biết.

Tác vụ và họ mô hình được hỗ trợ

Bốn mươi ba tổ hợp họ mô hình và tác vụ xuất được. deepstream_supported_tasks()deepstream_supported_families(task) trong libreyolo/export/deepstream.py trả về đúng những danh sách đó lúc runtime.

Tác vụnetwork-typeThư viện parserHọ mô hình
Phát hiện đối tượng0DeepStream-Yoloyolo9, yolo9_p2, yolo9_e2e, yolo1, yolo2, yolo3, yolo4, yolo7, yolox, yolonas, rtmdet, picodet, rfdetr, dfine, deim, deimv2, ec, rtdetr, rtdetrv2, rtdetrv4
Phân loại1Không cầnmobilenetv4, convnext, efficientnetv2, resnet, dinov2
Phân đoạn ngữ nghĩa2Không cầnpidnet, eomt, dinov2, lingbotvision
Phân đoạn thực thể3DeepStream-Yolo-Segrfdetr, dfine, ec
Tư thế100Không cầnyolo9, yolonas, rfdetr, ec
Độ sâu100Không cầndepth_anything, zipdepth
Phục hồi ảnh100Không cầnnafnet, realesrgan, swinir
Matting100Không cầnbirefnet
Gaze100Không cầnl2cs

network-type=100 nghĩa là DeepStream không có bộ hậu xử lý cho tác vụ đó. Những config này đặt output-tensor-meta=1, các đầu ra gốc của đồ thị đi qua nguyên vẹn, và ứng dụng tự giải mã chúng từ metadata của tensor. Đồ thị nhiều đầu ra vẫn ổn ở đó: mọi lớp đầu ra đều tới được metadata với đúng tên đầu ra và các trục động như một lần xuất ONNX thông thường.

Mỗi hàng của phân đoạn thực thể là hàng phát hiện đối tượng, theo sau là mặt nạ (mask) của thực thể đó, được duỗi phẳng ở (netH / 4, netW / 4), độ phân giải mà parser phân đoạn hardcode sẵn, dưới dạng xác suất cho segmentation-threshold.

Phân loại và gaze chạy như inference thứ cấp. Đặt process-mode=2operate-on-gie-id trong config được sinh ra để đưa một bộ phân loại ra sau một bộ phát hiện. Gaze là hợp đồng chỉ có phần head, mỗi đầu vào là một ảnh cắt khuôn mặt, nên nó cần một bộ phát hiện khuôn mặt đứng phía trước.

Ba họ mô hình vắng mặt là có chủ đích. segformer chưa được nối vào hợp đồng xuất ngữ nghĩa dùng chung và không xuất được sang ONNX ở bất kỳ dạng nào. RTMDet-Ins và YOLO9 bị chặn phần xuất phân đoạn thực thể ngay trong chính LibreYOLO. depth_anything3 chưa có phần hiện thực xuất.

Hai hàng trong bảng có khoảng trống về checkpoint phía sau. Chỉ có checkpoint ngữ nghĩa EoMT cỡ l được công bố, còn phân loại DINOv2 thì chưa có checkpoint công bố nào, nên tổ hợp đó cần trọng số do bạn tự tinh chỉnh (fine-tuning).

Khác biệt ở khâu tiền xử lý

nvinfer tính net-scale-factor * (x - offsets) trên từng kênh với một hệ số vô hướng, thứ không diễn đạt được độ lệch chuẩn riêng cho từng kênh. Những họ mô hình cần điều đó (rfdetr, ec, các cỡ deimv2 dùng backbone DINO, rtmdet, picodet, và mọi họ phân loại) được nhúng sẵn phép chuẩn hóa vào đồ thị đã xuất, và config được sinh ra sẽ đưa vào đồ thị đúng không gian đầu vào thô tương ứng.

Phần hình học mới là chỗ mà pipeline Python của chính LibreYOLO và nvinfer vẫn còn khác nhau:

  • Các họ dùng letterbox (yolo9, yolox, yolonas, rtmdet, yolo2, yolo3, yolo4, yolo7) vốn pad bằng màu xám. nvinfer pad bằng màu đen.
  • yolonas khi phát hiện đối tượng vốn resize cạnh dài nhất về 636 trong khung 640 của nó. maintain-aspect-ratio của nvinfer dùng trọn 640.
  • Phân loại vốn resize cạnh ngắn nhất rồi cắt giữa. nvinfer kéo giãn khung hình hoặc vùng ROI của vật thể về đúng đầu vào mạng, nên những chủ thể được cắt sát sẽ khác đi.
  • EoMT vốn chạy các ô cửa sổ trượt cho phân đoạn ngữ nghĩa. Đồ thị đã xuất là một khung bị kéo giãn duy nhất, nhanh hơn và kém chính xác hơn.
  • pidnet sinh ra bản đồ lớp đối tượng ở 1/8 độ phân giải đầu vào còn lingbotvision ở 1/16. DeepStream upsample bản đồ lớp đối tượng để hiển thị.

Cổng kiểm tra parity của ONNX nạp vào các tensor đã được tiền xử lý sẵn, nên nó kiểm tra đầu ra của đồ thị và không bắt được lỗi sai thứ tự kênh màu hay sai chính sách padding trong config. Hãy kiểm chứng trên dữ liệu của bạn trước khi triển khai một tải công việc đòi hỏi parity chính xác.

Các bẫy đã biết

Hai mô hình phát hiện đối tượng trong cùng một thư mục sẽ nạp engine của nhau

Mọi config phát hiện đối tượng đều mang cùng một dòng:

ini
model-engine-file=model_b1_gpu0_fp32.engine

Bộ build engine của parser đòi đúng tên tệp đó và nó không thay đổi theo mô hình. Xuất một mô hình phát hiện thứ hai vào cùng thư mục thì lần chạy thứ hai sẽ nạp engine đã cache của mô hình thứ nhất. Không có gì crash; chỉ có các box là sai. Hãy cho mỗi mô hình phát hiện một thư mục riêng. Lần chạy kiểm chứng đã phải tách D-FINE ra một thư mục riêng thì mới thử nghiệm được.

Một box chỉ mang được một lớp đối tượng

Định dạng hàng của nvinfer[x1, y1, x2, y2, score, class_id], mỗi box một lớp đối tượng, nên lần xuất sẽ ép điểm số của các lớp đối tượng về argmax của chúng. Một box mà predict báo dưới hai lớp đối tượng thì chỉ sống sót dưới một lớp. Trường hợp đo được: LibreYOLO báo vase 0.773bottle 0.383 trên cùng một box, còn đồ thị DeepStream giữ lại vase. Điều này là hệ quả của định dạng hàng mà parser dùng và không đổi được nếu không rời khỏi hợp đồng đó, nên đây là hành vi được trông đợi chứ không phải một regression.

Đã kiểm chứng

deepstream-app chạy tới EOS với App run successful trên cả hai kiểu head của bộ phát hiện, trên tệp sample_1080p_h264.mp4 (1443 frame) mà NVIDIA đóng gói kèm, có bật ghi KITTI cho từng frame.

YOLO9-sD-FINE-s
Kiểu headgridone-to-one
cluster-mode24
maintain-aspect-ratio10
Số frame có kết quả phát hiện14431443
Tổng số kết quả phát hiện1803171105

Biểu đồ tần suất lớp đối tượng trên toàn bộ 1443 frame xếp xe hơi ở vị trí đầu và người ở vị trí thứ hai với cả hai mô hình, đúng như một cảnh đường phố. Chênh lệch gấp bốn lần về số lượng kết quả phát hiện chính là khác biệt cluster-mode đang làm đúng việc của nó: D-FINE ở cluster-mode=4 không chạy clustering, nên mọi query vượt ngưỡng đều sống sót, kể cả những kết quả gần trùng nhau.

Hai mô hình được huấn luyện độc lập đặt vật thể chiếm ưu thế vào cùng một chỗ:

YOLO9  bus  [706.72,  0.82, 1916.34, 1062.97]  conf 0.965
D-FINE bus  [702.73,  2.93, 1916.24, 1069.32]  conf 0.965

Lần chạy đó khẳng định năm điều: TensorRT build được engine từ tệp ONNX đã xuất trên sm_120, nvinfer chấp nhận mọi khóa trong config được sinh ra, NvDsInferParseYolo đọc đúng bố cục tensor, các box rơi vào hệ tọa độ 1920x1080 theo độ phân giải nguồn, và nhãn được resolve đúng theo tệp nhãn được sinh ra.

Môi trường mà nó đã chạy:

Thành phầnGiá trị
Hệ điều hành máy chủWindows 11 Pro 26200
GPUNVIDIA GeForce RTX 5070 Ti, 16 GB
Driver591.86
Compute capability12.0 (Blackwell, sm_120)
Runtime containerDocker Desktop 29.4.3, backend WSL2
Image DeepStreamnvcr.io/nvidia/deepstream:8.0-samples-multiarch
Phiên bản DeepStream8.0.0
CUDA trong container12.8.1
Parsermarcoslucianops/DeepStream-Yolo tại HEAD

Song song với lần chạy pipeline, tests/unit/test_deepstream_export.py phủ các adapter của đồ thị và các khóa config được sinh ra, và 35 test của nó đều pass trên commit này.

Chưa được kiểm chứng

Nêu ra để phạm vi ở trên không bị hiểu rộng hơn thực tế.

  • Jetson và aarch64. Hợp đồng xuất không phụ thuộc vào kiến trúc, nhưng pipeline mới chỉ được chạy trên một GPU rời x86.
  • Bốn mươi mốt trong số 43 tổ hợp. Chỉ có phát hiện đối tượng với yolo9 và phát hiện đối tượng với dfine là đã đi qua DeepStream. Phân loại, phân đoạn ngữ nghĩa, phân đoạn thực thể và các tác vụ trả tensor thô được phủ bởi unit test và các bài kiểm tra parity ONNX, chứ không phải bởi một lần chạy pipeline.
  • FP16 và INT8. Chỉ network-mode=0 được thử.
  • Đa luồng (multi-stream) và batching. Một nguồn, batch-size=1.
  • Độ chính xác so với một tập dữ liệu (dataset) có ground truth. Các kết quả phát hiện chỉ được kiểm tra về mức hợp lý ngữ nghĩa và mức đồng thuận giữa hai mô hình, chứ không được chấm mAP qua DeepStream.

Viết ra từ lần kiểm chứng chạy thực tế ngày 2026-08-08. Danh sách họ mô hình, các khóa config và giá trị mặc định được đọc từ libreyolo/export/deepstream.py và libreyolo/export/exporter.py tại commit 5f81e11e, bản đã merge vào dev cùng ngày trong pull request 728.