TensorFlow
Tensor Conversion
tf.Tensor
numpy
tf.data.Dataset

Converting a tf.Tensor to numpy array in tf.data.Dataset.map graph mode in TF 2.0

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

A frequent TensorFlow question is why tensor.numpy() works in eager code but fails inside tf.data.Dataset.map. The reason is execution mode: map functions are traced into graph operations where Python value extraction is not available. The practical fix is to keep map transformations in TensorFlow operations, and use Python escape hatches only when strictly necessary.

Why .numpy() Fails in Graph-Mapped Functions

In eager mode, tensors hold concrete values immediately, so .numpy() is valid. In graph mode, map functions are compiled and run as a graph, so tensors in that function are symbolic placeholders during tracing.

This fails in graph execution:

python
1import tensorflow as tf
2
3
4def bad_map(x):
5    return x.numpy()  # invalid in traced graph function
6
7
8ds = tf.data.Dataset.range(5).map(bad_map)
9for item in ds:
10    print(item)

TensorFlow reports an error because it cannot call Python-side value extraction from compiled graph logic.

Preferred Approach: Keep the Map Function Pure TensorFlow

For speed and portability, express transformations with TensorFlow ops only.

python
1import tensorflow as tf
2
3
4def good_map(x):
5    x = tf.cast(x, tf.float32)
6    return tf.math.sqrt(x + 1.0)
7
8
9ds = tf.data.Dataset.range(6).map(good_map)
10for item in ds:
11    print(item.numpy())

Notice the NumPy conversion happens outside map, at iteration time in eager context.

This pattern scales better with parallel mapping, graph optimizations, and accelerator execution.

Use tf.py_function Only for Unavoidable Python Logic

Sometimes preprocessing depends on a NumPy or Python-only function. In that case, wrap the function with tf.py_function, but understand the tradeoffs.

python
1import numpy as np
2import tensorflow as tf
3
4
5def numpy_only_fn(x_np):
6    return np.log1p(x_np).astype(np.float32)
7
8
9def wrapped_map(x):
10    y = tf.py_function(func=numpy_only_fn, inp=[x], Tout=tf.float32)
11    y.set_shape(x.shape)
12    return y
13
14
15ds = tf.data.Dataset.from_tensor_slices(tf.constant([1.0, 3.0, 7.0]))
16ds = ds.map(wrapped_map)
17
18for item in ds:
19    print(item.numpy())

set_shape is important because shape inference is often lost when crossing into Python.

Tradeoffs of tf.py_function

Using Python inside data pipelines can become a bottleneck. Important implications:

  • Execution happens on host Python, not optimized TensorFlow kernels.
  • Serialization and export workflows become harder.
  • Debugging shape and dtype issues becomes more manual.
  • Distributed input pipelines may not behave as efficiently.

As a rule, use it as a bridge, not as default architecture.

Better Design for Heavy NumPy Preprocessing

If preprocessing is mostly NumPy, run it before building the dataset and feed processed arrays into TensorFlow.

python
1import numpy as np
2import tensorflow as tf
3
4raw = np.array([1.0, 4.0, 9.0], dtype=np.float32)
5processed = np.sqrt(raw)
6
7
8ds = tf.data.Dataset.from_tensor_slices(processed)
9for item in ds:
10    print(item.numpy())

This keeps the runtime pipeline simpler and avoids mixed execution semantics.

Debugging Tips for Mode Differences

When behavior differs between notebook experiments and training jobs, inspect execution settings and function tracing assumptions.

Useful checks:

  • Verify whether you are inside tf.function or graph-traced map.
  • Print dtypes and shapes at key steps.
  • Confirm that Python-side operations are not embedded in map logic.
  • Benchmark with and without tf.py_function to quantify impact.

For debugging only, you can temporarily enable eager execution for functions. Revert this setting before production training.

python
1import tensorflow as tf
2
3tf.config.run_functions_eagerly(True)
4# debug code here
5
6tf.config.run_functions_eagerly(False)

Common Pitfalls

  • Calling .numpy() directly inside Dataset.map and expecting eager behavior.
  • Using tf.py_function without explicitly restoring output shape.
  • Mixing Python objects and TensorFlow tensors inside the same mapped function.
  • Assuming debug-mode behavior will match optimized graph-mode performance.
  • Leaving eager debug settings enabled in production runs.

Summary

  • Inside graph-mapped dataset functions, tensors are symbolic, so .numpy() is not available.
  • Prefer pure TensorFlow ops in map functions for performance and portability.
  • Use tf.py_function only when required by external Python or NumPy logic.
  • Set output shape and dtype explicitly when using Python wrappers.
  • Separate heavy NumPy preprocessing from TensorFlow input pipelines when possible.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free 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