Weź udział w sympozjum Women in ML 7 grudnia Zarejestruj się teraz

TFDS i determinizm

Zobacz na TensorFlow.org Uruchom w Google Colab Zobacz na GitHub Pobierz notatnik

Ten dokument wyjaśnia:

  • TFDS gwarantuje determinizm
  • W jakiej kolejności TFDS odczytuje przykłady?
  • Różne zastrzeżenia i niedogodności

Ustawiać

Zbiory danych

Aby zrozumieć, w jaki sposób TFDS odczytuje dane, potrzebny jest pewien kontekst.

Podczas generacji TFDS pisać oryginalne dane do standaryzowanych .tfrecord plików. Dla dużych zbiorów danych, wiele .tfrecord pliki są tworzone, z których każda zawiera wiele przykładów. Nazywamy siebie .tfrecord złożyć odłamek.

Ten przewodnik wykorzystuje imagenet, który ma 1024 shardów:

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)

Znajdowanie identyfikatorów przykładowych zbiorów danych

Możesz przejść do następnej sekcji, jeśli chcesz wiedzieć tylko o determinizmie.

Każdy przykład rekord jest identyfikowany przez id (na przykład 'imagenet2012-train.tfrecord-01023-of-01024__32' ). Można odzyskać ten id przekazując read_config.add_tfds_id = True , która doda 'tfds_id' klucz w dict z tf.data.Dataset .

W tym samouczku zdefiniujemy mały program użytkowy, który wyświetli przykładowe identyfikatory zbioru danych (przekonwertowane na liczby całkowite, aby były bardziej czytelne dla człowieka):

Determinizm podczas czytania

Ta sekcja wyjaśnia deterministim gwarancję tfds.load .

Z shuffle_files=False (domyślnie)

Standardowo TFDS uzyskując przykłady deterministyczny ( 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]

Pod kątem wydajności, TFDS przeczytać wiele odłamków w tym samym czasie za pomocą tf.data.Dataset.interleave . Widzimy, że w tym przykładzie TFDS przełączyć się odłamek 2 po przeczytaniu przykłady (16 ..., 14, 15, 1251, 1252, ... ). Więcej o przeplataniu poniżej.

Podobnie subsplit API jest również deterministyczny:

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]

Jeśli jesteś szkolenia dla ponad jednej epoki, powyższa konfiguracja nie jest zalecane, ponieważ wszystkich epok odczyta odłamki w tej samej kolejności (tak losowość jest ograniczony do ds = ds.shuffle(buffer) rozmiar bufora).

Z shuffle_files=True

Z shuffle_files=True , odłamki tasuje dla każdej epoki, więc czytanie nie jest już deterministyczny.

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]

Zobacz przepis poniżej, aby uzyskać deterministyczne tasowanie plików.

Zastrzeżenie determinizmu: argumenty przeplatane

Zmiana read_config.interleave_cycle_length , read_config.interleave_block_length zmieni kolejność przykładów.

TFDS polega na tf.data.Dataset.interleave aby załadować tylko kilka odłamki naraz, poprawę wydajności i zmniejszenie zużycia pamięci.

Przykładowa kolejność jest gwarantowana tylko dla stałej wartości argumentów przeplatania. Zobacz przeplotu doc , aby zrozumieć, co cycle_length i block_length odpowiadają też.

  • cycle_length=16 , block_length=16 (domyślnie, jak wyżej)
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]

W drugim przykładzie widzimy, że zbiór danych czytać 2 ( block_length=2 ) Przykłady w skorupie, a następnie przejść do następnego odłamek. Co 2 * 3 ( cycle_length=3 ) Przykłady, przechodzi do pierwszej skorupie ( shard0-ex0, shard0-ex1, shard1-ex0, shard1-ex1, shard2-ex0, shard2-ex1, shard0-ex2, shard0-ex3, shard1-ex2, shard1-ex3, shard2-ex2,... ).

Podział i przykładowa kolejność

Każdy przykład ma id 0, 1, ..., num_examples-1 . Subsplit API wybrać kawałek przykładów (np train[:x] wybierz 0, 1, ..., x-1 ).

Jednak w ramach podziału przykłady nie są odczytywane w rosnącej kolejności identyfikatorów (ze względu na fragmenty i przeplatanie).

Dokładniej, ds.take(x) i split='train[:x]' nie są równoważne!

Można to łatwo zauważyć w powyższym przykładzie z przeplotem, w którym przykłady pochodzą z różnych fragmentów.

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]

Po 16 (block_length) przykładach .take(25) przełącza się w następnym fragmencie, gdy train[:25] w dalszym ciągu przykłady odczytu z pierwszej skorupie.

Przepisy

Uzyskaj deterministyczne tasowanie plików

Istnieją 2 sposoby na deterministyczne tasowanie:

  1. Ustawianie shuffle_seed . Uwaga: wymaga to zmiany nasion w każdej epoce, w przeciwnym razie odłamki będą odczytywane w tej samej kolejności między epokami.
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]
  1. Korzystanie experimental_interleave_sort_fn : Daje to pełną kontrolę nad którym odłamki są odczytywane i w jakiej kolejności, zamiast polegać na ds.shuffle kolejności.
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]

Uzyskaj deterministyczny potok z możliwością wywłaszczania

Ten jest bardziej skomplikowany. Nie ma łatwego, satysfakcjonującego rozwiązania.

  1. Bez ds.shuffle i deterministycznej tasowania, w teorii powinno być możliwe do policzenia przykłady, które zostały odczytane i wywnioskować, których przykłady zostały odczytane w ciągu w każdym fragmencie (jako funkcja cycle_length , block_length i porządku Shard). Następnie skip , take dla każdego odłamek może być wstrzykiwany przez experimental_interleave_sort_fn .

  2. Z ds.shuffle to prawdopodobnie niemożliwe bez odtwarzanie pełnego szkolenia rurociągu. Wymagałoby to zapisując ds.shuffle stanie wydedukować bufora których przykłady zostały odczytane. Przykłady mogą być nieciągłe (na przykład shard5_ex2 , shard5_ex4 odczytu, ale nie shard5_ex3 ).

  3. Z ds.shuffle , jednym ze sposobów byłoby zapisać wszystkie shards_ids / example_ids read (wyprowadzone z tfds_id ), a następnie wnioskowanie z instrukcjami plików z tego.

Najprostszy przypadek 1. ma mieć .skip(x).take(y) mecz train[x:x+y] dopasowanie. To wymaga:

  • Zestaw cycle_length=1 (tak odłamki są odczytywane kolejno)
  • Set shuffle_files=False
  • Nie używaj ds.shuffle

Powinno być używane tylko na ogromnym zbiorze danych, w którym trening trwa tylko 1 epokę. Przykłady byłyby odczytywane w domyślnej kolejności odtwarzania losowego.

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]

Znajdź, które fragmenty/przykłady są odczytywane dla danego subsplitu

Z tfds.core.DatasetInfo , masz bezpośredni dostęp do instrukcji odczytu.

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)]