Следуйте этому руководству, чтобы создать новый набор данных (либо в TFDS, либо в собственном репозитории).
Проверьте наш список наборов данных , чтобы узнать, есть ли там уже нужный вам набор данных.
Вкратце:
Самый простой способ записать новый набор данных — использовать интерфейс командной строки 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/`
Чтобы использовать новый набор данных с помощью tfds.load('my_dataset') :
-
tfds.loadавтоматически обнаружит и загрузит набор данных, сгенерированный в~/tensorflow_datasets/my_dataset/(например, с помощьюtfds build). - В качестве альтернативы вы можете явно
import my.project.datasets.my_datasetдля регистрации вашего набора данных:
import my.project.datasets.my_dataset # Register `my_dataset`
ds = tfds.load('my_dataset') # `my_dataset` registered
Обзор
Наборы данных распространяются в самых разных форматах и в самых разных местах, и не всегда они хранятся в формате, готовом для использования в конвейере машинного обучения. Здесь на помощь приходит TFDS.
TFDS обрабатывает эти наборы данных, преобразуя их в стандартный формат (внешние данные -> сериализованные файлы), которые затем могут быть загружены в качестве конвейера машинного обучения (сериализованные файлы -> tf.data.Dataset ). Сериализация выполняется только один раз. Последующий доступ будет осуществляться непосредственно из этих предварительно обработанных файлов.
Большая часть предварительной обработки выполняется автоматически. Каждый набор данных реализует подкласс tfds.core.DatasetBuilder , который определяет:
- Откуда поступают данные (т.е. их URL-адреса);
- Как выглядит набор данных (т.е. его характеристики);
- Как следует разделить данные (например,
TRAINиTEST); - а также отдельные примеры в наборе данных.
Напишите свой набор данных
Шаблон по умолчанию: tfds new
Используйте TFDS CLI для генерации необходимых шаблонных файлов Python.
cd path/to/project/datasets/ # Or use `--dir=path/to/project/datasets/` below
tfds new my_dataset
Эта команда создаст новую папку my_dataset/ со следующей структурой:
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).
Найдите здесь TODO(my_dataset) и внесите соответствующие изменения.
Пример набора данных
Все наборы данных реализованы как подклассы tfds.core.DatasetBuilder , который берет на себя большую часть шаблонного кода. Он поддерживает:
- Небольшие/средние наборы данных, которые можно сгенерировать на одном компьютере (данное руководство).
- Очень большие наборы данных, требующие распределенной генерации (с использованием Apache Beam , см. наше руководство по работе с огромными наборами данных ).
Вот минимальный пример конструктора наборов данных, основанного на 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',
}
Обратите внимание, что для некоторых конкретных форматов данных мы предоставляем готовые к использованию конструкторы наборов данных , которые позаботятся о большей части обработки данных.
Давайте подробно рассмотрим 3 абстрактных метода перезаписи.
_info : метаданные набора данных
_info возвращает объект tfds.core.DatasetInfo , содержащий метаданные набора данных .
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,
)
Большинство полей должны быть понятны сами собой. Некоторые уточнения:
-
features: Этот параметр определяет структуру, форму и т.д. набора данных. Поддерживаются сложные типы данных (аудио, видео, вложенные последовательности и т.д.). Дополнительную информацию см. в описании доступных характеристик или в руководстве по коннектору характеристик . -
disable_shuffling: См. раздел «Сохранение порядка набора данных» .
Создание файла BibText CITATIONS.bib :
- Найдите на веб-сайте набора данных инструкции по цитированию (используйте их в формате BibTex).
- Чтобы найти статьи на arXiv , перейдите по ссылке
BibTextв правой части страницы. - Найдите статью в Google Scholar , нажмите на двойные кавычки под заголовком, а во всплывающем окне выберите
BibTeX. - Если к документу нет прикрепленного материала (например, есть только веб-сайт), вы можете использовать онлайн-редактор BibTeX для создания пользовательской записи BibTeX (в выпадающем меню есть тип записи
Online).
Обновление файла TAGS.txt :
- Все разрешенные теги уже предварительно заполнены в сгенерированном файле.
- Удалите все теги, которые не относятся к набору данных.
- Список допустимых тегов находится в файле tensorflow_datasets/core/valid_tags.txt .
- Чтобы добавить тег в этот список, пожалуйста, отправьте запрос на слияние (PR).
Сохраняйте порядок набора данных.
По умолчанию записи в наборах данных перемешиваются при сохранении, чтобы сделать распределение классов более равномерным по всему набору данных, поскольку записи, принадлежащие к одному и тому же классу, часто располагаются рядом. Чтобы указать, что набор данных должен быть отсортирован по ключу, сгенерированному функцией _generate_examples поле disable_shuffling следует установить в True . По умолчанию оно установлено в False .
def _info(self):
return self.dataset_info_from_configs(
# [...]
disable_shuffling=True,
# [...]
)
Следует помнить, что отключение перемешивания данных влияет на производительность, поскольку параллельное чтение фрагментов данных больше невозможно.
_split_generators : загружает и разделяет данные
Загрузка и извлечение исходных данных
Для большинства наборов данных необходимо загрузить данные из интернета. Это делается с помощью входного аргумента tfds.download.DownloadManager функции _split_generators . dl_manager имеет следующие методы:
-
download: поддерживаетhttp(s)://,ftp(s):// -
extract: в настоящее время поддерживаются файлы.zip,.gzи.tar. -
download_and_extract: То же самое, чтоdl_manager.extract(dl_manager.download(urls))
Все эти методы возвращают tfds.core.Path (псевдонимы для epath.Path ), которые представляют собой объекты , подобные pathlib.Path .
Эти методы поддерживают произвольную вложенную структуру ( list , dict ), например:
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/'),
}
Ручная загрузка и распаковка
Некоторые данные не могут быть загружены автоматически (например, требуется авторизация). В этом случае пользователь вручную загрузит исходные данные и поместит их в manual_dir/ (по умолчанию — ~/tensorflow_datasets/downloads/manual/ ).
Затем доступ к файлам можно получить через 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)
...
Расположение каталога manual_dir можно настроить с помощью tfds build --manual_dir= или с помощью tfds.download.DownloadConfig .
Читать архив напрямую
dl_manager.iter_archive считывает архивы последовательно, не извлекая их. Это может сэкономить место на диске и повысить производительность в некоторых файловых системах.
for filename, fobj in dl_manager.iter_archive('path/to/archive.zip'):
...
Методы fobj аналогичны методам with open('rb') as fobj: (например, fobj.read() ).
Указание разделения набора данных
Если набор данных содержит предопределенные разделения (например, MNIST имеет разделения train и test выборки), сохраните их. В противном случае укажите только одно разделение all . Пользователи могут динамически создавать свои собственные подразделения с помощью API подразделений (например, split='train[80%:]' ). Обратите внимание, что в качестве имени разделения можно использовать любую буквенную строку, кроме упомянутого выше 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 : Генератор примеров
_generate_examples генерирует примеры для каждого разделения данных из исходного набора.
Этот метод обычно считывает артефакты исходного набора данных (например, CSV-файл) и возвращает кортежи (key, feature_dict) :
-
key: Идентификатор примера. Используется для детерминированного перемешивания примеров с помощьюhash(key)или для сортировки по ключу, когда перемешивание отключено (см. раздел «Поддержание порядка в наборе данных »). Должно быть:- уникальный : Если два примера используют один и тот же ключ, будет сгенерировано исключение.
- Детерминированный : не должен зависеть от
download_dir, порядкаos.path.listdirи т.д. Генерация данных дважды должна дать один и тот же ключ. - Compared : Если перемешивание отключено, для сортировки набора данных будет использоваться ключ.
-
feature_dict:dict, содержащий примеры значений.- Структура должна соответствовать структуре
features=определенной вtfds.core.DatasetInfo. - Сложные типы данных (изображения, видео, аудио и т. д.) будут кодироваться автоматически.
- Каждая функция часто принимает несколько типов входных данных (например, для видео: accept
/path/to/vid.mp4,np.array(shape=(l, h, w, c)),List[paths],List[np.array(shape=(h, w, c)],List[img_bytes],...). - Дополнительную информацию см. в руководстве по подключению функций .
- Структура должна соответствовать структуре
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'],
}
Доступ к файлам и tf.io.gfile
Для поддержки облачных хранилищ следует избегать использования встроенных в Python операций ввода-вывода.
Вместо этого dl_manager возвращает объекты, подобные pathlib, напрямую совместимые с хранилищем 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())
В качестве альтернативы для операций с файлами можно использовать API tf.io.gfile вместо встроенного:
-
open->tf.io.gfile.GFile -
os.rename->tf.io.gfile.rename - ...
Следует отдавать предпочтение Pathlib перед tf.io.gfile (см. обоснование ).
Дополнительные зависимости
Для некоторых наборов данных дополнительные зависимости Python требуются только на этапе генерации. Например, в наборе данных SVHN для загрузки некоторых данных используется scipy .
Если вы добавляете набор данных в репозиторий TFDS, пожалуйста, используйте tfds.core.lazy_imports , чтобы пакет tensorflow-datasets оставался небольшим. Пользователи будут устанавливать дополнительные зависимости только по мере необходимости.
Для использования lazy_imports :
- Добавьте запись для вашего набора данных в раздел
DATASET_EXTRASвsetup.py. Это позволит пользователям, например, установить дополнительные зависимостиpip install 'tensorflow-datasets[svhn]'. - Добавьте запись для вашего импорта в
LazyImporterи вLazyImportsTest. - Используйте
tfds.core.lazy_importsдля доступа к зависимости (например,tfds.core.lazy_imports.scipy) в вашемDatasetBuilder.
Поврежденные данные
Некоторые наборы данных не являются идеально чистыми и содержат поврежденные данные (например, изображения находятся в файлах JPEG, но некоторые из них являются некорректными JPEG-файлами). Такие примеры следует пропустить, но в описании набора данных следует указать, сколько примеров было удалено и почему.
Конфигурация/варианты набора данных (tfds.core.BuilderConfig)
Некоторые наборы данных могут иметь несколько вариантов или параметров предварительной обработки данных и записи на диск. Например, для cycle_gan существует одна конфигурация для каждой пары объектов ( cycle_gan/horse2zebra , cycle_gan/monet2photo и т. д.).
Это делается с помощью tfds.core.BuilderConfig :
Определите свой объект конфигурации как подкласс
tfds.core.BuilderConfig. Например,MyDatasetConfig.@dataclasses.dataclass class MyDatasetConfig(tfds.core.BuilderConfig): img_size: Tuple[int, int] = (0, 0)Определите член класса
BUILDER_CONFIGS = []вMyDataset, который перечисляетMyDatasetConfig, предоставляемые набором данных.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Используйте
self.builder_configвMyDatasetдля настройки генерации данных (например,shape=self.builder_config.img_size). Это может включать установку различных значений в_info()или изменение доступа к данным для загрузки.
Примечания:
- Каждый конфигурационный файл имеет уникальное имя. Полное имя конфигурационного файла — это
dataset_name/config_name(например,coco/2017). - Если не указано иное, будет использоваться первый параметр конфигурации из
BUILDER_CONFIGS(например,tfds.load('c4')по умолчаниюc4/en).
Пример набора данных, использующего BuilderConfig , можно найти в anli .
Версия
Вариант может иметь два разных значения:
- Внешняя версия исходных данных: например, COCO v2019, v2017,...
- Внутренняя версия кода TFDS: например, переименование функции в
tfds.features.FeaturesDict, исправление ошибки в_generate_examples
Для обновления набора данных:
- Для обновления «внешних» данных: нескольким пользователям может потребоваться одновременный доступ к определенному году/версии. Это делается с помощью одного
tfds.core.BuilderConfigдля каждой версии (например,coco/2017,coco/2019) или одного класса для каждой версии (например,Voc2007,Voc2012). - Для «внутреннего» обновления кода: пользователи загружают только самую последнюю версию. Любое обновление кода должно увеличивать атрибут класса
VERSION(например, с1.0.0наVERSION = tfds.core.Version('2.0.0')) в соответствии с семантическим версионированием .
Добавить импорт для регистрации
Не забудьте импортировать модуль набора данных в ваш проект __init__ , чтобы он автоматически регистрировался в tfds.load и tfds.builder .
import my_project.datasets.my_dataset # Register MyDataset
ds = tfds.load('my_dataset') # MyDataset available
Например, если вы вносите свой вклад в tensorflow/datasets , добавьте импорт модуля в файл __init__.py его подкаталога (например, image/__init__.py .
Проверьте наличие распространенных ошибок при реализации.
Пожалуйста, ознакомьтесь с распространенными проблемами, возникающими при реализации .
Проверьте свой набор данных.
Скачайте и подготовьте: tfds build
Для генерации набора данных запустите tfds build из каталога my_dataset/ :
cd path/to/datasets/my_dataset/
tfds build --register_checksums
Несколько полезных флагов для разработки:
-
--pdb: Перейти в режим отладки, если возникнет исключение. -
--overwrite: Удалить существующие файлы, если набор данных уже был сгенерирован. -
--max_examples_per_split: Генерировать только первые X примеров (по умолчанию 1), а не весь набор данных. -
--register_checksums: Записывать контрольные суммы загруженных URL-адресов. Использовать только в процессе разработки.
Полный список флагов см. в документации по интерфейсу командной строки .
Контрольные суммы
Рекомендуется записывать контрольные суммы ваших наборов данных для обеспечения детерминированности, упрощения документирования и т. д. Это делается путем генерации набора данных с параметром --register_checksums (см. предыдущий раздел).
Если вы публикуете свои наборы данных через PyPI, не забудьте экспортировать файлы checksums.tsv (например, в папку package_data вашего setup.py ).
Проведите модульное тестирование вашего набора данных.
tfds.testing.DatasetBuilderTestCase — это базовый TestCase для полной проверки набора данных. В качестве тестовых данных используются «фиктивные данные», имитирующие структуру исходного набора данных.
- Тестовые данные следует поместить в каталог
my_dataset/dummy_data/и они должны имитировать артефакты исходного набора данных в том виде, в котором они были загружены и извлечены. Их можно создать вручную или автоматически с помощью скрипта ( пример скрипта ). - Обязательно используйте разные данные для разделения тестовых наборов данных, так как тест завершится неудачей, если данные в ваших наборах данных будут перекрываться.
- Тестовые данные не должны содержать материалов, защищенных авторским правом . В случае сомнений не используйте материалы из исходного набора данных для создания данных.
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()
Выполните следующую команду для проверки набора данных.
python my_dataset_test.py
Отправьте нам отзыв
Мы постоянно работаем над улучшением процесса создания наборов данных, но можем сделать это только в том случае, если знаем о существующих проблемах. С какими проблемами или ошибками вы столкнулись при создании набора данных? Была ли какая-то часть, которая вызывала путаницу или не работала с первого раза?
Пожалуйста, поделитесь своим мнением на GitHub .