Недавно в статье Стандартизация веса предлагается новый метод нормализации не для активаций, а для самих весов.

Короче говоря, чтобы получить новые современные результаты, они объединили нормализацию партии и стандартизацию веса. Итак, в этом посте я расскажу, что такое стандартизация веса и как она помогает в тренировочном процессе, а также покажу свои собственные эксперименты с CIFAR-10, которым вы также можете следовать.

Блокнот для поста находится по этой ссылке. Для своих экспериментов я буду использовать циклическое обучение. Поскольку в статье обсуждается обучение с постоянной скоростью обучения, я бы использовал циклический LR, представленный Лесли Н. Смитом в его отчете. Я работаю над публикацией о состоянии методов обучения нейронных сетей в 2019 году, в котором я обобщу все эти методы вместе с теми, которые я изучил на курсах Fastai.

Чтобы все было чище, я бы использовал следующие обозначения: -

  1. BN - ›Пакетная нормализация
  2. GN - ›Групповая нормализация
  3. WS - ›Стандартизация веса

Что не так с BN и GN?

В идеале с ними все в порядке. Но чтобы получить максимальную отдачу от BN, мы должны использовать большой размер партии. А когда у нас есть партии меньшего размера, мы предпочитаем использовать GN. (Под меньшим я подразумеваю 1-2 изображения на графический процессор).

Почему это так?

Чтобы понять это, мы должны увидеть, как работает BN. Чтобы упростить задачу, представьте, что у нас есть только один канал, на котором мы хотим применить BN, и у нас есть 2 изображения в качестве размера нашей партии.

Теперь мы должны вычислить среднее значение и дисперсию, используя 2 изображения, а затем нормализовать один канал из 2 изображений. Итак, мы использовали 2 изображения для вычисления среднего и дисперсии. Это проблема.

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

GN была введена для случаев небольших партий, но она не смогла достичь результатов, которые BN смог достичь при использовании больших партий.

Как на самом деле помогают эти нормализации?

Это одно из ведущих направлений исследований. Но недавно в статье Инициализация исправлений: остаточное обучение без нормализации была показана причина увеличения производительности при использовании BN.

Короче говоря, это помогает сделать поверхность потерь гладкой.

Когда мы делаем поверхность потерь гладкой, мы можем делать более длинные шаги, что означает, что мы можем увеличить скорость обучения. Таким образом, использование Batch Norm фактически стабилизирует наши тренировки, а также ускоряет их.

Стандартизация веса

В отличие от BN и GN, которые мы применяем к активациям, то есть к выходным данным сверточного слоя, мы применяем Стандартизацию веса к весам самого сверточного слоя. Итак, мы применяем WS к ядрам, которые использует наш сверточный слой.

Как это помогает?

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

Но есть более простые способы понять это.

Сначала рассмотрим оптимизатор, который мы используем. Роль оптимизатора - оптимизировать веса нашей модели, но когда мы применяем уровни нормализации, такие как BN, мы не нормализуем наши веса, а вместо этого нормализуем активации, о которых оптимизатор даже не заботится.

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

Авторы статьи протестировали WS на различных задачах компьютерного зрения, и им удалось добиться лучших результатов с комбинацией WS + GN и WS + BN. Задачи, которые они тестировали, включали:

  1. Классификация изображений
  2. Обнаружение объекта
  3. Распознавание видео
  4. Семантическая сегментация
  5. Классификация облаков точек

Хватит разговоров, приступим к экспериментам

Код доступен в записной книжке.

Как реализовать WS?

class Conv2d(nn.Module):
    def __init__(self, in_chan, out_chan, kernel_size, stride=1, 
                 padding=0, dilation=1, groups=1, bias=True):
        super().__init__(in_chan, out_chan, kernel_size, stride, 
                         padding, dilation, groups, bias)
def forward(self, x):
        weight = self.weight
        weight_mean = weight.mean(dim=1, keepdim=True).mean(dim=2,
                                  keepdim=True).mean(dim=3, keepdim=True)
        weight = weight - weight_mean
        std = weight.view(weight.size(0), -1).std(dim=1).view(-1,1,1,1)+1e-5
        weight = weight / std.expand_as(weight)
        return F.conv2d(x, weight, self.bias, self.stride,
                        self.padding, self.dilation, self.groups)

Во-первых, давайте попробуем при размере партии = 64.

Это обеспечит основу того, чего нам следует ожидать. Для этого я создаю 2 модели resnet18:

  1. resnet18 - ›Он использует слои nn.Conv2d
  2. resnet18_ws - ›Он использует вышеупомянутый слой Conv2d, который использует стандартизацию веса

Я меняю заголовок модели resnet, так как изображения CIFAR уже имеют размер 32, и я не хочу изначально уменьшать их размер вдвое. Код можно найти в записной книжке. А для набора данных CIFAR я использую официальный поезд и действительный сплит.

Сначала я строю график зависимости потерь от скорости обучения. Чтобы узнать о теории, которая стоит за этим, обратитесь к курсам fastai или статьям Лесли Н. Смита.

Для тех, кто не знаком с графиком "потеря / скорость обучения". Ищем максимальное значение lr, при котором значение потерь начинает увеличиваться.

В этом случае max_lr составляет около 0,0005. Итак, давайте попробуем обучить модель некоторым шагам и посмотреть. Если вам интересно, во втором случае график более плоский в районе 1e-2, это потому, что масштаб двух графиков различается.

Итак, давайте обучим нашу модель и посмотрим, что произойдет. Я использую fit_one_cycle для обучения моей модели.

Между ними нет большой разницы, так как действительный проигрыш почти не меняется.

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

Одна вещь, которую я должен добавить здесь: потери быстро расходились, когда я использовал только BN, примерно после 40 итераций, в то время как в случае WS + BN потери не расходились.

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

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

Опять же, на графике мы видим, что мы можем использовать более высокую скорость обучения.

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

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