數學背後的 1D 卷積與 TF 中的高階示例
`要手動計算 1D 卷積,可以在輸入上滑動核心,計算逐元素乘法並求它們。
最簡單的方法是填充= 0,stride = 1
因此,如果你的 input = [1, 0, 2, 3, 0, 1, 1]
和 kernel = [2, 1, 3]
卷積的結果是 [8, 11, 7, 9, 4]
,這是通過以下方式計算的:
- 8 = 1 * 2 + 0 * 1 + 2 * 3
- 11 = 0 * 2 + 2 * 1 + 3 * 3
- 7 = 2 * 2 + 3 * 1 + 0 * 3
- 9 = 3 * 2 + 0 * 1 + 1 * 3
- 4 = 0 * 2 + 1 * 1 + 1 * 3
TF 的 conv1d 函式分批計算卷積,所以為了在 TF 中執行此操作,我們需要以正確的格式提供資料(doc 解釋輸入應該在 [batch, in_width, in_channels]
中,它還解釋了核心應該是什麼樣子)。所以
import tensorflow as tf
i = tf.constant([1, 0, 2, 3, 0, 1, 1], dtype=tf.float32, name='i')
k = tf.constant([2, 1, 3], dtype=tf.float32, name='k')
print i, '\n', k, '\n'
data = tf.reshape(i, [1, int(i.shape[0]), 1], name='data')
kernel = tf.reshape(k, [int(k.shape[0]), 1, 1], name='kernel')
print data, '\n', kernel, '\n'
res = tf.squeeze(tf.nn.conv1d(data, kernel, 1, 'VALID'))
with tf.Session() as sess:
print sess.run(res)
這將給你我們先前計算的相同答案:[ 8. 11. 7. 9. 4.]
用填充卷積
填充只是一種奇特的方式來告訴追加並在輸入前新增一些值。在大多數情況下,此值為 0,這就是為什麼大多數人將其命名為零填充。TF 支援’VALID’和’SAME’零填充,對於任意填充,你需要使用 tf.pad()
。 ‘VALID’填充意味著根本沒有填充,這意味著輸出將具有相同的輸入大小。讓我們在同一個例子中用 padding=1
計算卷積(請注意,對於我們的核心,這是’SAME’填充)。要做到這一點,我們只需在開頭/結尾新增 1 個零的陣列:input = [0, 1, 0, 2, 3, 0, 1, 1, 0]
。
在這裡你可以注意到你不需要重新計算所有內容:除了第一個/最後一個元素之外,所有元素保持不變:
- 1 = 0 * 2 + 1 * 1 + 0 * 3
- 3 = 1 * 2 + 1 * 1 + 0 * 3
結果是 [1, 8, 11, 7, 9, 4, 3]
與 TF 計算的結果相同:
res = tf.squeeze(tf.nn.conv1d(data, kernel, 1, 'SAME'))
with tf.Session() as sess:
print sess.run(res)
捲入大步
Strides 允許你在滑動時跳過元素。在我們之前的所有示例中,我們滑動了 1 個元素,現在你可以一次滑動 s
元素。因為我們將使用前面的例子,所以有一個技巧:通過 n
元素滑動相當於滑動 1 個元素並選擇每個第 n 個元素。
因此,如果我們將前一個示例與 padding=1
一起使用並將 stride
更改為 2,則只需取前一個結果 [1, 8, 11, 7, 9, 4, 3]
並保留每個 2-nd 元素,這將導致 [1, 11, 9, 3]
。你可以通過以下方式在 TF 中執行此操作:
res = tf.squeeze(tf.nn.conv1d(data, kernel, 2, 'SAME'))
with tf.Session() as sess:
print sess.run(res)