2017-07-28 4 views
0

私はTheanoとCNTKの性能を非常に簡単なタスク、GPUのマトリックスベクトルプロダクトと比較したいと考えています。私はTheano 0.9.0とCNTK 2.0を使用しています。TheanoとCNTKのベンチマークGPUの単純マトリクスベクトルプロダクト

ホストからデバイスへのデータ転送に使用される時間を除いて、デバイスでの計算に費やされる時間を測定したい、またはその逆を測定したいとします。 figure (timings theano vs cntk) (Nは繰り返し数であるD、行列のサイズは、10000に設定されました。)

質問1:

それ私が得た

結果はこのようなものでしたいくつかの準備(計算グラフをコンパイルするために使用される)のような時間は、CNTKの場合のmat-vec製品の最初の実行に含まれているようです。 CNTKで準備と実行を分割する方法はありますか?それはTheanoの場合のようですか?

質問2:

私はTheanoに使用されるが、CNTKで全く新しいので、私はCNTKコードはTheanoのコードと同等であればかなりわからないのです。 prod.eval()がnumpy.ndarrayを返すので、CNTKコードのforループの操作が実際にデバイスに閉じ込められているかどうかは特にわかりません。何か不足していますか?タイミングを測定するために使用される

コード:

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

なぜGPUカーネルの実行時間を直接測定するために 'nvprof'を使用しないのですか?単にcuBLASと呼ぶだけで大きな違いはないとは思うが、 – Kh40tiK

答えて

0

短い答えはノー、操作がデバイス上で囲まれていないされています。何が起こるのか:eval()を呼び出すと、可能であればデバイス上で操作を行うC++への呼び出しが行われます。 C++から出るとき、CNTKはas_numpyキーワード引数の値がデフォルトでTrueかどうかをチェックします。 as_numpyがTrueの場合、gpuバッファは熱心にNumPy配列にコピーされます。

prod.eval(as_numpy = False)を呼び出すと、evalを呼び出すと、gpuバッファがNumPy配列に変換されません。結果を単純な古い変数に代入すると、CNTK Valueオブジェクトが得られることがわかります。しかし、コードで.valueの属性をb_cに割り当てます。この割り当ては、valueプロパティの設定者によって処理されます(この回答は他の読者のためにはthis linkが含まれています)。 CNTKはデバイス上でこの割り当てを行いますが、それは伝えにくいです。これは、NumPy配列を返す.valueプロパティゲッターを呼び出す場合にb_c.valueを検査しようとすると発生するためです。結果はNumPy配列のようですが、これはちょうどb_c.valueを使用した結果です。それ以外の変数は、CNTK Valueオブジェクトであることがわかります。この場合も、eval(as_numpy=False)を実行したときに適用されます。

さらに、CNTKはタイムスタンプを使用するため、上記の評価はGPUで1回しか発生しません。 evalのに後続のすべてのN-1呼び出しは()ちょうどあなたがas_numpy=Falseを指定しない限り(numpyのへの変換が、しかし毎回起こるのだろうあなたに同じ値オブジェクトを返します。

最後に、私はこのことから、多くの有意義な教訓を学ぶことを期待しないでくださいベンチマーク:CNTKとTheanoの両方が同じCuDNN実装を呼び出していますが、CNTKの利点は、(a)高水準ライブラリが付属していること(b)ユーザーがバッチについて心配する必要がなく、 (c)効率的な再帰ネットワーク(d)効率的なi/o(e)容易な分散トレーニング。

あなたのセットアップ時間に関する質問に答えてください。あなたが関数を一度評価してコンパイルすれば、私の理解になります。 CNTKには実際に2種類のコンパイルがあります。初めての場合はevalです。フォワードパスをコンパイルします。後でfunction.gradを実行すると、evalコンパイルが破棄され、コンパイルされて順パスと逆パスの両方を処理できるようになります。

関連する問題