Tensorflow占位符vs Tensorflow常数vs numpy数组

时间:2019-05-18 04:03:46

标签: python tensorflow machine-learning deep-learning conv-neural-network

我试图在具有卷积层的卷积神经网络上运行前向传递,然后是池化层,最后是整流线性单元(ReLU)激活层。有关输入数据和卷积层过滤器的详细信息如下:

  • X:具有形状[N, H, W, C]的4维输入数据,其中N = 60000是批处理大小,H = 32是输入图像的高度,{{1} }是输入图像的宽度,W = 32是输入图像中的通道数。
  • C = 1:形状为W的4维卷积滤波器,其中[F, F, C, Cout]是滤波器的高度和宽度,F = 3是输入中的通道数图片,C = 1是输出图片中的频道数。

有三种方法可以做到这一点。

方法1:不使用Cout = 6tf.constant()

tf.placeholder()

方法2:使用import numpy as np import tensorflow as tf X = np.random.random([60000, 32, 32, 1]) W = np.random.random([3, 3, 1, 6]) C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID") P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID") A = tf.nn.relu(P) with tf.Session() as sess: result = sess.run(A) # Takes 14.98 seconds

tf.constant()

方法3:使用import numpy as np import tensorflow as tf X = tf.constant(np.random.random([60000, 32, 32, 1]), dtype=tf.float64) W = tf.constant(np.random.random([3, 3, 1, 6]), dtype=tf.float64) C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID") P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID") A = tf.nn.relu(P) with tf.Session() as sess: result = sess.run(A) # Takes 14.73 seconds

tf.placeholder()

方法3(使用import numpy as np import tensorflow as tf x = np.random.random([60000, 32, 32, 1]) w = np.random.random([3, 3, 1, 6]) X = tf.placeholder(dtype=tf.float64, shape=[None, 32, 32, 1]) W = tf.placeholder(dtype=tf.float64, shape=[3, 3, 1, 6]) C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID") P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID") A = tf.nn.relu(P) with tf.Session() as sess: result = sess.run(A, feed_dict={X:x, W:w}) # Takes 3.21 seconds )的运行速度比方法1和方法2快4-5倍。 所有这些实验都是在NVIDIA GeForce GTX 1080 GPU上进行的。

问题是,为什么与方法1和方法2相比,仅在方法3中使用tf.placeholder()就能获得近4-5倍的加速? 在其基础实现中,tf.placeholder()在做什么,从而使其具有如此出色的性能?

2 个答案:

答案 0 :(得分:2)

我分别得到12秒,12秒和1秒。但是。

您的方法不考虑设置时间:图形构造,内存分配,图形优化等。我自己承担了一点,以便进一步进行实验。即,我为每种方法对session.run()进行了10次调用,不仅测量了总时间,还测量了每个单独调用的时间。以下是这些实验的结果。有趣的是第一次调用所花的执行时间。

%%time
import numpy as np
import tensorflow as tf
​
X = np.random.random([60000, 32, 32, 1])
W = np.random.random([3, 3, 1, 6])
​
C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)
​
with tf.Session() as sess:
    for i in range(10):
        ts = time.time()
        result = sess.run(A)  
        te = time.time()
        print('%2.2f sec' % (te-ts))



10.44 sec
0.24 sec
0.23 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
CPU times: user 17 s, sys: 7.56 s, total: 24.5 s
Wall time: 13.8 s

2:

%%time
import numpy as np
import tensorflow as tf
​
X = tf.constant(np.random.random([60000, 32, 32, 1]), dtype=tf.float64)
W = tf.constant(np.random.random([3, 3, 1, 6]), dtype=tf.float64)
​
C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
A = tf.nn.relu(P)
​
with tf.Session() as sess:
    for i in range(10):
        ts = time.time()
        result = sess.run(A) 
        te = time.time()
        print('%2.2f sec' % (te-ts))



10.53 sec
0.23 sec
0.23 sec
0.24 sec
0.23 sec
0.23 sec
0.23 sec
0.23 sec
0.23 sec
0.26 sec
CPU times: user 17 s, sys: 7.77 s, total: 24.8 s
Wall time: 14.1 s

3:

    %%time
    import numpy as np
    import tensorflow as tf 
    ​
    x = np.random.random([60000, 32, 32, 1])
    w = np.random.random([3, 3, 1, 6])
    ​
    X = tf.placeholder(dtype=tf.float64, shape=[None, 32, 32, 1])
    W = tf.placeholder(dtype=tf.float64, shape=[3, 3, 1, 6])
    ​
    C = tf.nn.conv2d(X, W, strides=[1,1,1,1], padding="VALID")
    P = tf.nn.avg_pool(C, ksize=[1,2,2,1], strides=[1,2,2,1], padding="VALID")
    A = tf.nn.relu(P)
    ​
    with tf.Session() as sess:
        for i in range(10):
            ts = time.time()
            result = sess.run(A, feed_dict={X:x, W:w})  
            te = time.time()
            print('%2.2f sec' % (te-ts))

0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
0.45 sec
CPU times: user 2.81 s, sys: 2.31 s, total: 5.12 s
Wall time: 5.02 s

如您所见,对于前两种方法,第一次调用sess.run确实需要花费一些时间(10秒),而方法3始终需要0.45秒。但是,前两次的第二次和以后的运行速度只有0.23秒,是后者的两倍。

答案 1 :(得分:2)

对@ y.selivonchyk的宝贵实验大喊大叫,但是我感觉答案并没有详细说明为什么会出现这些结果。

我认为,这与其说是“占位符”不是“好”,不如说是另外两种方法是个坏主意。

我假设1)和2)实际上是相同的,并且1)在幕后将数组转换为常数-至少这可以解释相同的行为。

原因1)和2)之所以花很长时间,是因为constant被显式地嵌入到计算图中。由于它们是非常大的张量,因此可以解释为什么构建图需要这么长时间。但是,一旦构建了图形,后续的运行就会更快,因为其中的所有内容都“包含”在其中。通常,您应该尽量避免在图形本身中包含大量数据-理想情况下,它应该只是一组计算指令(即Tensorflow ops)。

使用3),图形的构建速度要快得多,因为我们没有在其中嵌入巨大的数组,而只是将其作为符号占位符。但是,执行速度比1)和2)慢,因为每次都需要将该值馈入占位符(这也意味着如果在其中运行,则必须将数据传输到GPU上)。