Skip to main content

Python TensorFlow: How to Use from_tensors() and from_tensor_slices() in TensorFlow Dataset in Python

Efficient data pipelines are crucial for training deep learning models at scale. TensorFlow's tf.data.Dataset API provides two fundamental methods for creating datasets from in-memory data: from_tensors() and from_tensor_slices(). Despite their similar names, these methods structure data in fundamentally different ways. One creates a single-element dataset containing all the data as one block, while the other slices the data into individual elements along the first axis.

Understanding this distinction is essential for building correct training pipelines and avoiding subtle shape mismatches that can be difficult to debug.

The Core Difference

Both methods accept the same input types (tensors, NumPy arrays, Python lists), but they interpret that input differently:

MethodBehaviorResulting Dataset Size
from_tensors()Treats the entire input as one elementAlways 1 element
from_tensor_slices()Slices along the first dimensionN elements (size of axis 0)

Using from_tensors(): A Single Block

from_tensors() wraps the entire input as a single dataset element, regardless of its shape or size:

import tensorflow as tf

# Create a 3x2 tensor
data = tf.constant([[1, 2], [3, 4], [5, 6]])

# Creates a dataset with ONE element: the entire 3x2 matrix
dataset = tf.data.Dataset.from_tensors(data)

print(f"Dataset size: {len(list(dataset))}")

for element in dataset:
print(f"Shape: {element.shape}")
print(element.numpy())

Output:

Dataset size: 1
Shape: (3, 2)
[[1 2]
[3 4]
[5 6]]

The entire 3x2 matrix is treated as a single item. Iterating over the dataset yields exactly one element.

When to use from_tensors():

  • Embedding lookup tables or reference data that should remain whole
  • Constants that need to be accessed as a complete unit during training
  • Single large items like a full image that requires specific processing as one piece

Using from_tensor_slices(): A Stream of Individual Elements

from_tensor_slices() creates individual elements by slicing along the first axis. This is the standard approach for building training datasets where each row represents a separate sample:

import tensorflow as tf

# Same 3x2 tensor
data = tf.constant([[1, 2], [3, 4], [5, 6]])

# Creates a dataset with THREE elements: one per row
dataset = tf.data.Dataset.from_tensor_slices(data)

print(f"Dataset size: {len(list(dataset))}")

for i, element in enumerate(dataset):
print(f"Element {i}: {element.numpy()}")

Output:

Dataset size: 3
Element 0: [1 2]
Element 1: [3 4]
Element 2: [5 6]

Each row of the original matrix becomes its own dataset element.

info

Each element produced by from_tensor_slices() has one fewer dimension than the input. A tensor of shape (100, 28, 28) becomes 100 individual elements, each with shape (28, 28). This is exactly what you want when each slice along axis 0 represents a separate data sample.

Side-by-Side Visual Comparison

Seeing both methods applied to the same data makes the difference unmistakable:

import tensorflow as tf

data = tf.constant([[1, 2], [3, 4], [5, 6]])

# from_tensors: 1 element containing everything
ds_tensors = tf.data.Dataset.from_tensors(data)
print("from_tensors elements:")
for item in ds_tensors:
print(f" {item.numpy()}")

print()

# from_tensor_slices: 3 separate elements, one per row
ds_slices = tf.data.Dataset.from_tensor_slices(data)
print("from_tensor_slices elements:")
for item in ds_slices:
print(f" {item.numpy()}")

Output:

from_tensors elements:
[[1 2]
[3 4]
[5 6]]

from_tensor_slices elements:
[1 2]
[3 4]
[5 6]

The first method yields a single 2D array. The second yields three separate 1D arrays.

Working with Feature-Label Pairs

from_tensor_slices() accepts tuples of tensors, automatically pairing corresponding elements from each tensor. This is the standard pattern for supervised learning datasets:

import tensorflow as tf

# Features: 4 samples with 3 features each
features = tf.constant([
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
[10.0, 11.0, 12.0]
])

# Labels: one label per sample
labels = tf.constant([0, 1, 0, 1])

# Creates a dataset of (feature, label) pairs
dataset = tf.data.Dataset.from_tensor_slices((features, labels))

for feature, label in dataset:
print(f"Features: {feature.numpy()}, Label: {label.numpy()}")

Output:

Features: [1. 2. 3.], Label: 0
Features: [4. 5. 6.], Label: 1
Features: [7. 8. 9.], Label: 0
Features: [10. 11. 12.], Label: 1
warning

When passing a tuple of tensors, all tensors must have the same size in their first dimension. If features has 4 rows but labels has 3 elements, TensorFlow raises an error. Always verify that your features and labels are aligned before creating the dataset.

Using Dictionaries for Named Features

For datasets with many feature columns, dictionaries provide clearer, self-documenting code:

import tensorflow as tf

data = {
"age": tf.constant([25, 30, 35, 40]),
"income": tf.constant([50000, 60000, 75000, 90000]),
"label": tf.constant([0, 0, 1, 1])
}

dataset = tf.data.Dataset.from_tensor_slices(data)

for sample in dataset.take(2):
print(f"Age: {sample['age'].numpy()}, "
f"Income: {sample['income'].numpy()}, "
f"Label: {sample['label'].numpy()}")

Output:

Age: 25, Income: 50000, Label: 0
Age: 30, Income: 60000, Label: 0

Each element in the dataset is a dictionary with the same keys, where each value is the corresponding scalar from that position in the original arrays.

Building an Optimized Training Pipeline

In practice, from_tensor_slices() is almost always the starting point for a training pipeline. You chain additional transformations to shuffle, batch, and prefetch the data:

import tensorflow as tf

# Sample training data
X = tf.random.normal((1000, 64)) # 1000 samples, 64 features
y = tf.random.uniform((1000,), 0, 10, dtype=tf.int32) # 10-class labels

# Build an optimized pipeline
dataset = (
tf.data.Dataset.from_tensor_slices((X, y))
.shuffle(buffer_size=1000) # Randomize order each epoch
.batch(32) # Group into batches of 32
.prefetch(tf.data.AUTOTUNE) # Overlap data loading with training
)

# Verify batch shapes
for batch_x, batch_y in dataset.take(1):
print(f"Feature batch shape: {batch_x.shape}")
print(f"Label batch shape: {batch_y.shape}")

Output:

Feature batch shape: (32, 64)
Label batch shape: (32,)
tip

The three chained operations serve distinct purposes:

  • shuffle() randomizes the order of samples each epoch, preventing the model from learning order-dependent patterns.
  • batch() groups individual samples into batches for efficient parallel processing on GPUs.
  • prefetch(AUTOTUNE) prepares the next batch in the background while the current batch is being processed, reducing idle time.

Method Selection Guide

ScenarioRecommended Method
Training dataset with individual samplesfrom_tensor_slices()
Validation or test datasetsfrom_tensor_slices()
Feature-label pairs for supervised learningfrom_tensor_slices((features, labels))
Image classification datasetsfrom_tensor_slices()
A single constant lookup tablefrom_tensors()
Reference data that must stay as one blockfrom_tensors()

In the vast majority of machine learning workflows, from_tensor_slices() is the method you want. from_tensors() is reserved for the uncommon case where the entire input should be treated as a single, indivisible element.

info

Both methods load all data into memory. For datasets too large to fit in RAM, consider using tf.data.TFRecordDataset for pre-serialized data on disk or tf.data.Dataset.from_generator() for lazy, on-demand data loading.

Summary

from_tensors() and from_tensor_slices() serve fundamentally different purposes despite their similar names.

  • from_tensor_slices() slices along the first dimension to create a dataset of individual elements, making it the standard choice for training pipelines where each row is a separate sample.
  • from_tensors() wraps the entire input as a single dataset element, which is useful only when you need to keep data together as one block.

For most training workflows, use from_tensor_slices() with feature-label tuples or dictionaries, then chain .shuffle(), .batch(), and .prefetch() to build an efficient pipeline that keeps your model fed during training.