私は、10クラスの画像分類タスクのために単純な多層パーセプトロンを訓練しようとしています。これは、Udacity Deep-Learningコースの課題の一部です。より正確には、タスクはさまざまなフォントからレンダリングされた文字を分類することです(データセットはnotMNISTと呼ばれます)。
最終的に私が作成したコードは非常に単純に見えますが、トレーニング中に常に非常に低いGPU使用率が得られます。 GPU-Zで負荷を測定すると、25〜30%しか表示されません。
ここに私の現在のコードがあります:
graph = tf.Graph()
with graph.as_default():
tf.set_random_seed(52)
# dataset definition
dataset = Dataset.from_tensor_slices({'x': train_data, 'y': train_labels})
dataset = dataset.shuffle(buffer_size=20000)
dataset = dataset.batch(128)
iterator = dataset.make_initializable_iterator()
sample = iterator.get_next()
x = sample['x']
y = sample['y']
# actual computation graph
keep_prob = tf.placeholder(tf.float32)
is_training = tf.placeholder(tf.bool, name='is_training')
fc1 = dense_batch_relu_dropout(x, 1024, is_training, keep_prob, 'fc1')
fc2 = dense_batch_relu_dropout(fc1, 300, is_training, keep_prob, 'fc2')
fc3 = dense_batch_relu_dropout(fc2, 50, is_training, keep_prob, 'fc3')
logits = dense(fc3, NUM_CLASSES, 'logits')
with tf.name_scope('accuracy'):
accuracy = tf.reduce_mean(
tf.cast(tf.equal(tf.argmax(y, 1), tf.argmax(logits, 1)), tf.float32),
)
accuracy_percent = 100 * accuracy
with tf.name_scope('loss'):
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
# ensures that we execute the update_ops before performing the train_op
# needed for batch normalization (apparently)
train_op = tf.train.AdamOptimizer(learning_rate=1e-3, epsilon=1e-3).minimize(loss)
with tf.Session(graph=graph) as sess:
tf.global_variables_initializer().run()
step = 0
Epoch = 0
while True:
sess.run(iterator.initializer, feed_dict={})
while True:
step += 1
try:
sess.run(train_op, feed_dict={keep_prob: 0.5, is_training: True})
except tf.errors.OutOfRangeError:
logger.info('End of Epoch #%d', Epoch)
break
# end of Epoch
train_l, train_ac = sess.run(
[loss, accuracy_percent],
feed_dict={x: train_data, y: train_labels, keep_prob: 1, is_training: False},
)
test_l, test_ac = sess.run(
[loss, accuracy_percent],
feed_dict={x: test_data, y: test_labels, keep_prob: 1, is_training: False},
)
logger.info('Train loss: %f, train accuracy: %.2f%%', train_l, train_ac)
logger.info('Test loss: %f, test accuracy: %.2f%%', test_l, test_ac)
Epoch += 1
これまでに試したことは次のとおりです。
入力パイプラインを単純なfeed_dict
からtensorflow.contrib.data.Dataset
に変更しました。私が理解している限りでは、入力の効率を管理することになっています。別のスレッドでデータをロードします。したがって、入力に関連するボトルネックはありません。
ここで提案されているようにトレースを収集しました: https://github.com/tensorflow/tensorflow/issues/1824#issuecomment-225754659 ただし、これらのトレースは '本当に面白いものは何でも見せてください。列車のステップの90%以上がmatmul操作です。
バッチサイズの変更。 128から512に変更すると、負荷は〜30%から〜38%に増加し、さらに2048に増加すると、負荷は〜45%になります。 6Gb GPUメモリがあり、データセットは単一チャンネルの28x28画像です。私は本当にそのような大きなバッチサイズを使用することになっていますか?さらに増やす必要がありますか?
一般的に、低負荷について心配する必要がありますか?それは本当にトレーニングが非効率的であるという兆候ですか?
バッチ内の128個の画像を含むGPU-Zスクリーンショットを次に示します。各エポック後にデータセット全体の精度を測定すると、100%に時々スパイクする低負荷を確認できます。
MNISTサイズのネットワークは非常に小さく、それらに対して高いGPU(またはCPU)効率を達成するのは困難です。30%はアプリケーションにとって珍しいことではないと思います。バッチサイズを大きくすると計算効率が向上します。つまり、1秒あたりのサンプル数を増やすことができますが、統計効率も低下します。したがって、トレードオフです。あなたのような小さなキャラクターモデルの場合、統計効率は100を超えると非常に急速に低下するため、おそらくトレーニング用のバッチサイズを大きくする価値はありません。推論には、可能な限り最大のバッチサイズを使用する必要があります。
NVidia GTX 1080では、MNISTデータベースで畳み込みニューラルネットワークを使用する場合、GPUの負荷は〜68%です。
単純な非畳み込みネットワークに切り替えると、GPUの負荷は最大20%になります。
これらの結果を複製するには、チュートリアルで連続してより高度なモデルを作成します Francis CholletによるKerasでのオートエンコーダーの構築 .