AlexNet: Глубокие сверточные нейронные сети на подъеме

глубокое обучение
AlexNet: Глубокие сверточные нейронные сети на подъеме

Учетная запись WeChat: ilulaoshi. Мой личный веб-сайт будет постоянно обновляться, нажмите, чтобы узнать подробности.Ссылка на сайт.

Накануне глубокого обучения

Хотя Янн ЛеКун предложил сверточную нейронную сеть LeNet еще в прошлом веке и использовал LeNet для классификации изображений, сверточная нейронная сеть не развивалась быстро. За почти 20 лет с тех пор, как был предложен LeNet, нейронные сети когда-то превзошли другие методы машинного обучения, такие как машины опорных векторов. Неспособность сверточных нейронных сетей быстро развиваться в то время была в основном ограничена:

1. Отсутствующие данные

Глубокое обучение требует большого количества размеченных данных, чтобы работать лучше, чем другие классические методы. Из-за ограниченного объема памяти первых компьютеров и ограниченных бюджетов на исследования в 1990-е годы большинство исследований основывалось только на небольших общедоступных наборах данных. Например, многие исследовательские работы основаны на нескольких общедоступных наборах данных, предоставленных Калифорнийским университетом в Ирвине (UCI), многие из которых содержат от нескольких сотен до нескольких тысяч изображений. Эта ситуация улучшилась с волной больших данных, которая возникла примерно в 2010 году. В частности, построение набора данных ImageNet под руководством Фейфей Ли. Набор данных ImageNet содержит 1000 категорий объектов, каждая из которых содержит до тысячи различных изображений, а общий объем данных достигает сотен гигабайт. Эта шкала не имела себе равных в других общедоступных наборах данных того времени. Кроме того, каждый год сообщество проводит конкурс под названием ImageNet Large-Scale Visual Recognition Challenge (ILSVRC), где участникам необходимо оптимизировать задачи, связанные с компьютерным зрением, на основе набора данных ImageNet. Можно сказать, что набор данных ImageNet вывел исследования компьютерного зрения и машинного обучения на новый этап.

2. Отсутствующее оборудование

Глубокое обучение очень требовательно к вычислительным ресурсам. Ограниченная вычислительная мощность раннего оборудования затрудняла обучение более сложных нейронных сетей. Однако появление графических процессоров общего назначения (GPGPU) изменило этот ландшафт. Графические процессоры давно разрабатывались для обработки изображений и компьютерных игр, особенно для высокопроизводительного матричного и векторного умножения. К счастью, математическое выражение этого похоже на выражение сверточных слоев в глубоких сетях. Концепция графических процессоров общего назначения зародилась в 2001 году с появлением таких сред программирования, как CUDA и OpenCL. Программный интерфейс CUDA не так уж сложен для начала, и ученые-исследователи могут использовать CUDA для ускорения своих научных вычислительных задач на графических процессорах NVIDIA. Примерно в 2010 году некоторые ресурсоемкие задачи начали переноситься на графические процессоры Nvidia.

Считается, что нынешний бум ИИ начался в 2012 году. В том же году Алекс Крижевский успешно обучил глубокую сверточную нейронную сеть AlexNet с использованием графического процессора NVIDIA и выиграл чемпионат в соревновании ImageNet с этой сетью, значительно повысив точность классификации изображений. В то время хранение и вычисление больших данных почти перестали быть узким местом, а предложение AlexNet также заставило академический круг и промышленность осознать потрясающую производительность глубоких нейронных сетей.

Структура сети AlexNet

Философия дизайна AlexNet и LeNet очень похожа, но есть и существенные различия.

LeNet与AlexNet

Во-первых, по сравнению с относительно небольшим LeNet, AlexNet содержит 8 слоев преобразований с 5 сверточными и 2 полностью связанными скрытыми слоями и 1 полностью связанным выходным слоем. Ниже мы подробно опишем конструкцию этих слоев.

Форма окна свертки в первом слое AlexNet составляет 11 × 11. Поскольку подавляющее большинство изображений в ImageNet более чем в 10 раз больше по высоте и ширине, чем изображения MNIST, объекты в изображениях ImageNet занимают больше пикселей, поэтому для захвата объектов требуются большие сверточные окна. Форма сверточных окон во втором слое уменьшается до 5×5, а затем и полностью до 3×3. Кроме того, за первым, вторым и пятым сверточными слоями следует слой максимального объединения с формой окна 3 × 3 и шагом 2. Более того, количество каналов свертки, используемых AlexNet, также в десятки раз превышает количество каналов свертки в LeNet.

За последним сверточным слоем следуют два полносвязных слоя с 4096 выходами. Эти два огромных полностью связанных слоя содержат почти 1 ГБ параметров модели. Из-за ранних ограничений видеопамяти самая ранняя AlexNet использовала двухпотоковую конструкцию, так что одному графическому процессору требовалось только для обработки половины модели. К счастью, за последние несколько лет видеокарты прошли долгий путь, поэтому обычно нам больше не нужны такие специальные конструкции.

Во-вторых, AlexNet изменил сигмовидную функцию активации на более простую функцию активации ReLU. С одной стороны, расчет функции активации ReLU проще, например, в ней нет операции возведения в степень в сигмовидной функции активации. С другой стороны, функция активации ReLU упрощает обучение модели с использованием различных методов инициализации параметров. Это связано с тем, что когда выход сигмовидной функции активации очень близок к 0 или 1, градиенты в этих областях почти равны 0, так что обратное распространение не может продолжать обновлять некоторые параметры модели; в то время как градиент функции активации ReLU в положительном интервал всегда равен 1. Следовательно, если параметры модели не инициализированы должным образом, сигмовидная функция может получить почти нулевой градиент в положительном диапазоне, что сделает невозможным эффективное обучение модели.

В-третьих, AlexNet использует Dropout для управления сложностью модели полносвязного слоя, чтобы избежать переобучения. И LeNet не использует метод сброса.

В-четвертых, AlexNet вводит обширную аугментацию изображения, такую ​​как переворачивание, обрезка и изменение цвета, чтобы еще больше увеличить набор данных, чтобы уменьшить переоснащение.

Ниже приведен немного упрощенный AlexNet, реализованный с использованием PyTorch. Эта сеть предполагает ввод 1 × 224 × 224, то есть вход имеет только один канал, например черно-белый одноцветный набор данных, такой как Fashion-MNIST.

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()

        # convolution layer will change input shape into: floor((input_shape - kernel_size + padding + stride) / stride)
        # input shape: 1 * 224 * 224
        # convolution part
        self.conv = nn.Sequential(
            # conv layer 1
            # floor((224 - 11 + 2 + 4) / 4) = floor(54.75) = 54
            # conv: 1 * 224 * 224 -> 96 * 54 * 54 
            nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
            # floor((54 - 3 + 2) / 2) = floor(26.5) = 26
            # 96 * 54 * 54 -> 96 * 26 * 26
            nn.MaxPool2d(kernel_size=3, stride=2), 
            # conv layer 2: decrease kernel size, add padding to keep input and output size same, increase channel number
            # floor((26 - 5 + 4 + 1) / 1) = 26
            # 96 * 26 * 26 -> 256 * 26 * 26
            nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1, padding=2), nn.ReLU(),
            # floor((26 - 3 + 2) / 2) = 12
            # 256 * 26 * 26 -> 256 * 12 * 12
            nn.MaxPool2d(kernel_size=3, stride=2),
            # 3 consecutive conv layer, smaller kernel size
            # floor((12 - 3 + 2 + 1) / 1) = 12
            # 256 * 12 * 12 -> 384 * 12 * 12
            nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # 384 * 12 * 12 -> 384 * 12 * 12
            nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # 384 * 12 * 12 -> 256 * 12 * 12
            nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # floor((12 - 3 + 2) / 2) = 5
            # 256 * 5 * 5
            nn.MaxPool2d(kernel_size=3, stride=2)
        )
        # fully connect part 
        self.fc = nn.Sequential(
            nn.Linear(256 * 5 * 5, 4096),
            nn.ReLU(),
            # Use the dropout layer to mitigate overfitting
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            # Output layer. 
            # the number of classes in Fashion-MNIST is 10
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output

обучение модели

Хотя AlexNet использует в статье набор данных ImageNet, поскольку время обучения набора данных ImageNet очень велико, мы используем набор данных Fashion-MNIST для демонстрации AlexNet. При чтении данных мы сделали дополнительный шаг, чтобы увеличить высоту и ширину изображения до 224 высоты и ширины изображения, используемых AlexNet. Это можно сделать с помощьюtorchvision.transforms.Resizeпример для достижения. То есть мыToTensorиспользовать перед экземпляромResizeэкземпляр, затем используйтеComposeinstance для объединения двух преобразований для упрощения вызова.

def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
    """Use torchvision.datasets module to download the fashion mnist dataset and then load into memory."""
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
    trans.append(torchvision.transforms.ToTensor())

    transform = torchvision.transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
    if sys.platform.startswith('win'):
        num_workers = 0
    else:
        num_workers = 4
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter

load_data_fashion_mnist()Метод определяет, как читать данные, Fashion-MNIST изначально был размером 1×28×28.resizeРазмер изображения изменяется на основе исходного изображения, и изображение можно настроить на нужный нам размер.

def train(net, train_iter, test_iter, batch_size, optimizer, num_epochs, device=mlutils.try_gpu()):
    net = net.to(device)
    print("training on", device)
    loss = torch.nn.CrossEntropyLoss()
    timer = mlutils.Timer()
    # in one epoch, it will iterate all training samples
    for epoch in range(num_epochs):
        # Accumulator has 3 parameters: (loss, train_acc, number_of_images_processed)
        metric = mlutils.Accumulator(3)
        # all training samples will be splited into batch_size
        for X, y in train_iter:
            timer.start()
            # set the network in training mode
            net.train()
            # move data to device (gpu)
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            with torch.no_grad():
                # all the following metrics will be accumulated into variable `metric`
                metric.add(l * X.shape[0], mlutils.accuracy(y_hat, y), X.shape[0])
            timer.stop()
            # metric[0] = l * X.shape[0], metric[2] = X.shape[0]
            train_l = metric[0]/metric[2]
            # metric[1] = number of correct predictions, metric[2] = X.shape[0]
            train_acc = metric[1]/metric[2]
        test_acc = mlutils.evaluate_accuracy_gpu(net, test_iter)
        if epoch % 1 == 0:
            print(f'epoch {epoch + 1} : loss {train_l:.3f}, train acc {train_acc:.3f}, test acc {test_acc:.3f}')
    # after training, calculate images/sec
    # variable `metric` is defined in for loop, but in Python it can be referenced after for loop
    print(f'total training time {timer.sum():.2f}, {metric[2] * num_epochs / timer.sum():.1f} images/sec ' f'on {str(device)}')

на протяжении всей программыmain()метод, сначала определите сеть, а затем используйтеload_data_fashion_mnist()Загрузите обучающие и тестовые данные и, наконец, используйтеtrain()Метод обучения модели:

def main(args):

    net = AlexNet()
    optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)

    # load data
    train_iter, test_iter = mlutils.load_data_fashion_mnist(batch_size=args.batch_size, resize=224)
    # train
    train(net, train_iter, test_iter, args.batch_size, optimizer, args.num_epochs)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Image classification')
    parser.add_argument('--batch_size', type=int, default=128, help='batch size')
    parser.add_argument('--num_epochs', type=int, default=10, help='number of train epochs')
    parser.add_argument('--lr', type=float, default=0.001, help='learning rate')
    args = parser.parse_args()
    main(args)

в,argsэто аргумент, который можно передать в командной строке.

Я загрузил исходный код наGitHubи предоставляет две версии PyTorch и TensorFlow.

использованная литература

  1. Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). Imagenet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).
  2. 2 come.love/chapter_con…
  3. Тан Шусен Что/погружение-в-Д…