Введение
Сверточные нейронные сети (CNN) стали основой для обработки изображений и компьютерного зрения. Однако их обучение требует тщательной настройки архитектуры и гиперпараметров, что может быть сложной задачей, особенно при работе с большими наборами данных. В этой статье мы подробно рассмотрим несколько методов оптимизации, используемых для повышения производительности CNN на примере набора данных CIFAR-10, и покажем, как различные техники влияют на потери и точность модели. Мы протестируем аугментацию данных, различные архитектурные решения, такие как Batch Normalization и Dropout, и адаптивные подходы к обучению.
Набор данных CIFAR-10 и предобработка
CIFAR-10 - это один из самых популярных наборов данных для обучения моделей компьютерного зрения. Он содержит 60,000 изображений размером 32x32 пикселя, разделенных на 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 2x2 оказались оптимальными, обеспечив 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) привел к ухудшению результатов.
Эксперимент 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 позволил несколько улучшить точность при ограниченных вычислительных ресурсах.
Эксперимент 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%, что доказывает его эффективность для предотвращения переобучения.
Заключение
Результаты экспериментов подтверждают, что оптимизация архитектуры и гиперпараметров CNN требует комплексного подхода, включающего различные методы.
Наилучшие результаты были достигнуты при использовании предобработки на основе Fashion-MNIST, Batch Normalization после каждого слоя, Dropout с вероятностью 0.2 и оптимизатора Adam. Эти методы в совокупности обеспечили улучшение точности, минимизацию потерь и повышение стабильности модели.
Адаптивные стратегии темпа обучения и размера пакета также показали хорошие результаты, особенно Gradient Accumulation для ограниченных вычислительных ресурсов.
Progressive Layer Growth и Pruning оказались полезными для регулировки сложности модели, помогая избежать переобучения и улучшить обобщающую способность.
Эти методы могут быть полезны при обучении сверточных нейронных сетей для более сложных задач и наборов данных. Надеемся, что эта статья поможет вам глубже понять процесс оптимизации CNN и применять его на практике.
Спасибо за внимание!
P.S. Пишите в комментариях, какие методы вы используете для улучшения моделей, и делитесь своими результатами!