Посмотреть на TensorFlow.org | Запускаем в Google Colab | Посмотреть на GitHub | Скачать блокнот |
В этом документе объясняется:
- TFDS гарантирует детерминизм
- В каком порядке TFDS читает примеры
- Различные предостережения и подводные камни
Настраивать
Наборы данных
Некоторый контекст необходим, чтобы понять, как TFDS считывает данные.
Во время генерации, TFDS записать исходные данные в стандартные .tfrecord
файлы. Для больших наборов данных, несколько .tfrecord
создаются файлы, каждый из которых содержит несколько примеров. Мы называем каждый .tfrecord
файл осколок.
В этом руководстве используется imagenet с 1024 осколками:
import re
import tensorflow_datasets as tfds
imagenet = tfds.builder('imagenet2012')
num_shards = imagenet.info.splits['train'].num_shards
num_examples = imagenet.info.splits['train'].num_examples
print(f'imagenet has {num_shards} shards ({num_examples} examples)')
imagenet has 1024 shards (1281167 examples)
Поиск идентификаторов примеров наборов данных
Вы можете перейти к следующему разделу, если хотите узнать только о детерминизме.
Каждый пример набора данных однозначно идентифицируется id
(например , 'imagenet2012-train.tfrecord-01023-of-01024__32'
). Вы можете восстановить этот id
, передавая read_config.add_tfds_id = True
, который добавит 'tfds_id'
ключ в Словаре от tf.data.Dataset
.
В этом руководстве мы определяем небольшую утилиту, которая будет печатать примеры идентификаторов набора данных (преобразованных в целое число, чтобы было удобнее читать):
def load_dataset(builder, **as_dataset_kwargs):
"""Load the dataset with the tfds_id."""
read_config = as_dataset_kwargs.pop('read_config', tfds.ReadConfig())
read_config.add_tfds_id = True # Set `True` to return the 'tfds_id' key
return builder.as_dataset(read_config=read_config, **as_dataset_kwargs)
def print_ex_ids(
builder,
*,
take: int,
skip: int = None,
**as_dataset_kwargs,
) -> None:
"""Print the example ids from the given dataset split."""
ds = load_dataset(builder, **as_dataset_kwargs)
if skip:
ds = ds.skip(skip)
ds = ds.take(take)
exs = [ex['tfds_id'].numpy().decode('utf-8') for ex in ds]
exs = [id_to_int(tfds_id, builder=builder) for tfds_id in exs]
print(exs)
def id_to_int(tfds_id: str, builder) -> str:
"""Format the tfds_id in a more human-readable."""
match = re.match(r'\w+-(\w+).\w+-(\d+)-of-\d+__(\d+)', tfds_id)
split_name, shard_id, ex_id = match.groups()
split_info = builder.info.splits[split_name]
return sum(split_info.shard_lengths[:int(shard_id)]) + int(ex_id)
Детерминизм при чтении
В этом разделе объясняется deterministim гарантии tfds.load
.
С shuffle_files=False
( по умолчанию)
По умолчанию TFDS дают примеры детерминировано ( shuffle_files=False
)
# Same as: imagenet.as_dataset(split='train').take(20)
print_ex_ids(imagenet, split='train', take=20)
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
Для повышения производительности, TFDS прочитал несколько черепков в то же время с помощью tf.data.Dataset.interleave . Мы видим , что в данном примере TFDS переключиться на осколок 2 после прочтения 16 примеров ( ..., 14, 15, 1251, 1252, ...
). Подробнее о чередовании ниже.
Аналогичным образом, API-интерфейс subplit также детерминирован:
print_ex_ids(imagenet, split='train[67%:84%]', take=20)
print_ex_ids(imagenet, split='train[67%:84%]', take=20)
[858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536] [858382, 858383, 858384, 858385, 858386, 858387, 858388, 858389, 858390, 858391, 858392, 858393, 858394, 858395, 858396, 858397, 859533, 859534, 859535, 859536]
Если вы тренируетесь более одной эпохи, выше настройка не рекомендуется , так как все эпохи прочтет черепки в том же порядке (так хаотичность ограничивается ds = ds.shuffle(buffer)
размер буфера).
С shuffle_files=True
С shuffle_files=True
, черепки перемешиваются для каждой эпохи, поэтому чтение не является детерминированным больше.
print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
print_ex_ids(imagenet, split='train', shuffle_files=True, take=20)
[568017, 329050, 329051, 329052, 329053, 329054, 329056, 329055, 568019, 568020, 568021, 568022, 568023, 568018, 568025, 568024, 568026, 568028, 568030, 568031] [43790, 43791, 43792, 43793, 43796, 43794, 43797, 43798, 43795, 43799, 43800, 43801, 43802, 43803, 43804, 43805, 43806, 43807, 43809, 43810]
См. Рецепт ниже, чтобы получить детерминированное перемешивание файлов.
Предупреждение о детерминизме: чередование аргументов
Изменение read_config.interleave_cycle_length
, read_config.interleave_block_length
изменит порядок примеры.
TFDS полагается на tf.data.Dataset.interleave только загружать несколько черепков сразу, улучшая производительность и снижает потребление памяти.
Примерный порядок гарантированно будет одинаковым только для фиксированного значения аргументов чередования. См перемежения документ , чтобы понять , что cycle_length
и block_length
соответствуют тоже.
-
cycle_length=16
,block_length=16
( по умолчанию, то же самое, что и выше):
print_ex_ids(imagenet, split='train', take=20)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254]
-
cycle_length=3
,block_length=2
:
read_config = tfds.ReadConfig(
interleave_cycle_length=3,
interleave_block_length=2,
)
print_ex_ids(imagenet, split='train', read_config=read_config, take=20)
[0, 1, 1251, 1252, 2502, 2503, 2, 3, 1253, 1254, 2504, 2505, 4, 5, 1255, 1256, 2506, 2507, 6, 7]
Во втором примере, мы видим , что набор данных чтения 2 ( block_length=2
) примеров в осколке, а затем перейти к следующему осколка. Каждые 2 * 3 ( cycle_length=3
) примеры, она восходит к первому осколок ( shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,...
).
Подразделение и пример заказа
Каждый пример имеет идентификатор 0, 1, ..., num_examples-1
. Subsplit API выбрать кусочек примеров (например , train[:x]
выберите 0, 1, ..., x-1
).
Однако внутри подразделения примеры не читаются в порядке возрастания идентификаторов (из-за осколков и чередования).
Более конкретно, ds.take(x)
и split='train[:x]'
не эквивалентны!
Это легко увидеть в приведенном выше примере чередования, где примеры взяты из разных сегментов.
print_ex_ids(imagenet, split='train', take=25) # tfds.load(..., split='train').take(25)
print_ex_ids(imagenet, split='train[:25]', take=-1) # tfds.load(..., split='train[:25]')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
После того, как 16 (block_length) примеров, .take(25)
переключается на следующий осколок в то время как train[:25]
продолжить чтение примеров в первом из осколка.
Рецепты
Получите детерминированное перемешивание файлов
Есть 2 способа детерминированного перемешивания:
- Установка
shuffle_seed
. Примечание. Для этого необходимо менять начальное число в каждую эпоху, иначе осколки будут считываться в том же порядке между эпохами.
read_config = tfds.ReadConfig(
shuffle_seed=32,
)
# Deterministic order, different from the default shuffle_files=False above
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
print_ex_ids(imagenet, split='train', shuffle_files=True, read_config=read_config, take=22)
[176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652] [176411, 176412, 176413, 176414, 176415, 176416, 176417, 176418, 176419, 176420, 176421, 176422, 176423, 176424, 176425, 176426, 710647, 710648, 710649, 710650, 710651, 710652]
- Использование
experimental_interleave_sort_fn
: Это дает полный контроль над которым черепки считываются и в каком порядке, а не полагаться наds.shuffle
порядке.
def _reverse_order(file_instructions):
return list(reversed(file_instructions))
read_config = tfds.ReadConfig(
experimental_interleave_sort_fn=_reverse_order,
)
# Last shard (01023-of-01024) is read first
print_ex_ids(imagenet, split='train', read_config=read_config, take=5)
[1279916, 1279917, 1279918, 1279919, 1279920]
Получите детерминированный вытесняемый конвейер
Этот более сложный. Нет простого удовлетворительного решения.
Без
ds.shuffle
и с детерминированной перетасовкой, в теории это должно быть возможным подсчитать примеры , которые были прочитаны и выводят примеры , которые были прочитаны в каждом осколке (в зависимости отcycle_length
,block_length
и порядка шарда). Тогдаskip
,take
для каждого осколка может быть введены черезexperimental_interleave_sort_fn
.С
ds.shuffle
это, вероятно , невозможно без Воспроизводится полный трубопровод обучения. Это потребует сохраненияds.shuffle
состояния буфера вывести примеры , которые были прочитаны. Примеры могут быть прерывистыми (например ,shard5_ex2
,shard5_ex4
читать , но неshard5_ex3
).С
ds.shuffle
, один из способов будет сохранить все shards_ids / example_ids читать (выведенные изtfds_id
), затем выводя инструкции файла из этого.
Простейший случай для 1.
должен иметь .skip(x).take(y)
матч train[x:x+y]
совпадают. Это требует:
- Набор
cycle_length=1
(так Осколки считываются последовательно) - Набор
shuffle_files=False
- Не используйте
ds.shuffle
Его следует использовать только на огромном наборе данных, где обучение длится всего 1 эпоха. Примеры будут читаться в порядке перемешивания по умолчанию.
read_config = tfds.ReadConfig(
interleave_cycle_length=1, # Read shards sequentially
)
print_ex_ids(imagenet, split='train', read_config=read_config, skip=40, take=22)
# If the job get pre-empted, using the subsplit API will skip at most `len(shard0)`
print_ex_ids(imagenet, split='train[40:]', read_config=read_config, take=22)
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61] [40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61]
Найдите, какие осколки / примеры читаются для данного подразделения
С tfds.core.DatasetInfo
, у вас есть прямой доступ к командам чтения.
imagenet.info.splits['train[44%:45%]'].file_instructions
[FileInstruction(filename='imagenet2012-train.tfrecord-00450-of-01024', skip=700, take=-1, num_examples=551), FileInstruction(filename='imagenet2012-train.tfrecord-00451-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00452-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00453-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00454-of-01024', skip=0, take=-1, num_examples=1252), FileInstruction(filename='imagenet2012-train.tfrecord-00455-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00456-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00457-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00458-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00459-of-01024', skip=0, take=-1, num_examples=1251), FileInstruction(filename='imagenet2012-train.tfrecord-00460-of-01024', skip=0, take=1001, num_examples=1001)]