tensorflow
Matematyka stojąca za splotem 2D z zaawansowanymi przykładami w TF
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:
Po uruchomieniu jądra otrzymasz następujące dane wyjściowe: , 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:
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:
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
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
w przypadku s = 2
będzie to:
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.