數學背後的 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)