這幾天在看代碼,然後看網上關於一維卷積介紹的文檔很多,但是對於tf.nn.conv1d 矩陣運算過程幾乎沒有介紹,這裏我就將剛弄懂的寫出來,希望能幫到大家理解這個函數,也爲了讓自己以後能更好的查閱~~
conv1d(value, filters, stride, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
value: A 3D Tensor
. Must be of type float16
or float32
.
被卷積的矩陣:可以當成是[batch, sample, feature]
filters: A 3D Tensor
. Must have the same type as input
.
卷積核:形狀 [filter_width, in_channels, out_channels]
stride: An integer
. The number of entries by which the filter is moved right at each step.
步長:卷積核滑動步長
padding: ‘SAME’ or ‘VALID’
same和valid可以參考這篇文檔:深度學習面試題09:一維卷積(Full卷積、Same卷積、Valid卷積、帶深度的一維卷積)
廢話不多說,咱們今天不講原理,只講其中矩陣運算過程~ 幫助大家理解~
卷積核filter 的形狀:[filter_width, in_channels, out_channels]
- in_channels 必須等於被卷積的矩陣的第3維數,即列數
- out_channels 表示卷積核數目,類似於圖像裏,一個卷積核得到一個輸出,這裏有|out_channels |個卷積核,則輸出必定有|out_channels| 列。
- filter_width 表示與 value 進行卷積的 個數/每次 (這裏不懂可以以往後看例子)
例一:filter_width=1時:
舉例,令被一維卷積的value維度是:(2,3,4)
令卷積核的filter維度是:(1,4,5)
即 [filter_width, in_channels, out_channels] = (1,4,5)
filter_width =1 說明每次對value裏的一行進行卷積:
以value第一個batch裏爲例:
第一個batch中第1行的計算過程是:
第一個batch中第2行的計算過程是:
tf.nn.conv1d 一次對一個batch進行卷積處理,batch之間互不影響。
以此類推,完成一個batch的計算後,第二個batch的計算過程也如此。
所以value整體卷積的結果是:
以上filter_width=1時的代碼如下:
sess= tf.Session()
a = tf.Variable(np.random.randint(1,3,(2,3,4)))
filt = tf.get_variable("weights", initializer=np.random.randint(1,3,(1,4,5)))
sess.run(tf.global_variables_initializer())
print(sess.run(a))
print('\n')
print(sess.run(filt))
filt_input = tf.nn.conv1d(tf.to_float(a), tf.to_float(filt), 1, "VALID", name="embedded_input")
print(sess.run(filt_input))
由於filter_width=1時,valid和same的計算沒啥區別,這裏不講,在filter_width>1時介紹。
例二:filter_width>1時:
舉例,令被一維卷積的value維度是:(2,3,4)
令卷積核的filter維度是:(2,4,5)
即 [filter_width, in_channels, out_channels] = (2,4,5)
filter_width =2 說明每次對value裏的2行進行卷積:
1. VALID 時:
tf.nn.conv1d 一次對一個batch進行卷積處理,batch之間互不影響 ,所以這裏以第一個batch中的計算進行說明:
咱們這裏設置的filter_width =2, 說明每次對value裏的2行進行卷積。
則:
- 第一次計算取1,2行進行卷積,第1行和卷積核的第一大塊,也就是(0,:,:)進行矩陣乘法,第2行和卷積核的第二大塊,也就是(1,:,:)進行矩陣乘法,結果相加得到第一次卷積結果:
- 第二次計算取2,3行進行卷積,第2行和卷積核的第一大塊,也就是(0,:,:)進行矩陣乘法,第3行和卷積核的第二大塊,也就是(1,:,:)進行矩陣乘法,結果相加得到第二次卷積結果:
以此類推,完成一個batch的計算後,第二個batch的計算過程也如此。
所以value整體VALID卷積的結果是:
2. SAME 時:
剛纔在VALID中,第一次計算取value的1,2行進行卷積,第二次取value的2,3行進行卷積。
也就是說VALID中,filter_width =2時每次取兩行來卷積,然後只要value的1,2,3行全部都處理完了就OK。也就是一共進行了兩次卷積,得到的卷積的結果,每個batch的行數爲兩行。
而這裏的 “SAME”,爲了保證卷積後的結果每個batch的行數和原先VALUE行數相等(原先Value中每個batch有3行),所以還需要再在VALID基礎上做一次卷積!
以第一個batch爲例,整體運算過程是:
- 第一次計算取1,2行進行卷積,第1行和卷積核的第一大塊,也就是(0,:,:)進行矩陣乘法,第2行和卷積核的第二大塊,也就是(1,:,:)進行矩陣乘法,相加得到第一次卷積結果:
- 第二次計算取2,3行進行卷積,第2行和卷積核的第一大塊,也就是(0,:,:)進行矩陣乘法,第3行和卷積核的第二大塊,也就是(1,:,:)進行矩陣乘法,相加得到第二次卷積結果:
- 第三次計算按理來說,每次取兩行來卷積,這裏就要取3,4行進行卷積,但是這裏VALUE中沒有第4行,所以只需要第3行和卷積核的第一大塊,也就是(0,:,:)進行矩陣乘法,得到第三次卷積結果:
這樣就保證了計算結果是3行。
以此類推,完成一個batch的計算後,第二個batch的計算過程也如此。
所以value整體SAME卷積的結果是:
這裏VALID和SAME代碼如下:
sess= tf.Session()
a = tf.Variable(np.random.randint(1,3,(2,3,4)))
filt = tf.get_variable("weights", initializer=np.random.randint(1,3,(2,4,5)))
sess.run(tf.global_variables_initializer())
print(sess.run(a))
print('\n')
print(sess.run(filt))
filt_input = tf.nn.conv1d(tf.to_float(a), tf.to_float(filt), 1, "VALID", name="embedded_input")
filt_input2 = tf.nn.conv1d(tf.to_float(a), tf.to_float(filt), 1, "SAME", name="embedded_input")
print(sess.run(filt_input))
print(sess.run(filt_input2))
個人覺得filter_width=1的時候更適合做embedding~~