Какие преимущества и недостатки у свёрточных нейронных сетей (CNN)?

Ответ

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

  • Автоматическое извлечение пространственных признаков: Свёртки самостоятельно учатся обнаруживать иерархические паттерны (края → текстуры → объекты), избавляя от ручного feature engineering для изображений.
  • Параметр-эффективность за счёт разделения весов (weight sharing): Один и тот же фильтр применяется ко всей карте признаков, что резко сокращает количество обучаемых параметров по сравнению с полносвязными слоями.
  • Инвариантность к небольшим трансформациям: Комбинация свёрток и пулинга обеспечивает устойчивость к малым сдвигам, поворотам и искажениям.
  • Оптимизированы для hardware: Операции свёртки эффективно параллелятся на GPU/TPU.

Недостатки CNN:

  • Требуют больших размеченных датасетов для обучения с нуля, иначе склонны к переобучению. Часто используют transfer learning.
  • Плохая интерпретируемость: Являются "чёрным ящиком", хотя методы вроде Grad-CAM помогают визуализировать важные области.
  • Жёсткая индуктивная bias для пространственных данных: Менее эффективны для не имеющих spatial-структуры данных (например, табличных), где полносвязные сети или трансформеры могут быть лучше.
  • Потеря позиционной информации при агрессивном пулинге.

Пример архитектуры на PyTorch для классификации изображений:

import torch.nn as nn
import torch.nn.functional as F

class ConvNet(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64 * 8 * 8, 128)  # Пример для 32x32 изображения
        self.fc2 = nn.Linear(128, num_classes)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))  # -> [batch, 32, 16, 16]
        x = self.pool(F.relu(self.conv2(x)))  # -> [batch, 64, 8, 8]
        x = x.view(-1, 64 * 8 * 8)           # Flatten
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

Ответ 18+ 🔞

Эх, ну что тут рассказывать, как будто впервые слышишь. CNN — это, блядь, наш спаситель от ручного выковыривания признаков из картинок. Всё за тебя делает, ебать мои старые костыли! Сам учится находить: сначала края, потом какие-то закорючки, а в итоге — о, это же котик! Всё иерархически, красиво. Раньше бы инженеры с этими гистограммами ориентации градиентов сидели, а теперь — хуй с горы, сеть сама разберётся.

Плюсы, конечно, овердохуища. Во-первых, экономия на параметрах — один и тот же фильтр по всему изображению скользит, weight sharing это называется. Не нужно на каждый пиксель свой вес, как в этих дурацких полносвязных слоях, которые параметров жрут, как не в себя. Во-вторых, устойчивость к мелким пиздюлинам: картинку чуть сдвинули, повернули — сети похуй, она за счёт свёрток и пулинга всё равно котика опознает. И для железа оптимизировано — эти операции свёртки на видеокартах просто летают.

Но и без минусов, конечно, никуда. Главная беда — жрёт данные, как оголтелая. Чтобы с нуля обучить, нужно дохуя размеченных картинок, иначе переобучится на раз-два. Поэтому все умные люди используют transfer learning — берут сеть, которую на миллионах изображений уже отучили, и немного её под свои нужды допиливают. А ещё это классический чёрный ящик: работает — работает, а почему так — хуй поймёшь. Правда, методы вроде Grad-CAM появились, которые показывают, куда сеть смотрела, но всё равно полной ясности нет. И главное — это узкий специалист, чувак. Заточен под пространственные данные. Попробуй ей табличку с продажами скормить — нихуя не выйдет, там уже другие модели рулят. И ещё из-за пулинга может потеряться важная позиционная информация, но это уже тонкости.

Вот, смотри, как на PyTorch простенькую архитектуру для классификации собирают. Код не трогаю, он святой.

import torch.nn as nn
import torch.nn.functional as F

class ConvNet(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64 * 8 * 8, 128)  # Пример для 32x32 изображения
        self.fc2 = nn.Linear(128, num_classes)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))  # -> [batch, 32, 16, 16]
        x = self.pool(F.relu(self.conv2(x)))  # -> [batch, 64, 8, 8]
        x = x.view(-1, 64 * 8 * 8)           # Flatten
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

В общем, инструмент мощный, но со своими тараканами. Для картинок — почти идеально, для всего остального — ищи другую модель, не позорься.