tensorflow
Wiskunde achter 2D-convolutie met geavanceerde voorbeelden in TF
Zoeken…
Invoering
2D-convolutie wordt op dezelfde manier berekend als men 1D-convolutie zou berekenen: je schuift je kernel over de invoer, berekent de elementgewijze vermenigvuldigingen en vat ze samen. Maar in plaats van dat uw kernel / invoer een array is, zijn dit matrices.
Geen opvulling, pas = 1
Dit is het meest eenvoudige voorbeeld met de eenvoudigste berekeningen. Laten we aannemen dat uw input
en kernel
zijn:
Wanneer u uw kernel ontvangt, ontvangt u de volgende uitvoer: , die op de volgende manier wordt berekend:
- 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
De conv2d- functie van TF berekent convoluties in batches en gebruikt een iets ander formaat. Voor een invoer is het [batch, in_height, in_width, in_channels]
voor de kernel is het [filter_height, filter_width, in_channels, out_channels]
. We moeten de gegevens dus in het juiste formaat leveren:
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')
Nadien wordt de convolutie berekend met:
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)
En zal gelijk zijn aan degene die we met de hand hebben berekend.
Enkele vulling, pas = 1
Opvulling is gewoon een mooie naam om te vertellen: omring je inputmatrix met een constante. In de meeste gevallen is de constante nul en daarom noemen mensen het nul opvulling. Dus als u een opvulling van 1 in onze oorspronkelijke invoer wilt gebruiken (controleer het eerste voorbeeld met padding=0, strides=1
), ziet de matrix er als volgt uit:
Om de waarden van de convolutie te berekenen, doet u hetzelfde glijden. Merk op dat in ons geval veel waarden in het midden niet opnieuw hoeven te worden berekend (ze zullen hetzelfde zijn als in het vorige voorbeeld. Ik zal ook niet alle berekeningen hier tonen, omdat het idee eenvoudig is. Het resultaat is:
waar
- 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 ondersteunt geen willekeurige padding in conv2d- functie, dus als u padding nodig hebt die niet wordt ondersteund, gebruikt u tf.pad () . Gelukkig voor onze input zal de padding 'SAME' gelijk zijn aan padding = 1. Dus we moeten bijna niets veranderen in ons vorige voorbeeld:
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)
U kunt controleren of het antwoord hetzelfde is als met de hand berekend.
Opvulling en passen (het meest algemene geval)
Nu zullen we een stapsgewijze convolutie toepassen op ons eerder beschreven opgevulde voorbeeld en de convolutie berekenen waarbij p = 1, s = 2
Eerder toen we strides = 1
, werd ons schuifvenster met 1 positie verplaatst, met strides = s
beweegt het met s
posities (je moet s^2
elementen minder berekenen. Maar in ons geval kunnen we een snelkoppeling nemen en geen Omdat we de waarden voor s = 1
al hebben berekend, kunnen we in ons geval elk tweede element gewoon pakken.
Dus als de oplossing het geval is van s = 1
was
in het geval van s = 2
is dit:
Controleer de posities van waarden 14, 2, 12, 6 in de vorige matrix. De enige wijziging die we in onze code moeten doorvoeren, is om de stappen van 1 naar 2 te wijzigen voor de breedte- en hoogteafmeting (2-de, 3-de).
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 2, 2, 1], "SAME"))
with tf.Session() as sess:
print sess.run(res)
Trouwens, er is niets dat ons ervan weerhoudt om verschillende passen voor verschillende dimensies te gebruiken.