J'essaie de former un simple perceptron multicouche pour une tâche de classification d'images de 10 classes, qui fait partie de la mission du cours Udacity Deep-Learning. Pour être plus précis, la tâche consiste à classer les lettres rendues à partir de différentes polices (le jeu de données s'appelle notMNIST).
Le code avec lequel je me suis retrouvé a l'air assez simple, mais peu importe l'utilisation que je fais toujours du GPU lors de la formation. Je mesure la charge avec GPU-Z et il montre seulement 25-30%.
Voici mon code actuel:
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
Voici ce que j'ai essayé jusqu'à présent:
J'ai changé le pipeline d'entrée de feed_dict
simple à tensorflow.contrib.data.Dataset
. Pour autant que je sache, il est supposé prendre en charge l'efficacité de l'entrée, par exemple. charger des données dans un thread séparé. Il ne devrait donc pas y avoir de goulot d'étranglement associé à l'entrée.
J'ai collecté des traces comme suggéré ici: https://github.com/tensorflow/tensorflow/issues/1824#issuecomment-225754659 Cependant, ces traces ne montrent rien d'intéressant. > 90% de l’étape de train est constituée d’opérations matmul.
Modification de la taille du lot. Lorsque je le modifie de 128 à 512, la charge augmente d'environ 30% à environ 38%. Lorsque je l'augmente encore jusqu'à 2048, la charge passe à environ 45%. J'ai une mémoire GPU de 6 Go et l'ensemble de données est constitué d'un seul canal d'images 28x28. Suis-je vraiment censé utiliser une taille de lot aussi importante? Devrais-je l'augmenter davantage?
De manière générale, devrais-je m'inquiéter de la faible charge, est-ce vraiment un signe que je m'entraîne de manière inefficace?
Voici les captures d'écran de GPU-Z avec 128 images dans le lot. Vous pouvez voir une faible charge avec des pointes occasionnelles pouvant atteindre 100% lorsque je mesure la précision de l'ensemble de données après chaque époque.
Les réseaux de taille MNIST sont minuscules et il est difficile d'obtenir une efficacité élevée du processeur graphique (GPU), je pense que 30% n'est pas inhabituel pour votre application. Vous obtiendrez une efficacité de calcul supérieure avec une taille de lot plus importante, ce qui signifie que vous pourrez traiter plus d'exemples par seconde, mais vous obtiendrez également une efficacité statistique plus faible, ce qui signifie que vous devrez traiter plus d'exemples au total pour atteindre la précision cible. C'est donc un compromis. Pour les modèles de personnages minuscules comme le vôtre, l'efficacité statistique diminue très rapidement après un 100; il n'est donc probablement pas intéressant d'essayer d'augmenter la taille du lot pour la formation. Pour inférence, vous devez utiliser la plus grande taille de lot possible.
Sur mon nVidia GTX 1080, si j'utilise un réseau de neurones à convolution sur la base de données MNIST, la charge du processeur graphique est d'environ 68%.
Si je passe à un réseau simple et non convolutif, la charge du processeur graphique est d'environ 20%.
Vous pouvez reproduire ces résultats en construisant successivement des modèles plus avancés dans le tutoriel Construire des auto-encodeurs dans Keras de Francis Chollet .