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

Какие знаешь вариации anchor-free детекторов?

2.4 Senior🔥 92 комментариев
#Глубокое обучение#Опыт и проекты

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Вариации Anchor-Free Детекторов объектов

Anchor-free подход к детекции объектов — это парадигма, которая отказывается от предопределённых якорей (anchor boxes) в пользу прямого предсказания параметров объекта. Это упрощает архитектуру, снижает требования к памяти и часто показывает лучшее качество. Вот основные вариации.

1. CornerNet

Использует детекцию углов (corners) объектов:

# Концепция CornerNet:
# - Вместо прямоугольников детектируем верхний-левый и нижний-правый углы
# - Используем стеки hourglass-сетей для обработки особенностей
# - Применяем группировку углов (corner grouping) для связи пар углов

class CornerNetHead(nn.Module):
    def __init__(self):
        super().__init__()
        # Две отдельные ветви для двух типов углов
        self.tl_branch = HourglassStack()  # Top-left
        self.br_branch = HourglassStack()  # Bottom-right
        
        # Выходные головы
        self.tl_heat = nn.Conv2d(256, 1, kernel_size=1)  # Heatmap углов
        self.br_heat = nn.Conv2d(256, 1, kernel_size=1)
        
        self.tl_offset = nn.Conv2d(256, 2, kernel_size=1)  # Смещения
        self.br_offset = nn.Conv2d(256, 2, kernel_size=1)
    
    def forward(self, features):
        # Извлекаем особенности для углов
        tl_features = self.tl_branch(features)
        br_features = self.br_branch(features)
        
        # Heatmaps для локализации углов
        tl_heatmap = self.tl_heat(tl_features)
        br_heatmap = self.br_heat(br_features)
        
        # Смещения для точной локализации
        tl_offsets = self.tl_offset(tl_features)
        br_offsets = self.br_offset(br_features)
        
        return {
            'tl_heatmap': tl_heatmap,
            'br_heatmap': br_heatmap,
            'tl_offsets': tl_offsets,
            'br_offsets': br_offsets
        }

Преимущества:

  • Интуитивный подход (углы легко интерпретировать)
  • Отсутствие якорей

Недостатки:

  • Требует связи углов (corner grouping) — вычислительно сложно
  • Может неправильно соединить углы разных объектов

2. CenterNet v1

Детектирует центр и размер объекта:

class CenterNetHead(nn.Module):
    """CenterNet v1: детекция центров + регрессия размеров"""
    
    def __init__(self):
        super().__init__()
        # Основная сеть признаков (например, ResNet)
        self.backbone = ResNet50()
        
        # Выходные головы
        # 1. Heatmap центров объектов
        self.heatmap = nn.Sequential(
            nn.Conv2d(2048, 256, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 80, kernel_size=1)  # 80 классов COCO
        )
        
        # 2. Регрессия размера (width, height)
        self.size_regression = nn.Sequential(
            nn.Conv2d(2048, 256, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 2, kernel_size=1)  # w, h
        )
        
        # 3. Смещение центра (для точности)
        self.offset = nn.Sequential(
            nn.Conv2d(2048, 256, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 2, kernel_size=1)  # dx, dy
        )
    
    def forward(self, x):
        features = self.backbone(x)
        
        heatmap = self.heatmap(features)  # Где центры
        size = self.size_regression(features)  # Размеры
        offset = self.offset(features)  # Точные позиции
        
        return {
            'heatmap': heatmap,
            'size': size,
            'offset': offset
        }

# Loss функция
def centernet_loss(predictions, targets):
    # Focal loss для heatmap (справляется с дисбалансом background/foreground)
    heatmap_loss = focal_loss(predictions['heatmap'], targets['heatmap'])
    
    # L1 loss для размеров и смещений
    size_loss = F.l1_loss(predictions['size'], targets['size'])
    offset_loss = F.l1_loss(predictions['offset'], targets['offset'])
    
    return heatmap_loss + size_loss + offset_loss

Преимущества:

  • Простая архитектура
  • Быстрая инференция
  • Хорошее качество

Недостатки:

  • Может перекрываться на объекты (оба центра в одной ячейке)

3. FCOS (Fully Convolutional One-Stage)

Пиксель-ориентированный подход — каждый пиксель предсказывает, является ли он внутри объекта:

class FCOSHead(nn.Module):
    """FCOS: каждый пиксель предсказывает объект"""
    
    def __init__(self, num_classes):
        super().__init__()
        self.num_classes = num_classes
        
        # Регрессионные ветви (FPN features)
        self.cls_convs = nn.Sequential(
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.GroupNorm(32, 256),
            nn.ReLU(),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.GroupNorm(32, 256),
            nn.ReLU()
        )
        
        self.bbox_convs = nn.Sequential(
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.GroupNorm(32, 256),
            nn.ReLU(),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.GroupNorm(32, 256),
            nn.ReLU()
        )
        
        # Выходы
        self.cls_logits = nn.Conv2d(256, num_classes, kernel_size=3, padding=1)
        self.bbox_pred = nn.Conv2d(256, 4, kernel_size=3, padding=1)  # (l, r, t, b)
        self.centeredness = nn.Conv2d(256, 1, kernel_size=3, padding=1)  # центрированность
    
    def forward(self, fpn_features):
        outputs = []
        
        for level, features in enumerate(fpn_features):
            cls_out = self.cls_logits(self.cls_convs(features))
            bbox_out = self.bbox_pred(self.bbox_convs(features))
            center_out = self.centeredness(self.bbox_convs(features))
            
            # Применяем экспоненту для положительности расстояний
            bbox_out = torch.exp(bbox_out)
            
            outputs.append({
                'classifications': cls_out,
                'bbox': bbox_out,
                'centeredness': center_out
            })
        
        return outputs

# Ключевая особенность FCOS: каждый пиксель предсказывает расстояния до границ (l, r, t, b)
# Если пиксель внутри объекта, он предсказывает расстояния
# Если снаружи, игнорируется (background)

Преимущества:

  • Полностью свёртываемый (FCN) подход
  • Нет якорей и группировки
  • Высокое качество
  • Масштабируемость

Недостатки:

  • Может быть медленнее при инференции из-за постобработки

4. KeypointNet / CenterNet v2

Детекция ключевых точек объекта:

class KeypointDetectorHead(nn.Module):
    """Детекция объектов через ключевые точки"""
    
    def __init__(self, num_keypoints=17):
        super().__init__()
        self.num_keypoints = num_keypoints
        
        # Heatmaps для каждой ключевой точки
        self.keypoint_heatmaps = nn.ModuleList([
            nn.Conv2d(256, 1, kernel_size=1) for _ in range(num_keypoints)
        ])
        
        # Смещения для каждой ключевой точки
        self.keypoint_offsets = nn.ModuleList([
            nn.Conv2d(256, 2, kernel_size=1) for _ in range(num_keypoints)
        ])
    
    def forward(self, features):
        heatmaps = [h(features) for h in self.keypoint_heatmaps]
        offsets = [o(features) for o in self.keypoint_offsets]
        
        # Bounding box вычисляется из ключевых точек
        # (min/max координат)
        keypoints = self._decode_keypoints(heatmaps, offsets)
        bbox = self._compute_bbox_from_keypoints(keypoints)
        
        return {
            'keypoints': keypoints,
            'bbox': bbox
        }
    
    def _compute_bbox_from_keypoints(self, keypoints):
        """Вычисляем bounding box из ключевых точек"""
        # Берём минимальные и максимальные координаты
        x_coords = keypoints[:, :, 0]
        y_coords = keypoints[:, :, 1]
        
        x_min, x_max = torch.min(x_coords, dim=1)[0], torch.max(x_coords, dim=1)[0]
        y_min, y_max = torch.min(y_coords, dim=1)[0], torch.max(y_coords, dim=1)[0]
        
        return torch.stack([x_min, y_min, x_max, y_max], dim=1)

5. RepPoints

Использует адаптивные точки представления:

class RepPointsHead(nn.Module):
    """RepPoints: объекты представлены адаптивными точками"""
    
    def __init__(self, num_points=9):
        super().__init__()
        self.num_points = num_points
        
        # Классификация
        self.cls_conv = nn.Conv2d(256, 80, kernel_size=3, padding=1)
        
        # Регрессия points (начальные и уточнённые)
        # Начальные точки: на сетке регулярной сетки
        self.init_points_conv = nn.Conv2d(
            256, 2 * num_points, kernel_size=1
        )
        
        # Уточнённые точки (через рефайнмент)
        self.refine_points_conv = nn.Conv2d(
            256, 2 * num_points, kernel_size=1
        )
    
    def forward(self, features):
        cls_logits = self.cls_conv(features)
        init_pts = self.init_points_conv(features)  # Начальные точки
        refine_pts = self.refine_points_conv(features)  # Уточнённые
        
        # Финальные точки = начальные + смещение
        final_points = init_pts + refine_pts
        
        return {
            'cls_logits': cls_logits,
            'points': final_points
        }

# Особенность RepPoints: объект представлен облаком точек
# Это более гибко, чем прямоугольник, особенно для объектов с непрямоугольной формой

Преимущества:

  • Гибкое представление объектов
  • Лучше справляется с необычными формами
  • Может использоваться для различных задач (детекция, сегментация)

6. DETR (Detection Transformer)

Трансформер-подход к детекции:

class DETRHead(nn.Module):
    """DETR: Set-based object detection"""
    
    def __init__(self, num_classes, num_queries=100):
        super().__init__()
        self.num_queries = num_queries
        
        # Обучаемые запросы (queries) — представляют объекты
        self.object_queries = nn.Parameter(
            torch.randn(num_queries, 256)
        )
        
        # Трансформер для обработки запросов
        self.transformer = nn.TransformerDecoder(
            decoder_layer=nn.TransformerDecoderLayer(
                d_model=256,
                nhead=8,
                dim_feedforward=2048,
                dropout=0.1
            ),
            num_layers=6
        )
        
        # Выходные головы
        self.class_head = nn.Linear(256, num_classes)
        self.bbox_head = nn.Sequential(
            nn.Linear(256, 256),
            nn.ReLU(),
            nn.Linear(256, 4)  # (cx, cy, w, h) — нормализованные
        )
    
    def forward(self, backbone_features, query_pos):
        # Трансформер обрабатывает запросы относительно особенностей
        decoder_out = self.transformer(
            self.object_queries,
            backbone_features,
            query_pos_embed=query_pos
        )
        
        # Получаем классы и bounding boxes
        class_logits = self.class_head(decoder_out)
        bbox_pred = self.bbox_head(decoder_out)
        
        return {
            'class_logits': class_logits,
            'bbox': bbox_pred
        }

Преимущества:

  • Глобальный контекст (внимание трансформера)
  • Конец-в-конец обучение
  • Естественное многообъектное давление

Недостатки:

  • Медленнее традиционных CNN подходов
  • Требует больше GPU памяти

Сравнение подходов

МетодПарадигмаСкоростьКачествоПрименение
CornerNetУглыСредняяХорошееНаучные работы
CenterNetЦентрыБыстраяХорошееПрактика
FCOSПикселиБыстраяОтличноеСтандарт в индустрии
RepPointsТочкиСредняяОтличноеГибкие формы
DETRТрансформерМедленнаяОтличноеСовременный подход

Заключение

Anchor-free подходы революционизировали детекцию объектов, убрав якоря и упростив архитектуры. Каждый вариант имеет свои сильные стороны:

  • Для баланса скорости и качества: FCOS или CenterNet
  • Для сложных форм: RepPoints
  • Для глобального контекста: DETR
  • Для научных работ: Экспериментируй со специальными архитектурами

Современные детекторы (YOLOv5+, EfficientDet) часто комбинируют идеи из разных подходов для оптимального баланса.

Какие знаешь вариации anchor-free детекторов? | PrepBro