Path: blob/master/site/ja/datasets/determinism.ipynb
25115 views
Copyright 2020 The TensorFlow Authors.
TFDS と決定論
このドキュメントでは、以下について説明します。
TFDS は決定論を保証する
TFDS が例を読み取る順序
さまざまな警告と落とし穴
MNIST モデルをビルドする
データセット
TFDS がデータを読み取る仕組みを理解するには、何らかのこんてきすとが必要です。
TFDS は生成中に、元のデータを標準化された .tfrecord
ファイルに書き込みます。大型のデータセットの場合、複数の .tfrecord
ファイルが作成され、ファイルごとに複数の Example が含められます。これらの .tfrecord
ファイルはそれぞれシャードと呼ばれています。
このガイドでは、1024 個のシャードを持つ imagenet を使用します。
データセットの Example ID を特定する
決定論についてのみ関心がある場合は、次のセクションにスキップできます。
各データセットの Example は、id
によって一意に識別されています(例: 'imagenet2012-train.tfrecord-01023-of-01024__32'
)。この id
は、read_config.add_tfds_id = True
によって回復できます。これにより、tf.data.Dataset
からの dict に 'tfds_id'
キーが追加されます。
このチュートリアルでは、データセットの Example ID を出力する小さな util を定義します(人間が読めるように数値に変換します)。
読み取る際の決定論
このセクションでは、tfds.load
の決定論的保証を説明します。
shuffle_files=False
を使用する(デフォルト)
デフォルトでは、TFDS は決定論的に Example を生成します(shuffle_files=False
)。
パフォーマンスについては、TFDS は tf.data.Dataset.interleave を使用して同時に複数のシャードを読み取ります。この例では、TFDS が 16 個の Example(..., 14, 15, 1251, 1252, ...
)を読み取った後に、シャード 2 に切り替えているのがわかります。(..., 14, 15, 1251, 1252, ...
)。インターリーブについて以下をご覧ください。
同様に、subsplit API も決定論的です。
2 エポック以上をトレーニングしている場合、すべてのエポックが同じ順序でシャードを読み取るため、上記のセットアップは推奨されません(つまりランダム性は、ds = ds.shuffle(buffer)
バッファサイズに制限されています)。
shuffle_files=True
を使用する
shuffle_files=True
を使用すると、シャードはエポックごとにシャッフルされるため、読み取りは決定論的でなくなってしまいます。
注意: shuffle_files=True
に設定することでも、パフォーマンスを促進するために、tf.data.Options
で deterministic
が無効化されます。そのため、シャードが 1 つしかないような小さなデータセット(mnist など)であっても、非決定論的になります。
決定論的ファイルをシャッフルするには、以下のレシピをご覧ください。
決定論の注意事項: インターリーブ引数
read_config.interleave_cycle_length
を変更すると、read_config.interleave_block_length
によって Example の順序が変わります。
TFDS は tf.data.Dataset.interleave を使用して、一度に読み込むシャード数を少なくし、パフォーマンスの改善とメモリ使用率の低減を行っています。
Example の順序は、インターリーブ引数の固定値に対してのみ同じであることが保証されています。どの cycle_length
と block_length
が対応しているかも知るには、インターリーブのドキュメントをご覧ください。
cycle_length=16
、block_length=16
(デフォルト、上記と同じ):
cycle_length=3
、block_length=2
:
2 つ目の例では、データセットがシャード内の 2 つの Example(block_length=2
)を読み取ってから次のシャードに切り替えていることがわかります。2 x 3(cycle_length=3
)Example ごとに、最初のシャードに戻ります(shard0-ex0、shard0-ex1、shard1-ex0、shard1-ex1、shard2-ex0、shard2-ex1、shard0-ex2、shard0-ex3、shard1-ex2、shard1-ex3、shard2-ex2、など
)。
Subsplit と Example の順序
各 Example には id 0, 1, ..., num_examples-1
があります。subsplit API は、Example のスライス(例: train[:x]
select 0, 1, ..., x-1
)を選択します。
ただし、Subsplit の中では、Example は ID の昇順には読み取られません(シャードとインターリーブのため)。
より具体的には、ds.take(x)
と split='train[:x]'
は同等ではありません!
このことは、Example が様々なシャードから取得される上記のインターリーブの例で簡単に確認できます。
16(block_length)の Example の後、train[:25]
が最初のシャードの Example を読み取り続ける間、.take(25)
は次のシャードに切り替えます。
レシピ
決定論的ファイルシャッフル
決定的シャッフルを行うには 2 つの方法があります。
shuffle_seed
を設定する方法。注意: これにはエポックごとにシードを変更する必要があります。変更しない場合、シャードは、エポックごとに同じ順序で読み取られてしまいます。
experimental_interleave_sort_fn
を使用する方法: この場合、ds.shuffle
の順序に依存せずに、どのシャードがどの順序で読み取られるかを完全に制御できます。
決定論的プリエンプティブルパイプライン
これはより複雑なレシピです。簡単で満足のいくソリューションはありません。
ds.shuffle
を使用せず、決定論的シャッフルを使用すると、理論的には、読み取られた Example をカウントし、どの Example が書くシャード内で読み取られたか(関数cycle_length
、block_length
、およびシャード順)を演繹することは可能です。その後に、skip
と各シャードのtake
をexperimental_interleave_sort_fn
を介して注入することができます。ds.shuffle
を使用した場合、完全なトレーニングパイプラインを再生せずにはほぼ不可能です。どの Example が読み取られたかを演繹するには、ds.shuffle
バッファの状態を保存する必要があります。Example は非連続的(たとえばshard5_ex2
,shard5_ex4
が読み取られてもshard5_ex3
は読み取られないなど)となる可能性があります。.ds.shuffle
を使用した場合、読み取られたすべての shards_ids/example_ids(tfds_id
から演繹)を保存し、そのからファイルの命令を演繹する方法が考えられます。
1.
の最も単純なケースは、.skip(x).take(y)
を train[x:x+y]
をマッチさせることです。これには以下が必要となります。
cycle_length=1
を設定する(シャードが順次読み取られるように)shuffle_files=False
を設定するds.shuffle
を使用しない
トレーニングが 1 エポックだけの大型のデータセットでのみ使用することをお勧めします。Example はデフォルトのシャッフル順に読み取られます。
特定の Subsplit でどのシャード/Example が読み取られたかを調べる
tfds.core.DatasetInfo
を使うと、読み取り命令に直接アクセスできます。