Conv1D¶
Описание¶
Этот модуль выполняет операцию одномерной свёртки. Для более подробной теоретической информации об операции свёртки см. ConvND.
Для входного тензора с размерами (N, C_{in}, L_{in}), выходного с размерами (N, C_{out}, L_{out}) и ядра свёртки размера size операция проводится следующим образом (рассматриваем i-й элемент батча и j-ую карту выходного тензора):
где
N - размер батча;
C - количество карт в тензоре;
L - размер последовательности;
bias - тензор смещений слоя свёртки, имеет размеры (1, C_{out}, 1, 1);
weight - тензор весов слоя свёртки, имеет размеры (C_{out}, C_{in}, 1, size);
\star - оператор взаимной корреляции.
Инициализация¶
def __init__(self, inmaps, outmaps, size, stride=1, pad=0, dilation=1, wscale=1.0, useBias=True, name=None,
initscheme=None, empty=False, groups=1):
Параметры
Параметр | Возможные типы | Описание | По умолчанию |
---|---|---|---|
inmaps | int | Количество карт во входном тензоре. | - |
outmaps | int | Количество карт в выходном тензоре. | - |
size | int | Размер ядра свёртки. | - |
stride | int | Шаг свёртки. | 1 |
pad | int | Паддинг карт. | 0 |
dilation | int | Разрежение окна свёртки. | 1 |
wscale | float | Дисперсия случайных весов слоя. | 1.0 |
useBias | bool | Использовать или нет вектор смещений. | True |
initscheme | Union[tuple, str] | Указывает схему инициализации весов слоя (см. createTensorWithScheme). | None -> ("xavier_uniform", "in") |
name | str | Имя слоя. | None |
empty | bool | Инициализировать ли матрицу весов и смещений. | False |
groups | int | На сколько групп разбиваются карты для раздельной обработки. | 1 |
Пояснения
Info
Для вышерассмотренных входного (N, C_{in}, L_{in}) и выходного (N, C_{out}, L_{out}) тензоров существует зависимость между их размерами: \begin{equation} L_{out} = \frac{L_{in} + 2pad - dil(size - 1) - 1}{stride} + 1 \end{equation}
pad
- возможна передача только единой величины отступа для всех сторон карт. Возможности создания асимметричного паддинга (заполнение дополнительными элементами только с одной стороны тензора) для данного модуля не предусмотрено, используйте Pad1D;
groups
- количество групп, на которое разбивается множество карт для того, чтобы быть свернуто отдельно.
По умолчанию каждая выходная карта взаимодействует со всеми входными картами (groups=1
).
Если группы две, операция становится эквивалентна двум слоям свертки, каждый из которых видит половину входных каналов другого и производит половину выходных каналов. Каждая из этих выходных последовательностей затем выстраивается друг за другом.
Если количество групп совпадает с количеством входных карт, каждый канал сворачивается со своим собственным набором ядер размера (\frac{outmaps}{inmaps})
Значения параметров inmaps
и outmaps
должны делиться на значение параметра groups
.
Примеры¶
Базовый пример свёртки¶
Необходимые импорты.
>>> import numpy as np
>>> from PuzzleLib.Backend import gpuarray
>>> from PuzzleLib.Modules import Conv1D
>>> from PuzzleLib.Variable import Variable
Info
gpuarray
необходим для правильного размещения тензора на GPU
Зададим параметры тензоров таким образом, чтобы можно было наглядно продемонстрировать работу модуля: установим количество входных и выходных карт равными 1 и 2 соответственно.
>>> batchsize, inmaps, l = 1, 1, 10
>>> outmaps = 2
>>> data = gpuarray.to_gpu(np.arange(batchsize * inmaps * l).reshape((batchsize, inmaps, l)).astype(np.float32))
>>> data
[[[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]]]
Размер фильтра зададим 2, остальные параметры свёртки оставим по умолчанию (stride=1
, pad=0
, dilation=1
, groups=1
). Использование смещений явно отключим (хоть по умолчанию их тензор будет нулевым и не будет оказывать влияния на итоговый результат):
>>> size = 2
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=size, useBias=False)
В этом моменте проведён небольшой хак, чтобы задать веса явно. Так как выходных карт две и тензоры весов имеют размерности вида (C_{out}, C_{in}, 1, size):
def customW(size):
w1 = np.ones(shape=(1, 1, 1, size))
w2 = np.arange(size).reshape((1, 1, 1, size)) * -1
w = np.vstack([w1, w2]).astype(np.float32)
return w
>>> w = customW(size)
>>> print(w)
[[[[ 1. 1.]]]
[[[ 0. -1.]]]]
Установим веса на модуль:
>>> conv.setVar("W", Variable(gpuarray.to_gpu(w)))
Important
Вводим условие, что веса модуля для всех примеров задаются функцией customW
. Для краткости этот момент в примерах кода будет опущен.
Проведём операцию свёртки на синтетическом тензоре. Так как паддинг не был задан, то размер карт выходного тензора меньше, чем входного:
>>> conv(data)
[[[ 1. 3. 5. 7. 9. 11. 13. 15. 17.]
[-1. -2. -3. -4. -5. -6. -7. -8. -9.]]]
Параметр size¶
Используем всё то же самое, что и в предыдущем примере, но зададим другой размер фильтра:
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=3, useBias=False)
>>> conv(data)
[[[ 3. 6. 9. 12. 15. 18. 21. 24.]
[ -5. -8. -11. -14. -17. -20. -23. -26.]]]
Параметр pad¶
Используем параметры с предыдущего примера, но, допустим, хотим сохранить размеры тензора. Учитывая, что размер фильтра равен 3, а шаг свёртки 1, то для сохранения размера 5 нам понадобится паддинг 1, т.е. исходный тензор будет выглядеть следующим образом:
[[[0. 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 0.]]]
Переинициализируем свёртку:
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=3, pad=1, useBias=False)
>>> conv(data)
[[[ 1. 3. 6. 9. 12. 15. 18. 21. 24. 17.]
[ -2. -5. -8. -11. -14. -17. -20. -23. -26. -9.]]]
Параметр stride¶
Вернёмся к параметрам по умолчанию и фильтру размера 2, но изменим шаг свёртки:
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=2, stride=2, useBias=False)
>>> conv(data)
[[[ 1. 5. 9. 13. 17.]
[-1. -3. -5. -7. -9.]]]
Чтобы сохранить размер исходного тензора, придётся выставлять паддинг, равный 2:
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=2, stride=2, pad=2, useBias=False)
>>> conv(data)
[[[ 0. 0. 0. 3. 7. 11. 15. 9. 0. 0.]
[ 0. 0. 0. -2. -4. -6. -8. 0. 0. 0.]]]
Параметр dilation¶
Параметр dilation
производит разрежение фильтров свёртки, вставляя между оригинальными значениями фильтров нулевые элементы. Подробнее о разрежении см. теорию в ConvND.
>>> conv = Conv1D(inmaps=inmaps, outmaps=outmaps, size=2, stride=1, pad=0, dilation=2, useBias=False)
>>> conv(data)
[[[ 2. 4. 6. 8. 10. 12. 14. 16.]
[-2. -3. -4. -5. -6. -7. -8. -9.]]]
Параметр groups¶
Для данного примера вывод тензоров приведёт к очень громоздким конструкциям, поэтому опустим их.
В данном примере переинициализации весов функциией customW
не происходит.
>>> batchsize, inmaps, l = 1, 16, 10
>>> outmaps = 32
>>> groups = 1
>>> conv = Conv1D(inmaps, outmaps, size=2, initscheme="gaussian", groups=groups)
>>> print(conv.W.shape)
(32, 16, 1, 2)
>>> groups = 4
>>> conv = Conv1D(inmaps, outmaps, size=2, initscheme="gaussian", groups=groups)
>>> print(conv.W.shape)
(32, 4, 1, 2)
Чтобы получить Depthwise Separable Convolution блок:
>>> from PuzzleLib.Containers import Sequential
>>> seq = Sequential()
>>> seq.append(Conv2D(inmaps, inmaps, size=size, initscheme="gaussian", groups=inmaps, name="depthwise"))
>>> seq.append(Conv2D(inmaps, outmaps, size=1, initscheme="gaussian", name="pointwise"))
>>> print(seq["depthwise"].W.shape)
>>> (16, 1, 1, 2)
>>> print(seq["pointwise"].W.shape)
>>> (32, 16, 1, 1)
>>> data = gpuarray.to_gpu(np.random.randn(batchsize, inmaps, l).astype(np.float32))
>>> seq(data)