Классы Conv2d и MaxPool2d

Смотреть материал на YouTube | RuTube

На прошлом занятии мы с вами подробно рассмотрели принцип работы сверточных слоев НС. Во фреймворке PyTorch они реализуются с помощью классов:

  • nn.Conv1d – для одномерной свертки;
  • nn.Conv2d – для двумерной свертки (например, изображение);
  • nn.Conv3d – для трехмерной свертки (например, снимок МРТ).

А операция MaxPooling классами:

  • nn.MaxPool1d – для одномерного сигнала;
  • nn.MaxPool2d – для двумерного сигнала;
  • nn.MaxPool3d – для трехмерного сигнала.

Начнем непосредственно с классов сверточных слоев и в качестве примера выберем наиболее распространенный класс Conv2d для обработки двумерного сигнала. Два других класса используются по аналогии и имеют тот же набор параметров.

Класс nn.Conv2d

На вход этого слоя подается тензор в формате:

(batch, channels, H, W)

А сам класс Conv2d имеет следующие основные параметры:

  • in_channels – число каналов во входном тензоре (обязательный параметр).

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

У вас здесь может возникнуть вопрос, зачем вообще указывать число входных каналов? Разве нельзя эту информацию можно извлечь из входного тензора? На первый взгляд, да. Однако всегда следует помнить, что сверточный слой работает со строго определенным числом входных каналов и этот параметр в процессе работы меняться не должен. Если определять параметр in_channels по входному тензору, то может возникнуть ситуация разного числа входных каналов при разных тензорах. Это считается недопустимым. Соответственно, входные тензоры должны содержать строго определенное число каналов параметром in_channels.

  • out_channels – число выходных каналов (обязательный параметр).

Этот параметр, фактически, определяет количество ядер (фильтров), которые будут применяться к входным тензорам. На выходе будем иметь множество карт признаков в количестве out_channels. При этом каждая карта, полученная как результат применения текущего фильтра к входному тензору, в общем случае будет иметь размеры Hm x Wm, отличающиеся от входных размеров H x W. В результате тензор на выходе сверточного слоя будет иметь размеры:

(batch, out_channels, Hm, Wm)

  • kernel_size – размер ядра (фильтра).

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

kernel_size = 3

тогда ядро будет иметь равные значения по строкам и столбцам 3x3. Либо определяться через кортеж:

kernel_size = (3, 3)

Первый элемент задает число строк, а второй – число столбцов ядра. Например, если прописать:

kernel_size = (3, 5)

то получим ядро с тремя строками и пятью столбцами (3x5):

Обратите внимание, что параметр kernel_size определяет размеры ядра для осей H и W входного тензора. Размерность ядра по каналам задается параметром in_channels. То есть, результирующий размер каждого ядра:

(in_channels, kernel_size)

  • stride=1 – шаг сканирования (необязательный параметр).

Следующий уже необязательный параметр позволяет задавать произвольные смещения фильтра при сканировании входного тензора. По умолчанию stride равен единице, что означает изменение положения фильтра на один элемент по строкам и столбцам при проходе по тензору в осях (H, W).

Параметр stride также можно определять либо целым положительным числом, либо кортежем. Например, записи:

stride=1
stride=(1, 1)

будут означать одно и то же. Или, можно указать:

stride=2
stride=(2, 2)

Тогда фильтры при сканировании тензора будут смещаться на два элемента. Причем первое значение в кортеже – это шаг по оси H (строкам), а второе – шаг по оси W (столбцам).

  • padding=0 – расширение входного тензора по осям (H, W).

С помощью этого параметра можно расширять область тензора по осям (H, W), заполняя новые элементы заданным значением, обычно, нулями. По умолчанию аргумент padding=0 означает отсутствие какого-либо расширения. Однако если прописать:

padding=1

или

padding=(1, 1)

то к тензору добавятся строки сверху, снизу, слева и справа, заполненные нулевыми значениями:

Если же передать аргумент:

padding=(1, 2)

то добавится по одной строке сверху и снизу, и по два столбца слева и справа:

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

Фреймворк PyTorch позволяет прописывать еще два специальных значения параметра padding, заданных строками:

  • padding='valid' – то же самое, что и padding=0;
  • padding='same' – формирование такого расширения, чтобы итоговые размеры карт признаков соответствовали размерам входного тензора (применяется только совместно с параметром stride=1).

Зачем нужно такое расширение тензора вы уже должны знать из предыдущего занятия. Напомню, что благодаря этим новым элементам, например, появляется возможность позиционировать фильтры так, чтобы на выходе получать карты признаков тех же размеров HxW, что и у входного тензора. Вообще, зная параметры kernel_size, stride и padding можно рассчитать итоговые размеры карт признаков по формулам (справедливы при параметре dilation=1):

 

  • bias=True – учет смещения (bias) при вычислении свертки.

Последний параметр, который мы рассмотрим, позволяет добавлять или убирать смещение, задаваемое параметром , при вычислении сверток по формуле:

При bias=True параметр  присутствует, а при bias=False – отсутствует (или, что то же самое, равен нулю). Мы уже знаем, что в ряде случаев такое поведение желательное, например, если после сверточного слоя используется нормализация по батчам (Batch Normalization). Тогда в смещении нет особого смысла и его следует отбросить.

Вот основные параметры класса Conv2d, который отвечает за формирование сверточных слоев НС.

Класс nn.MaxPool2d

В заключение этого занятия рассмотрим еще один класс MaxPool2d, который часто применяют после сверточных слоев. Он имеет следующий набор параметров:

torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

Но чаще всего используются первые два:

  • kernel_size – размер области выделения максимального значения;
  • stride=None – смещение области выделения (при None смещение выполняется на величину kernel_size, то есть, области не пересекаются).

Например, при stride=None и:

kernel_size=2

или

kernel_size=(2, 2)

карта признаков покрывается непересекающимися блоками размером 2x2 элемента и в каждом выделяются максимальные значения. Получается результат работы слоя MaxPool2d:

При этом размер выходного тензора будет:

(batch, channels, H // 2, W // 2)

Если же взять размер ядра:

kernel_size=(3, 2)

(три строки и два столбца), то получим результат:

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

Видео по теме