J'essaie de pré-extraire les données de formation pour masquer la latence d'E/S. Je voudrais écrire un code Python personnalisé qui charge les données du disque et prétraite les données (par exemple en ajoutant une fenêtre de contexte). En d'autres termes, un thread fait du prétraitement des données et l'autre fait de la formation. Est-ce possible dans TensorFlow?
Mise à jour: j'ai un exemple de travail basé sur l'exemple de @ mrry.
import numpy as np
import tensorflow as tf
import threading
BATCH_SIZE = 5
TRAINING_ITERS = 4100
feature_input = tf.placeholder(tf.float32, shape=[128])
label_input = tf.placeholder(tf.float32, shape=[128])
q = tf.FIFOQueue(200, [tf.float32, tf.float32], shapes=[[128], [128]])
enqueue_op = q.enqueue([label_input, feature_input])
label_batch, feature_batch = q.dequeue_many(BATCH_SIZE)
c = tf.reshape(feature_batch, [BATCH_SIZE, 128]) + tf.reshape(label_batch, [BATCH_SIZE, 128])
sess = tf.Session()
def load_and_enqueue(sess, enqueue_op, coord):
with open('dummy_data/features.bin') as feature_file, open('dummy_data/labels.bin') as label_file:
while not coord.should_stop():
feature_array = np.fromfile(feature_file, np.float32, 128)
if feature_array.shape[0] == 0:
print('reach end of file, reset using seek(0,0)')
feature_file.seek(0,0)
label_file.seek(0,0)
continue
label_value = np.fromfile(label_file, np.float32, 128)
sess.run(enqueue_op, feed_dict={feature_input: feature_array,
label_input: label_value})
coord = tf.train.Coordinator()
t = threading.Thread(target=load_and_enqueue, args=(sess,enqueue_op, coord))
t.start()
for i in range(TRAINING_ITERS):
sum = sess.run(c)
print('train_iter='+str(i))
print(sum)
coord.request_stop()
coord.join([t])
Il s'agit d'un cas d'utilisation courant et la plupart des implémentations utilisent files d'attente de TensorFlow pour dissocier le code de prétraitement du code de formation. Il y a n tutoriel sur la façon d'utiliser les files d'attente , mais les principales étapes sont les suivantes:
Définissez une file d'attente, q
, qui tamponnera les données prétraitées. TensorFlow prend en charge le simple tf.FIFOQueue
qui produit les éléments dans l'ordre dans lequel ils ont été mis en file d'attente, et le plus avancé tf.RandomShuffleQueue
qui produit les éléments de manière aléatoire ordre. Un élément de file d'attente est un tuple d'un ou plusieurs tenseurs (qui peuvent avoir différents types et formes). Toutes les files d'attente prennent en charge les opérations à élément unique (enqueue
, dequeue
) et batch (enqueue_many
, dequeue_many
), Mais pour utiliser les opérations batch, vous devez spécifier le les formes de chaque tenseur dans un élément de file d'attente lors de la construction de la file d'attente.
Créez un sous-graphique qui met en file d'attente les éléments prétraités dans la file d'attente. Une façon de le faire serait de définir quelques tf.placeholder()
ops pour les tenseurs correspondant à un seul exemple d'entrée, puis de les passer à q.enqueue()
. (Si votre prétraitement produit un lot à la fois, vous devez utiliser q.enqueue_many()
à la place.) Vous pouvez également inclure des opérations TensorFlow dans ce sous-graphique.
Créez un sous-graphique qui effectue la formation. Cela ressemblera à un graphique TensorFlow normal, mais obtiendra son entrée en appelant q.dequeue_many(BATCH_SIZE)
.
Commencez votre session.
Créez un ou plusieurs threads qui exécutent votre logique de prétraitement, puis exécutez l'opération de mise en file d'attente, en alimentant les données prétraitées. Vous pouvez trouver les classes utilitaires tf.train.Coordinator
et tf.train.QueueRunner
utiles pour cela.
Exécutez votre graphique d'entraînement (optimiseur, etc.) comme d'habitude.
EDIT: Voici une simple fonction load_and_enqueue()
et un fragment de code pour vous aider à démarrer:
# Features are length-100 vectors of floats
feature_input = tf.placeholder(tf.float32, shape=[100])
# Labels are scalar integers.
label_input = tf.placeholder(tf.int32, shape=[])
# Alternatively, could do:
# feature_batch_input = tf.placeholder(tf.float32, shape=[None, 100])
# label_batch_input = tf.placeholder(tf.int32, shape=[None])
q = tf.FIFOQueue(100, [tf.float32, tf.int32], shapes=[[100], []])
enqueue_op = q.enqueue([feature_input, label_input])
# For batch input, do:
# enqueue_op = q.enqueue_many([feature_batch_input, label_batch_input])
feature_batch, label_batch = q.dequeue_many(BATCH_SIZE)
# Build rest of model taking label_batch, feature_batch as input.
# [...]
train_op = ...
sess = tf.Session()
def load_and_enqueue():
with open(...) as feature_file, open(...) as label_file:
while True:
feature_array = numpy.fromfile(feature_file, numpy.float32, 100)
if not feature_array:
return
label_value = numpy.fromfile(feature_file, numpy.int32, 1)[0]
sess.run(enqueue_op, feed_dict={feature_input: feature_array,
label_input: label_value})
# Start a thread to enqueue data asynchronously, and hide I/O latency.
t = threading.Thread(target=load_and_enqueue)
t.start()
for _ in range(TRAINING_EPOCHS):
sess.run(train_op)
En d'autres termes, un thread effectue le prétraitement des données et l'autre la formation. Est-ce possible dans TensorFlow?
Oui, ça l'est. La solution de mrry fonctionne, mais il existe plus simple.
tf.py_func
encapsule une fonction python et l'utilise comme opérateur TensorFlow. Ainsi, nous pouvons charger les données dans sess.run()
à chaque fois Le problème avec cette approche est que les données sont chargées pendant sess.run()
via le thread principal.
Un exemple minimal:
def get_numpy_tensor():
return np.array([[1,2],[3,4]], dtype=np.float32)
tensorflow_tensor = tf.py_func(get_numpy_tensor, [], tf.float32)
Un exemple plus complexe:
def get_numpy_tensors():
# Load data from the disk into numpy arrays.
input = np.array([[1,2],[3,4]], dtype=np.float32)
target = np.int32(1)
return input, target
tensorflow_input, tensorflow_target = tf.py_func(get_numpy_tensors, [], [tf.float32, tf.int32])
tensorflow_input, tensorflow_target = 2*tensorflow_input, 2*tensorflow_target
sess = tf.InteractiveSession()
numpy_input, numpy_target = sess.run([tensorflow_input, tensorflow_target])
assert np.all(numpy_input==np.array([[2,4],[6,8]])) and numpy_target==2
Pour mettre nos données en file d'attente dans un autre thread (afin que sess.run()
n'ait pas à attendre les données), nous pouvons utiliser tf.train.batch()
sur nos opérateurs à partir de tf.py_func()
.
Un exemple minimal:
tensor_shape = get_numpy_tensor().shape
tensorflow_tensors = tf.train.batch([tensorflow_tensor], batch_size=32, shapes=[tensor_shape])
# Run `tf.train.start_queue_runners()` once session is created.
Nous pouvons omettre l'argument shapes
si tensorflow_tensor
A sa forme spécifiée:
tensor_shape = get_numpy_tensor().shape
tensorflow_tensor.set_shape(tensor_shape)
tensorflow_tensors = tf.train.batch([tensorflow_tensor], batch_size=32)
# Run `tf.train.start_queue_runners()` once session is created.
Un exemple plus complexe:
input_shape, target_shape = (2, 2), ()
def get_numpy_tensors():
input = np.random.Rand(*input_shape).astype(np.float32)
target = np.random.randint(10, dtype=np.int32)
print('f', end='')
return input, target
tensorflow_input, tensorflow_target = tf.py_func(get_numpy_tensors, [], [tf.float32, tf.int32])
batch_size = 2
tensorflow_inputs, tensorflow_targets = tf.train.batch([tensorflow_input, tensorflow_target], batch_size, shapes=[input_shape, target_shape], capacity=2)
# Internal queue will contain at most `capasity=2` times `batch_size=2` elements `[tensorflow_input, tensorflow_target]`.
tensorflow_inputs, tensorflow_targets = 2*tensorflow_inputs, 2*tensorflow_targets
sess = tf.InteractiveSession()
tf.train.start_queue_runners() # Internally, `tf.train.batch` uses a QueueRunner, so we need to ask tf to start it.
for _ in range(10):
numpy_inputs, numpy_targets = sess.run([tensorflow_inputs, tensorflow_targets])
assert numpy_inputs.shape==(batch_size, *input_shape) and numpy_targets.shape==(batch_size, *target_shape)
print('r', end='')
# Prints `fffffrrffrfrffrffrffrffrffrffrf`.
Si get_numpy_tensor()
renvoie un lot de tenseurs, alors tf.train.batch(..., enqueue_many=True)
vous aidera.