実装に関する一般的な注意点

このページでは、新しいデータセットを実装する際の一般的な実装の問題について説明します。

従来のSplitGenerator避けるべきです

古いtfds.core.SplitGenerator API は非推奨になりました。

def _split_generator(...):
  return [
      tfds.core.SplitGenerator(name='train', gen_kwargs={'path': train_path}),
      tfds.core.SplitGenerator(name='test', gen_kwargs={'path': test_path}),
  ]

以下のように置き換える必要があります。

def _split_generator(...):
  return {
      'train': self._generate_examples(path=train_path),
      'test': self._generate_examples(path=test_path),
  }

理論的根拠: 新しい API は冗長性が低く、より明示的になっています。古い API は将来のバージョンで削除される予定です。

新しいデータセットはフォルダー内に自己完結型である必要があります

tensorflow_datasets/リポジトリ内にデータセットを追加する場合は、フォルダーとしてのデータセット構造 (すべてのチェックサム、ダミー データ、フォルダー内に自己完結型の実装コード) に従っていることを確認してください。

  • 古いデータセット (不良): <category>/<ds_name>.py
  • 新しいデータセット (良好): <category>/<ds_name>/<ds_name>.py

TFDS CLI ( tfds new 、またはgtfds new for googlers) を使用してテンプレートを生成します。

根拠: 古い構造では、チェックサムや偽のデータに絶対パスが必要で、データセット ファイルがさまざまな場所に分散されていました。そのため、TFDS リポジトリの外部にデータセットを実装することが困難になってきました。一貫性を保つために、今後はどこでも新しい構造を使用する必要があります。

説明リストはマークダウンとしてフォーマットする必要があります

DatasetInfo.description strマークダウンとしてフォーマットされます。マークダウン リストでは、最初の項目の前に空の行が必要です。

_DESCRIPTION = """
Some text.
                      # << Empty line here !!!
1. Item 1
2. Item 1
3. Item 1
                      # << Empty line here !!!
Some other text.
"""

根拠: 不適切な形式の説明により、カタログ ドキュメントに視覚的なアーチファクトが作成されます。空行がないと、上記のテキストは次のように表示されます。

いくつかのテキスト。 1. 項目 1 2. 項目 1 3. 項目 1 他のテキスト

クラスラベル名を忘れた場合

tfds.features.ClassLabelを使用する場合は、人間が読めるラベルstrnames=またはnames_file= ( num_classes=10の代わりに) を指定するようにしてください。

features = {
    'label': tfds.features.ClassLabel(names=['dog', 'cat', ...]),
}

理論的根拠: 人間が判読できるラベルはさまざまな場所で使用されています。

画像の形を忘れた

tfds.features.Imagetfds.features.Videoを使用する場合、画像が静的な形状を持つ場合は、明示的に指定する必要があります。

features = {
    'image': tfds.features.Image(shape=(256, 256, 3)),
}

理論的根拠: バッチ処理に必要な静的な形状推論 (例: ds.element_spec['image'].shape ) が可能になります (未知の形状の画像をバッチ処理するには、最初にサイズを変更する必要があります)。

tfds.features.Tensorの代わりに、より具体的な型を優先します。

可能であれば、汎用のtfds.features.Tensorではなく、より具体的な型tfds.features.ClassLabeltfds.features.BBoxFeatures 、... を優先してください。

根拠: 特定の機能は、意味的により正確であることに加えて、追加のメタデータをユーザーに提供し、ツールによって検出されます。

グローバル空間での遅延インポート

遅延インポートはグローバル空間から呼び出すべきではありません。たとえば、次のような記述は誤りです。

tfds.lazy_imports.apache_beam # << Error: Import beam in the global scope

def f() -> beam.Map:
  ...

理論的根拠: グローバル スコープで遅延インポートを使用すると、すべての tfds ユーザーに対してモジュールがインポートされるため、遅延インポートの目的が無効になります。

トレーニング/テスト分割を動的に計算する

データセットが公式の分割を提供しない場合、TFDS も提供すべきではありません。次のことは避けてください。

_TRAIN_TEST_RATIO = 0.7

def _split_generator():
  ids = list(range(num_examples))
  np.random.RandomState(seed).shuffle(ids)

  # Split train/test
  train_ids = ids[_TRAIN_TEST_RATIO * num_examples:]
  test_ids = ids[:_TRAIN_TEST_RATIO * num_examples]
  return {
      'train': self._generate_examples(train_ids),
      'test': self._generate_examples(test_ids),
  }

理論的根拠: TFDS は、元のデータに限りなく近いデータセットを提供しようとします。ユーザーが必要なサブスプリットを動的に作成できるようにするには、代わりにサブスプリット APIを使用する必要があります。

ds_train, ds_test = tfds.load(..., split=['train[:80%]', 'train[80%:]'])

Python スタイルガイド

pathlib API を使用することを推奨します

tf.io.gfile API の代わりに、 pathlib APIを使用することをお勧めします。すべてのdl_managerメソッドは、GCS、S3 などと互換性のある 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())

理論的根拠: pathlib API は、ボイラープレートを削除する最新のオブジェクト指向ファイル API です。 .read_text() / .read_bytes()を使用すると、ファイルが正しく閉じられることも保証されます。

メソッドがselfを使用していない場合は、関数である必要があります

クラスメソッドがselfを使用していない場合、それは単純な関数 (クラスの外部で定義される) である必要があります。

理論的根拠: これにより、関数に副作用や隠蔽された入出力がないことが読者に明示されます。

x = f(y)  # Clear inputs/outputs

x = self.f(y)  # Does f depend on additional hidden variables ? Is it stateful ?

Python での遅延インポート

TensorFlow のような大きなモジュールを遅延インポートします。遅延インポートでは、モジュールの実際のインポートがモジュールの最初の使用まで延期されます。したがって、この大きなモジュールを必要としないユーザーは決してインポートしません。 etils.epy.lazy_importsを使用します。

from tensorflow_datasets.core.utils.lazy_imports_utils import tensorflow as tf
# After this statement, TensorFlow is not imported yet

...

features = tfds.features.Image(dtype=tf.uint8)
# After using it (`tf.uint8`), TensorFlow is now imported

内部では、 LazyModuleクラスはファクトリとして機能し、属性がアクセスされたとき ( __getattr__ ) にのみ実際にモジュールをインポートします。

コンテキスト マネージャーと組み合わせて便利に使用することもできます。

from etils import epy

with epy.lazy_imports(error_callback=..., success_callback=...):
  import some_big_module