Conseils de performances

Ce document fournit des conseils de performances spécifiques à TFDS. Notez que TFDS fournit des ensembles de données comme tf.data.Dataset s, donc les conseils du tf.data Guide est toujours valable.

Ensembles de données de référence

Utilisation tfds.benchmark(ds) de référence tout tf.data.Dataset objet.

Assurez - vous d'indiquer le batch_size= pour normaliser les résultats (par exemple 100 iter / s -> 3200 ex / s). Cela fonctionne avec tout itérable (par exemple tfds.benchmark(tfds.as_numpy(ds)) ).

ds = tfds.load('mnist', split='train').batch(32).prefetch()
# Display some benchmark statistics
tfds.benchmark(ds, batch_size=32)
# Second iteration is much faster, due to auto-caching
tfds.benchmark(ds, batch_size=32)

Petits ensembles de données (< Go)

Tous les ensembles de données TFDS stockent les données sur le disque dans le TFRecord format. Pour les petits ensembles de données (par exemple Mnist, ICRA, ...), la lecture de .tfrecord peut ajouter les frais généraux importants.

Comme ces ensembles de données tiennent en mémoire, il est possible d'améliorer considérablement les performances en mettant en cache ou en préchargeant l'ensemble de données. Notez que TFDS met automatiquement en cache les petits ensembles de données (voir la section suivante pour plus de détails).

Mettre en cache l'ensemble de données

Voici un exemple de pipeline de données qui met explicitement en cache l'ensemble de données après normalisation des images.

def normalize_img(image, label):
  """Normalizes images: `uint8` -> `float32`."""
  return tf.cast(image, tf.float32) / 255., label


ds, ds_info = tfds.load(
    'mnist',
    split='train',
    as_supervised=True,  # returns `(img, label)` instead of dict(image=, ...)
    with_info=True,
)
# Applying normalization before `ds.cache()` to re-use it.
# Note: Random transformations (e.g. images augmentations) should be applied
# after both `ds.cache()` (to avoid caching randomness) and `ds.batch()` (for
# vectorization [1]).
ds = ds.map(normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds = ds.cache()
# For true randomness, we set the shuffle buffer to the full dataset size.
ds = ds.shuffle(ds_info.splits['train'].num_examples)
# Batch after shuffling to get unique batches at each epoch.
ds = ds.batch(128)
ds = ds.prefetch(tf.data.experimental.AUTOTUNE)

Lors de l'itération sur cet ensemble de données, la deuxième itération sera beaucoup plus rapide que la première grâce à la mise en cache.

Mise en cache automatique

Par défaut, TFD caches d'auto-(avec ds.cache() ) ensembles de données répondant aux contraintes suivantes:

  • La taille totale de l'ensemble de données (toutes les divisions) est définie et < 250 Mio
  • shuffle_files est désactivé, ou seulement un seul tesson est lu

Il est possible de retirer de l' auto-mise en cache en passant try_autocaching=False à tfds.ReadConfig dans tfds.load . Consultez la documentation du catalogue de jeux de données pour voir si un jeu de données spécifique utilisera la mise en cache automatique.

Chargement des données complètes en un seul Tensor

Si votre ensemble de données tient dans la mémoire, vous pouvez également charger l'ensemble de données complet sous la forme d'un seul tableau Tensor ou NumPy. Il est possible de le faire par le réglage batch_size=-1 à lot tous les exemples dans un seul tf.Tensor . Ensuite , utilisez tfds.as_numpy pour la conversion de tf.Tensor à np.array .

(img_train, label_train), (img_test, label_test) = tfds.as_numpy(tfds.load(
    'mnist',
    split=['train', 'test'],
    batch_size=-1,
    as_supervised=True,
))

Grands ensembles de données

Les grands ensembles de données sont fragmentés (scindés en plusieurs fichiers) et ne tiennent généralement pas en mémoire, ils ne doivent donc pas être mis en cache.

Shuffle et entraînement

Pendant la formation, il est important de bien mélanger les données ; des données mal mélangées peuvent entraîner une diminution de la précision de l'entraînement.

En plus d'utiliser ds.shuffle pour mélanger les dossiers, vous devez également définir shuffle_files=True pour obtenir un bon comportement traînante pour grands ensembles de données qui sont fragmentées en plusieurs fichiers. Sinon, les époques liront les fragments dans le même ordre et les données ne seront donc pas vraiment aléatoires.

ds = tfds.load('imagenet2012', split='train', shuffle_files=True)

De plus, lorsque shuffle_files=True , TFDS désactivent options.experimental_deterministic , ce qui peut donner un coup de pouce légère performance. Pour obtenir brassage déterministe, il est possible d'opt-out de cette fonctionnalité avec tfds.ReadConfig : soit en fixant read_config.shuffle_seed ou écraser read_config.options.experimental_deterministic .

Partage automatique de vos données entre les travailleurs (TF)

Lorsque la formation sur plusieurs travailleurs, vous pouvez utiliser le input_context argument tfds.ReadConfig , de sorte que chaque travailleur va lire un sous - ensemble des données.

input_context = tf.distribute.InputContext(
    input_pipeline_id=1,  # Worker id
    num_input_pipelines=4,  # Total number of workers
)
read_config = tfds.ReadConfig(
    input_context=input_context,
)
ds = tfds.load('dataset', split='train', read_config=read_config)

Ceci est complémentaire à l'API subsplit. D' abord l'API subplit est appliqué ( le train[:50%] est converti en une liste de fichiers à lire), puis un ds.shard() op est appliquée sur ces fichiers. Exemple: lors de l' utilisation train[:50%] avec num_input_pipelines=2 , chacun des deux travailleur sera lu 1/4 des données.

Lorsque shuffle_files=True , les fichiers sont mélangés au sein d' un travailleur, mais pas entre les travailleurs. Chaque travailleur lira le même sous-ensemble de fichiers entre les époques.

Partage automatique de vos données entre les travailleurs (Jax)

Avec Jax, vous pouvez utiliser la tfds.even_splits API pour distribuer vos données sur les travailleurs. Voir le guide de l' API split .

splits = tfds.even_splits('train', n=jax.process_count(), drop_remainder=True)
# The current `process_index` load only `1 / process_count` of the data.
ds = tfds.load('my_dataset', split=splits[jax.process_index()])

Décodage d'image plus rapide

Par défaut, TFDS décode automatiquement les images. Cependant, il y a des cas où il peut être plus ignorer l'performant image de décodage avec tfds.decode.SkipDecoding et appliquer manuellement le tf.io.decode_image op:

  • Lors du filtrage des exemples (avec ds.filter ), pour décoder des images après des exemples ont été filtrés.
  • Lors du recadrage des images, d'utiliser la fusion tf.image.decode_and_crop_jpeg op.

Le code pour les deux exemples est disponible dans le guide de décodage .

Ignorer les fonctionnalités inutilisées

Si vous n'utilisez qu'un sous-ensemble des fonctionnalités, il est possible d'ignorer entièrement certaines fonctionnalités. Si votre jeu de données comporte de nombreuses fonctionnalités inutilisées, ne pas les décoder peut améliorer considérablement les performances. voir https://www.tensorflow.org/datasets/decode#only_decode_a_sub-set_of_the_features