tensorflow
Matematica dietro la convoluzione 2D con esempi avanzati in TF
Ricerca…
introduzione
La convoluzione 2D è calcolata in un modo simile per calcolare la convoluzione 1D : si scorre il kernel sull'input, si calcolano le moltiplicazioni elementali e si sommano. Ma invece del tuo kernel / input è un array, qui sono matrici.
Nessuna spaziatura, passi = 1
Questo è l'esempio più semplice, con i calcoli più semplici. Supponiamo che il tuo input
e il tuo kernel
siano:
Al tuo kernel riceverai il seguente output: , che viene calcolato nel modo seguente:
- 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
La funzione conv2d di TF calcola le convoluzioni in lotti e utilizza un formato leggermente diverso. Per un input è [batch, in_height, in_width, in_channels]
per il kernel è [filter_height, filter_width, in_channels, out_channels]
. Quindi dobbiamo fornire i dati nel formato corretto:
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')
Successivamente la convoluzione è calcolata con:
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)
E sarà equivalente a quello che abbiamo calcolato a mano.
Alcuni padding, passi = 1
Padding è solo un nome di fantasia: racchiudi la tua matrice di input con una certa costante. Nella maggior parte dei casi la costante è zero ed è per questo che la gente lo chiama zero padding. Quindi se vuoi usare un padding di 1 nel nostro input originale (controlla il primo esempio con padding=0, strides=1
), la matrice sarà simile a questa:
Per calcolare i valori della convoluzione fai lo stesso scorrimento. Si noti che nel nostro caso molti valori nel mezzo non devono essere ricalcolati (saranno uguali a quelli dell'esempio precedente. Inoltre non mostrerò tutti i calcoli qui, perché l'idea è diretta.
dove
- 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 non supporta un padding arbitrario nella funzione conv2d , quindi se hai bisogno di un padding che non è supportato, usa tf.pad () . Fortunatamente per il nostro input il padding "SAME" sarà uguale a padding = 1. Quindi, nel nostro esempio precedente, non dobbiamo cambiare quasi nulla:
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)
Puoi verificare che la risposta sia la stessa calcolata a mano.
Imbottitura e falcate (il caso più generale)
Ora applicheremo una convoluzione scalata al nostro esempio imbottito precedentemente descritto e calcoleremo la convoluzione dove p = 1, s = 2
Precedentemente, quando abbiamo usato strides = 1
, la nostra finestra slitta si sposta di 1 posizione, con strides = s
si muove per le posizioni s
(devi calcolare s^2
elementi in meno. Ma nel nostro caso possiamo prendere una scorciatoia e non eseguire alcuna Computations affatto. Perché abbiamo già calcolato i valori per s = 1
, nel nostro caso possiamo semplicemente prendere ogni secondo elemento.
Quindi se la soluzione è caso di s = 1
era
in caso di s = 2
sarà:
Controllare le posizioni dei valori 14, 2, 12, 6 nella matrice precedente. L'unico cambiamento che dobbiamo eseguire nel nostro codice è quello di modificare le falcate da 1 a 2 per la larghezza e la quota (2-nd, 3-rd).
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 2, 2, 1], "SAME"))
with tf.Session() as sess:
print sess.run(res)
A proposito, non c'è nulla che ci impedisca di usare passi diversi per dimensioni diverse.