MaxPool2D

Описание

Info

Родительский класс: Pool2D

Производные классы: -

Этот модуль реализует операцию двумерного максимизирующего пулинга (двумерного максимизирующего объединения). Подробное теоретическое описание см. в Pool2D.

Для входного тензора с размерами (N, C, H_{in}, W_{in}) и выходного с размерами (N, C, H_{out}, W_{out}) операция проводится следующим образом (рассматриваем i-й элемент батча, j-ую карту выходного тензора):

out(N_i, C_j, h, w) = \max\limits_{m=0..k_h-1}\max\limits_{n=0..k_w-1}(input(N_i, C_j, stride_h \times h + m, stride_w \times w + n))

где

N - размер батча;
C - количество карт в тензоре;
H - размер карт по высоте;
W - размер карт по ширине;
stride_h, stride_w - шаг пулинга вдоль высоты и ширины карт соответственно;
k_h, k_w - размер окна пулинга по высоте и ширине соответственно.

Инициализация

def __init__(self, size=2, stride=2, pad=0, useMask=False, name=None):

Параметры

Параметр Возможные типы Описание По умолчанию
size Union[int, tuple] Размер ядра. 2
stride Union[int, tuple] Шаг пулинга. 2
pad Union[int, tuple] Паддинг входных карт. 0
useMask bool Сохранять ли тензор с индексами максимумов. False
name str Имя слоя. None

Пояснения

size - возможна передача как единого размера окна пулинга, в таком случае оно будет квадратным, так и tuple вида (size_h, size_w), где size_h - высота окна пулинга, а size_w - его ширина;


stride - возможна передача как единой величины шага пулинга по высоте и ширине карт, так и tuple вида (stride_h, stride_w), где stride_h - величина шага пулинга вдоль высоты карты, а stride_w - вдоль ширины;


pad - возможна передача как единой величины отсупа для всех сторон карт, так и tuple вида (pad_h, pad_w), где pad_h - величина отступа с каждой стороны вдоль высоты картинки, а pad_w - вдоль ширины. Возможности создания асимметричного паддинга (заполнение дополнительными элементами только с одной стороны тензора) для данного модуля не предусмотрено, используйте Pad2D.

Примеры


Базовый пример пулинга


Необходимые импорты.

>>> import numpy as np
>>> from PuzzleLib.Backend import gpuarray
>>> from PuzzleLib.Modules import MaxPool2D

Info

gpuarray необходим для правильного размещения тензора на GPU

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

>>> batchsize, maps, h, w = 1, 1, 6, 6
>>> data = gpuarray.to_gpu(np.arange(batchsize * maps * h * w).reshape((batchsize, maps, h, w)).astype(np.float32))
>>> data
[[[[ 0.  1.  2.  3.  4.  5.]
   [ 6.  7.  8.  9. 10. 11.]
   [12. 13. 14. 15. 16. 17.]
   [18. 19. 20. 21. 22. 23.]
   [24. 25. 26. 27. 28. 29.]
   [30. 31. 32. 33. 34. 35.]]]]

Инициализируем модуль со стандартными параметрами (size=2, stride=2, pad=0, useMask=False):

>>> avgpool = MaxPool2D()
>>> avgpool(data)
[[[[ 7.  9. 11.]
   [19. 21. 23.]
   [31. 33. 35.]]]]

Параметр size


Оставим все параметры такими же, кроме size:

>>> pool = MaxPool2D(size=4)
>>> pool(data)
[[[[21. 23.]
   [33. 35.]]]]
Параметр size можно задавать разным для высоты и ширины карты:
>>> pool = MaxPool2D(size=(4, 2))
>>> pool(data)
[[[[19. 21. 23.]
   [31. 33. 35.]]]]


Параметр stride


Установим значение stride равным 1:

>>> pool = MaxPool2D(stride=1)
>>> pool(data)
[[[[ 7.  8.  9. 10. 11.]
   [13. 14. 15. 16. 17.]
   [19. 20. 21. 22. 23.]
   [25. 26. 27. 28. 29.]
   [31. 32. 33. 34. 35.]]]]

Параметр stride также можно задавать разным для высоты и ширины карты:

>>> pool = MaxPool2D(stride=(1, 3))
>>> pool(data)
[[[[ 7. 10.]
   [13. 16.]
   [19. 22.]
   [25. 28.]
   [31. 34.]]]]

Изменим теперь и stride, и size:

>>> pool = MaxPool2D(size=4, stride=4)
>>> pool(data)

[[[[21.]]]]

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


Параметр pad


Чтобы включить проигнорированные элементы из предыдущего примера, инициализируем паддинг:

>>> pool = MaxPool2D(size=4, stride=4, pad=1)
>>> pool(data)
[[[[14. 17.]
   [32. 35.]]]]
Обратите внимание, что паддинг в модуле всегда симметричный - по одному новому элементу (строка или столбец) было добавлено с каждой стороны исходного тензора, т.е. его вид после паддинга:
[[[[ 0.  0.  0.  0.  0.  0.  0.  0.]
   [ 0.  0.  1.  2.  3.  4.  5.  0.]
   [ 0.  6.  7.  8.  9. 10. 11.  0.]
   [ 0. 12. 13. 14. 15. 16. 17.  0.]
   [ 0. 18. 19. 20. 21. 22. 23.  0.]
   [ 0. 24. 25. 26. 27. 28. 29.  0.]
   [ 0. 30. 31. 32. 33. 34. 35.  0.]
   [ 0.  0.  0.  0.  0.  0.  0.  0.]]]]

Параметр pad также можно задавать разным для высоты и ширины карты. Например pad=(0, 2), тогда:

[[[[ 0.  0.  0.  1.  2.  3.  4.  5.  0.  0.]
   [ 0.  0.  6.  7.  8.  9. 10. 11.  0.  0.]
   [ 0.  0. 12. 13. 14. 15. 16. 17.  0.  0.]
   [ 0.  0. 18. 19. 20. 21. 22. 23.  0.  0.]
   [ 0.  0. 24. 25. 26. 27. 28. 29.  0.  0.]
   [ 0.  0. 30. 31. 32. 33. 34. 35.  0.  0.]]]]


Параметр useMask


Параметр useMask отвечает за то, будет ли сохранён тензор индексов максимальных элементов. Чтобы продемонстрировать его работу, переинициализируем тензор данных:

>>> data = gpuarray.to_gpu(np.random.randint(low=0, high=9, size=(batchsize, maps, insize)).astype(np.float32))
>>> print(data)
[[[[4. 5. 7. 8. 3. 1.]
   [7. 7. 4. 0. 7. 7.]
   [2. 1. 2. 1. 0. 4.]
   [6. 8. 5. 5. 1. 1.]
   [1. 2. 5. 8. 4. 4.]
   [6. 0. 2. 6. 8. 5.]]]]
>>> pool = MaxPool2D(useMask=True)
>>> pool(data)
[[[[7. 8. 7.]
   [8. 5. 4.]
   [6. 8. 8.]]]]
>>> print(pool.mask)
[[[[ 6  3 10]
   [19 20 17]
   [30 27 34]]]]
Для каждого элемента батча и каждой карты индексы возвращаются отдельно:
>>> maps = 2
>>> data = gpuarray.to_gpu(np.random.randint(low=0, high=9, size=(batchsize, maps, insize)).astype(np.float32))
>>> print(data)
[[[[3. 3. 8. 6. 4. 3.]
   [2. 7. 0. 2. 6. 4.]
   [1. 5. 4. 7. 2. 8.]
   [7. 6. 8. 4. 7. 0.]
   [4. 0. 8. 1. 5. 3.]
   [0. 6. 8. 2. 3. 4.]]

  [[7. 4. 6. 7. 1. 4.]
   [6. 5. 5. 1. 5. 7.]
   [3. 7. 3. 6. 2. 7.]
   [2. 8. 7. 3. 5. 5.]
   [4. 8. 2. 1. 4. 1.]
   [2. 7. 3. 1. 4. 0.]]]]
>>> pool = MaxPool2D(useMask=True)
>>> pool(data)
[[[[7. 8. 6.]
   [7. 8. 8.]
   [6. 8. 5.]]

  [[7. 7. 7.]
   [8. 7. 7.]
   [8. 3. 4.]]]]
>>> print(pool.mask)
[[[[ 7  2 10]
   [18 20 17]
   [31 26 28]]

  [[ 0  3 11]
   [19 20 17]
   [25 32 28]]]]
>>> print(pool.mask.shape)
(1, 2, 3, 3)