NMS theo lớp chỉ cho các hộp cùng nhãn cạnh tranh với nhau. NMS gộp lớp cho mọi hộp trong cùng một ảnh cạnh tranh, kể cả khi chúng mang nhãn khác nhau. Ở ngưỡng IoU 0,50, cả hai cách đều bỏ được hộp người trùng trong ví dụ dưới đây, nhưng chỉ cách theo lớp giữ được chiếc xe đạp nằm chồng lên người. Ở ảnh khác, nơi hộp xe đạp thực ra là một dự đoán sai, cách gộp lớp lại loại đúng hộp đó.
Hai cách lọc khác nhau ở đâu?
Non-maximum suppression (NMS) duyệt các hộp ứng viên theo điểm confidence từ cao xuống thấp. Sau khi giữ một hộp, thuật toán loại các ứng viên có điểm thấp hơn đủ chồng lấn với nó. Mức chồng lấn được đo bằng IoU, tức diện tích phần giao chia cho diện tích phần hợp của hai hộp. Bài giới thiệu Non-maximum Suppression mô tả thuật toán tham lam và biến thể Soft-NMS. Ở đây, cả hai chế độ đều dùng quy tắc loại hộp của hard NMS.
- Theo lớp (class-wise). Truyền ID lớp vào
batched_nms(boxes, scores, labels, threshold). Các hộp khác lớp có thể cùng tồn tại kể cả khi IoU rất cao. Trong lệnh này,labelsdùng để phân nhóm hộp trong một ảnh. Tài liệutorchvision.ops.batched_nms - Gộp lớp (class-agnostic). Gọi
nms(boxes, scores, threshold)trên toàn bộ hộp ứng viên của một ảnh. Hộp có điểm cao hơn có thể loại hộp khác lớp. Nhãn của hộp giữ lại vẫn được giữ nguyên. Torchvision dùng điều kiện loại IoU lớn hơn ngưỡng, nên hai hộp có IoU bằng ngưỡng vẫn có thể được giữ. Tài liệutorchvision.ops.nms
Hai ảnh giả lập có nhãn chuẩn
Ảnh A có hai đối tượng thật là một người và một xe đạp. Hộp chuẩn của chúng trùng tọa độ với P1 và B1. Ảnh B chỉ có một người, với hộp chuẩn trùng tọa độ P3. Mỗi ảnh có thêm một dự đoán người trùng. Ảnh B có thêm B2, hộp xe đạp sai nhãn nằm gần như cùng vị trí với người. Tọa độ dùng thứ tự (x1, y1, x2, y2). Lớp 0 là người và 1 là xe đạp.
| Ảnh | Hộp | Lớp | Confidence | Tọa độ | Vai trò |
|---|---|---|---|---|---|
| A | P1 | Người | 0,95 | (10, 10, 110, 110) |
Dự đoán đúng |
| A | P2 | Người | 0,82 | (12, 12, 112, 112) |
Trùng P1 |
| A | B1 | Xe đạp | 0,90 | (30, 15, 130, 115) |
Dự đoán đúng |
| B | P3 | Người | 0,96 | (10, 10, 110, 110) |
Dự đoán đúng |
| B | P4 | Người | 0,83 | (13, 12, 113, 112) |
Trùng P3 |
| B | B2 | Xe đạp | 0,70 | (12, 11, 112, 111) |
Sai lớp |
| B | B3 | Xe đạp | 0,20 | (160, 10, 200, 50) |
Bị loại trước NMS |
Thí nghiệm tách bước lọc confidence, bước loại hộp trùng và bước đánh giá.
- Lọc confidence. Chỉ đưa hộp có điểm từ 0,50 vào NMS. B3 rớt ở bước này ở cả hai chế độ.
- Lọc theo IoU giữa các dự đoán. Thử lần lượt ngưỡng NMS 0,50, 0,70 và 0,95, giữ nguyên danh sách ứng viên và điểm số. Tính IoU bằng
box_iou. Tài liệutorchvision.ops.box_iou - Đánh giá đầu ra. Duyệt hộp còn lại theo confidence giảm dần. Mỗi hộp được ghép với tối đa một hộp chuẩn chưa ghép, cùng lớp và có IoU cao nhất, miễn IoU từ 0,50 trở lên. Số ghép thành công là TP, số dự đoán còn lại là FP và số hộp chuẩn chưa ghép là FN. Ngưỡng ghép giữ cố định trong cả ba lượt thử.
Kết quả chạy trên CPU
| Ảnh | Ngưỡng NMS | Theo lớp giữ | TP / FP / FN | Gộp lớp giữ | TP / FP / FN |
|---|---|---|---|---|---|
| A | 0,50 | P1, B1 | 2 / 0 / 0 | P1 | 1 / 0 / 1 |
| A | 0,70 | P1, B1 | 2 / 0 / 0 | P1, B1 | 2 / 0 / 0 |
| A | 0,95 | P1, B1, P2 | 2 / 1 / 0 | P1, B1, P2 | 2 / 1 / 0 |
| B | 0,50 | P3, B2 | 1 / 1 / 0 | P3 | 1 / 0 / 0 |
| B | 0,70 | P3, B2 | 1 / 1 / 0 | P3 | 1 / 0 / 0 |
| B | 0,95 | P3, P4, B2 | 1 / 2 / 0 | P3, P4 | 1 / 1 / 0 |
Ảnh A mất hộp đúng khi gộp lớp
Ở ngưỡng 0,50, P1 đứng đầu với confidence 0,95. Nó loại P2 trong cả hai chế độ vì hai hộp cùng lớp và IoU là 0,9238. P1 và B1 có IoU 0,6129. NMS gộp lớp cho cặp này cạnh tranh nên B1 bị loại và số FN tăng từ 0 lên 1. NMS theo lớp giữ B1 vì B1 thuộc lớp xe đạp.
Cả hai chế độ đều loại P2. Sơ đồ chỉ vẽ P1 và B1 để thấy phần chồng lấn giữa hai lớp.
Ở ngưỡng 0,70, IoU giữa P1 và B1 đủ thấp để gộp lớp giữ cả hai, trong khi P2 vẫn bị loại. Tăng tiếp lên 0,95 giữ luôn P2 và tạo thêm một FP.
Ảnh B giữ nhãn sai khi lọc theo lớp
P3 là hộp có điểm cao nhất. Ở ngưỡng 0,50 và 0,70, cả hai chế độ đều bỏ P4 vì IoU với P3 là 0,9059. B2 chồng lên P3 với IoU 0,9421. NMS theo lớp giữ B2 do khác nhãn, tạo một FP. NMS gộp lớp bỏ B2 do nó có điểm thấp hơn P3.
Ở ngưỡng 0,95, gộp lớp vẫn bỏ B2 dù IoU giữa B2 và P3 thấp hơn ngưỡng. P4 sống sót sau lượt xử lý P3, rồi loại B2 ở lượt kế tiếp vì IoU giữa P4 và B2 là 0,9610. Muốn tìm lý do một hộp biến mất, cần xem các hộp được giữ theo thứ tự điểm, kể cả hộp đứng sau hộp đầu bảng.
Chạy lại bằng Torchvision
Mã chạy với Python 3.10.11, PyTorch 2.8.0 và Torchvision 0.23.0 trên CPU arm64. Lưu đoạn dưới thành experiment.py rồi chạy python3 experiment.py trong môi trường đã cài hai phiên bản thư viện trên. Tất cả tọa độ và điểm đều cố định, với điểm phân biệt trong từng ảnh.
1import platform
2
3import torch
4import torchvision
5from torchvision.ops import batched_nms, box_iou, nms
6
7CONFIDENCE = 0.50
8MATCH_IOU = 0.50
9# (mã, lớp, điểm, xyxy); lớp 0 là người, lớp 1 là xe đạp.
10SCENES = {
11 "A": (
12 [(0, [10, 10, 110, 110]), (1, [30, 15, 130, 115])],
13 [
14 ("P1", 0, 0.95, [10, 10, 110, 110]),
15 ("P2", 0, 0.82, [12, 12, 112, 112]),
16 ("B1", 1, 0.90, [30, 15, 130, 115]),
17 ],
18 ),
19 "B": (
20 [(0, [10, 10, 110, 110])],
21 [
22 ("P3", 0, 0.96, [10, 10, 110, 110]),
23 ("P4", 0, 0.83, [13, 12, 113, 112]),
24 ("B2", 1, 0.70, [12, 11, 112, 111]),
25 ("B3", 1, 0.20, [160, 10, 200, 50]),
26 ],
27 ),
28}
29
30
31def candidates(predictions):
32 rows = [row for row in predictions if row[2] >= CONFIDENCE]
33 ids, labels, scores, boxes = zip(*rows)
34 return ids, torch.tensor(labels), torch.tensor(scores), torch.tensor(boxes, dtype=torch.float32)
35
36
37def evaluate(ground_truth, labels, boxes, keep):
38 gt_labels, gt_boxes = zip(*ground_truth)
39 overlaps = box_iou(boxes, torch.tensor(gt_boxes, dtype=torch.float32))
40 unmatched = set(range(len(gt_boxes)))
41 tp = 0
42 for i in keep.tolist():
43 eligible = [j for j in sorted(unmatched) if labels[i].item() == gt_labels[j]]
44 best = max(eligible, key=lambda j: overlaps[i, j].item(), default=None)
45 if best is not None and overlaps[i, best].item() >= MATCH_IOU:
46 unmatched.remove(best)
47 tp += 1
48 return tp, len(keep) - tp, len(unmatched)
49
50
51def main():
52 print(f"Python {platform.python_version()} / torch {torch.__version__} / "
53 f"torchvision {torchvision.__version__} / {platform.machine()} CPU")
54 for scene, (ground_truth, predictions) in SCENES.items():
55 ids, labels, scores, boxes = candidates(predictions)
56 overlaps = box_iou(boxes, boxes)
57 print(f"{scene}: IoU({ids[0]}, {ids[1]})={overlaps[0, 1]:.4f}, "
58 f"IoU({ids[0]}, {ids[2]})={overlaps[0, 2]:.4f}, "
59 f"IoU({ids[1]}, {ids[2]})={overlaps[1, 2]:.4f}")
60 for threshold in (0.50, 0.70, 0.95):
61 for mode, keep in (
62 ("theo lớp", batched_nms(boxes, scores, labels, threshold)),
63 ("gộp lớp", nms(boxes, scores, threshold)),
64 ):
65 kept_ids = ",".join(ids[i] for i in keep.tolist())
66 counts = evaluate(ground_truth, labels, boxes, keep)
67 print(f"{scene} {threshold:.2f} {mode}: {kept_ids} | TP,FP,FN={counts}")
68
69
70if __name__ == "__main__":
71 main()
Kiểm tra chế độ NMS trên dữ liệu của mô hình
Nếu dữ liệu có nhiều vật thể khác lớp đứng sát hoặc che khuất nhau, hãy kiểm tra xem chế độ gộp lớp làm giảm recall của lớp nào. Nếu mô hình hay gán hai nhãn cạnh tranh cho cùng một vật thể, hãy kiểm tra xem chế độ gộp lớp giảm được bao nhiêu FP và nhãn nào thường thắng. Ở ảnh B, điểm của người cao hơn nhãn xe đạp sai. Nếu thứ tự điểm đảo ngược, luật gộp lớp có thể giữ nhãn sai và loại hộp người.
Khi so sánh trên tập validation, lưu các hộp trước NMS và chạy hai chế độ trên cùng một tập ứng viên. Giữ cố định ngưỡng confidence, giới hạn số hộp nếu pipeline có dùng, và quy tắc đánh giá. Sau đó mới quét ngưỡng IoU NMS và xem lại từng cặp gây FN hoặc FP. Hai ảnh giả lập ở đây chỉ đo các tình huống đã thiết kế. Để chọn cấu hình cho một detector cụ thể, cần đo trên dữ liệu có nhãn của detector đó, với các chỉ số và nhóm ảnh phù hợp với ứng dụng. Tổng quan về SSD và các mô hình single-shot object detection
Bình luận