Postępuj zgodnie z tym przewodnikiem, aby utworzyć nowy zestaw danych (w TFDS albo we własnym repozytorium).
Sprawdź naszą listę zestawów danych , aby upewnić się, czy interesujący Cię zestaw danych już się tam znajduje.
Krótko mówiąc
Najprostszym sposobem utworzenia nowego zestawu danych jest użycie interfejsu wiersza poleceń TFDS :
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.loadautomatycznie wykryje i załaduje zestaw danych wygenerowany w~/tensorflow_datasets/my_dataset/(np. przeztfds 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
Zbiory danych są dystrybuowane w najróżniejszych formatach i miejscach, a nie zawsze są przechowywane w formacie gotowym do wprowadzenia do procesu uczenia maszynowego. W tym momencie pojawia się TFDS.
TFDS przetwarza te zbiory danych do standardowego formatu (dane zewnętrzne -> pliki serializowane), który następnie można załadować jako potok uczenia maszynowego (pliki serializowane -> tf.data.Dataset ). Serializacja jest wykonywana tylko raz. Późniejszy dostęp będzie polegał na bezpośrednim odczycie tych wstępnie przetworzonych plików.
Większość wstępnego przetwarzania odbywa się automatycznie. Każdy zbiór danych implementuje podklasę tfds.core.DatasetBuilder , która określa:
- Skąd pochodzą dane (czyli ich adresy URL);
- Jak wygląda zbiór danych (tj. jakie są jego cechy);
- Jak dane powinny być podzielone (np.
TRAINiTEST); - i poszczególne przykłady w zbiorze danych.
Napisz swój zestaw danych
Domyślny szablon: tfds new
Użyj interfejsu wiersza poleceń TFDS CLI , aby wygenerować wymagane pliki szablonów w języku Python.
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 tutaj TODO(my_dataset) i zmodyfikuj odpowiednio.
Przykład zestawu danych
Wszystkie zestawy danych są zaimplementowane jako podklasy klasy tfds.core.DatasetBuilder , która zajmuje się większością szablonów. Obsługuje ona:
- Małe/średnie zbiory danych, które można wygenerować na jednym komputerze (ten samouczek).
- Bardzo duże zbiory danych wymagające rozproszonego generowania (przy użyciu Apache Beam , zobacz nasz przewodnik po dużych zbiorach danych )
Oto minimalny przykład konstruktora zbiorów danych opartego 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',
}
Należy pamiętać, że w przypadku niektórych konkretnych formatów danych udostępniamy gotowe do użycia narzędzia do tworzenia zbiorów danych , które wykonają większość zadań związanych z przetwarzaniem danych.
Przyjrzyjmy się szczegółowo trzem abstrakcyjnym metodom nadpisywania.
_info : metadane zbioru 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 uściśleń:
-
features: Określa strukturę zbioru danych, kształt,... Obsługa złożonych typów danych (audio, wideo, sekwencje zagnieżdżone,...). Więcej informacji można znaleźć w dostępnych funkcjach lub przewodniku po łącznikach funkcji . -
disable_shuffling: Zobacz sekcję Utrzymywanie kolejności zbiorów danych .
Zapisywanie pliku BibText CITATIONS.bib :
- Przeszukaj witrynę zbioru danych pod kątem instrukcji cytowania (użyj ich w formacie BibTex).
- W przypadku artykułów arXiv : znajdź artykuł i kliknij odnośnik
BibTextpo prawej stronie. - Znajdź artykuł w serwisie Google Scholar i kliknij znak cudzysłowu pod tytułem. W wyskakującym okienku kliknij
BibTeX. - Jeśli nie masz powiązanego dokumentu (na przykład istnieje tylko strona internetowa), możesz użyć edytora BibTeX Online , aby utworzyć własny wpis BibTeX (w menu rozwijanym znajduje się 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 zestawu danych.
- Lista prawidłowych tagów znajduje się w pliku tensorflow_datasets/core/valid_tags.txt .
- Aby dodać tag do tej listy, wyślij wiadomość PR.
Utrzymuj kolejność zbioru danych
Domyślnie rekordy zestawów danych są tasowane podczas przechowywania, aby zapewnić bardziej równomierny rozkład klas w całym zestawie danych, ponieważ często rekordy należące do tej samej klasy są ciągłe. Aby określić, że zestaw danych powinien być sortowany według klucza wygenerowanego przez _generate_examples , pole disable_shuffling powinno być ustawione na True . Domyślnie jest ono ustawione na False .
def _info(self):
return self.dataset_info_from_configs(
# [...]
disable_shuffling=True,
# [...]
)
Należy pamiętać, że wyłączenie funkcji tasowania ma wpływ na wydajność, ponieważ nie można już odczytywać fragmentów równolegle.
_split_generators : pobiera i dzieli dane
Pobieranie i wyodrębnianie danych źródłowych
Większość zestawów danych wymaga pobrania danych z internetu. Odbywa się to za pomocą argumentu wejściowego tfds.download.DownloadManager funkcji _split_generators . dl_manager ma następujące metody:
-
download: obsługujehttp(s)://,ftp(s):// -
extract: obecnie obsługuje pliki.zip,.gzi.tar. -
download_and_extract: Tak samo jakdl_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ą dowolną zagnieżdżoną strukturę ( list , dict ), taką 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 wyodrębnianie
Niektórych danych nie można pobrać automatycznie (np. wymagają logowania). W takim przypadku użytkownik musi ręcznie pobrać dane źródłowe i umieścić je w manual_dir/ (domyślnie ~/tensorflow_datasets/downloads/manual/ ).
Dostęp do plików można uzyskać poprzez 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)
...
Lokalizację manual_dir można dostosować za pomocą tfds build --manual_dir= lub korzystając z tfds.download.DownloadConfig .
Przeczytaj archiwum bezpośrednio
dl_manager.iter_archive odczytuje archiwa sekwencyjnie, bez ich rozpakowywania. Może to zaoszczędzić miejsce na dysku i poprawić wydajność w niektórych systemach plików.
for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
...
fobj ma te same metody co with open('rb') as fobj: (np. fobj.read() )
Określanie podziałów zbioru danych
Jeśli zbiór danych zawiera predefiniowane podziały (np. MNIST ma podziały train i test ), zachowaj je. W przeciwnym razie określ tylko jeden podział all . Użytkownicy mogą dynamicznie tworzyć własne podpodziały za pomocą interfejsu API podpodziałów (np. split='train[80%:]' ). Należy pamiętać, że jako nazwę podziału można użyć dowolnego ciągu alfabetycznego, z wyjątkiem wspomnianego wcześniej all .
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 : Generator przykładów
_generate_examples generuje przykłady dla każdego podziału na podstawie danych źródłowych.
Ta metoda zazwyczaj odczytuje artefakty źródłowego zbioru danych (np. plik CSV) i zwraca krotki (key, feature_dict) :
-
key: Identyfikator przykładu. Służy do deterministycznego tasowania przykładów za pomocąhash(key)lub do sortowania według klucza, gdy tasowanie jest wyłączone (patrz sekcja „Utrzymywanie kolejności zbioru danych ”). Powinno być:- unique : Jeśli dwa przykłady używają tego samego klucza, zostanie zgłoszony wyjątek.
- deterministyczny : Nie powinien zależeć od
download_dir,os.path.listdir, kolejności,... Dwukrotne wygenerowanie danych powinno dać ten sam klucz. - porównywalne : Jeśli tasowanie jest wyłączone, klucz zostanie użyty do posortowania zestawu danych.
-
feature_dict:dictzawierający przykładowe wartości.- Struktura powinna być zgodna ze strukturą
features=zdefiniowaną wtfds.core.DatasetInfo. - Złożone typy danych (obrazy, wideo, audio,...) zostaną zakodowane automatycznie.
- Każda funkcja często akceptuje wiele typów danych wejściowych (np. akceptacja wideo
/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 po złączach funkcji .
- Struktura powinna być zgodna ze strukturą
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 pliku i tf.io.gfile
Aby obsługiwać systemy pamięci masowej w chmurze, należy unikać korzystania z wbudowanych operacji wejścia/wyjścia języka Python.
Zamiast tego dl_manager zwraca obiekty typu pathlib, które są bezpośrednio kompatybilne z usługą przechowywania danych w chmurze Google:
path = dl_manager.download_and_extract('http://some-website/my_data.zip')
json_path = path / 'data/file.json'
json.loads(json_path.read_text())
Alternatywnie, użyj interfejsu API tf.io.gfile zamiast wbudowanego do operacji na plikach:
-
open->tf.io.gfile.GFile -
os.rename->tf.io.gfile.rename - ...
Należy preferować Pathlib zamiast tf.io.gfile (patrz rational .
Dodatkowe zależności
Niektóre zestawy danych wymagają dodatkowych zależności Pythona tylko podczas generowania. Na przykład zestaw danych SVHN używa scipy do załadowania niektórych danych.
Jeśli dodajesz zbiór danych do repozytorium TFDS, użyj tfds.core.lazy_imports , aby zachować niewielki rozmiar pakietu tensorflow-datasets . Użytkownicy będą instalować dodatkowe zależności tylko w razie potrzeby.
Aby użyć lazy_imports :
- Dodaj wpis dla swojego zestawu danych w
DATASET_EXTRASwsetup.py. Dzięki temu użytkownicy będą mogli na przykład wykonaćpip install 'tensorflow-datasets[svhn]'aby zainstalować dodatkowe zależności. - Dodaj wpis dotyczący importu do
LazyImporteriLazyImportsTest. - Użyj
tfds.core.lazy_imports, aby uzyskać dostęp do zależności (na przykładtfds.core.lazy_imports.scipy) wDatasetBuilder.
Uszkodzone dane
Niektóre zbiory danych nie są idealnie czyste i zawierają uszkodzone dane (na przykład obrazy są w plikach JPEG, ale niektóre są w nieprawidłowym formacie JPEG). Te przykłady należy pominąć, ale w opisie zbioru danych należy umieścić informację o liczbie pominiętych przykładów i przyczynach ich usunięcia.
Konfiguracja/warianty zestawu danych (tfds.core.BuilderConfig)
Niektóre zestawy danych mogą mieć wiele wariantów lub opcji dotyczących sposobu wstępnego przetwarzania i zapisywania danych na dysku. Na przykład cycle_gan ma jedną konfigurację na parę obiektów ( cycle_gan/horse2zebra , cycle_gan/monet2photo ,...).
Można to zrobić za pomocą pliku tfds.core.BuilderConfig :
Zdefiniuj obiekt konfiguracji jako podklasę
tfds.core.BuilderConfig. Na przykładMyDatasetConfig.@dataclasses.dataclass class MyDatasetConfig(tfds.core.BuilderConfig): img_size: Tuple[int, int] = (0, 0)Zdefiniuj element klasy
BUILDER_CONFIGS = []wMyDataset, który zawiera listę konfiguracjiMyDatasetConfigudostępnianych przez zbiór 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-argsUżyj
self.builder_configwMyDataset, 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 pobierania danych.
Uwagi:
- Każda konfiguracja ma unikalną nazwę. Pełna nazwa konfiguracji to
dataset_name/config_name(np.coco/2017). - Jeżeli nie określono inaczej, zostanie użyta pierwsza konfiguracja w
BUILDER_CONFIGS(np.tfds.load('c4')domyślniec4/en).
Przykład zestawu danych wykorzystującego BuilderConfig można znaleźć w anli .
Wersja
Wersja może odnosić się do dwóch różnych znaczeń:
- Wersja danych oryginalnych „zewnętrznych”, np. COCO v2019, v2017,...
- Wersja kodu „wewnętrznego” TFDS: np. zmiana nazwy funkcji w
tfds.features.FeaturesDict, naprawa błędu w_generate_examples
Aby zaktualizować zestaw danych:
- W przypadku aktualizacji danych „zewnętrznych”: wielu użytkowników może chcieć uzyskać dostęp do określonego roku/wersji jednocześnie. Można to zrobić, używając jednego pliku
tfds.core.BuilderConfigna wersję (np.coco/2017,coco/2019) lub jednej klasy na wersję (np.Voc2007,Voc2012). - W przypadku aktualizacji kodu „wewnętrznego”: Użytkownicy pobierają tylko najnowszą wersję. Każda aktualizacja kodu powinna zwiększyć atrybut klasy
VERSION(np. z1.0.0doVERSION = tfds.core.Version('2.0.0')) po przeprowadzeniu semantycznej kontroli wersji .
Dodaj import do rejestracji
Nie zapomnij zaimportować modułu zestawu danych do swojego projektu __init__ , aby został on automatycznie zarejestrowany w tfds.load i tfds.builder .
import my_project.datasets.my_dataset # Register MyDataset
ds = tfds.load('my_dataset') # MyDataset available
Na przykład, jeśli dodajesz coś do tensorflow/datasets , dodaj moduł import do jego podkatalogu __init__.py (np. image/__init__.py .
Sprawdź typowe problemy z implementacją
Sprawdź typowe problemy związane z implementacją .
Przetestuj swój zestaw danych
Pobierz i przygotuj: tfds build
Aby wygenerować zbiór danych, uruchom polecenie tfds build z katalogu my_dataset/ :
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Kilka przydatnych flag dla rozwoju:
-
--pdb: Przejdź do trybu debugowania, jeśli zostanie zgłoszony wyjątek. -
--overwrite: Usuń istniejące pliki, jeśli zestaw danych został już wygenerowany. -
--max_examples_per_split: Generuje tylko pierwsze X przykładów (domyślnie 1), a nie cały zestaw danych. -
--register_checksums: Rejestruje sumy kontrolne pobranych adresów URL. Należy z niego korzystać wyłącznie w fazie rozwoju.
Pełną listę flag można znaleźć w dokumentacji CLI .
Sumy kontrolne
Zaleca się rejestrowanie sum kontrolnych zestawów danych w celu zagwarantowania determinizmu, ułatwienia dokumentacji itd. Można to zrobić, generując zestaw danych za pomocą opcji --register_checksums (patrz poprzednia sekcja).
Jeśli udostępniasz zestawy danych za pośrednictwem PyPI, nie zapomnij wyeksportować plików checksums.tsv (np. w package_data w pliku setup.py ).
Przeprowadź test jednostkowy swojego zestawu danych
tfds.testing.DatasetBuilderTestCase to podstawowy TestCase do pełnego testowania zbioru danych. Wykorzystuje on „dane pozorne” jako dane testowe, które naśladują strukturę zbioru źródłowego.
- Dane testowe powinny zostać umieszczone w katalogu
my_dataset/dummy_data/i powinny odzwierciedlać artefakty źródłowego zbioru danych pobrane i wyodrębnione. Można je utworzyć ręcznie lub automatycznie za pomocą skryptu ( przykładowy skrypt ). - Pamiętaj o użyciu różnych danych w podziale danych testowych, ponieważ test zakończy się niepowodzeniem, jeśli podziały zbioru danych będą się na siebie nakładać.
- Dane testowe nie powinny zawierać żadnych materiałów chronionych prawem autorskim . W razie wątpliwości nie należy tworzyć danych, korzystając z materiałów z oryginalnego zestawu 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 opinię
Nieustannie staramy się usprawniać proces tworzenia zbioru danych, ale możemy to robić tylko wtedy, gdy jesteśmy świadomi problemów. Jakie problemy lub błędy napotkałeś podczas tworzenia zbioru danych? Czy jakaś część była myląca lub nie działała za pierwszym razem?
Podziel się swoją opinią na GitHub .