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 = 6
或tf.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()
在做什么,从而使其具有如此出色的性能?
答案 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上)。