形状[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つのスレッドでのみ実行されているようです。
tf.map_fn()
コンストラクトは、GPUでopsを実行する関数で使用できます。デフォルトでは、TensorFlowはGPUで可能な限り多くの関数を実行しようとし、GPUと互換性のない操作はCPUで実行されます。プログラムでは、elementwise_op()
関数全体がGPU互換の操作から構築されているため、反復ごとにCPUとGPU間で追加のコピーを行う必要はありません。
GPU使用率が低い原因を、プログラムフラグメントから特定することは困難です。たとえば、A
とB
が比較的小さく、Pythonからフィードし、すぐに結果をフェッチしている場合、 GPUとの間で初期データをコピーするオーバーヘッドが支配的です。これを追跡する最良の方法は、GPUプロファイラーを使用することです。これは tfprof
または-を使用して取得できます。 NVIDIA Visual Profiler 。