Введение

Конволюция – это математическая операция, в результате которой продукт является интегралом 2-х функций (сигналов), при этом один из сигналов перевернут вверх дном. Например, ниже мы свернули 2 сигнала f(t) и g(t).

Итак, первое, что нужно сделать, это перевернуть сигнал g по горизонтали (180 градусов), а затем перевернуть g на f, умножив и накопив все его значения.

Порядок свертки сигналов не имеет значения для конечного результата, поэтому conv(a,b)==conv(b,a)

В этом случае учтите, что синий сигнал f(τ) является нашим входным сигналом, а g(t) – нашим ядром, термин ядро используется при использовании конверсий для фильтрации сигналов.

Размер выходного сигнала 1D

В случае 1D свертки выходной размер рассчитывается таким образом:

outputSize=(InputSize-KernelSize)+1

Применение сверток

Свертывание при обработке сигналов используется в следующих случаях использования:

Фильтрация сигналов (1D звук, 2D обработка изображения)

Проверьте, насколько один сигнал связан с другим

Поиск закономерностей в сигналах

Простой пример в матлабе и питоне (пронумерованном).

Ниже представлены два сигнала x = (0,1,2,3,4) с w = (1,-1,2).


Делать от руки

Чтобы лучше понять концепцию свертки, давайте возьмем приведенный выше пример из рук. В основном мы будем свертывать 2 сигнала (x,w). Первое, что нужно сделать – перевернуть W по горизонтали (O повернуть на 180 градусов влево)

После этого нам нужно сдвинуть перевернутую W на вход X

Обратите внимание, что в шагах 3,4,5 перевернутое окно полностью находится внутри входного сигнала. Эти результаты называются “действительными” свертками. В тех случаях, когда перевернутое окно не полностью находится во входном окне(X), мы можем считать их нулями или вычислить, что можно вычислить, например, на шаге 1 мы умножаем 1 на ноль, а остальное просто игнорируется.

Входная подкладка

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

Куда вы помещаете нули, зависит от того, что вы хотите сделать, то есть: в 1D случае вы можете скомкатировать их на каждом конце, но в 2D случае вы обычно размещаете их по всему исходному сигналу.

На матлабе можно использовать команду “padarray” для подкладки входа:

>> x

x(:,:,1) =

1 1 0 2 0

2 2 2 2 1

0 0 0 2 1

2 2 2 2 1

2 0 2 2 1

x(:,:,2) =

2 1 0 0 0

0 2 0 1 0

1 0 1 2 0

1 2 0 2 1

1 2 1 2 2

x(:,:,3) =

2 1 1 2 2

1 1 1 0 0

2 0 1 0 2

0 2 0 2 1

0 0 2 1 0

>> padarray(x,[1 1])

ans(:,:,1) =

0 0 0 0 0 0 0

0 1 1 0 2 0 0

0 2 2 2 2 1 0

0 0 0 0 2 1 0

0 2 2 2 2 1 0

0 2 0 2 2 1 0

0 0 0 0 0 0 0

ans(:,:,2) =

0 0 0 0 0 0 0

0 2 1 0 0 0 0

0 0 2 0 1 0 0

0 1 0 1 2 0 0

0 1 2 0 2 1 0

0 1 2 1 2 2 0

0 0 0 0 0 0 0

ans(:,:,3) =

0 0 0 0 0 0 0

0 2 1 1 2 2 0

0 1 1 1 0 0 0

0 2 0 1 0 2 0

0 0 2 0 2 1 0

0 0 0 2 1 0 0

0 0 0 0 0 0 0

Выходной размер для 2D

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

Если рассматривать свертку входа, пространственных размеров [H, W] с P, с квадратным ядром размера F и с использованием шага S, то выходной размер свертки определяется как:

outputSizeW=(W-F+2P)/S+1 выводSizeH=(H-F+2P)/S+1 вывод

F – это размер ядра, мы обычно используем квадратные ядра, поэтому F – это и ширина, и высота ядра

Реализация конвволюционной операции

В примере ниже будет задействован вход 5x5x3 (LxHx3), с уровнем конвекции со следующими параметрами Stride=2, Pad=1, F=3 (ядро 3×3), и K=2 (два фильтра).

Наш вход имеет 3 канала, поэтому нам нужен вес ядра 3x3x3. У нас есть 2 фильтра (K=2), поэтому в конце у нас будет 2 выходных активации. Далее мы можем вычислить размер этих двух выходов: (5 – 3 + 2)/2 + 1 = 3.

Этот код ниже (ванильная версия) не может быть использован в реальной жизни, потому что он будет медленным, но это хорошо для базового понимания. Обычно библиотеки глубокого изучения делают свертку как одноматричное умножение, используя метод im2col/col2im.

%% n размеры свертки

% Следующий код является просто расширением conv2d_vanila для n размеров.

% Параметры:

% Ввод: Н х Ш х Глубина

% K: ядро F x F x глубина

% S: шажок (сколько пикселей он будет скользить по входу)

% Эта реализация похожа на параметр ‘valid’ при нормальной свертке.

функция outConv = convn_vanilla(input, kernel, S)

% Получить размер ввода в виде строк и столбцов. Весы должны иметь

% та же глубина, что и входной объем (изображение)

[rowsIn, colsIn, ~] = size(input);

% Получить дименсио объем

depthInput = ndims(вход);

% Получить размер ядра, учитывая квадратное ядро всегда.

F = размер(ядро,1);

% % % инициализировать выходы

sizeRowsOut = ((rowsIn-F)/S) + 1;

sizeColsOut = ((rowsIn-F)/S) + 1;

outConvAcc = нули(sizeRowsOut , sizeColsOut, depthInput);

%% Сделать свертку

% Проведите каждый канал на входе с соответствующим каналом ядра,

% на конечной сумме всех результатов канала.

для depth=1:depthInput

% Выберите канал ввода и канал ядра тока

inputCurrDepth = input(:,:, depth);

kernelCurrDepth = kernel(:,:,depth);

% итерация на каждую строку и столбец, (с использованием шага)

для r=1:S:(rowsIn-1)

для c=1:S:(colsIn-1)

% Избегайте выборки из изображения.

если (((c+F)-1) <= colsIn) && ((((r+F)-1) <= rowsIn)

% Выберите окно по вводу громкости (патч)

sampleWindow = inputCurrDepth(r:(r+F)-1,c:(c+F)-1);

% Делать точечный продукт

dotProd = sum(sampleWindow(:) .* kernelCurrDepth(:));

% Результат хранения

outConvAcc(ceil(r/S),ceil(c/S),depth) = dotProd;

конец

конец

конец

конец

% Суммировать элементы над размером входного объема (просуммировать все каналы)

outConv = sum(outConvAcc, depthInput);

конец