← Назад к вопросам

Как работает 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 удаляет лишние, перекрывающиеся боксы, оставляя только наиболее достоверные.

Как это работает

Алгоритм по шагам:

  1. Сортировка — все боксы сортируются по confidence score (вероятности) в убывающем порядке
  2. Выбор лучшего — берёшь бокс с наивысшим score, добавляешь в результат
  3. Вычисление IOU — считаешь Intersection over Union со всеми оставшимися боксами
  4. Фильтрация — удаляешь боксы с IOU > порог (обычно 0.5)
  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'а компьютерного зрения.