Tensorflow: загрузка данных в несколько потоков на cpu
у меня есть класс python SceneGenerator
, который имеет несколько функций-членов для предварительной обработки и функцию генератора generate_data()
. Основная структура такова:
class SceneGenerator(object):
def __init__(self):
# some inits
def generate_data(self):
"""
Generator. Yield data X and labels y after some preprocessing
"""
while True:
# opening files, selecting data
X,y = self.preprocess(some_params, filenames, ...)
yield X, y
я использовал функцию-член класса sceneGenerator.generate_data () в модели keras.функция fit_generator () для чтения данных с диска, предварительной обработки и получения. В keras это делается на нескольких потоках CPU, если о model.fit_generator()
имеет значение что-то > 1.
теперь я хочу используйте то же самое SceneGenerator
класс в tensorflow. Мой нынешний подход таков:--10-->
sceneGenerator = SceneGenerator(some_params)
for X, y in sceneGenerator.generate_data():
feed_dict = {ops['data']: X,
ops['labels']: y,
ops['is_training_pl']: True
}
summary, step, _, loss, prediction = sess.run([optimization_op, loss_op, pred_op],
feed_dict=feed_dict)
это, однако, медленно и не использует несколько потоков. Я нашел tf.data.Dataset
api с некоторыми документация, но я не могу реализовать методы.
Edit: обратите внимание, что я не работаю с изображениями, так что механизмы загрузки изображений с путями файлов и т. д. здесь не работают.
Мой SceneGenerator
загружает данные из файлов hdf5. Но не полностью наборы данных, но - в зависимости от параметров инициализации - только части набора данных. Я хотел бы сохранить функцию генератора как есть и узнать, как этот генератор может быть непосредственно использован в качестве входа для tensorflow и работает на нескольких потоках на CPU. Перезапись данных из файлов hdf5 в csv не является хорошим вариантом, потому что он дублировал много данных.
Edit 2:: я думаю, что-то похожее на это может помочь: parallelising tf.данные.Набор данных.from_generator
2 ответов
предполагая, что вы используете последний Tensorflow (1.4 на момент написания этой статьи), вы можете сохранить генератор и использовать tf.data.*
API следующим образом (я выбрал произвольные значения для номера потока, размера буфера предварительной выборки, размера пакета и типов выходных данных):
NUM_THREADS = 5
sceneGen = SceneGenerator()
dataset = tf.data.Dataset.from_generator(sceneGen.generate_data, output_types=(tf.float32, tf.int32))
dataset = dataset.map(lambda x,y : (x,y), num_parallel_calls=NUM_THREADS).prefetch(buffer_size=1000)
dataset = dataset.batch(42)
X, y = dataset.make_one_shot_iterator().get_next()
чтобы показать, что на самом деле это несколько потоков, извлекаемых из генератора, я изменил ваш класс следующим образом:
import threading
class SceneGenerator(object):
def __init__(self):
# some inits
pass
def generate_data(self):
"""
Generator. Yield data X and labels y after some preprocessing
"""
while True:
# opening files, selecting data
X,y = threading.get_ident(), 2 #self.preprocess(some_params, filenames, ...)
yield X, y
таким образом, создавая сеанс Tensorflow и получая один пакет показывает идентификаторы потоков потоков, получающих данные. На моем ПК работает:
sess = tf.Session()
print(sess.run([X, y]))
печать
[array([ 8460., 8460., 8460., 15912., 16200., 16200., 8460.,
15912., 16200., 8460., 15912., 16200., 16200., 8460.,
15912., 15912., 8460., 8460., 6552., 15912., 15912.,
8460., 8460., 15912., 9956., 16200., 9956., 16200.,
15912., 15912., 9956., 16200., 15912., 16200., 16200.,
16200., 6552., 16200., 16200., 9956., 6552., 6552.], dtype=float32),
array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])]
Примечание: возможно, вы захотите поэкспериментировать с удалением map
вызов (который мы используем только для нескольких потоков) и проверка, если prefetch
буфера достаточно, чтобы удалить узкое место во входном конвейере (даже с одним потоком, часто входная предварительная обработка быстрее, чем фактическое выполнение графика, поэтому буфер достаточно иметь предварительная обработка идет так быстро, как только может).
запуск сеанса с feed_dict
действительно довольно медленно:
Feed_dict выполняет однопоточную memcpy содержимого из среды выполнения Python в среду выполнения TensorFlow.
более быстрый способ подачи данных с помощью tf.train.string_input_producer
+ *Reader
+ tf.train.Coordinator
, который будет паковать данные в нескольких потоках. Для этого Вы читаете данные непосредственно в тензоры, например, вот способ чтения и процесс :
def batch_generator(filenames):
filename_queue = tf.train.string_input_producer(filenames)
reader = tf.TextLineReader(skip_header_lines=1)
_, value = reader.read(filename_queue)
content = tf.decode_csv(value, record_defaults=record_defaults)
content[4] = tf.cond(tf.equal(content[4], tf.constant('Present')),
lambda: tf.constant(1.0),
lambda: tf.constant(0.0))
features = tf.stack(content[:N_FEATURES])
label = content[-1]
data_batch, label_batch = tf.train.shuffle_batch([features, label],
batch_size=BATCH_SIZE,
capacity=20*BATCH_SIZE,
min_after_dequeue=10*BATCH_SIZE)
return data_batch, label_batch
эта функция получает список входных файлов, создает считыватель и преобразования данных и выводит тензоров, которые оцениваются по содержанию этих файлов. Ваш генератор сцен, вероятно, будет выполнять различные преобразования, но идея одна и та же.
Далее, вы начинаете tf.train.Coordinator
распараллелить этот:
with tf.Session() as sess:
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
for _ in range(10): # generate 10 batches
features, labels = sess.run([data_batch, label_batch])
print(features)
coord.request_stop()
coord.join(threads)
по моему опыту, этот способ подает данные намного быстрее и позволяет использовать вся доступная мощность GPU. Полный рабочий пример можно найти здесь.