我想比较Theano和CNTK在一个非常简单的任务上的性能:GPU上的矩阵矢量产品。我正在使用Theano 0.9.0和CNTK 2.0。
我想测量仅在设备上计算所用的时间,不包括从主机到设备的数据传输时间,反之亦然。
我得到的结果是这样的: figure (timings theano vs cntk) (N是重复次数.D,矩阵的大小,设置为10000.)
问题1:
似乎用于某些准备的时间(编译计算图?)包含在CNTK案例中第一次执行mat-vec产品时。 有没有办法在CNTK中分割准备和执行,就像在Theano案件中一样?
问题2:
我习惯了Theano,但CNTK全新,所以我不太确定CNTK代码是否等同于Theano代码。 我特别不确定CNTK代码的for循环中的操作是否真的包含在设备中,因为prod.eval()返回一个numpy.ndarray。我错过了什么吗?
用于衡量时间的代码:
import numpy as np
import time
# theano
def test_matVecDot_theano(D, N):
import theano
import theano.tensor as T
A_cpu = np.random.normal(size=[D,D]).astype(np.float32)
x_cpu = np.random.normal(size=[D]).astype(np.float32)
A_gpu = theano.shared(A_cpu)
x_gpu = theano.shared(x_cpu)
b_gpu = theano.shared(x_cpu)
b_gpu_new = T.dot(A_gpu,x_gpu)
fnc = theano.function(inputs=[], outputs=None, updates=[(b_gpu, b_gpu_new)], allow_input_downcast=True)
tic = time.time()
for i in range(N):
fnc()
toc = time.time()
print("time_theano:",toc-tic)
# cntk
def test_matVecDot_CNTK(D, N):
import cntk as C
A_cpu = np.random.normal(size=[D,D]).astype(np.float32)
x_cpu = np.random.normal(size=[D,1]).astype(np.float32)
A_c = C.Parameter(init=A_cpu, dtype=np.float32)
x_c = C.Parameter(init=x_cpu, dtype=np.float32)
b_c = C.Parameter(init=x_cpu, dtype=np.float32)
prod = C.times(A_c, x_c)
tic = time.time()
for i in range(N):
b_c.value = prod.eval() # is this operation enclosed in the device?
toc = time.time()
print("time_cntk:",toc-tic)
答案 0 :(得分:0)
简短的回答是否定,操作未包含在设备上。这是发生的事情:当你调用eval()时,调用转到C ++,如果可能的话,它会对设备进行操作。当退出C ++时,CNTK会检查as_numpy
关键字参数的值是否为True,默认为True。当as_numpy
为True时,急切地将gpu缓冲区复制到NumPy数组。
如果你调用prod.eval(as_numpy = False),那么对eval
的调用将不会将gpu缓冲区转换为NumPy数组。如果将结果分配给普通旧变量,则可以看到您获得了CNTK Value对象。但是,在您的代码中,您分配了.value
的{{1}}属性。这个赋值由b_c
属性的setter处理(因为这个答案有点太技术性,我为了其他读者而包括this link)。 CNTK在设备上执行此任务,但很难说。这是因为如果您正在调用value
属性getter来检查b_c.value
,那么它将为您提供一个NumPy数组。所以看起来结果是NumPy数组,但这只是使用.value
的结果。任何其他变量都可以让你看到它是CNTK Value对象。同样,所有这些都适用于b_c.value
。
此外,CNTK使用时间戳,因此上述评估仅在GPU上发生一次。对eval()的所有后续eval(as_numpy=False)
调用只会返回相同的值对象(除非您指定N-1
,否则每次都会转换为Numpy。
最后,我不希望从这个基准测试中学到很多有意义的经验教训:CNTK和Theano都在调用相同的CuDNN实现,CNTK的优势更多地围绕更高层次的事情,例如(a)带有高 - 级别库(b)除少数专门操作外,用户不必担心批次和顺序轴(c)高效的循环网络(d)高效的i / o(e)易于分布式培训。
并回答你关于设置时间的问题:我的理解是你只需要对函数进行一次评估,然后编译它。 CNTK实际上有两种编译:如果你第一次编译正向传递你只是as_numpy=False
。如果你以后执行eval
它将丢弃eval编译并再次编译它,以便它可以处理前向和后向传递。