Tìm hiểu về End-to-End Detection trong Ultralytics YOLO26
Giới thiệu
Nếu bạn đang nâng cấp lên YOLO26 từ một model cũ hơn như YOLOv8 hoặc YOLO11, một trong những thay đổi lớn nhất bạn sẽ nhận thấy là việc loại bỏ Non-Maximum Suppression (NMS). Các model YOLO truyền thống tạo ra hàng ngàn dự đoán chồng chéo, cần một bước hậu xử lý NMS riêng biệt để lọc ra các kết quả cuối cùng. Điều này làm tăng độ trễ, làm phức tạp các đồ thị export và có thể hoạt động không nhất quán trên các nền tảng phần cứng khác nhau.
YOLO26 áp dụng một cách tiếp cận khác. Nó xuất kết quả phát hiện cuối cùng trực tiếp từ model — không cần lọc bên ngoài. Đây được gọi là end-to-end object detection, và nó được bật theo mặc định trong tất cả các model YOLO26. Kết quả là một pipeline triển khai đơn giản hơn, độ trễ thấp hơn và tốc độ inference nhanh hơn tới 43% trên các CPU.
Hướng dẫn này sẽ giải thích chi tiết những thay đổi, liệu bạn có cần cập nhật code của mình không, những định dạng export nào hỗ trợ end-to-end inference và cách migrate mượt mà từ các model YOLO cũ hơn.
Để có cái nhìn sâu sắc hơn về động lực đằng sau sự thay đổi kiến trúc này, hãy xem bài viết trên blog của Ultralytics về lý do YOLO26 loại bỏ NMS.
- Sử dụng Ultralytics API hoặc CLI? Không cần thay đổi — chỉ cần đổi tên model của bạn thành
yolo26n.pt. - Sử dụng code inference tùy chỉnh (ONNX Runtime, TensorRT, v.v.)? Cập nhật hậu xử lý của bạn — đầu ra phát hiện bây giờ ở định dạng
(N, 300, 6)trongxyxy, không cần NMS. Các tác vụ khác sẽ đính kèm thêm dữ liệu (hệ số mask, keypoints hoặc góc). - Đang export? Hầu hết các định dạng hỗ trợ đầu ra end-to-end một cách tự nhiên. Tuy nhiên, một vài định dạng (NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX và Edge TPU) sẽ tự động quay lại đầu ra truyền thống do các hạn chế về operator không được hỗ trợ (ví dụ:
torch.topk).
Cách thức hoạt động của End-to-End Detection
YOLO26 sử dụng dual-head architecture trong quá trình đào tạo. Cả hai head đều chia sẻ chung backbone và neck, nhưng tạo ra đầu ra theo những cách khác nhau:
| Head | Mục đích | Đầu ra phát hiện | Hậu xử lý |
|---|---|---|---|
| One-to-One (mặc định) | End-to-end inference | (N, 300, 6) | Chỉ dựa trên ngưỡng tin cậy |
| One-to-Many | Đầu ra YOLO truyền thống | (N, nc + 4, 8400) | Cần NMS |
Các shape ở trên là dành cho phát hiện. Các tác vụ khác mở rộng đầu ra one-to-one với dữ liệu bổ sung cho mỗi phát hiện:
| Tác vụ | Đầu ra End-to-End | Dữ liệu bổ sung |
|---|---|---|
| Detection | (N, 300, 6) | — |
| Segmentation | (N, 300, 6 + nm) + proto (N, nm, H, W) | nm hệ số mask (mặc định 32) |
| Pose | (N, 300, 57) | 17 keypoints × 3 (x, y, độ hiển thị) |
| OBB | (N, 300, 7) | Góc xoay |
Trong quá trình training, cả hai head đều chạy đồng thời — head one-to-many cung cấp tín hiệu học tập phong phú hơn, trong khi head one-to-one học cách tạo ra các dự đoán sạch, không chồng chéo. Trong quá trình suy luận và export, chỉ có one-to-one head được kích hoạt theo mặc định, tạo ra tới 300 phát hiện mỗi ảnh ở định dạng [x1, y1, x2, y2, confidence, class_id].
Khi bạn gọi model.fuse(), nó sẽ gộp các lớp Conv + BatchNorm để tăng tốc inference và, trên các model end-to-end, cũng loại bỏ head one-to-many — làm giảm kích thước model và FLOPs. Để biết thêm chi tiết về kiến trúc dual-head, hãy xem trang model YOLO26.
Tôi có cần thay đổi code của mình không?
Sử dụng Ultralytics Python API hoặc CLI
Không cần thay đổi. Nếu bạn sử dụng Ultralytics Python API hoặc CLI tiêu chuẩn, mọi thứ sẽ hoạt động tự động — dự đoán, xác thực, và export tất cả đều xử lý các model end-to-end ngay lập tức.
from ultralytics import YOLO
# Load a YOLO26 model
model = YOLO("yolo26n.pt")
# Predict — no NMS step, no code changes
results = model.predict("image.jpg")Sử dụng Custom Inference Code
Có, định dạng đầu ra đã thay đổi. Nếu bạn đã viết logic hậu xử lý tùy chỉnh cho YOLOv8 hoặc YOLO11 (ví dụ: khi chạy inference với ONNX Runtime hoặc TensorRT), bạn sẽ cần cập nhật nó để xử lý shape đầu ra mới:
| YOLOv8 / YOLO11 | YOLO26 (end-to-end) | |
|---|---|---|
| Đầu ra phát hiện | (N, nc + 4, 8400) | (N, 300, 6) |
| Định dạng BBox | xywh (tâm x, tâm y, chiều rộng, chiều cao) | xyxy (góc trên bên trái x, góc trên bên trái y, góc dưới bên phải x, góc dưới bên phải y) |
| Bố cục | Tọa độ BBox + điểm số lớp cho mỗi anchor | [x1, y1, x2, y2, conf, class_id] |
| Cần NMS | Có | Không |
| Hậu xử lý | NMS + lọc theo độ tin cậy | Chỉ lọc theo độ tin cậy |
Đối với segmentation, pose, và OBB các tác vụ, YOLO26 đính kèm dữ liệu đặc thù cho từng tác vụ vào mỗi phát hiện — xem bảng shape đầu ra ở trên.
Trong đó N là batch size và nc là số lượng lớp (ví dụ: 80 cho COCO).
Với các model end-to-end, hậu xử lý trở nên đơn giản hơn nhiều — ví dụ: khi sử dụng ONNX Runtime:
import onnxruntime as ort
# Load and run the exported end-to-end model
session = ort.InferenceSession("yolo26n.onnx")
output = session.run(None, {session.get_inputs()[0].name: input_tensor})
# End-to-end output: (batch, 300, 6) → [x1, y1, x2, y2, confidence, class_id]
detections = output[0][0] # first image in batch
detections = detections[detections[:, 4] > conf_threshold] # confidence filter — that's it!Chuyển sang One-to-Many Head
Nếu bạn cần định dạng đầu ra YOLO truyền thống (ví dụ: để tái sử dụng code hậu xử lý dựa trên NMS hiện có), bạn có thể chuyển sang head one-to-many bất cứ lúc nào bằng cách đặt end2end=False:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
# Prediction with NMS (traditional behavior)
results = model.predict("image.jpg", end2end=False)
# Validation with NMS
metrics = model.val(data="coco.yaml", end2end=False)
# Export without end-to-end
model.export(format="onnx", end2end=False)Khả năng tương thích định dạng Export
Hầu hết các export formats hỗ trợ end-to-end inference ngay lập tức, bao gồm ONNX, TensorRT, CoreML, OpenVINO, TFLite, TF.js, và MNN.
Các định dạng sau không hỗ trợ end-to-end và tự động quay lại head one-to-many: NCNN, RKNN, PaddlePaddle, ExecuTorch, IMX, và Edge TPU.
TensorRT hỗ trợ end-to-end, nhưng nó bị tự động vô hiệu hóa khi export với int8=True trên TensorRT ≤10.3.0.
Đánh đổi giữa Độ chính xác và Tốc độ
Phát hiện end-to-end mang lại những lợi ích triển khai đáng kể với tác động tối thiểu lên accuracy:
| Chỉ số | End-to-End (mặc định) | One-to-Many + NMS (end2end=False) |
|---|---|---|
| Tốc độ suy luận CPU | Lên tới 43% nhanh hơn | Cơ sở (Baseline) |
| Tác động tới mAP | ~0.5 mAP thấp hơn | Bằng hoặc vượt trội so với YOLO11 |
| Hậu xử lý | Chỉ lọc theo độ tin cậy | Pipeline NMS đầy đủ |
| Độ phức tạp triển khai | Tối thiểu | Yêu cầu cài đặt NMS |
Đối với hầu hết các ứng dụng thực tế, sự khác biệt ~0.5 mAP là không đáng kể, đặc biệt khi xem xét tốc độ và lợi ích về độ đơn giản. Nếu độ chính xác tối đa là ưu tiên hàng đầu, bạn luôn có thể quay lại sử dụng head one-to-many thông qua end2end=False.
Xem phần Các chỉ số hiệu năng của YOLO26 để xem các benchmark chi tiết cho tất cả các kích thước model (n, s, m, l, x).
Chuyển đổi từ YOLOv8 hoặc YOLO11
Nếu bạn đang nâng cấp dự án hiện có lên YOLO26, đây là danh sách kiểm tra nhanh để đảm bảo quá trình chuyển đổi suôn sẻ:
- Người dùng Ultralytics API / CLI: Không cần thay đổi — chỉ cần cập nhật tên model thành
yolo26n.pt(hoặcyolo26n-seg.pt,yolo26n-pose.pt,yolo26n-obb.pt) - Code xử lý hậu kỳ (post-processing) tùy chỉnh: Cập nhật để xử lý các shape đầu ra mới —
(N, 300, 6)cho tác vụ phát hiện (detection), cùng với dữ liệu đặc thù tác vụ cho segmentation, pose, và OBB. Cũng cần lưu ý sự thay đổi định dạng hộp từxywhđếnxyxy - Pipeline xuất (Export): Kiểm tra phần tương thích định dạng phía trên cho định dạng mục tiêu của bạn
- TensorRT + INT8: Xác minh phiên bản TensorRT của bạn phải >10.3.0 để hỗ trợ end-to-end
- Xuất FP16: Nếu bạn cần tất cả đầu ra ở định dạng FP16, hãy xuất với
end2end=False— xem lý do output0 giữ ở định dạng FP32 - iOS / CoreML: Chế độ end-to-end được hỗ trợ hoàn toàn. Nếu bạn cần hỗ trợ Xcode Preview, hãy sử dụng
end2end=Falsevớinms=True - Thiết bị Edge (NCNN, RKNN): Các định dạng này tự động chuyển đổi về one-to-many, vì vậy hãy bao gồm NMS trong pipeline trên thiết bị của bạn
Câu hỏi thường gặp (FAQ)
Tôi có thể sử dụng end2end=True và nms=True cùng nhau không?
Không. Các tùy chọn này loại trừ lẫn nhau. Nếu bạn thiết lập nms=True trên một model end-to-end trong quá trình export, nó sẽ tự động bị buộc chuyển sang nms=False kèm theo cảnh báo. Head end-to-end đã xử lý việc lọc các kết quả trùng lặp nội bộ, vì vậy NMS bên ngoài là không cần thiết.
Tuy nhiên, end2end=False kết hợp với nms=True là một cấu hình hợp lệ — nó tích hợp NMS truyền thống vào đồ thị xuất (export graph). Điều này có thể hữu ích cho CoreML các bản xuất vì nó cho phép bạn sử dụng tính năng Preview trong Xcode trực tiếp với model phát hiện.
Tham số max_det kiểm soát điều gì trong các model end-to-end?
Phương thức max_det tham số (mặc định: 300) thiết lập số lượng phát hiện tối đa mà head one-to-one có thể xuất ra cho mỗi ảnh. Bạn có thể điều chỉnh nó tại thời điểm suy luận (inference) hoặc thời điểm xuất (export):
model.predict("image.jpg", max_det=100) # fewer detections, slightly faster
model.export(format="onnx", max_det=500) # more detections for dense scenesLưu ý rằng các checkpoint mặc định của YOLO26 được huấn luyện với max_det=300. Mặc dù bạn có thể tăng giá trị này, head one-to-one đã được tối ưu hóa trong quá trình huấn luyện để tạo ra tối đa 300 phát hiện sạch, vì vậy các phát hiện vượt quá giới hạn đó có thể có chất lượng thấp hơn. Nếu bạn cần nhiều hơn 300 phát hiện cho mỗi ảnh, hãy cân nhắc việc huấn luyện lại với giá trị max_det cao hơn.
Model ONNX đã xuất của tôi có đầu ra (1, 300, 6) — điều đó có đúng không?
Có, đó là định dạng đầu ra end-to-end dự kiến cho phát hiện: batch size với 1, lên đến 300 phát hiện, mỗi phát hiện có 6 giá trị [x1, y1, x2, y2, confidence, class_id]. Chỉ cần lọc theo ngưỡng tin cậy (confidence threshold) là xong — không cần NMS.
Đối với các tác vụ khác, shape đầu ra sẽ khác:
| Tác vụ | Shape đầu ra | Mô tả (Description) |
|---|---|---|
| Detection | (1, 300, 6) | [x1, y1, x2, y2, conf, class_id] |
| Segmentation | (1, 300, 38) + (1, 32, 160, 160) | 6 giá trị hộp + 32 hệ số mask, cộng với một tensor prototype mask |
| Pose | (1, 300, 57) | 6 giá trị hộp + 17 điểm đặc trưng × 3 (x, y, độ hiển thị) |
| OBB | (1, 300, 7) | 6 giá trị hộp + 1 góc xoay |
Làm cách nào để kiểm tra xem model đã xuất của tôi có phải là end-to-end không?
Bạn có thể kiểm tra bằng cách sử dụng Ultralytics Python API hoặc bằng cách kiểm tra trực tiếp metadata của model ONNX đã xuất:
from ultralytics import YOLO
model = YOLO("yolo26n.onnx")
model.predict(verbose=False) # run predict to setup predictor first
print(model.predictor.model.end2end) # True if end-to-end is enabledNgoài ra, hãy kiểm tra shape đầu ra — các model phát hiện end-to-end xuất ra (1, 300, 6), trong khi các model truyền thống xuất ra (1, nc + 4, 8400). Đối với các shape tác vụ khác, hãy xem FAQ về shape đầu ra.
End-to-end có được hỗ trợ cho các tác vụ phân đoạn (segmentation), pose, và OBB không?
Có. Tất cả các biến thể tác vụ của YOLO26 — phát hiện, segmentation, pose estimation, và phát hiện đối tượng định hướng (OBB) — hỗ trợ suy luận end-to-end theo mặc định. Chế độ dự phòng end2end=False cũng khả dụng trên tất cả các tác vụ.
Mỗi tác vụ mở rộng đầu ra phát hiện cơ sở với dữ liệu đặc thù cho tác vụ đó:
| Tác vụ | Model | Đầu ra End-to-End |
|---|---|---|
| Detection | yolo26n.pt | (N, 300, 6) |
| Segmentation | yolo26n-seg.pt | (N, 300, 38) + proto (N, 32, 160, 160) |
| Pose | yolo26n-pose.pt | (N, 300, 57) |
| OBB | yolo26n-obb.pt | (N, 300, 7) |