TensorFlow
data augmentation
dataset API
machine learning
computer vision

Correct way of doing data augmentation in TensorFlow with the dataset api?

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

The correct way to do data augmentation with tf.data is to apply random transformations only to the training dataset, inside the input pipeline, using TensorFlow operations rather than Python-side image code. That keeps augmentation fast, reproducible enough to debug, and compatible with batching, prefetching, and accelerator training.

The Basic Pipeline Shape

A good training pipeline usually follows this order:

  • load and decode the example
  • shuffle training data
  • apply augmentation with map
  • batch
  • prefetch

Validation and test datasets should skip the random augmentation step so metrics reflect real, stable inputs.

python
1import tensorflow as tf
2
3def preprocess(image, label):
4    image = tf.image.convert_image_dtype(image, tf.float32)
5    image = tf.image.resize(image, [224, 224])
6    return image, label
7
8def augment(image, label):
9    image = tf.image.random_flip_left_right(image)
10    image = tf.image.random_brightness(image, max_delta=0.2)
11    return image, label
12
13train_ds = raw_train_ds.map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)
14train_ds = train_ds.shuffle(1000)
15train_ds = train_ds.map(augment, num_parallel_calls=tf.data.AUTOTUNE)
16train_ds = train_ds.batch(32).prefetch(tf.data.AUTOTUNE)
17
18val_ds = raw_val_ds.map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)
19val_ds = val_ds.batch(32).prefetch(tf.data.AUTOTUNE)

This is the core idea: training gets randomness, evaluation does not.

Why map Is the Right Place

Putting augmentation in Dataset.map(...) means each example is transformed on the fly as the pipeline feeds the model. You do not need to save augmented files to disk unless you have a specific offline-data requirement.

On-the-fly augmentation has three advantages:

  • it keeps storage small
  • it produces a new random view of the same example across epochs
  • it composes naturally with parallel data loading

That is why tf.data is usually a better place for augmentation than a hand-written Python loop.

Keep the Augmentation TensorFlow-Native

Use TensorFlow image ops or Keras preprocessing layers, not arbitrary Python image manipulation inside map. Python-side logic can become a performance bottleneck and can interfere with graph execution.

For example, Keras preprocessing layers can be embedded in the pipeline:

python
1import tensorflow as tf
2
3augmenter = tf.keras.Sequential([
4    tf.keras.layers.RandomFlip("horizontal"),
5    tf.keras.layers.RandomRotation(0.1),
6    tf.keras.layers.RandomZoom(0.1),
7])
8
9def augment_batch(images, labels):
10    return augmenter(images, training=True), labels
11
12train_ds = raw_train_ds.map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)
13train_ds = train_ds.shuffle(1000).batch(32)
14train_ds = train_ds.map(augment_batch, num_parallel_calls=tf.data.AUTOTUNE)
15train_ds = train_ds.prefetch(tf.data.AUTOTUNE)

This batched form is often convenient because many Keras augmentation layers naturally work on batches.

Labels Must Stay Correct

Most augmentation bugs are label bugs. If you flip or crop an image for image classification, the label usually stays the same. If you are doing object detection, segmentation, keypoints, or OCR, the labels often need to be transformed along with the image.

That means the "correct way" depends on the task. For classification, augmenting only the image tensor is usually enough. For structured prediction, you must update boxes, masks, or coordinates in the same map function.

Reproducibility and Performance

Random augmentation is intentionally nondeterministic, but you can still make training easier to debug by controlling seeds and by keeping the pipeline observable. Two practical rules help:

  • use TensorFlow random ops instead of Python's random module inside the pipeline
  • keep num_parallel_calls=tf.data.AUTOTUNE and prefetch(tf.data.AUTOTUNE) so augmentation does not stall model execution

If the input pipeline is slow, the GPU or TPU ends up waiting for augmented batches instead of training.

Common Pitfalls

  • Applying augmentation to validation or test data and then trusting the resulting metrics.
  • Doing augmentation in Python code outside tf.data, which often becomes slow and harder to scale.
  • Forgetting to transform labels for tasks more complex than classification.
  • Caching randomly augmented data in the wrong place and freezing the same randomness every epoch.
  • Putting expensive augmentation after a weak pipeline and then blaming the model for poor throughput.

Summary

  • Augment training data inside the tf.data pipeline with map.
  • Keep validation and test datasets deterministic and unaugmented.
  • Use TensorFlow ops or Keras preprocessing layers instead of Python-side image code.
  • Preserve label correctness when augmentations change geometry.
  • Combine augmentation with batching and prefetching so the model is not starved by the input pipeline.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design