Operacje łączenia i konwolucji przesuwają „okno” w poprzek tensora wejściowego. Na tf.nn.conv2d
przykładzie: Jeśli tensor wejściowy ma 4 wymiary [batch, height, width, channels]
:, to splot działa na oknie 2D na height, width
wymiarach.
strides
określa, o ile przesuwa się okno w każdym z wymiarów. Typowe użycie ustawia pierwszy (partia) i ostatni (głębokość) krok na 1.
Posłużmy się bardzo konkretnym przykładem: uruchomienie splotu dwuwymiarowego na obrazie wejściowym 32x32 w skali szarości. Mówię o odcieniach szarości, ponieważ wtedy obraz wejściowy ma głębię = 1, co pomaga zachować prostotę. Niech ten obraz będzie wyglądał tak:
00 01 02 03 04 ...
10 11 12 13 14 ...
20 21 22 23 24 ...
30 31 32 33 34 ...
...
Uruchommy okno splotu 2x2 na jednym przykładzie (rozmiar wsadu = 1). Damy splotowi głębokość kanału wyjściowego równą 8.
Wejście do splotu ma shape=[1, 32, 32, 1]
.
Jeśli określisz za strides=[1,1,1,1]
pomocą padding=SAME
, wyjście filtru będzie [1, 32, 32, 8].
Filtr najpierw utworzy wynik dla:
F(00 01
10 11)
A potem dla:
F(01 02
11 12)
i tak dalej. Następnie przejdzie do drugiego wiersza, obliczając:
F(10, 11
20, 21)
następnie
F(11, 12
21, 22)
Jeśli określisz krok [1, 2, 2, 1], nie spowoduje to nałożenia okien. Obliczy:
F(00, 01
10, 11)
i wtedy
F(02, 03
12, 13)
Stride działa podobnie dla operatorów poolingu.
Pytanie 2: Dlaczego strides [1, x, y, 1] dla konwetów
Pierwsza 1 to partia: zwykle nie chcesz pomijać przykładów w swojej grupie lub nie powinieneś ich uwzględniać w pierwszej kolejności. :)
Ostatnie 1 to głębokość splotu: zwykle nie chcesz pomijać danych wejściowych z tego samego powodu.
Operator conv2d jest bardziej ogólne, więc mógł tworzyć zwoje że przesuń okno wzdłuż innych wymiarów, ale to nie jest typowe zastosowanie w convnets. Typowym zastosowaniem jest użycie ich w przestrzeni.
Dlaczego zmiana kształtu na -1 -1 jest symbolem zastępczym, który mówi „dostosuj w razie potrzeby, aby dopasować rozmiar wymagany dla pełnego tensora”. Jest to sposób na uniezależnienie kodu od rozmiaru wsadu wejściowego, dzięki czemu można zmienić potok i nie trzeba dostosowywać rozmiaru wsadu wszędzie w kodzie.
Wejścia są 4-wymiarowe i mają postać:
[batch_size, image_rows, image_cols, number_of_colors]
Kroki na ogół definiują nakładanie się operacji stosowania. W przypadku conv2d określa, jaka jest odległość między kolejnymi zastosowaniami filtrów splotowych. Wartość 1 w określonym wymiarze oznacza, że stosujemy operator w każdym wierszu / kolumnie, wartość 2 oznacza każdą sekundę i tak dalej.
Ad 1) Wartości, które mają znaczenie dla zwojów to 2 i 3 i reprezentują one nakładanie się filtrów splotowych wzdłuż wierszy i kolumn. Wartość [1, 2, 2, 1] mówi, że chcemy zastosować filtry do co drugiego wiersza i kolumny.
Ad 2) Nie znam ograniczeń technicznych (może to być wymóg CuDNN), ale zazwyczaj ludzie używają kroków wzdłuż wymiarów rzędów lub kolumn. Niekoniecznie ma to sens, jeśli chodzi o wielkość partii. Nie jestem pewien ostatniego wymiaru.
Ad 3) Ustawienie -1 dla jednego z wymiarów oznacza „ustawić wartość dla pierwszego wymiaru tak, aby całkowita liczba elementów w tensorze pozostała niezmieniona”. W naszym przypadku -1 będzie równe batch_size.
źródło
Zacznijmy od tego, co robi stride w przypadku 1-dim.
Załóżmy, że twój
input = [1, 0, 2, 3, 0, 1, 1]
ikernel = [2, 1, 3]
wynik splotu jest[8, 11, 7, 9, 4]
obliczany przez przesuwanie jądra po danych wejściowych, mnożenie elementów i sumowanie wszystkiego. W ten sposób :Tutaj przesuwamy się o jeden element, ale nic Cię nie powstrzyma, używając innej liczby. Ta liczba to Twój krok. Możesz o tym myśleć jako o zmniejszaniu częstotliwości próbkowania wyniku 1-krokowego splotu, po prostu biorąc każdy s-ty wynik.
Znając rozmiar wejściowy i , rozmiar jądra k , krok s i wypełnienie p , możesz łatwo obliczyć rozmiar wyjściowy splotu jako:
Tutaj || operator oznacza obsługę sufitu. Dla warstwy puli s = 1.
Obudowa N-dim.
Znajomość matematyki dla przypadku 1-dim jest łatwa, gdy zobaczysz, że każdy dim jest niezależny. Więc po prostu przesuń każdy wymiar osobno. Oto przykład dla 2-d . Zauważ, że nie musisz mieć tego samego kroku na wszystkich wymiarach. Więc dla wejścia / jądra N-dim należy podać N kroków.
Więc teraz łatwo jest odpowiedzieć na wszystkie pytania:
źródło
@dga wykonał wspaniałą pracę, wyjaśniając i nie mogę być wystarczająco wdzięczny, jak bardzo był pomocny. W podobny sposób chciałbym podzielić się moimi odkryciami na temat tego, jak
stride
działa splot 3D.Zgodnie z dokumentacją TensorFlow na conv3d, kształt wejścia musi być w następującej kolejności:
[batch, in_depth, in_height, in_width, in_channels]
Wyjaśnijmy zmienne od skrajnej prawej do lewej na przykładzie. Zakładając, że kształt wejściowy to
input_shape = [1000,16,112,112,3]
Poniżej znajduje się podsumowująca dokumentacja dotycząca sposobu wykorzystania kroku.
Jak wskazano w wielu pracach, kroki oznaczają po prostu, o ile kroków okno lub jądro odskakuje od najbliższego elementu, czy to ramki danych, czy piksela (jest to parafrazowane).
Z powyższej dokumentacji krok w 3D będzie wyglądał następująco: strides = (1, X , Y , Z , 1).
Dokumentacja to podkreśla
strides[0] = strides[4] = 1
.strides [X] oznacza, ile przeskoków powinniśmy wykonać w skupionych klatkach. Na przykład, jeśli mamy 16 klatek, X = 1 oznacza użycie każdej klatki. X = 2 oznacza użycie co drugiej klatki i trwa i trwa
strides [y] i strides [z] postępują zgodnie z wyjaśnieniem @dga, więc nie będę powtarzał tej części.
Jednak w kerasach wystarczy określić krotkę / listę 3 liczb całkowitych, określając skoki splotu wzdłuż każdego wymiaru przestrzennego, gdzie wymiar przestrzenny to krok [x], kroki [y] i kroki [z]. strides [0] i strides [4] ma już domyślną wartość 1.
Mam nadzieję, że ktoś uzna to za pomocne!
źródło