web-dev-qa-db-ja.com

GPUでtensorflowmap_fnを使用する方法はありますか?

形状[a、n]のテンソル[〜#〜] a [〜#〜]があり、別のテンソルでopmy_opを実行する必要があります。 [〜#〜] b [〜#〜]形状[b、n]の結果のテンソル[〜#〜] c [〜#〜]が形状を持つように[a、b]。

言い換えると、[〜#〜] a [〜#〜]eachサブテンサーの場合(A [0]、A 1 、 ... A [n])[〜#〜] b [〜#〜]eachサブテンサーを使用して要素ごとの操作を実行する必要があります。

したがって、結果のテンソルには次のものが含まれます。

[ [ A[0] op B[0] , A[0] op B[1], ... , A[0] op B[b] ],
  [ A[1] op B[0] , A[1] op B[1], ... , A[1] op B[b] ],
  [ ...                                             ],
  [ A[a] op B[0] , A[a] op B[1], ... , A[a] op B[b] ] ]

これを達成するために私が見つけた唯一の方法は、 tf.map_fn をネストして使用することです。

import tensorflow as tf
import time
import numpy as np

a_size = 64
b_size = 256*256
n = 256
A = tf.placeholder(tf.float32,[a_size,n])
B = tf.placeholder(tf.float32,[b_size,n])

def elementwise_op(a,b):
    return tf.reduce_sum(tf.multiply(a,b))

def intermediate_op(sub_a,my_b):
    sample_values = tf.map_fn(lambda x: elementwise_op(sub_a,x),my_b)
    return sample_values

my_op = tf.map_fn(lambda x: intermediate_op(x,B),A)

with tf.Session() as sess:
    a = np.random.Rand(a_size,n)
    b = np.random.Rand(b_size,n)
    start_time = time.time()
    result = sess.run (my_op,feed_dict={A:a,B:b})
    print ("exec time: " ,time.time()-start_time)
    print (result.shape)

上記のコードは正常に実行されますが、GPUを十分に使用していません(nvidia-smiによると、使用率は約15%にすぎません)。実際、のみ CPU!!を使用すると、1桁速く実行されます。 (私の12コアマシンで)GPUを使用して実行すると、GPU使用率が非常に低く(〜15%)、CPUコアのoneで100%になります。 CPUのみで実行すると、すべてのCPUコアで100%の使用率が見られます。

5 CPUの平均タイミングのみが実行されます:11.33s

5つのGPU実行の平均タイミング:111.88s

上記のテストは、公式のTensorflowdockerイメージを使用して実行されました:tensorflow/tensorflow:latest-py3(CPUの場合)およびtensorflow/tensorflow:latest-gpu-py3(GPUの場合)

私の推測では、map_fnは、pythonラムダを介して、everyの反復でCPUとGPUの間でデータを強制的にコピーし、 opのネストされた性質は、それを悪化させるだけです。未回答のSO質問 ここ のコメントは、これが事実であることを示唆しています。

この記事 次のように主張しています:

ラムダ式は、GPU使用率が低い主な理由です。

-

だから私の質問は:map_fnにGPUを使用させる方法はありますか?または、Pythonラムダを回避するには?

あるいは、グラフをGPUで実行するために、上記の結果を達成するための他の(おそらくよりテンソルフロー-y)方法はありますか?

編集:プロファイラーを実行した後(プロファイラーを実行するには、配列のサイズを大幅に縮小する必要がありました。RAM狂ったように)、次の行が私の注意を引きました:

node name     |     output bytes     |      total execution time     | accelerator execution time     |     cpu execution time

Mul                    1.02KB (22.23%, 0.29%),      195.07ms (85.00%, 13.06%),       5.29ms (100.00%, 25.79%),      189.78ms (84.79%, 12.89%)

Sum                      256B (21.41%, 0.07%),      241.48ms (69.08%, 16.17%),        6.01ms (74.21%, 29.29%),      235.47ms (69.01%, 15.99%)

TensorArrayScatterV3      512B (0.64%, 0.15%),      658.31ms (46.87%, 44.09%),        9.19ms (44.80%, 44.80%),      649.12ms (46.90%, 44.08%)

特定の操作は主にCPUで実行されており、その時点では1つのスレッドでのみ実行されているようです。

14
frxbst

tf.map_fn() コンストラクトは、GPUでopsを実行する関数で使用できます。デフォルトでは、TensorFlowはGPUで可能な限り多くの関数を実行しようとし、GPUと互換性のない操作はCPUで実行されます。プログラムでは、elementwise_op()関数全体がGPU互換の操作から構築されているため、反復ごとにCPUとGPU間で追加のコピーを行う必要はありません。

GPU使用率が低い原因を、プログラムフラグメントから特定することは困難です。たとえば、ABが比較的小さく、Pythonからフィードし、すぐに結果をフェッチしている場合、 GPUとの間で初期データをコピーするオーバーヘッドが支配的です。これを追跡する最良の方法は、GPUプロファイラーを使用することです。これは tfprof または-を使用して取得できます。 NVIDIA Visual Profiler

3
mrry