← Назад к вопросам
Как работает NMS (Non-Maximum Suppression) и для чего нужен?
2.0 Middle🔥 172 комментариев
#Глубокое обучение#Машинное обучение
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Назначение и контекст
NMS (Non-Maximum Suppression) — это классический алгоритм постобработки в компьютерном зрении, используется после детекции объектов. Когда модель (YOLO, Faster R-CNN, SSD) обнаруживает объект, она генерирует множество предположений (боксы) с разными координатами и confidence scores. NMS удаляет лишние, перекрывающиеся боксы, оставляя только наиболее достоверные.
Как это работает
Алгоритм по шагам:
- Сортировка — все боксы сортируются по confidence score (вероятности) в убывающем порядке
- Выбор лучшего — берёшь бокс с наивысшим score, добавляешь в результат
- Вычисление IOU — считаешь Intersection over Union со всеми оставшимися боксами
- Фильтрация — удаляешь боксы с IOU > порог (обычно 0.5)
- Повтор — процесс повторяется для следующего по score бокса
Код реализации:
import numpy as np
def non_max_suppression(boxes, scores, iou_threshold=0.5):
"""
boxes: array формы (N, 4) с координатами [x1, y1, x2, y2]
scores: array формы (N,) с confidence scores
"""
# Сортируем по scores в убывающем порядке
sorted_indices = np.argsort(scores)[::-1]
keep = []
while len(sorted_indices) > 0:
# Берём индекс с наивысшим score
current = sorted_indices[0]
keep.append(current)
if len(sorted_indices) == 1:
break
# Вычисляем IoU с остальными боксами
current_box = boxes[current]
other_boxes = boxes[sorted_indices[1:]]
# Площадь пересечения
x1_inter = np.maximum(current_box[0], other_boxes[:, 0])
y1_inter = np.maximum(current_box[1], other_boxes[:, 1])
x2_inter = np.minimum(current_box[2], other_boxes[:, 2])
y2_inter = np.minimum(current_box[3], other_boxes[:, 3])
inter_area = np.maximum(0, x2_inter - x1_inter) * np.maximum(0, y2_inter - y1_inter)
# Площадь объединения
box_area = (current_box[2] - current_box[0]) * (current_box[3] - current_box[1])
other_areas = (other_boxes[:, 2] - other_boxes[:, 0]) * (other_boxes[:, 3] - other_boxes[:, 1])
union_area = box_area + other_areas - inter_area
# IOU
iou = inter_area / union_area
# Оставляем боксы с IOU <= threshold
sorted_indices = sorted_indices[1:][iou <= iou_threshold]
return np.array(keep)
Пример
Допустим, детектор нашёл 5 боксов одной собаки:
- Бокс A: score=0.95, координаты [100, 100, 200, 200]
- Бокс B: score=0.92, координаты [105, 105, 205, 205] (IOU=0.89 с A)
- Бокс C: score=0.88, координаты [110, 110, 210, 210] (IOU=0.78 с A)
- Бокс D: score=0.72, координаты [300, 300, 400, 400]
- Бокс E: score=0.68, координаты [305, 305, 405, 405] (IOU=0.89 с D)
Результат с порогом 0.5:
- Оставляем A (наивысший score)
- Удаляем B и C (IOU > 0.5 с A)
- Оставляем D
- Удаляем E (IOU > 0.5 с D)
Итог: 2 бокса (A и D) вместо 5
Когда использовать разные пороги
- 0.3-0.4 — строгая фильтрация, когда критично не допустить дубликатов
- 0.5 — стандартный, хороший компромисс
- 0.7-0.9 — мягкая фильтрация, когда разрешены близко расположенные объекты
Варианты NMS
- Soft-NMS — вместо удаления уменьшают score перекрывающихся боксов
- DIoU-NMS — использует Distance IoU (учитывает расстояние между центрами)
- GIoU-NMS — использует Generalized IoU
NMS критична для качества детекции и считается неотъемлемой частью pipeline'а компьютерного зрения.