Wprowadzenie
Konwulsja jest operacją matematyczną, która sprawia, że produkt jest zintegrowany z 2 funkcjami (sygnałami), przy czym jeden z sygnałów jest odwrócony do góry nogami. Na przykład, poniżej zawijamy 2 f(t) i g(t) sygnałów.
Tak więc pierwszą rzeczą do zrobienia jest obrócić sygnał g w poziomie (180 stopni), a następnie przesunąć g do góry nogami na f, mnożąc i gromadząc wszystkie jego wartości.
Kolejność, w jakiej sygnały są zwodzone, nie ma znaczenia dla wyniku końcowego, więc conv(a,b)==conv(b,a)
W tym przypadku należy wziąć pod uwagę, że niebieski sygnał f(τ) jest naszym sygnałem wejściowym, a g(t) naszym jądrem, termin “jądro” jest używany w przypadku używania zawirowań do filtrowania sygnałów.
Wielkość sygnału wyjściowego 1D
W przypadku zwoju 1D wielkość wyjściowa jest obliczana w ten sposób:
outputSize=(InputSizee-KernelSize)+1
Stosowanie zwojów
Ludzie używają konwulsji do przetwarzania sygnału w następujących przypadkach użycia:
Filtrowanie sygnałów (dźwięk 1D, przetwarzanie obrazu 2D)
Sprawdź, na ile jeden sygnał jest powiązany z innym
Znajdowanie wzorców w sygnałach
Prosty przykład w matlabie i pytonie (zdrętwiały)
Poniżej znajdują się dwa sygnały x = (0,1,2,3,4) i w = (1,-1,2).
Zrób to ręcznie
Aby lepiej zrozumieć pojęcie konwekcji, weźmy powyższy przykład za rękę. Zasadniczo zamierzamy zawinąć 2 sygnały (x,w). Pierwszą rzeczą jest odwrócenie W poziomo (O obrót w lewo o 180 stopni)
Następnie musimy przesunąć odwrócone W nad wejście X
Należy pamiętać, że w krokach 3,4,5 odwrócone okno znajduje się całkowicie wewnątrz sygnału wejściowego. Wyniki te nazywane są “ważnymi” zawirowaniami. W przypadkach, w których odwrócone okno nie znajduje się całkowicie wewnątrz okna wejściowego(X), możemy je uznać za zero, lub możemy obliczyć, co można obliczyć, np. w kroku 1 mnoży się 1 przez zero, a resztę po prostu ignoruje.
Wyściółka wejściowa
Aby utrzymać wielkość wyniku zwinięcia równą wielkości wejścia, oraz aby uniknąć efektu zwanego zwinięciem okrężnym, podajemy sygnał zerami.
Miejsce umieszczenia zer zależy od tego, co chcesz zrobić, to znaczy: w przypadku 1D możesz je połączyć na każdym końcu, ale w przypadku 2D zazwyczaj umieszczamy je wokół oryginalnego sygnału.
Na matlab można użyć komendy ‘padarray’ do podparcia wejścia:
>> 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
Wielkość wyjściowa dla 2D
Proszę wiedzieć, jaka będzie wielkość naszej produkcji po wykonaniu na niej pewnych operacji zwijania. Na szczęście istnieje przydatna formuła, która dokładnie to nam mówi.
Jeśli weźmiemy pod uwagę zwijanie się wejścia o wymiarach przestrzennych [H, W] wypełnionego P, z kwadratowym jądrem o rozmiarze F i z rozkrokiem S, wówczas rozmiar wyjściowy zwijania się definiowany jest jako:
outputSizeW=(W-F+2P)/S+1 outputSizeH=(H-F+2P)/S+1 output
F jest wielkością jądra, zwykle używamy kwadratowych ziaren, więc F jest zarówno szerokością jak i wysokością jądra
Realizacja operacji konwekcyjnych
Poniższy przykład będzie obejmował wejście 5x5x3 (LxHx3), z poziomem konwertora o następujących parametrach Stride=2, Pad=1, F=3 (jądro 3×3), oraz K=2 (dwa filtry).
Nasze wejście ma 3 kanały, więc potrzebujemy 3x3x3 masy jądra. Mamy 2 filtry (K=2), więc na końcu będziemy mieli 2 aktywacje wyjściowe. Dalej możemy obliczyć wielkość tych dwóch wyjść, które mają być: (5 – 3 + 2)/2 + 1 = 3.
Poniższy kod (wersja waniliowa) nie może być używany w prawdziwym życiu, ponieważ będzie powolny, ale jest dobry dla podstawowego zrozumienia. Zazwyczaj głęboko uczące się biblioteki robią konwulsje jako pojedyncze mnożenie matrycowe, używając metody im2col/col2im.
%% Konwulsja n wymiary
% Poniższy kod jest tylko rozszerzeniem conv2d_vanila dla n wymiarów.
% Parametry:
% Wejście: Wys. x Szer. x Głębokość
% K: jądro F x F x głębokość
% S: rozkrok (ile pikseli przesunie okno na wejściu)
% Ta implementacja jest jak parametr “ważny” w przypadku normalnego zwoju
funkcja outConv = convn_vanilla(wejście, jądro, S)
% Uzyskaj rozmiar wejściowy w postaci rzędów i kolumn. Wagi powinny mieć
% taka sama głębokość jak objętość wejściowa (obraz)
[rowsIn, colsIn, ~] = rozmiar(wejście);
% Get volume dimensio
deepInput = ndims(wejście);
% Uzyskaj rozmiar jądra, biorąc pod uwagę kwadratowe jądro zawsze
F = rozmiar (jądro, 1);
%% Inicjacja wyjścia
sizeRowsOut = ((rowsIn-F)/S) + 1;
sizeColsOut = ((colsIn-F)/S) + 1; sizeColsOut = ((colsIn-F)/S) + 1;
OutConvAcc = zera (sizeRowsOut , sizeColsOut, depthInput);
% % % Do zwoju
% Obróć każdy kanał na wejściu za pomocą odpowiedniego kanału jądrowego,
% na końcu sumy wszystkich wyników kanału.
dla głębokości=1:depthInput
% Wybierz wejście i kanał prądowy jądra
inputCurrDepth = wejście(:,:,depth);
kernelCurrRDepth = jądro(:,:,depth);
% Iteracja na każdym wierszu i col, (przy użyciu rozkroku)
dla r=1:S: (wiersze IN-1)
dla c=1:S:(colsIn-1)
%Należy unikać pobierania próbek z obrazu.
if (((c+F)-1) <= colsIn) && (((r+F)-1) <= rowsIn)
% Wybierz okno dotyczące objętości wejściowej (łatka)
sampleWindow = wejścieCurrDepth(r:(r+F)-1,c:(c+F)-1);
% Czy produkt kropkowy
dotProd = suma(próbkaWindow(:) .* kernelCurrDepth(:));
% Wynik przechowywania
OutConvAcc(ceil(r/S), ceil(c/S), głębokość) = dotProd;
koniec
koniec
koniec
koniec
% Suma elementów nad wymiarem objętości wejściowej (suma wszystkich kanałów)
outConv = suma (outConvAcc, depthInput);
koniec