TensorFlow
keras
image preprocessing
dataset
troubleshooting

Passing in training labels to tf.keras.preprocessing.image_dataset_from_directory doesn't work

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

image_dataset_from_directory is designed mainly for folder-based image classification, where labels are inferred from subdirectory names. Problems start when people try to pass their own label array without matching the exact file order that the function discovers internally. If the labels and files are not aligned one-for-one, the dataset silently becomes wrong or fails at runtime.

Understand what the function expects

Out of the box, the function expects a directory layout such as:

text
data/train/
  cats/
  dogs/

Then it can infer labels directly from the subdirectory names:

python
1import tensorflow as tf
2
3train_ds = tf.keras.preprocessing.image_dataset_from_directory(
4    "data/train",
5    image_size=(224, 224),
6    batch_size=32,
7    label_mode="int"
8)

This is the simplest and safest path when the folder structure already represents the classes.

If you pass labels, order is everything

When you provide the labels argument yourself, the list must match the exact order of files discovered by the function. That is where many bugs come from.

A debugging pattern looks like this:

python
1import pathlib
2import tensorflow as tf
3
4root = pathlib.Path("data/train")
5files = sorted(str(p) for p in root.glob("*/*.jpg"))
6labels = [0 if "/cats/" in p else 1 for p in files]
7
8train_ds = tf.keras.preprocessing.image_dataset_from_directory(
9    root,
10    labels=labels,
11    label_mode="int",
12    shuffle=False,
13    image_size=(224, 224),
14    batch_size=32
15)

Notice the shuffle=False. That is important while verifying alignment, because shuffling hides ordering mistakes.

The key idea is simple: the label array is not matched by filename string. It is matched by position.

Prefer class_names when the classes come from folders

If the real requirement is only to control class-to-index mapping, class_names is a better fit than a manual label array.

python
1train_ds = tf.keras.preprocessing.image_dataset_from_directory(
2    "data/train",
3    class_names=["cats", "dogs"],
4    label_mode="int",
5    image_size=(224, 224),
6    batch_size=32
7)

This keeps the mapping explicit without forcing you to maintain a custom label list manually.

Inspect a batch before training

Do not trust the pipeline until you inspect it:

python
for images, labels in train_ds.take(1):
    print(images.shape)
    print(labels[:10].numpy())

If you are using one-hot encoded labels, choose label_mode="categorical" and verify the batch shape matches the number of classes:

python
1val_ds = tf.keras.preprocessing.image_dataset_from_directory(
2    "data/val",
3    class_names=["cats", "dogs"],
4    label_mode="categorical",
5    image_size=(224, 224),
6    batch_size=32
7)

Early inspection is much cheaper than discovering label drift after hours of training.

Use tf.data when labels come from external metadata

If your labels live in a CSV, database, or annotation file rather than in folder names, building the dataset manually is often the cleaner solution.

python
1import pandas as pd
2import tensorflow as tf
3
4meta = pd.DataFrame(
5    {
6        "path": ["data/train/cat1.jpg", "data/train/dog1.jpg"],
7        "label": [0, 1],
8    }
9)
10
11def load_image(path, label):
12    image = tf.io.read_file(path)
13    image = tf.image.decode_jpeg(image, channels=3)
14    image = tf.image.resize(image, [224, 224])
15    image = tf.cast(image, tf.float32) / 255.0
16    return image, label
17
18ds = tf.data.Dataset.from_tensor_slices((meta["path"].tolist(), meta["label"].tolist()))
19ds = ds.map(load_image).batch(32)

That approach gives you full control and avoids reverse-engineering image_dataset_from_directory when your data no longer matches its intended use case.

Common Pitfalls

The most common mistake is assuming a custom labels list is matched to filenames automatically. It is matched only by position.

Another common issue is leaving shuffle=True while debugging alignment. That makes it much harder to verify whether labels correspond to the correct files.

People also use manual labels when class_names would have solved the actual problem more safely.

Finally, if labels come from external metadata, forcing them into image_dataset_from_directory is often more fragile than building a simple tf.data pipeline directly.

Summary

  • 'image_dataset_from_directory works best when labels come from folder names.'
  • If you pass labels, they must match the discovered file order exactly.
  • Use shuffle=False while validating custom label alignment.
  • Use class_names when you only need a stable class-to-index mapping.
  • Switch to tf.data when labels come from external metadata rather than the directory tree.

Course illustration
Course illustration

All Rights Reserved.