Как построить модель для классификации изображений на бинарной маске с квадратами, кругами и треугольниками?

Ответ

Для классификации простых бинарных фигур (масок) я бы использовал сверточную нейронную сеть (CNN), но начал бы с очень простой архитектуры, так как задача не требует глубоких признаков.

Архитектура модели (PyTorch):

import torch.nn as nn

class ShapeClassifier(nn.Module):
    def __init__(self, num_classes=3):
        super().__init__()
        # Вход: 1 канал (бинарная маска)
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=3, padding=1), # 8 фильтров
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(8, 16, kernel_size=3, padding=1), # 16 фильтров
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Flatten()
        )
        # Размер входа в полносвязный слой зависит от размера изображения.
        # Для изображения 64x64 после двух пулингов (64->32->16) размер будет: 16 * 16 * 16 = 4096
        self.classifier = nn.Linear(4096, num_classes)

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

Ключевые этапы:

  1. Подготовка данных: Генерация синтетического датасета с бинарными масками фигур (1 - фигура, 0 - фон). Добавляю аугментации: небольшие повороты, сдвиги, шум, чтобы модель обобщала.
  2. Обучение: Использую CrossEntropyLoss и оптимизатор Adam. Так как фигуры простые, обучение сходится быстро даже на небольшой выборке (несколько тысяч примеров).
  3. Почему простая CNN? Глубокие предобученные сети (ResNet) избыточны для бинарных масок. Моя легкая сеть быстро обучается и меньше склонна к переобучению на таком синтетическом датасете.
  4. Альтернативный подход: Для абсолютной интерпретируемости можно было бы использовать классические методы компьютерного зрения (например, поиск контуров и анализ числа вершин с помощью cv2.approxPolyDP), но нейросетевое решение более устойчиво к артефактам рендеринга и шуму.

Ответ 18+ 🔞

А, ну слушай, классификация простых фигур, да? Бинарные маски, кружочки, квадратики... Ёпта, задачка-то вроде простая, но если подойти с умом, можно и тут накосячить, как последний распиздяй.

Вот смотри, я бы, конечно, взял свёрточную сеть, но не ту, что на каждом углу кричат — ResNet, EfficientNet и прочий овердохуища. Нет, зачем нам эта махина, если фигуры проще пареной репы? Мы же не в космос летим, а квадрат от треугольника отличить хотим.

Вот моя архитектура, проще некуда (PyTorch):

import torch.nn as nn

class ShapeClassifier(nn.Module):
    def __init__(self, num_classes=3):
        super().__init__()
        # Вход: 1 канал (бинарная маска)
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=3, padding=1), # 8 фильтров
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(8, 16, kernel_size=3, padding=1), # 16 фильтров
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Flatten()
        )
        # Размер входа в полносвязный слой зависит от размера изображения.
        # Для изображения 64x64 после двух пулингов (64->32->16) размер будет: 16 * 16 * 16 = 4096
        self.classifier = nn.Linear(4096, num_classes)

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

А теперь по шагам, как я бы это делал, чтобы не вышло пиздопроебибны:

  1. Данные. Ну, тут всё ясно — нагенерируем кучу этих самых масок. Круг, квадрат, треугольник. Но, бля, если просто нагенерить идеальные фигуры, модель потом на реальных данных обосрётся. Поэтому я бы добавил аугментаций: чутка покрутить, подвигать, шумку подсыпать, чтобы она не заучила пиксели, а реально признаки искала. Иначе получится манда с ушами, а не модель.

  2. Обучение. Берём CrossEntropyLoss, Adam, и вперёд. Фигуры-то простые, даже на паре тысяч примеров всё схватит на раз-два. Главное — валидацию не забыть, а то будет переобучение, и потом удивление пиздец, почему на новых данных не работает.

  3. Почему такая простая сеть? Да потому что доверия ебать ноль к тем, кто для таких задач тащит 50-слойные монстры. Это как молотком гвоздь забивать — можно, но зачем, если есть обычный молоток? Наша лёгкая сеть обучается за минуты, меньше шансов накосячить, и её хоть как-то можно понять, что внутри происходит.

  4. А что, по-старинке нельзя? Можно, ёпта! Взять OpenCV, найти контуры, посчитать углы... Но это путь садомазохиста. Малейший шум, артефакт рендеринга — и всё, алгоритм пошёл по пизде рулю. Нейросеть в этом плане живучее, она как хитрая жопа — к шуму привыкает и всё равно своё делает. Но за интерпретируемость классических методов, конечно, хуй с горы не оторвёшь — там всё на виду.