Szukaj…


Wprowadzenie

Splot 2D jest obliczany w podobny sposób, jak obliczyć splot 1D : przesuwasz jądro nad wejściem, obliczasz mnożenia elementarne i sumujesz je. Ale zamiast jądra / danych wejściowych będących tablicą, tutaj są macierzami.

Bez wyściółki, kroki = 1

Jest to najbardziej podstawowy przykład z najłatwiejszymi obliczeniami. Załóżmy, że twoje input i kernel to: wprowadź opis zdjęcia tutaj

Po uruchomieniu jądra otrzymasz następujące dane wyjściowe: wprowadź opis zdjęcia tutaj , który jest obliczany w następujący sposób:

  • 14 = 4 * 1 + 3 * 0 + 1 * 1 + 2 * 2 + 1 * 1 + 0 * 0 + 1 * 0 + 2 * 0 + 4 * 1
  • 6 = 3 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 0 * 1 + 1 * 0 + 2 * 0 + 4 * 0 + 1 * 1
  • 6 = 2 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 2 * 1 + 4 * 0 + 3 * 0 + 1 * 0 + 0 * 1
  • 12 = 1 * 1 + 0 * 0 + 1 * 1 + 2 * 2 + 4 * 1 + 1 * 0 + 1 * 0 + 0 * 0 + 2 * 1

Funkcja conv2d TF oblicza sploty w partiach i używa nieco innego formatu. Dla danych wejściowych jest to [batch, in_height, in_width, in_channels] dla jądra to [filter_height, filter_width, in_channels, out_channels] . Musimy więc podać dane we właściwym formacie:

import tensorflow as tf
k = tf.constant([
    [1, 0, 1],
    [2, 1, 0],
    [0, 0, 1]
], dtype=tf.float32, name='k')
i = tf.constant([
    [4, 3, 1, 0],
    [2, 1, 0, 1],
    [1, 2, 4, 1],
    [3, 1, 0, 2]
], dtype=tf.float32, name='i')
kernel = tf.reshape(k, [3, 3, 1, 1], name='kernel')
image  = tf.reshape(i, [1, 4, 4, 1], name='image')

Następnie oblicza się splot z:

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "VALID"))
# VALID means no padding
with tf.Session() as sess:
   print sess.run(res)

I będzie równoważny z tym, który obliczyliśmy ręcznie.

Trochę wypełnienia, kroki = 1

Padding to tylko wymyślna nazwa mówienia: otaczaj matrycę wejściową pewną stałą. W większości przypadków stała wynosi zero i dlatego ludzie nazywają ją wypełnieniem zerowym. Więc jeśli chcesz użyć dopełnienia 1 w naszym oryginalnym wejściu (sprawdź pierwszy przykład z padding=0, strides=1 ), macierz będzie wyglądać następująco:

wprowadź opis zdjęcia tutaj

Aby obliczyć wartości splotu, wykonuje się to samo przesuwanie. Zauważ, że w naszym przypadku wiele wartości pośrodku nie musi być ponownie obliczanych (będą one takie same jak w poprzednim przykładzie. Nie pokażę też tutaj wszystkich obliczeń, ponieważ pomysł jest prosty. Wynik jest następujący:

wprowadź opis zdjęcia tutaj

gdzie

  • 5 = 0 * 1 + 0 * 0 + 0 * 1 + 0 * 2 + 4 * 1 + 3 * 0 + 0 * 0 + 0 * 1 + 1 * 1
  • ...
  • 6 = 4 * 1 + 1 * 0 + 0 * 1 + 0 * 2 + 2 * 1 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 1

TF nie obsługuje arbitralnego wypełniania w funkcji conv2d , więc jeśli potrzebujesz wypełnienia, które nie jest obsługiwane, użyj tf.pad () . Na szczęście dla naszego wkładu dopełnienie „SAME” będzie równe dopełnieniu = 1. Więc w poprzednim przykładzie nie musimy zmieniać prawie nic:

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "SAME"))
# 'SAME' makes sure that our output has the same size as input and 
# uses appropriate padding. In our case it is 1.
with tf.Session() as sess:
   print sess.run(res)

Możesz sprawdzić, czy odpowiedź będzie taka sama, jak obliczona ręcznie.

Padding i strides (najbardziej ogólny przypadek)

Teraz zastosujemy krokowy splot do naszego wcześniej opisanego wypełnionego przykładu i obliczymy splot, gdzie p = 1, s = 2

wprowadź opis zdjęcia tutaj

Wcześniej, gdy używaliśmy strides = 1 , nasze okno przesuwne przesuwało się o 1 pozycję, a strides = s poruszają się o pozycje s (musisz obliczyć s^2 elementy mniej. Ale w naszym przypadku możemy skorzystać ze skrótu i nie wykonywać żadnych w ogóle. Ponieważ już oblicziliśmy wartości dla s = 1 , w naszym przypadku możemy po prostu pobrać co drugi element.

Więc jeśli rozwiązaniem jest przypadek s = 1 było

wprowadź opis zdjęcia tutaj

w przypadku s = 2 będzie to:

wprowadź opis zdjęcia tutaj

Sprawdź pozycje wartości 14, 2, 12, 6 w poprzedniej macierzy. Jedyną zmianą, którą musimy wykonać w naszym kodzie, jest zmiana kroków od 1 do 2 dla wymiaru szerokości i wysokości (2-gi, 3-gi).

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 2, 2, 1], "SAME"))
with tf.Session() as sess:
   print sess.run(res)

Nawiasem mówiąc, nic nie stoi na przeszkodzie, abyśmy używali różnych kroków dla różnych wymiarów.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow