Глубокая оптимизация сверточных нейронных сетей: Анализ методов улучшения модели на примере CIFAR-10

от автора

Введение

Сверточные нейронные сети (CNN) стали основой для обработки изображений и компьютерного зрения. Однако их обучение требует тщательной настройки архитектуры и гиперпараметров, что может быть сложной задачей, особенно при работе с большими наборами данных. В этой статье мы подробно рассмотрим несколько методов оптимизации, используемых для повышения производительности CNN на примере набора данных CIFAR-10, и покажем, как различные техники влияют на потери и точность модели. Мы протестируем аугментацию данных, различные архитектурные решения, такие как Batch Normalization и Dropout, и адаптивные подходы к обучению.

Набор данных CIFAR-10 и предобработка

CIFAR-10 — это один из самых популярных наборов данных для обучения моделей компьютерного зрения. Он содержит 60,000 изображений размером 32×32 пикселя, разделенных на 10 классов, что делает его отличным кандидатом для тестирования эффективности различных методов оптимизации.

import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms  # Определение преобразований для набора данных CIFAR-10 transform_cifar = transforms.Compose([     transforms.RandomHorizontalFlip(),     transforms.RandomCrop(32, padding=4),     transforms.ToTensor(),     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ])  # Загрузка набора данных cifar10_train = datasets.CIFAR10(root='data', train=True, transform=transform_cifar, download=True) cifar10_loader_train = DataLoader(cifar10_train, batch_size=32, shuffle=True) 

Для эксперимента мы применили несколько различных преобразований, включая аугментацию с предобработкой на основе наборов данных Fashion-MNIST и SVHN.

# Загрузка каждого набора данных с предобработкой cifar10_loader = DataLoader(cifar10_train, batch_size=32, shuffle=True) fmnist_loader = DataLoader(fmnist_train, batch_size=32, shuffle=True) svhn_loader = DataLoader(svhn_train, batch_size=32, shuffle=True)   # Добавляем трансформацию для повторения канала в Fashion-MNIST transform_fmnist_rgb = transforms.Compose([     transform_fmnist,  # Текущие трансформации для FMNIST     Lambda(lambda x: x.repeat(3, 1, 1))  # Повторяем канал 3 раза ])  # Загрузка данных Fashion-MNIST с трансформацией повторения каналов fmnist_cifar_transform = DataLoader(     datasets.FashionMNIST(root='data', train=True, transform=transform_fmnist_rgb, download=True),     batch_size=32, shuffle=True )  svhn_loader = DataLoader(svhn_train, batch_size=32, shuffle=True)  # Загрузка CIFAR-10 с альтернативной предобработкой cifar10_fmnist_transform = DataLoader(datasets.CIFAR10(root='data', train=True, transform=transform_fmnist, download=True), batch_size=32, shuffle=True) cifar10_svhn_transform = DataLoader(datasets.CIFAR10(root='data', train=True, transform=transform_svhn, download=True), batch_size=32, shuffle=True)  # Загрузка Fashion-MNIST с альтернативной предобработкой #fmnist_cifar_transform = DataLoader(datasets.FashionMNIST(root='data', train=True, transform=transform_cifar, download=True), batch_size=32, shuffle=True) fmnist_svhn_transform = DataLoader(datasets.FashionMNIST(root='data', train=True, transform=transform_svhn, download=True), batch_size=32, shuffle=True)  # Загрузка SVHN с альтернативной предобработкой svhn_cifar_transform = DataLoader(datasets.SVHN(root='data', split='train', transform=transform_cifar, download=True), batch_size=32, shuffle=True) svhn_fmnist_transform = DataLoader(datasets.SVHN(root='data', split='train', transform=transform_fmnist, download=True), batch_size=32, shuffle=True) 

Эксперимент 1: Влияние предобработки данных

Различные методы предобработки могут влиять на то, какие особенности изображений извлекает модель. Мы протестировали несколько вариантов, включая стандартную предобработку CIFAR-10, а также трансформации, адаптированные из других наборов данных:

  • Оригинальная предобработка CIFAR-10

  • Предобработка, основанная на Fashion-MNIST

  • Предобработка на основе SVHN

Обучение CIFAR-10 с собственной предобработкой:

Эпоха 1

Потери: 1.6969

Точность: 37.90%

Эпоха 5

Потери: 1.1355

Точность: 59.95%

Обучение CIFAR-10 с предобработкой Fashion-MNIST

Эпоха 1

Потери: 1.4984

Точность: 46.54%

Эпоха 5

Потери: 0.9440

Точность: 66.95%

Обучение CIFAR-10 с предобработкой SVHN

Эпоха 1

Потери: 1.6444

Точность: 39.83%

Эпоха 5

Потери: 1.0892

Точность: 61.49%

Результаты: Предобработка, основанная на Fashion-MNIST, продемонстрировала лучшие результаты, обеспечив потерю в 0.9440 и точность 66.95%. Это позволяет предположить, что некоторые преобразования могут быть лучше адаптированы для задач классификации изображений, несмотря на различия в данных.

Результаты для различных видов предобработки данных

Результаты для различных видов предобработки данных

Эксперимент 2: Оптимизация архитектуры — выбор фильтров и пуллинга

Следующим шагом было изучение того, как количество фильтров и метод пуллинга влияет на результаты. Мы тестировали три варианта с разным количеством фильтров (16, 32 и 64), а также несколько типов пуллинга.

Пример конфигурации фильтров:

import torch.nn as nn  class CNNVariant1(nn.Module):     def __init__(self):         super(CNNVariant1, self).__init__()         self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)         self.pool = nn.MaxPool2d(2, 2)         self.fc1 = nn.Linear(16 * 16 * 16, 10)          def forward(self, x):         x = self.pool(torch.relu(self.conv1(x)))         x = x.view(-1, 16 * 16 * 16)         x = self.fc1(x)         return x 

Результаты: Среднее количество фильтров (32 фильтра) и Max Pooling 2×2 оказались оптимальными, обеспечив 57.33% точности и потерю в 1.2018. Использование большого количества фильтров привело к переобучению, а использование Average Pooling показало менее хорошие результаты.

Результаты по различным количествам фильтров

Результаты по различным количествам фильтров

Эксперимент 3: Batch Normalization и Dropout

Batch Normalization и Dropout — это техники, которые помогают улучшить стабильность и обобщающую способность модели. Мы тестировали Batch Normalization после каждого сверточного слоя и только в начале и в конце, а также различные значения Dropout.

Пример использования Batch Normalization и Dropout:

class CNNBatchNorm(nn.Module):     def __init__(self):         super(CNNBatchNorm, self).__init__()         self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)         self.bn1 = nn.BatchNorm2d(32)         self.dropout = nn.Dropout(0.2)         self.fc1 = nn.Linear(32 * 16 * 16, 10)      def forward(self, x):         x = self.dropout(self.bn1(torch.relu(self.conv1(x))))         x = x.view(-1, 32 * 16 * 16)         x = self.fc1(x)         return x 

Результаты: Batch Normalization после каждого слоя обеспечил лучшую стабильность обучения и точность 60.25%. Умеренный Dropout (0.2) оказался наиболее эффективным и достиг 63.79% точности, тогда как сильный Dropout (0.5) привел к ухудшению результатов.

Результаты по количеству уровней Dropout

Результаты по количеству уровней Dropout

Эксперимент 4: Адаптивные стратегии темпа обучения и динамический размер пакета

Использование адаптивного темпа обучения и динамического изменения размера пакета — это современные подходы, которые помогают улучшить обучение модели.

Cyclic Learning Rate:

from torch.optim.lr_scheduler import CyclicLR  optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9) scheduler = CyclicLR(optimizer, base_lr=0.001, max_lr=0.01, step_size_up=5) 

Результаты: Оптимизатор Adam показал лучшие результаты по сравнению с Cyclic Learning Rate, с потерей 1.2331 и точностью 57.42%. Dynamic Batch Size и Gradient Accumulation продемонстрировали схожие результаты, но Gradient Accumulation позволил несколько улучшить точность при ограниченных вычислительных ресурсах.

Результаты для адаптивных стратегий оптимизации архитектуры

Результаты для адаптивных стратегий оптимизации архитектуры
ROC-AUC для модели с адаптивным темпом (Adam)

ROC-AUC для модели с адаптивным темпом (Adam)

Эксперимент 5: Progressive Layer Growth и Pruning

Мы протестировали Progressive Layer Growth и Random Filter Pruning для регулировки сложности модели.

Пример Progressive Layer Growth:

class SimpleCNN(nn.Module):     def __init__(self):         super(SimpleCNN, self).__init__()         self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)         self.fc1 = nn.Linear(16 * 16 * 16, 10)  # После нескольких эпох добавляем слои class ExtendedCNN(SimpleCNN):     def __init__(self):         super(ExtendedCNN, self).__init__()         self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)         self.fc2 = nn.Linear(32 * 8 * 8, 10) 

Результаты: Progressive Layer Growth улучшил точность модели до 60.87%, а Random Filter Pruning обеспечил точность в 64.80%, что доказывает его эффективность для предотвращения переобучения.

Визуализация результатов Progressive Layer Growth

Визуализация результатов Progressive Layer Growth

Заключение

Результаты экспериментов подтверждают, что оптимизация архитектуры и гиперпараметров CNN требует комплексного подхода, включающего различные методы.

  • Наилучшие результаты были достигнуты при использовании предобработки на основе Fashion-MNIST, Batch Normalization после каждого слоя, Dropout с вероятностью 0.2 и оптимизатора Adam. Эти методы в совокупности обеспечили улучшение точности, минимизацию потерь и повышение стабильности модели.

  • Адаптивные стратегии темпа обучения и размера пакета также показали хорошие результаты, особенно Gradient Accumulation для ограниченных вычислительных ресурсов.

  • Progressive Layer Growth и Pruning оказались полезными для регулировки сложности модели, помогая избежать переобучения и улучшить обобщающую способность.

Эти методы могут быть полезны при обучении сверточных нейронных сетей для более сложных задач и наборов данных. Надеемся, что эта статья поможет вам глубже понять процесс оптимизации CNN и применять его на практике.

Спасибо за внимание!


P.S. Пишите в комментариях, какие методы вы используете для улучшения моделей, и делитесь своими результатами!

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Как вы оптимизируете свёрточные сети?

100% Подбор гиперпараметров1
100% Использование Batch Normalization и Dropout1
0% Увеличение или уменьшение количества фильтров и слоев0
0% Аугментация данных0
0% Применение современных оптимизаторов0
0% Модели с Progressive Layer Growth и Pruning0
100% Использование предобученных моделей1
0% Комбинация нескольких методов0
0% Не использую оптимизацию0

Проголосовал 1 пользователь. Воздержавшихся нет.

ссылка на оригинал статьи https://habr.com/ru/articles/857620/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *