как перебирать данные, подаваемые с заполнителем в тензорном потоке, только один раз, используя новый API набора данных

Я начинаю использовать новый API набора данных, и одна вещь, которую я хочу сделать, не описана в документе (https://www.tensorflow.org/programmers_guide/datasets#training_workflows)

Мои данные помещаются в память, поэтому я хочу загрузить их в тензорный поток, чтобы сделать обучение эффективным, и для этого я сейчас вижу 2 способа сделать это:

один загружает данные в график напрямую следующим образом:

dataset = tf.contrib.data.Dataset.from_tensor_slices((X, Y))
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# loop on epochs
for _ in range(5):
    # Initialize an iterator over the training dataset.
    sess.run(iterator.initializer)
    # loop over all the batch
    for _ in range(1000):
        s = time.time()
        try:
            sess.run(next_element)
        except tf.errors.OutOfRangeError:
            print("Finish epoch")

другой - загрузить данные в заполнитель, чтобы данные не сохранялись на графике:

features_placeholder = tf.placeholder(features.dtype, features.shape)
labels_placeholder = tf.placeholder(labels.dtype, labels.shape)

dataset = tf.contrib.data.Dataset.from_tensor_slices((features_placeholder, labels_placeholder))
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# loop on epochs
for _ in range(5):
    # Initialize an iterator over the training dataset.
    sess.run(iterator.initializer, feed_dict={features_placeholder: X, labels_placeholder: Y})
    # loop over all the batch
    for _ in range(1000):
        s = time.time()
        try:
            sess.run(next_element)
        except tf.errors.OutOfRangeError:
            print("Finish epoch")

Во-вторых, я считаю, что лучше всего экономить память, но я не хочу передавать данные в каждую эпоху. Это действительно потеря производительности ни за что.

Есть ли способ инициализировать итератор только один раз с заполнителем?

что-то вроде этого:

sess.run(iterator.initializer, feed_dict={features_placeholder: X, labels_placeholder: Y})

# loop on epochs
for _ in range(5):
    # Initialize an iterator over the training dataset.
    sess.run(iterator.initializer)
    # loop over all the batch
    for _ in range(1000):
        s = time.time()
        try:
            sess.run(next_element)
        except tf.errors.OutOfRangeError:
            print("Finish epoch")

Таким образом, мы можем сохранить производительность первого решения и сэкономить память, как и второе решение.

Примечание:

одно из решений состоит в том, чтобы определить количество эпох с помощью метода dataset.repeat(), но с ним мы как бы теряем контроль над тем, на каком этапе обучения мы находимся.

Я хочу проверять после каждой эпохи (один проход по всем данным) эволюцию потери.


person rAyyy    schedule 06.10.2017    source источник


Ответы (2)


Прежде всего, я бы порекомендовал количественно оценить накладные расходы производительности на передачу X и Y каждый раз, когда вы инициализируете итератор. Для примитивных типов, таких как tf.int32 и tf.float32, часто можно передать значение без копирования каких-либо данных, и в этом случае накладные расходы будут незначительными. Даже если копия необходима, это повлечет за собой один memcpy(), который может быть удивительно быстрым. (С другой стороны, передача тензора tf.string может быть более дорогостоящей, поскольку для преобразования между строковыми представлениями Python и C++ требуется несколько небольших копий.)

Предполагая, что это значительные накладные расходы, вы можете сделать их единовременными, сохранив входные данные в файле tf.Variable. Например:

placeholder_X = tf.placeholder(X.dtype, X.shape)
var_X = tf.Variable(placeholder_X)
placeholder_Y = tf.placeholder(Y.dtype, Y.shape)
var_Y = tf.Variable(placeholder_Y)

dataset = tf.contrib.data.Dataset.from_tensor_slices((var_X, var_Y))
iterator = dataset.make_initializable_iterator()

# ...

# The contents of `X` and `Y` will be copied once, in this call.
sess.run(tf.global_variables_initializer(), feed_dict={
    placeholder_X: X, placeholder_Y = Y})

for _ in range(5):
  # The iterator will be initialized from the variables with no copy.
  sess.run(iterator.initializer)

  # ...
person mrry    schedule 09.10.2017
comment
Спасибо за Ваш ответ. В документе говорится, что передача данных напрямую с помощью Dataset.from_tensor_slices сохранит их как tf.constant, что приведет к пустой трате памяти. Так что же изменится в плане памяти, сохранив его в tf.Variable? - person rAyyy; 10.10.2017
comment
Операция tf.constant() изначально была разработана для небольших констант (таких как скаляры и фигуры), поэтому среда выполнения обычно для удобства хранит ее несколько разных копий. Напротив, ожидается, что переменные будут большими, и TensorFlow по умолчанию будет хранить только одну их копию. - person mrry; 10.10.2017

Я не думаю, что вам нужно инициализировать каждую эпоху. Вы можете сделать это один раз перед тренировочным циклом. Но вам также нужно сообщить набору данных, чтобы он повторялся и перетасовывался на каждой итерации, когда вы определяете набор данных:

features_placeholder = tf.placeholder(features.dtype, features.shape)
labels_placeholder = tf.placeholder(labels.dtype, labels.shape)

dataset = tf.data.Dataset.from_tensor_slices((features_placeholder, labels_placeholder)).shuffle(buffer_value,reshuffle_each_iteration=True).repeat().batch(batch_num)
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

#Initialize an iterator over the training dataset.
sess.run(iterator.initializer, feed_dict={features_placeholder: X, labels_placeholder: Y})
    # loop on epochs
for _ in range(5):
    # loop over all the batch
    for _ in range(1000):
        s = time.time()
        sess.run(next_element)

Обратите внимание, что, поскольку повторение включено, вам нужно вычислить точное количество итераций, которое вам нужно для циклического перебора данных один раз в каждую эпоху, и установить его во внутреннем цикле.

person Ali Samz    schedule 01.10.2018
comment
дело в том, что когда вы используете повтор, я думаю, что он полностью заполнит пакет, даже если набор данных не является кратным, поэтому есть риск, что некоторые данные появятся 2 раза - person rAyyy; 02.10.2018
comment
Верно, но если размер вашего пакета не слишком велик, повторение нескольких точек данных дважды за одну итерацию может не вызвать особых проблем, я думаю... - person Ali Samz; 03.10.2018