Giải thích kiến trúc YOLO: từ YOLOv3 đến YOLO26#
Mỗi model Ultralytics YOLO được xây dựng từ ba giai đoạn: backbone trích xuất đặc trưng, neck hợp nhất đặc trưng ở nhiều tỷ lệ và head dự đoán bounding box cùng lớp. Hướng dẫn này ghi lại các module cấu thành từng giai đoạn và những thay đổi của chúng từ YOLOv3 đến YOLO26, đồng thời truy nguyên từng thành phần đến định nghĩa trong các file cấu hình bên dưới ultralytics/cfg/models/ và các lớp module trong ultralytics/nn/modules/.
Mỗi model được định nghĩa theo kiểu khai báo trong file YAML dưới dạng danh sách các layer có thứ tự, trong đó mỗi layer tuân theo định dạng [from, repeats, module, args]: layer nào cung cấp đầu vào, module lặp bao nhiêu lần, lớp layer (Conv, C3k2, SPPF, Detect, …) và các tham số khởi tạo. Hướng dẫn cấu hình YAML cho model trình bày định dạng này — bao gồm cách repeats và args được điều chỉnh theo các hệ số độ sâu và chiều rộng của biến thể — cùng toàn bộ hệ thống phân giải module. Hướng dẫn này tập trung vào bản thân các module và những thay đổi của chúng qua từng phiên bản.
Ba giai đoạn#
Mỗi model Ultralytics YOLO đưa ảnh qua ba giai đoạn tuần tự, mỗi giai đoạn đảm nhiệm một vai trò riêng:
| Giai đoạn | Vai trò | Đầu ra |
|---|---|---|
| Backbone | Trích xuất đặc trưng từ ảnh đầu vào ở nhiều độ phân giải | Feature map có stride 8, 16 và 32 (P3, P4, P5) |
| Neck | Hợp nhất đặc trưng ở nhiều tỷ lệ để cả vật thể nhỏ lẫn lớn đều có ngữ cảnh | Feature map hợp nhất đa tỷ lệ |
| Head | Dự đoán bounding box và điểm số lớp từ các đặc trưng đã hợp nhất | Các đối tượng được phát hiện tại mỗi điểm neo |
Đơn vị cơ bản là block Conv (được định nghĩa trong conv.py): một phép tích chập 2D, chuẩn hóa theo batch và hàm kích hoạt SiLU, được áp dụng tuần tự. Mọi module lớn hơn bên dưới đều được xây dựng bằng cách kết hợp các block Conv.
Sơ đồ kiến trúc#
Mỗi phiên bản giữ nguyên bộ khung backbone → neck → head và thay đổi một số stage cụ thể. Các tab bên dưới thể hiện cấu trúc theo từng phiên bản: các stage backbone và neck tuân theo cấu hình trong ultralytics/cfg/models/, trong khi head của YOLOv3 và YOLOv5 được minh họa theo dạng anchor-based nguyên bản thay vì head biến thể anchor-free u thực sự được đóng gói trong cấu hình của chúng. Duyệt qua các tab sẽ cho thấy mỗi thế hệ đã bổ sung những gì. Tóm lại, quá trình phát triển diễn ra như sau: YOLOv3 là detector anchor-based chỉ dùng FPN; YOLOv5 bổ sung đường dẫn PAN bottom-up và SPPF; YOLOv8 chuyển sang block C2f với head anchor-free và DFL; YOLO11 bổ sung attention C2PSA và block C3k2; còn YOLO26 bổ sung một nhánh residual SPPF, loại bỏ DFL và thêm head tùy chọn không cần NMS.
Sơ đồ YOLOv3 và YOLOv5 thể hiện head nguyên bản dựa trên anchor. Package ultralytics cung cấp cấu hình YOLOv3u và YOLOv5u không dùng anchor — vẫn sử dụng backbone Darknet-53 và C3, nhưng kết hợp với head Detect của YOLOv8 — được mô tả trong mục Detection Head.
Các block backbone: Bottleneck → C3 → C2f → C3k2#
Backbone xếp chồng một block CSP (một phần xuyên giai đoạn) lặp lại giữa các layer downsampling Conv có stride 2. Block lặp này là phần thay đổi nhiều nhất qua các phiên bản. Tất cả block bên dưới nằm trong block.py; c1/c2 là số kênh đầu vào/đầu ra và c = 0.5 * c2 là chiều rộng ẩn.
Bottleneck (YOLOv3)#
Đơn vị cơ sở là Bottleneck: hai layer Conv (kernel mặc định (3, 3)) cùng phép cộng residual tùy chọn khi shortcut=True và c1 == c2. Backbone Darknet-53 của YOLOv3 xếp chồng trực tiếp các block này, không có CSP split, và phát hiện ở ba tỷ lệ (stride 8, 16, 32).
C3 (YOLOv5)#
C3 của YOLOv5 chia đầu vào qua hai phép tích chập 1x1: cv1 đưa dữ liệu qua tuần tự n block Bottleneck (kernel lần lượt là (1, 1) và (3, 3)), còn cv2 bỏ qua các block này. Hai nhánh được nối với nhau rồi hợp nhất bằng một 1x1 Conv thứ ba:
def forward(self, x):
# C3: nhánh bottleneck m(cv1(x)) được nối với nhánh bỏ qua cv2(x), sau đó hợp nhất bằng cv3
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))Chỉ đầu ra bottleneck cuối cùng đến được phép tích chập hợp nhất, vì vậy cv3 nhận 2 feature map.
C2f (YOLOv8)#
C2f của YOLOv8 ("CSP Bottleneck với 2 phép tích chập, nhanh hơn") thay đổi các đặc trưng được đưa đến phép tích chập hợp nhất:
cv1 = Conv(c1, 2 * c, 1), sau đóchunk(2)chia đầu ra thành hai tensor cóckênh.- Các block
nBottleneck(c, c)(kernel(3, 3),(3, 3)) chạy tuần tự; mỗi block nhận đầu ra của block trước đó làm đầu vào. - Tất cả tensor trung gian
n + 2được nối với nhau rồi hợp nhất bằngcv2 = Conv((2 + n) * c, c2, 1).
Trong khi C3 đưa 2 feature map vào phép tích chập hợp nhất, C2f đưa n + 2 — tái sử dụng mọi đầu ra bottleneck trung gian.
C3k2 (YOLO11 và YOLO26)#
YOLO11 và YOLO26 sử dụng C3k2, một lớp con của C2f thay thế đơn vị lặp. Tùy theo các cờ khởi tạo, mỗi block n sẽ là:
- một
Bottleneckthông thường (mặc định,c3k=False), - một block
C3k(c3k=True) — biến thểC3có kích thước kernel cấu hình được, hoặc - một cặp
Bottleneck+PSABlock(attn=True).
Đối số YAML thứ hai đặt c3k, trừ khi scale là m, l hoặc x, khi đó giá trị này được đặt thành True — nhờ vậy một yolo11.yaml có thể phục vụ cả năm biến thể. Do đó, [-1, 2, C3k2, [512, False]] tạo các thành phần bên trong Bottleneck ở n và s, nhưng tạo các thành phần bên trong C3k ở m, l và x; 512 là số kênh trước khi scale, được hệ số chiều rộng của biến thể chuyển thành 128 ở n và 768 ở x. Với các module CSP, trường repeats — ở đây là 2 trước khi được scale theo hệ số độ sâu của biến thể — trở thành số lần lặp bên trong block thay vì xếp chồng các module riêng biệt.
Pooling không gian: SPP → SPPF#
Ở cuối backbone, một block spatial pyramid pooling mở rộng receptive field. YOLOv5 thay SPP đa kernel nguyên bản bằng SPPF (Pooling tháp không gian - Nhanh): một MaxPool2d(kernel_size=5, stride=1, padding=2) duy nhất được áp dụng tuần tự n = 3 lần, sau đó đầu vào cùng cả ba đầu ra pooling được nối và hợp nhất bằng một 1x1 Conv. Cách này tương đương về mặt toán học với SPP(k=(5, 9, 13)) nhưng ít tốn tài nguyên hơn, vì các phép pooling 5x5 nối tiếp bao phủ receptive field của các kernel lớn hơn.
YOLO26 truyền cờ shortcut (SPPF, [1024, 5, 3, True]); vì c1 == c2 == 1024 ở layer sâu nhất nên SPPF thêm một kết nối residual (return y + x).
Attention không gian: C2PSA (YOLO11 trở lên)#
YOLO11 bổ sung C2PSA sau SPPF. Đây là block CSP có nhánh hoạt động gồm một chuỗi n module PSABlock (Attention nhạy với vị trí): cv1 = Conv(c1, 2 * c, 1) chia các đặc trưng, một nửa đi qua chuỗi PSABlock, rồi cv2 = Conv(2 * c, c1, 1) hợp nhất phần nối. Mỗi PSABlock áp dụng attention đa đầu, sau đó là mạng feed-forward hai lớp (Conv(c, 2 * c, 1) → Conv(2 * c, c, 1)); mỗi phần đều có kết nối residual. YOLO26 giữ nguyên backbone C3k2 + C2PSA.
Neck: FPN + PAN#
Neck hợp nhất các feature map P3/P4/P5 của backbone bằng Mạng tháp đặc trưng (FPN) theo hướng top-down, sau đó là Mạng tổng hợp đường dẫn (PAN) theo hướng bottom-up. Trong phần head của YAML, FPN là nn.Upsample + Concat (đưa thông tin ngữ nghĩa xuống các độ phân giải cao hơn), còn PAN là Conv + Concat stride 2 (đưa thông tin định vị ngược lên):
# YOLO11 head (FPN top-down, then PAN bottom-up)
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 2, C3k2, [512, False]] # 13
# ... second upsample + concat to P3 ...
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 13], 1, Concat, [1]] # cat head P4 (PAN)
- [-1, 2, C3k2, [512, False]] # 19Neck tái sử dụng block backbone của thế hệ tương ứng — C3 trong YOLOv5, C2f trong YOLOv8, C3k2 trong YOLO11 và YOLO26 — vì vậy mỗi điểm hợp nhất chạy cùng module mà backbone sử dụng. Ba đầu ra đã hợp nhất được đưa vào head. YOLOv3 là trường hợp ngoại lệ: neck chỉ có FPN top-down (phần head trong YAML không có downsampling stride 2), không có nhánh PAN bottom-up mà YOLOv5 đã giới thiệu.
Detection head: dựa trên anchor → không dùng anchor → không dùng NMS#
Head chuyển ba feature map đã hợp nhất thành các dự đoán cho tác vụ phát hiện. Thiết kế này đã thay đổi qua các phiên bản, từ dựa trên anchor sang không dùng anchor rồi đến không dùng NMS.
Detect không dùng anchor, tách nhánh#
YOLOv3 và YOLOv5 nguyên bản sử dụng head dựa trên anchor, kết hợp: các anchor box được xác định trước và một nhánh dùng chung để dự đoán box cùng lớp. Các repository độc lập ultralytics/yolov3 và ultralytics/yolov5 vẫn giữ thiết kế dựa trên anchor đó. Thay vào đó, package ultralytics chính cung cấp các biến thể YOLOv3u và YOLOv5u không dùng anchor — vẫn sử dụng backbone Darknet-53 và C3, nhưng kết hợp với head Detect không dùng anchor của YOLOv8 — còn các cấu hình yolov3.yaml và yolov5.yaml được trình bày ở đây là các biến thể u, không phải thiết kế trước đây.
Head Detect (head.py) không dùng anchor và tách nhánh: tại mỗi mức của pyramid, head chạy hai nhánh song song và dự đoán trực tiếp trên các điểm lưới thay vì dựa vào anchor box.
- Nhánh box (
cv2):Conv(x, c2, 3)→Conv(c2, c2, 3)→Conv2d(c2, 4 * reg_max, 1). - Nhánh lớp (
cv3): trong YOLO11 và YOLO26, hai block depthwise-separable (DWConv+1x1 Conv) →Conv2d(c3, nc, 1); YOLOv8 sử dụng biến thể cũ, gồm hai layer3x3 Conv→Conv2d(c3, nc, 1).
Do đó, mỗi điểm anchor tạo ra no = nc + 4 * reg_max đầu ra. Việc loại bỏ anchor được xác định trước giúp bỏ kích thước và tỷ lệ khung hình của anchor box khỏi các hyperparameter cần tinh chỉnh.
Distribution Focal Loss (DFL)#
YOLOv8 và YOLO11 hồi quy mỗi tọa độ trong 4 tọa độ box dưới dạng một phân phối trên reg_max = 16 bin thay vì một giá trị vô hướng (dạng tích phân của Generalized Focal Loss). Module DFL đổi hình dạng 4 * reg_max kênh box thành (4, reg_max), áp dụng softmax trên reg_max bin rồi lấy chỉ số bin kỳ vọng — nhân từng chỉ số bin với xác suất softmax tương ứng rồi tính tổng — làm tọa độ dự đoán. Cách này được triển khai bằng một phép tích chập 1x1 cố định, có trọng số là các chỉ số bin arange(reg_max), nhờ vậy tổng có trọng số chỉ cần một phép nhân vô hướng.
YOLO26: không dùng NMS, không dùng DFL#
YOLO26 đặt hai tham số YAML được head đọc trực tiếp:
end2end: True—Detectsao chép sâu các nhánh sang một head một-một (one2one_cv2/one2one_cv3) để học một dự đoán duy nhất cho mỗi đối tượng. Thiết lập YAML này tạo cả hai head trong quá trình huấn luyện; khi chạy,nms=Falsechọn head một-một và bỏ bước hậu xử lý Non-Maximum Suppression (NMS). Xem Hướng dẫn phát hiện end-to-end để biết chi tiết về inference và export.reg_max: 1— với một bin,self.dfltrở thànhnn.Identity()vàno = nc + 4; head hồi quy trực tiếp các tọa độ và không có phép toán DFL nào trong graph ONNX được export.
Trong năm kích thước model (n/s/m/l/x), YOLO26 đạt 40.9-57.5 mAP trên COCO với độ trễ TensorRT T4 là 1.7-11.8 ms, theo báo cáo trong bài báo YOLO26.
Tóm tắt theo phiên bản#
| Phiên bản | Block backbone | Pooling không gian | Attention | Detection head | DFL |
|---|---|---|---|---|---|
| YOLOv3 | Darknet-53 (Bottleneck) | không có trong cấu hình cơ sở | không có | Nguyên bản: dựa trên anchor; biến thể u: không dùng anchor | không / có (u) |
| YOLOv5 | C3 (CSP) | SPPF | không có | Nguyên bản: dựa trên anchor; biến thể u: không dùng anchor | không / có (u) |
| YOLOv8 | C2f | SPPF | không có | Không dùng anchor, tách nhánh | có (reg_max=16) |
| YOLO11 | C3k2 | SPPF | C2PSA | Không dùng anchor, tách nhánh | có (reg_max=16) |
| YOLO26 | C3k2 | SPPF + shortcut | C2PSA | Không dùng anchor, hai head; có thể tùy chọn không dùng NMS | đã loại bỏ (reg_max=1) |
Để xem chi tiết từng model, bảng hiệu năng và ví dụ sử dụng, hãy tham khảo các trang riêng về YOLOv3, YOLOv5, YOLOv8, YOLO11 và YOLO26.
Tự kiểm tra kiến trúc#
Phương thức model.info() in ra bản tóm tắt về layer, parameter và FLOPs, đồng thời danh sách module đã phân tích có thể truy cập qua model.model.model.
from ultralytics import YOLO
# Tải model pretrained
model = YOLO("yolo11n.pt")
# Gộp các layer Conv + BatchNorm để số liệu khớp với thông số đã công bố
model.fuse()
# In bản tóm tắt: layer, parameter, gradient, GFLOPs
model.info()
# Kiểm tra detection head (module cuối cùng trong network)
head = model.model.model[-1]
print(type(head).__name__, "| reg_max:", head.reg_max, "| DFL layer:", type(head.dfl).__name__)Chạy đoạn mã trên ba thế hệ cho thấy các thay đổi bằng số liệu cụ thể. Đây là đầu ra thực tế từ model đã gộp trong package ultralytics, khớp với số lượng parameter và FLOPs được công bố trên từng trang model:
| Model | Layer | Tham số | GFLOPs | reg_max | Layer DFL |
|---|---|---|---|---|---|
| YOLOv8n | 72 | 3,151,904 | 8.7 | 16 | DFL |
| YOLO11n | 100 | 2,616,248 | 6.5 | 16 | DFL |
| YOLO26n | 120 | 2,408,932 | 5.5 | 1 | Identity |
YOLO26n báo cáo reg_max=1 và một layer DFL Identity. Theo mặc định, prediction và validation sử dụng one-to-many cùng NMS; đặt nms=False để inference không dùng NMS. Cả hai head đều được huấn luyện.
Các giá trị tham số và FLOPs được báo cáo cho model đã hợp nhất (model.fuse()), trong đó mỗi Conv được gộp với lớp batch normalization. Các giá trị này khớp với thông số đã công bố; checkpoint mới tải sẽ có số lượng cao hơn đôi chút trước khi hợp nhất.
Kết luận#
Qua các phiên bản, kiến trúc YOLO thay đổi từng stage một: backbone chuyển từ Darknet-53 sang các block dựa trên CSP C3, C2f và C3k2 có attention C2PSA; neck giữ cấu trúc FPN + PAN trong khi SPP được thay bằng SPPF; còn head chuyển từ anchor-based sang anchor-free, rồi đến thiết kế không dùng DFL của YOLO26 với head end-to-end tùy chọn không cần NMS.
Để định nghĩa kiến trúc tùy chỉnh, hãy xem Hướng dẫn cấu hình YAML của model hoặc so sánh các model trên trang model. Nếu có câu hỏi, hãy liên hệ qua GitHub hoặc Discord.
Câu hỏi thường gặp#
Model YOLO có backbone trích xuất đặc trưng từ ảnh tại các stride 8, 16 và 32, neck hợp nhất các đặc trưng đó qua nhiều tỷ lệ bằng FPN và PAN, cùng head dự đoán bounding box và điểm số lớp. Mọi model Ultralytics YOLO từ YOLOv3 đến YOLO26 đều tuân theo thiết kế ba giai đoạn này.
C2f(YOLOv8) là block CSP nối đầu ra của mọi feature mapBottleneck—n + 2bên trong — trước convolution hợp nhất; trong khiC3cũ chỉ truyền 2 đầu ra.C3k2(YOLO11 và YOLO26) là lớp con củaC2f, có thể thay mỗiBottleneckbằng blockC3k(một biến thểC3có kích thước kernel cấu hình được) khi bật cờc3k. Cả hai được định nghĩa trongblock.py.YOLO11 thực hiện ba thay đổi cấu trúc so với YOLOv8: thay block backbone và neck
C2fbằngC3k2, chèn block self-attentionC2PSAsauSPPF, và chuyển nhánh phân loại của head sang các convolution depthwise-separable nhẹ hơn. Cả hai vẫn giữ headDetecttách nhánh, không dùng anchor, với hồi quy DFLreg_max=16, vì vậy các thay đổi này giảm số lượng tham số và FLOPs, đồng thời tăng độ chính xác thay vì thiết kế lại giao diện phát hiện.Các model Ultralytics YOLO hiện đại không dùng anchor. YOLOv8, YOLO11 và YOLO26 sử dụng head
Detecttách nhánh, không dùng anchor, với các nhánh riêng cho hồi quy box và phân loại. YOLOv3 và YOLOv5 ban đầu dùng anchor, nhưng Ultralytics cung cấp chúng dưới dạng các biến thể YOLOv3u và YOLOv5u, với cấu hình sử dụng cùng head không dùng anchor như YOLOv8.Có — YOLO26 có head one-to-one hỗ trợ suy luận không cần NMS bằng
nms=False. Đường suy luận one-to-many mặc định sử dụng NMS và cả hai head đều nhận giám sát trong quá trình huấn luyện. Xem Hướng dẫn phát hiện end-to-end để biết chi tiết.DFL hồi quy từng tọa độ của box dưới dạng phân phối softmax trên
reg_maxbin (mặc định là 16 trong YOLOv8 và YOLO11), sau đó lấy giá trị kỳ vọng làm tọa độ thay vì dự đoán một giá trị vô hướng. YOLO26 đặtreg_max=1, khiến lớp DFL trở thành phép toán đồng nhất; head hồi quy trực tiếp các tọa độ và đồ thị ONNX hoặc TensorRT đã export không có toán tử DFL.Tải model trong Python và gọi
model.info()để xem tóm tắt về lớp, tham số và GFLOPs. Các lớp đã phân tích nằm trongmodel.model.model— chẳng hạn,model.model.model[-1]là headDetect, cung cấp các thuộc tính nhưreg_maxvàdfl. Toàn bộ kiến trúc được định nghĩa trong file cấu hình YAML của model.