Pisanie niestandardowych zestawów danych

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Postępuj zgodnie z tym przewodnikiem, aby utworzyć nowy zestaw danych (w TFDS lub we własnym repozytorium).

Sprawdź naszą listę zestawów danych , aby zobaczyć, czy żądany zestaw danych już istnieje.

TL;DR

Najłatwiejszym sposobem napisania nowego zestawu danych jest użycie TFDS CLI :

cd path/to/my/project/datasets/
tfds new my_dataset  # Create `my_dataset/my_dataset.py` template files
# [...] Manually modify `my_dataset/my_dataset_dataset_builder.py` to implement your dataset.
cd my_dataset/
tfds build  # Download and prepare the dataset to `~/tensorflow_datasets/`

Aby użyć nowego zestawu danych za pomocą tfds.load('my_dataset') :

  • tfds.load automatycznie wykryje i załaduje zestaw danych wygenerowany w ~/tensorflow_datasets/my_dataset/ (np. przez tfds build ).
  • Alternatywnie możesz jawnie import my.project.datasets.my_dataset , aby zarejestrować swój zestaw danych:
import my.project.datasets.my_dataset  # Register `my_dataset`

ds = tfds.load('my_dataset')  # `my_dataset` registered

Przegląd

Zestawy danych są dystrybuowane w różnych formatach iw różnych miejscach i nie zawsze są przechowywane w formacie, który jest gotowy do wprowadzenia do potoku uczenia maszynowego. Wpisz TFDS.

TFDS przetwarza te zestawy danych do standardowego formatu (dane zewnętrzne -> pliki serializowane), który można następnie załadować jako potok uczenia maszynowego (pliki serializowane -> tf.data.Dataset ). Serializacja jest wykonywana tylko raz. Późniejszy dostęp spowoduje bezpośrednie odczytanie z tych wstępnie przetworzonych plików.

Większość wstępnego przetwarzania odbywa się automatycznie. Każdy zestaw danych implementuje podklasę tfds.core.DatasetBuilder , która określa:

  • Skąd pochodzą dane (tj. ich adresy URL);
  • Jak wygląda zbiór danych (tj. jego cechy);
  • Jak należy podzielić dane (np. TRAIN i TEST );
  • oraz poszczególne przykłady w zbiorze danych.

Napisz swój zestaw danych

Domyślny szablon: tfds new

Użyj TFDS CLI , aby wygenerować wymagane pliki szablonów Pythona.

cd path/to/project/datasets/  # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset

To polecenie wygeneruje nowy folder my_dataset/ o następującej strukturze:

my_dataset/
    __init__.py
    README.md # Markdown description of the dataset.
    CITATIONS.bib # Bibtex citation for the dataset.
    TAGS.txt # List of tags describing the dataset.
    my_dataset_dataset_builder.py # Dataset definition
    my_dataset_dataset_builder_test.py # Test
    dummy_data/ # (optional) Fake data (used for testing)
    checksum.tsv # (optional) URL checksums (see `checksums` section).

Wyszukaj TODO(my_dataset) tutaj i odpowiednio zmodyfikuj.

Przykład zbioru danych

Wszystkie zestawy danych są zaimplementowanymi podklasami tfds.core.DatasetBuilder , który zajmuje się większością szablonów. To wspiera:

Oto minimalny przykład konstruktora zestawu danych, który jest oparty na tfds.core.GeneratorBasedBuilder :

class Builder(tfds.core.GeneratorBasedBuilder):
  """DatasetBuilder for my_dataset dataset."""

  VERSION = tfds.core.Version('1.0.0')
  RELEASE_NOTES = {
      '1.0.0': 'Initial release.',
  }

  def _info(self) -> tfds.core.DatasetInfo:
    """Dataset metadata (homepage, citation,...)."""
    return self.dataset_info_from_configs(
        features=tfds.features.FeaturesDict({
            'image': tfds.features.Image(shape=(256, 256, 3)),
            'label': tfds.features.ClassLabel(
                names=['no', 'yes'],
                doc='Whether this is a picture of a cat'),
        }),
    )

  def _split_generators(self, dl_manager: tfds.download.DownloadManager):
    """Download the data and define splits."""
    extracted_path = dl_manager.download_and_extract('http://data.org/data.zip')
    # dl_manager returns pathlib-like objects with `path.read_text()`,
    # `path.iterdir()`,...
    return {
        'train': self._generate_examples(path=extracted_path / 'train_images'),
        'test': self._generate_examples(path=extracted_path / 'test_images'),
    }

  def _generate_examples(self, path) -> Iterator[Tuple[Key, Example]]:
    """Generator of examples for each split."""
    for img_path in path.glob('*.jpeg'):
      # Yields (key, example)
      yield img_path.name, {
          'image': img_path,
          'label': 'yes' if img_path.name.startswith('yes_') else 'no',
      }

Pamiętaj, że w przypadku niektórych określonych formatów danych udostępniamy gotowe do użycia narzędzia do tworzenia zestawów danych, które zajmują się przetwarzaniem większości danych.

Przyjrzyjmy się szczegółowo 3 abstrakcyjnym metodom nadpisywania.

_info : metadane zestawu danych

_info zwraca tfds.core.DatasetInfo zawierający metadane zestawu danych .

def _info(self):
  # The `dataset_info_from_configs` base method will construct the
  # `tfds.core.DatasetInfo` object using the passed-in parameters and
  # adding: builder (self), description/citations/tags from the config
  # files located in the same package.
  return self.dataset_info_from_configs(
      homepage='https://dataset-homepage.org',
      features=tfds.features.FeaturesDict({
          'image_description': tfds.features.Text(),
          'image': tfds.features.Image(),
          # Here, 'label' can be 0-4.
          'label': tfds.features.ClassLabel(num_classes=5),
      }),
      # If there's a common `(input, target)` tuple from the features,
      # specify them here. They'll be used if as_supervised=True in
      # builder.as_dataset.
      supervised_keys=('image', 'label'),
      # Specify whether to disable shuffling on the examples. Set to False by default.
      disable_shuffling=False,
  )

Większość pól powinna być oczywista. Kilka precyzji:

Zapisywanie pliku BibText CITATIONS.bib :

  • Przeszukaj witrynę zestawu danych w celu znalezienia instrukcji cytowania (użyj jej w formacie BibTex).
  • W przypadku dokumentów arXiv : znajdź artykuł i kliknij link BibText po prawej stronie.
  • Znajdź artykuł w Google Scholar i kliknij podwójny cudzysłów pod tytułem, a następnie w wyskakującym okienku kliknij BibTeX .
  • Jeśli nie ma powiązanego artykułu (na przykład jest tylko strona internetowa), możesz użyć Edytora BibTeX Online , aby utworzyć niestandardowy wpis BibTeX (menu rozwijane zawiera typ wpisu Online ).

Aktualizowanie pliku TAGS.txt :

  • Wszystkie dozwolone tagi są wstępnie wypełnione w wygenerowanym pliku.
  • Usuń wszystkie tagi, które nie mają zastosowania do zbioru danych.
  • Prawidłowe tagi są wymienione w pliku tensorflow_datasets/core/valid_tags.txt .
  • Aby dodać tag do tej listy, wyślij PR.

Zachowaj kolejność zbiorów danych

Domyślnie rekordy zestawów danych są tasowane podczas przechowywania w celu ujednolicenia rozkładu klas w zbiorze danych, ponieważ często rekordy należące do tej samej klasy są ciągłe. Aby określić, że zbiór danych ma być sortowany według klucza wygenerowanego przez _generate_examples należy ustawić pole disable_shuffling na True . Domyślnie jest ustawiony na False .

def _info(self):
  return self.dataset_info_from_configs(
    # [...]
    disable_shuffling=True,
    # [...]
  )

Pamiętaj, że wyłączenie mieszania ma wpływ na wydajność, ponieważ fragmentów nie można już odczytywać równolegle.

_split_generators : pobiera i dzieli dane

Pobieranie i wyodrębnianie danych źródłowych

Większość zestawów danych wymaga pobierania danych z Internetu. Odbywa się to za pomocą argumentu wejściowego tfds.download.DownloadManager _split_generators . dl_manager ma następujące metody:

  • download : obsługuje http(s):// , ftp(s)://
  • extract : obecnie obsługuje pliki .zip , .gz i .tar .
  • download_and_extract : To samo co dl_manager.extract(dl_manager.download(urls))

Wszystkie te metody zwracają tfds.core.Path (aliasy dla epath.Path ), które są obiektami podobnymi do pathlib.Path .

Metody te obsługują dowolne zagnieżdżone struktury ( list , dict ), takie jak:

extracted_paths = dl_manager.download_and_extract({
    'foo': 'https://example.com/foo.zip',
    'bar': 'https://example.com/bar.zip',
})
# This returns:
assert extracted_paths == {
    'foo': Path('/path/to/extracted_foo/'),
    'bar': Path('/path/extracted_bar/'),
}

Ręczne pobieranie i ekstrakcja

Niektórych danych nie można pobrać automatycznie (np. wymagają logowania), w takim przypadku użytkownik ręcznie pobierze dane źródłowe i umieści je w manual_dir/ (domyślnie ~/tensorflow_datasets/downloads/manual/ ).

Dostęp do plików można następnie uzyskać za pomocą dl_manager.manual_dir :

class MyDataset(tfds.core.GeneratorBasedBuilder):

  MANUAL_DOWNLOAD_INSTRUCTIONS = """
  Register into https://example.org/login to get the data. Place the `data.zip`
  file in the `manual_dir/`.
  """

  def _split_generators(self, dl_manager):
    # data_path is a pathlib-like `Path('<manual_dir>/data.zip')`
    archive_path = dl_manager.manual_dir / 'data.zip'
    # Extract the manually downloaded `data.zip`
    extracted_path = dl_manager.extract(archive_path)
    ...

Położenie manual_dir można dostosować za pomocą tfds build --manual_dir= lub tfds.download.DownloadConfig .

Czytaj archiwum bezpośrednio

dl_manager.iter_archive odczytuje archiwa sekwencyjnie bez ich rozpakowywania. Może to zaoszczędzić miejsce w pamięci masowej i poprawić wydajność w niektórych systemach plików.

for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
  ...

fobj ma takie same metody jak with open('rb') as fobj: (np fobj.read() )

Określanie podziałów zbioru danych

Jeśli zestaw danych zawiera wstępnie zdefiniowane podziały (np. MNIST ma podział na train i test ), zachowaj je. W przeciwnym razie określ tylko jeden podział tfds.Split.TRAIN . Użytkownicy mogą dynamicznie tworzyć własne podpodziały za pomocą interfejsu API podpodziałów (np. split='train[80%:]' ).

def _split_generators(self, dl_manager):
  # Download source data
  extracted_path = dl_manager.download_and_extract(...)

  # Specify the splits
  return {
      'train': self._generate_examples(
          images_path=extracted_path / 'train_imgs',
          label_path=extracted_path / 'train_labels.csv',
      ),
      'test': self._generate_examples(
          images_path=extracted_path / 'test_imgs',
          label_path=extracted_path / 'test_labels.csv',
      ),
  }

_generate_examples : Przykładowy generator

_generate_examples generuje przykłady dla każdego podziału z danych źródłowych.

Ta metoda zwykle odczytuje artefakty źródłowego zestawu danych (np. plik CSV) i zwraca krotki (key, feature_dict) :

  • key : przykładowy identyfikator. Służy do deterministycznego tasowania przykładów za pomocą hash(key) lub do sortowania według klucza, gdy tasowanie jest wyłączone (zobacz sekcję Utrzymanie kolejności zestawu danych ). Powinien być:
    • unique : Jeśli dwa przykłady używają tego samego klucza, zostanie zgłoszony wyjątek.
    • deterministic : nie powinien zależeć od download_dir , os.path.listdir order,... Dwukrotne wygenerowanie danych powinno dać ten sam klucz.
    • porównywalny : Jeśli tasowanie jest wyłączone, klucz będzie używany do sortowania zestawu danych.
  • feature_dict : dict zawierający przykładowe wartości.
    • Struktura powinna być zgodna ze strukturą features= zdefiniowaną w tfds.core.DatasetInfo .
    • Złożone typy danych (obraz, wideo, audio,...) zostaną automatycznie zakodowane.
    • Każda funkcja często akceptuje wiele typów danych wejściowych (np. video accept /path/to/vid.mp4 , np.array(shape=(l, h, w, c)) , List[paths] , List[np.array(shape=(h, w, c)] , List[img_bytes] ,...)
    • Więcej informacji można znaleźć w przewodniku dotyczącym łącznika funkcji .
def _generate_examples(self, images_path, label_path):
  # Read the input data out of the source files
  with label_path.open() as f:
    for row in csv.DictReader(f):
      image_id = row['image_id']
      # And yield (key, feature_dict)
      yield image_id, {
          'image_description': row['description'],
          'image': images_path / f'{image_id}.jpeg',
          'label': row['label'],
      }

Dostęp do plików i tf.io.gfile

Aby obsługiwać systemy pamięci masowej w chmurze, unikaj korzystania z wbudowanych operacji wejścia/wyjścia Pythona.

Zamiast tego dl_manager zwraca obiekty podobne do pathlib bezpośrednio kompatybilne z pamięcią Google Cloud:

path = dl_manager.download_and_extract('http://some-website/my_data.zip')

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

Ewentualnie użyj API tf.io.gfile zamiast wbudowanego do operacji na plikach:

Pathlib powinien być preferowany niż tf.io.gfile (patrz .

Dodatkowe zależności

Niektóre zestawy danych wymagają dodatkowych zależności języka Python tylko podczas generowania. Na przykład zestaw danych SVHN używa scipy do ładowania niektórych danych.

Jeśli dodajesz zestaw danych do repozytorium TFDS, użyj tfds.core.lazy_imports , aby pakiet tensorflow-datasets mały. Użytkownicy będą instalować dodatkowe zależności tylko w razie potrzeby.

Aby użyć lazy_imports :

  • Dodaj wpis dla swojego zestawu danych do DATASET_EXTRAS w setup.py . Dzięki temu użytkownicy mogą na przykład wykonać pip install 'tensorflow-datasets[svhn]' aby zainstalować dodatkowe zależności.
  • Dodaj wpis dla swojego importu do LazyImporter i do LazyImportsTest .
  • Użyj tfds.core.lazy_imports , aby uzyskać dostęp do zależności (na przykład tfds.core.lazy_imports.scipy ) w DatasetBuilder .

Uszkodzone dane

Niektóre zestawy danych nie są idealnie czyste i zawierają uszkodzone dane (na przykład obrazy są w plikach JPEG, ale niektóre są nieprawidłowymi plikami JPEG). Te przykłady należy pominąć, ale w opisie zestawu danych należy zostawić informację, ile przykładów zostało usuniętych i dlaczego.

Konfiguracja/warianty zestawu danych (tfds.core.BuilderConfig)

Niektóre zestawy danych mogą mieć wiele wariantów lub opcji dotyczących sposobu wstępnego przetwarzania danych i zapisywania ich na dysku. Na przykład cycle_gan ma jedną konfigurację na parę obiektów ( cycle_gan/horse2zebra , cycle_gan/monet2photo ,...).

Odbywa się to poprzez tfds.core.BuilderConfig s:

  1. Zdefiniuj obiekt konfiguracyjny jako podklasę tfds.core.BuilderConfig . Na przykład MyDatasetConfig .

    @dataclasses.dataclass
    class MyDatasetConfig(tfds.core.BuilderConfig):
      img_size: Tuple[int, int] = (0, 0)
    
  2. Zdefiniuj element członkowski klasy BUILDER_CONFIGS = [] w MyDataset , który zawiera listę MyDatasetConfig s, które uwidacznia zestaw danych.

    class MyDataset(tfds.core.GeneratorBasedBuilder):
      VERSION = tfds.core.Version('1.0.0')
      # pytype: disable=wrong-keyword-args
      BUILDER_CONFIGS = [
          # `name` (and optionally `description`) are required for each config
          MyDatasetConfig(name='small', description='Small ...', img_size=(8, 8)),
          MyDatasetConfig(name='big', description='Big ...', img_size=(32, 32)),
      ]
      # pytype: enable=wrong-keyword-args
    
  3. Użyj self.builder_config w MyDataset , aby skonfigurować generowanie danych (np. shape=self.builder_config.img_size ). Może to obejmować ustawienie innych wartości w _info() lub zmianę dostępu do pobieranych danych.

Uwagi:

  • Każda konfiguracja ma unikalną nazwę. W pełni kwalifikowana nazwa konfiguracji to dataset_name/config_name (np. coco/2017 ).
  • Jeśli nie zostanie określony, zostanie użyta pierwsza konfiguracja w BUILDER_CONFIGS (np tfds.load('c4') domyślnie c4/en )

Zobacz anli , aby zapoznać się z przykładem zestawu danych, który używa BuilderConfig s.

Wersja

Wersja może odnosić się do dwóch różnych znaczeń:

  • Oryginalna wersja danych „zewnętrzna”: np. COCO v2019, v2017,...
  • „Wewnętrzna” wersja kodu TFDS: np. zmień nazwę funkcji w tfds.features.FeaturesDict , napraw błąd w _generate_examples

Aby zaktualizować zestaw danych:

  • W przypadku „zewnętrznej” aktualizacji danych: wielu użytkowników może jednocześnie chcieć uzyskać dostęp do określonego roku/wersji. Odbywa się to za pomocą jednego tfds.core.BuilderConfig na wersję (np. coco/2017 , coco/2019 ) lub jednej klasy na wersję (np. Voc2007 , Voc2012 ).
  • W przypadku „wewnętrznej” aktualizacji kodu: użytkownicy pobierają tylko najnowszą wersję. Każda aktualizacja kodu powinna zwiększać atrybut klasy VERSION (np. z 1.0.0 do VERSION = tfds.core.Version('2.0.0') ) po wersjonowaniu semantycznym .

Dodaj import do rejestracji

Nie zapomnij zaimportować modułu zestawu danych do swojego projektu __init__ , aby został on automatycznie zarejestrowany w tfds.load , tfds.builder .

import my_project.datasets.my_dataset  # Register MyDataset

ds = tfds.load('my_dataset')  # MyDataset available

Na przykład, jeśli współtworzysz tensorflow/datasets , dodaj moduł import do jego podkatalogu __init__.py (np. image/__init__.py .

Sprawdź typowe problemy z implementacją

Proszę sprawdzić typowe błędy implementacji .

Przetestuj swój zestaw danych

Pobierz i przygotuj: tfds build

Aby wygenerować zestaw danych, uruchom tfds build z katalogu my_dataset/ :

cd path/to/datasets/my_dataset/
tfds build --register_checksums

Kilka przydatnych flag do programowania:

  • --pdb : Wejdź w tryb debugowania, jeśli zostanie zgłoszony wyjątek.
  • --overwrite : Usuń istniejące pliki, jeśli zestaw danych został już wygenerowany.
  • --max_examples_per_split : Generuj tylko pierwsze X przykładów (domyślnie 1), zamiast pełnego zestawu danych.
  • --register_checksums : Zapisz sumy kontrolne pobranych adresów URL. Powinien być używany tylko podczas opracowywania.

Zobacz dokumentację interfejsu CLI, aby uzyskać pełną listę flag.

Sumy kontrolne

Zaleca się zapisywanie sum kontrolnych zestawów danych, aby zagwarantować determinizm, pomoc w dokumentacji,... Odbywa się to poprzez wygenerowanie zestawu danych za pomocą opcji --register_checksums (patrz poprzednia sekcja).

Jeśli udostępniasz swoje zestawy danych przez PyPI, nie zapomnij wyeksportować plików checksums.tsv (np. w package_data twojego setup.py ).

Przetestuj swój zestaw danych

tfds.testing.DatasetBuilderTestCase to podstawowy TestCase do pełnego sprawdzenia zestawu danych. Używa „fikcyjnych danych” jako danych testowych, które naśladują strukturę źródłowego zestawu danych.

  • Dane testowe należy umieścić w my_dataset/dummy_data/ i powinny naśladować artefakty źródłowego zestawu danych w postaci pobranej i wyodrębnionej. Można go utworzyć ręcznie lub automatycznie za pomocą skryptu ( przykładowy skrypt ).
  • Pamiętaj, aby używać różnych danych w testowych podziałach danych, ponieważ test zakończy się niepowodzeniem, jeśli podziały zbiorów danych będą się pokrywać.
  • Dane testowe nie powinny zawierać żadnych materiałów chronionych prawem autorskim . W razie wątpliwości nie twórz danych przy użyciu materiału z oryginalnego zbioru danych.
import tensorflow_datasets as tfds
from . import my_dataset_dataset_builder


class MyDatasetTest(tfds.testing.DatasetBuilderTestCase):
  """Tests for my_dataset dataset."""
  DATASET_CLASS = my_dataset_dataset_builder.Builder
  SPLITS = {
      'train': 3,  # Number of fake train example
      'test': 1,  # Number of fake test example
  }

  # If you are calling `download/download_and_extract` with a dict, like:
  #   dl_manager.download({'some_key': 'http://a.org/out.txt', ...})
  # then the tests needs to provide the fake output paths relative to the
  # fake data directory
  DL_EXTRACT_RESULT = {
      'name1': 'path/to/file1',  # Relative to my_dataset/dummy_data dir.
      'name2': 'file2',
  }


if __name__ == '__main__':
  tfds.testing.test_main()

Uruchom następujące polecenie, aby przetestować zestaw danych.

python my_dataset_test.py

Wyślij nam opinie

Nieustannie staramy się ulepszyć przepływ pracy tworzenia zestawu danych, ale możemy to zrobić tylko wtedy, gdy jesteśmy świadomi problemów. Jakie problemy lub błędy napotkałeś podczas tworzenia zbioru danych? Czy była jakaś część, która była myląca lub nie działała za pierwszym razem?

Podziel się swoją opinią na GitHub .