Conv1D(过滤器= N,kernel_size = K)与Dense(output_dim = N)层

时间:2019-01-22 10:19:15

标签: tensorflow keras deep-learning convolution

我有一个大小为[batch_size=B, sequence_length=L, dim=K]的输入张量T。应用N个滤波器和内核大小K的一维卷积是否与应用具有N个输出维的密集层相同?

例如在Keras中:

Conv1D(filters=N, kernel_size=K)

vs

Dense(units=N)

注意Conv1D,我将张量T整形为[batch_size*sequence_length, dim=K, 1]以进行卷积。

两者均导致可学习的权重为20,480 + 256(偏差)。但是使用Conv1D最初对我的学习要快得多。我没有看到Dense()在这种情况下有什么不同,我想使用Dense()方法以降低vram消耗并且不改变张量。


后续澄清:

两个答案提供了执行一维卷积的两种不同方法。以下方法有何不同?:

方法1:

- Reshape input to [batch_size * frames, frame_len]
- convolve with Conv1D(filters=num_basis, kernel_size=frame_len)
- Reshape the output of the convolution layer to [batch_size, frames, num_basis]

方法2:

- Convolve with Conv1D(filters=num_basis, kernel_size=1) on Input=[batch_size, frames, frame_len]. No input reshaping.
- No need to reshape output, it's already [batch_size, frames, num_basis]

我的理解是,它是相同的操作(它们具有相同的#parameters)。但是,使用方法1可以更快地收敛。

2 个答案:

答案 0 :(得分:2)

要实现与使用Conv1d层的密集层相同的行为,您需要确保Conv1d的任何输出神经元都连接到每个输入神经元。

对于大小为[batch_size,L,K]的输入,您的Conv1d需要具有大小为L的内核,并且需要输出神经元的过滤器数量尽可能多。要了解原因,让我们回到一维卷积或时间卷积的定义。

Conv1d图层的参数由一组可学习的过滤器组成。每个过滤器通常在时间上都很小,并延伸到输入体积的整个深度。例如,在您的问题中,典型的过滤器的大小可能为5xK(即序列的5步,而K为K,因为您的输入的深度为K)。在前进过程中,我们将每个过滤器滑过(更确切地说是卷积)跨输入体积序列的不同步骤,并计算过滤器条目与输入在任何位置之间的点积。滑动滤镜时,我们将生成一维激活图,该图会给出该滤镜在每个空间位置的响应。

现在,如果您的过滤器的尺寸为LxK,则可以轻松地看到,只有一个可能的空间位置(过滤器的尺寸与序列的尺寸相同)将是整个输入量与每个滤波器的权重LxK。现在,构成Conv1d的不同过滤器的行为与构成Dense层的单元相同:它们已完全连接到您的输入。

您可以使用以下代码验证此行为:

import tensorflow as tf
import numpy as np

l = 10
k = 2
n = 5

x = tf.placeholder(tf.float32, [None, l, k])
c = tf.layers.conv1d(inputs=x, strides=1, filters=n, kernel_size=l, kernel_initializer=tf.ones_initializer())
d = tf.layers.dense(inputs=tf.reshape(x, [-1, l*k]), units=n, kernel_initializer=tf.ones_initializer())

batch_size = 10

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    r_conv, r_dense = sess.run([c, d], {x: np.random.normal(size=[batch_size, l, k])})

print(r_conv.shape, r_dense.shape)
#(10, 1, 5) (10, 5)

print(np.allclose(r_conv.reshape([batch_size, -1]), r_dense.reshape([batch_size, -1])))
#True

对于相同的初始化,输出确实相等。

关于速度,我认为Conv1d更快且占用更多VRAM的主要原因之一是由于您的重塑:您实际上在增加批处理大小,以牺牲内存为代价来改善并行化。


后续澄清后进行编辑:

也许我误解了你的问题。方法1和方法2相同,但与将密集层应用于Input = [B,LxK]相同。

在这里,将输出连接到完整尺寸K,然后对序列的每个时间步使用相同的权重,这意味着这两种方法仅完全连接到框架,而不完全连接到序列。实际上,这等效于[BxL,K]上的密集层。

您可以使用以下代码验证此行为:

l = 10
k = 2
n = 5

x = tf.placeholder(tf.float32, [None, l, k])
c2 = tf.layers.conv1d(inputs=x, strides=1, filters=n, kernel_size=1, kernel_initializer=tf.ones_initializer())
c3 = tf.layers.conv1d(inputs=tf.reshape(x, [-1, k, 1]), strides=1, filters=n, kernel_size=k, kernel_initializer=tf.ones_initializer())
d2 = tf.layers.dense(inputs=tf.reshape(x, [-1, k]), units=n, kernel_initializer=tf.ones_initializer())

batch_size = 10

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    r_d2, r_c2, r_c3 = sess.run([d2, c2, c3], {x: np.random.normal(size=[batch_size, l, k])})
    r_d2 = r_d2.reshape([10, 10, 5])
    r_c3 = r_c3.reshape([10, 10, 5])

print(r_d2.shape, r_c2.shape, r_c3.shape)
#(10, 10, 5) (10, 10, 5) (10, 10, 5)

print(np.allclose(r_d2, r_c2))
#True
print(np.allclose(r_d2, r_c3))
#True
print(np.allclose(r_c2, r_c3))
#True

关于速度,这一定是因为方法1中只有一个点积才能计算结果,而方法2中的L加上其他运算。

答案 1 :(得分:0)

在两种情况下,Conv1D和Dense的运算具有相同的结果:

  1. 对于形状为(batch, length, channels)的3D输入,这两个是相同的:

    • Conv1D(filters=N, kernel_size =1)
    • Dense(units=N)

这使用Dense层来模拟与kernel_size=1的卷积。
这样kernel_size > 1不能达到更大的内核大小(Dense)。

  1. 对于具有输入(batch, length, features)的Conv1D和具有(batch, length * features)输入的Dense,这两个是相同的:

    • Conv1D(filters=N, kernel_size=length, padding='valid')
    • Dense(units=N)

这使用Conv1D层来模拟完全连接的层。
注意,尽管两个层可能具有相同数量的参数,但是卷积是完全不同的操作。如果您更改填充,您将在Conv1D中执行更多的乘法运算,并获得不同的输出。

关于速度,DenseConv1D是不同的算法,尽管在上述两种情况下它们的结果相同。不同的算法实现方式有所不同,因此不要指望完全相同的速度。

相关问题