カスタムデータセットを書く

新しいデータセットを作成するには(TFDS または独自のリポジトリ)、このガイドに従ってください。

必要なデータセットがすでに存在するかどうか、データセットのリストを確認してください。

要約

新しいデータセットを書く場合の最も簡単な方法は、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/`

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)
  • データセットはどのように見えるか(特徴量)
  • データはどのように分割されるか(TRAINTEST など)
  • データセット内の個別の Example

データセットを書く

デフォルトテンプレート: 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) を探して、適宜、変更してください。

データセットの Example

すべてのデータセットには、ほとんどのボイラープレートを処理する 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: データセットのメタデータ

_infotfds.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,
  )

ほとんどのフィールドには説明はいりませんが、以下にいくつか補足します。

BibText CITATIONS.bib ファイルを書く:

  • データセットのウェブサイトで引用方法を検索します(BibTex 形式で使用します)。
  • arXiv の論文: 論文を見つけ、右側の BibText リンクをクリックします。
  • Google Scholar で論文を検索し、題名の下にある二重引用符をクリックし、ポップアップ表示に示される BibTeX をクリックします。
  • 関連する論文がない場合(ウェブサイトのみなど)、BibTeX Online Editor を使用して、カスタム 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: データのダウンロードと分割

ソースデータをダウンロードして抽出する

ほとんどのデータセットはウェブからダウンロードする必要があります。これは、_split_generatorstfds.download.DownloadManager 入力引数を使用して行います。dl_manager には、次のメソッドがあります。

  • download: http(s)://ftp(s):// がサポートされています。
  • extract: 現在サポートされているのは .zip.gz、および .tar ファイルです。
  • download_and_extract: dl_manager.extract(dl_manager.download(urls)) と同じです。

これらのすべてのメソッドは tfds.core.Pathepath.Path のエイリアス)を戻し、これらは pathlib.Path のようなオブジェクトです。

これらのメソッドは次のように、任意のネストされた構造(listdict など)をサポートしています。

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() など)。

データセットの分割を指定する

データセットに事前定義済みの Split が含まれる場合(たとえば MNIST には traintest の Split が含まれます)、それらを維持してください。含まれない場合は、単一の all Split のみを指定します。ユーザーはサブスプリット API で独自のサブスプリットを動的に作成できます(split='train[80%:]' など)。Split 名には、前述の 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: Example ジェネレータ

_generate_examples はソースデータから各 Split の Example を生成します。

このメソッドは通常、ソースデータセットのアーティファクト(CSV ファイルなど)を読み取り、(key, feature_dict) タプルを生成します。

  • key: Example の識別子。hash(key) を使って Example を確定的にシャッフルするか、シャッフルが無効である場合に key で並べ替えるために使用されます(「データセットの順序を維持する」をご覧ください)。次のようである必要があります。
    • 一意であること: 2 つの Example が同じ key を使用している場合、例がが発生します。
    • 確定的であること: download_diros.path.listdir の順などに依存してはいけません。データを 2 回生成すると、同じ kye が生成されてしまいます。
    • 比較可能であること: シャッフルが無効である場合、key はデータセットの並べ替えに使用されます。
  • feature_dict: Example の値を含む dict です。
    • 構造は tfds.core.DatasetInfo で定義されている features= 構造に一致する必要があります。
    • 複雑なデータタイプ(画像、動画、音声など)は自動的に暗号化されます。
    • 各特徴量は通常、複数の入力タイプを受け入れます(たとえば、動画は /path/to/vid.mp4np.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'],
      }

警告: 整数または文字列のブール値を解析する場合は、tfds.core.utils.bool_utils.parse_bool ユーティリティ関数を使用して、解析エラーを回避してください(例: bool("False") == True)。

ファイルのアクセスと tf.io.gfile

クラウドストレージシステムをサポートするために、Python のビルトイン I/O 演算子の使用は避けれください。

代わりに、dl_manager は Google Cloud Storage に対応する pathlib のようなオブジェクトを直接返します。

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

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

または、ファイル操作を行う場合は、ビルトインの代わりに tf.io.gfile API を使用してください。

Pathlib は tf.io.gfile よりも優先される必要があります(rational の項目をご覧ください)。

追加の依存関係

一部のデータセットでは、生成中のみに追加の Python 依存関係が必要となります。たとえば、SVHN データセットでは、データの読み込みに scipy を使用しています。

データセットを TFDS リポジトリに追加する場合は、tfds.core.lazy_imports を使用して tensorflow-datasets パッケージを小さく維持してください。ユーザーは必要な場合にのみ追加の依存関係をインストールします。

lazy_imports を使用するには、次を実行します。

  • データセットのエントリを setup.pyDATASET_EXTRAS に追加します。これにより、たとえば pip install 'tensorflow-datasets[svhn]' を使用して、追加の依存関係をインストールできるようになります。
  • インポートのエントリを LazyImporterLazyImportsTest に追加します。
  • tfds.core.lazy_imports を使用して、DatasetBuilder の依存関係(tfds.core.lazy_imports.scipy など)にアクセスします。

破損データ

一部のデータセットは完全にはクリーンでなく、破損データ(画像は JPEG ファイルですが無効な JPEG であるなど)が含まれているものもあります。これらのサンプルをスキップし、いくつのサンプルが削除され、なぜ削除されたのかのメモを残す必要があります。

データセットの構成/バリアント(tfds.core.BuilderConfig)

一部のデータセットには、複数のバリアントや、データの処理方法とディスクへの書き込み方法のオプションがあります。たとえば、cycle_gan の構成は、オブジェクトペア(cycle_gan/horse2zebracycle_gan/monet2photo など)ごとに 1 つです。

これは、tfds.core.BuilderConfig を使って以下のように行います。

  1. 構成オブジェクトを tfds.core.BuilderConfig のサブクラスとして、MyDatasetConfig というように定義します。

    @dataclasses.dataclass
    class MyDatasetConfig(tfds.core.BuilderConfig):
      img_size: Tuple[int, int] = (0, 0)
    

    注意: https://bugs.python.org/issue33129 のバグにより、デフォルト値が必要です。

  2. データセットが公開する MyDatasetConfig をリストする BUILDER_CONFIGS = [] クラスメンバーを MyDataset に定義します。

    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
    

    注意: # pytype: disable=wrong-keyword-args は必要です。データクラスの継承に関する Pytype バグがあります。

  3. self.builder_configMyDataset に使用して、データ生成を構成します(shape=self.builder_config.img_size など)。これには、_info() にさまざまな値の設定や、ダウンロードデータへのアクセスの変更が含まれる場合があります。

注意:

  • 構成にはそれぞれに一意の名前があります。構成の完全修飾名は dataset_name/config_name です(例: coco/2017)。
  • 指定されていない場合は、BUILDER_CONFIGS の最初の構成が使用されます(たとえば、tfds.load('c4') はデフォルトで c4/en となります)。

anli で、BuilderConfig を使用するデータセットの例をご覧ください。

バージョン

バージョンには、2 つの意味があります。

  • 「外部」の元のデータバージョン: COCO v2019、v2017、など。
  • 「内部」の TFDS コードバージョン: tfds.features.FeaturesDict の特徴量の名前の変更、_generate_examples のバグの修正など。

データセットを更新するには、次のように行います。

  • 「外部」データの更新: 不空数のユーザーが特定の年/バージョンに同時にアクセスすることがあります。これは、バージョン当たり 1 つの tfds.core.BuilderConfigcoco/2017coco/2019 など)またはバージョン当たり 1 つのクラス(Voc2007Voc2012 など)を使って行われます。
  • 「内部」コードの更新: ユーザーは最も最近のバージョンのみをダウンロードします。コードが更新されると、VERSION クラス属性が増加(1.0.0 から VERSION = tfds.core.Version('2.0.0') など)します。これはセマンティックバージョン管理に従います。

import を追加して登録する

プロジェクトの __init__ に忘れずにデータセットモジュールをインポートして、tfds.loadtfds.builder に自動的に登録されるようにします。

import my_project.datasets.my_dataset  # Register MyDataset

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

たとえば、tensorflow/datasets に貢献する場合は、モジュールのインポートをサブディレクトリの __init__.pyimage/__init__.py など)に追加します。

共通する実装の落とし穴を確認する

共通する実装の落とし穴をご覧ください。

データセットをテストする

ダウンロードと準備: tfds build

データセットを生成するには、my_dataset/ ディレクトリから tfds build を実行します。

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

開発に使用できる便利なフラグには、次のようなものがあります。

  • --pdb: 例外が発生すると、デバッグモードに入ります。
  • --overwrite: データセットがすでに生成されている場合、既存のファイルを削除します。
  • --max_examples_per_split: データセットすべてではなく、最初の X 個の Example (デフォルトは 1 個)のみを生成します。
  • --register_checksums: ダウンロード URL のチェックサムを記録します。開発中のみに使用します。

全フラグのリストは、CLI ドキュメントをご覧ください。

チェックサム

決定性の確保や文書化などのために、データセットのチェックサムを記録することをお勧めします。これは、--register_checksums を使ってデータセットを生成して行います(前のセクションをご覧ください)。

PyPI を介してデータセットをリリースする場合、忘れずに (setup.pypackage_data などの)checksums.tsv ファイルをエクスポートしてください。

データセットのユニットテストを行う

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 にお送りください。