Fast input pipelines

TensorFlow · Practical · 12 min

A slow input pipeline leaves expensive GPUs idle; tuning tf.data is often the cheapest speedup available.

Training steps on a GPU are fast; if loading and preprocessing the next batch takes longer, the accelerator waits. tf.data has built-in tools to overlap and parallelise that work:

  • .map(fn, num_parallel_calls=tf.data.AUTOTUNE) runs preprocessing on several CPU cores.
  • .prefetch(tf.data.AUTOTUNE) at the end of the pipeline prepares the next batches while the model trains on the current one. Almost every pipeline should end with it.
  • .cache() stores elements after the first epoch, in memory or in a file (cache("path")). Put it after expensive deterministic work but before random augmentation and shuffling.
  • .interleave(...) reads from several files concurrently, essential for sharded TFRecord datasets on network storage.
  • Vectorise: batching first and then mapping a function that handles a whole batch is often faster than mapping per element.

AUTOTUNE lets the runtime pick buffer sizes and parallelism dynamically.

A typical order: list files, interleave reads, parse/decode with parallel map, cache, shuffle, batch, augment, prefetch. To diagnose, time an epoch of the dataset alone without the model, or use the TensorBoard profiler's input-pipeline analyser. If the dataset alone is slower than training, the pipeline is your bottleneck.

Example

import tensorflow as tf

AUTOTUNE = tf.data.AUTOTUNE

def load(path):
    img = tf.io.decode_jpeg(tf.io.read_file(path), channels=3)
    return tf.image.resize(img, [224, 224]) / 255.0

def augment(batch):
    return tf.image.random_flip_left_right(batch)

files = tf.data.Dataset.list_files("images/*.jpg", shuffle=True)
ds = (files
      .map(load, num_parallel_calls=AUTOTUNE)    # decode on several cores
      .cache("/tmp/img_cache")                   # reuse decoded images after epoch 1
      .shuffle(1000)
      .batch(64)
      .map(augment, num_parallel_calls=AUTOTUNE) # random work after the cache
      .prefetch(AUTOTUNE))                       # overlap input with training
# model.fit(ds, epochs=10)

Remember

  • Parallel map + final prefetch(AUTOTUNE) in almost every pipeline
  • Cache after expensive deterministic steps, before random ones
  • Time the dataset alone to find input bottlenecks

Watch out for

  • Caching after random augmentation, which freezes one set of augmentations for all epochs.

Check yourself

  1. Where should .prefetch(tf.data.AUTOTUNE) usually go?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.tensorflow.org/guide/data_performance