TensorFlow
Keras
AttributeError
Debugging
Machine Learning

Tensor object has no attribute keras_shape

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 error AttributeError: 'Tensor' object has no attribute '_keras_shape' (or 'keras_shape') occurs when mixing TensorFlow 1.x-style code with TensorFlow 2.x, or when passing raw TensorFlow tensors to Keras layers that expect Keras tensors. In TensorFlow 1.x, Keras tracked shape information using a custom _keras_shape attribute on tensors. TensorFlow 2.x removed this attribute because eager execution makes shape information available directly through tensor.shape. The fix is to use TensorFlow 2.x APIs consistently and avoid mixing raw tf.Tensor objects with Keras symbolic tensors.

Why This Error Happens

In TensorFlow 1.x with standalone Keras, the library attached a _keras_shape attribute to tensors to track shape information through the computation graph:

python
1# TensorFlow 1.x / standalone Keras (OLD — causes error in TF2)
2import keras
3from keras import backend as K
4
5# In TF1, Keras added _keras_shape to tensors
6x = K.placeholder(shape=(None, 10))
7print(x._keras_shape)  # (None, 10) — worked in TF1
8
9# In TF2, this attribute no longer exists
10import tensorflow as tf
11x = tf.constant([[1.0, 2.0]])
12print(x._keras_shape)  # AttributeError!

Fix 1: Use tf.keras Instead of Standalone Keras

The most common cause is importing standalone keras instead of tf.keras:

python
1# WRONG — standalone keras may conflict with TF2
2import keras
3from keras.layers import Dense
4from keras.models import Model
5
6# CORRECT — use tf.keras for TF2 compatibility
7import tensorflow as tf
8from tensorflow.keras.layers import Dense
9from tensorflow.keras.models import Model

Uninstall standalone Keras if both are installed:

bash
pip uninstall keras
pip install tensorflow  # tf.keras is included

Fix 2: Use Keras Input Layers Instead of Raw Tensors

Keras layers expect keras.Input() tensors, not raw tf.Tensor objects:

python
1import tensorflow as tf
2
3# WRONG — raw tensor passed to Keras layer
4raw_tensor = tf.constant([[1.0, 2.0, 3.0]])
5dense = tf.keras.layers.Dense(10)
6output = dense(raw_tensor)  # May work in eager mode but fails in model building
7
8# CORRECT — use keras.Input for model definition
9inputs = tf.keras.Input(shape=(3,))
10x = tf.keras.layers.Dense(10, activation='relu')(inputs)
11outputs = tf.keras.layers.Dense(1)(x)
12model = tf.keras.Model(inputs=inputs, outputs=outputs)

Fix 3: Replace K.placeholder with tf.keras.Input

TensorFlow 2.x deprecated K.placeholder(). Use tf.keras.Input() instead:

python
1# WRONG — TF1 style
2from tensorflow.keras import backend as K
3x = K.placeholder(shape=(None, 784))  # May cause _keras_shape error
4
5# CORRECT — TF2 style
6x = tf.keras.Input(shape=(784,))

Fix 4: Update Custom Layers

Custom layers that access _keras_shape must be updated:

python
1# WRONG — accessing deprecated attribute
2class OldCustomLayer(tf.keras.layers.Layer):
3    def call(self, inputs):
4        shape = inputs._keras_shape  # AttributeError in TF2
5        return tf.reshape(inputs, (-1, shape[-1]))
6
7# CORRECT — use tensor.shape or tf.shape()
8class CustomLayer(tf.keras.layers.Layer):
9    def call(self, inputs):
10        # Static shape (known at graph build time)
11        static_shape = inputs.shape  # TensorShape object
12        last_dim = static_shape[-1]
13
14        # Dynamic shape (evaluated at runtime)
15        dynamic_shape = tf.shape(inputs)
16
17        return tf.reshape(inputs, (-1, last_dim))

Getting Tensor Shapes in TF2

python
1import tensorflow as tf
2
3x = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
4
5# Static shape — known at graph construction time
6print(x.shape)           # TensorShape([2, 3])
7print(x.shape[0])        # 2
8print(x.shape.as_list()) # [2, 3]
9
10# Dynamic shape — evaluated at runtime (useful inside tf.function)
11dynamic = tf.shape(x)    # Tensor([2, 3], dtype=int32)
12
13# For Keras layers, use build() to access input shape
14class MyLayer(tf.keras.layers.Layer):
15    def build(self, input_shape):
16        self.units = input_shape[-1]
17        self.kernel = self.add_weight(
18            shape=(self.units, 64),
19            initializer='glorot_uniform'
20        )
21
22    def call(self, inputs):
23        return tf.matmul(inputs, self.kernel)

Static vs Dynamic Shapes

python
1@tf.function
2def process(x):
3    # Static shape — may have None dimensions
4    print(x.shape)  # (None, 10) — batch size unknown
5
6    # Dynamic shape — always concrete at runtime
7    batch_size = tf.shape(x)[0]  # Actual batch size
8
9    return tf.reshape(x, (batch_size, -1))
10
11# When shapes are fully known
12concrete = tf.constant([[1.0, 2.0]])
13print(concrete.shape)  # (1, 2) — fully defined
14
15# When shapes have unknown dimensions (inside tf.function, Keras models)
16inputs = tf.keras.Input(shape=(10,))
17print(inputs.shape)  # (None, 10) — batch dim is None

Migration Checklist

python
1# 1. Replace standalone keras imports
2# OLD: import keras
3# NEW: import tensorflow as tf; from tensorflow import keras
4
5# 2. Replace K.placeholder
6# OLD: x = K.placeholder(shape=(None, 10))
7# NEW: x = tf.keras.Input(shape=(10,))
8
9# 3. Replace _keras_shape access
10# OLD: shape = tensor._keras_shape
11# NEW: shape = tensor.shape
12
13# 4. Replace K.int_shape
14# OLD: dims = K.int_shape(tensor)
15# NEW: dims = tensor.shape.as_list()
16
17# 5. Update get_shape() calls
18# OLD: tensor.get_shape().as_list()
19# NEW: tensor.shape.as_list()  # Simpler in TF2

Common Pitfalls

  • Having both keras and tensorflow installed: Standalone keras and tf.keras conflict. Uninstall standalone keras (pip uninstall keras) and use only tensorflow.keras to avoid attribute errors.
  • Using K.placeholder() in TF2: K.placeholder() creates TF1-style placeholders that lack TF2 shape tracking. Replace with tf.keras.Input() which integrates with the Keras functional API.
  • Passing raw tensors to Keras model-building code: Keras functional API expects Input() tensors at the start of the graph. Passing tf.constant() or tf.Variable directly to layers during model construction causes shape tracking failures.
  • Confusing static and dynamic shapes: tensor.shape returns the static shape (may contain None). tf.shape(tensor) returns the dynamic shape (always concrete at runtime). Use static shapes for layer configuration and dynamic shapes for runtime operations.
  • Running TF1 code without tf.compat.v1 compatibility mode: Legacy code that depends on _keras_shape can be run with import tensorflow.compat.v1 as tf; tf.disable_v2_behavior() as a temporary migration aid, but updating to TF2 APIs is the proper fix.

Summary

  • The _keras_shape attribute was removed in TensorFlow 2.x — use tensor.shape instead
  • Replace import keras with from tensorflow import keras (or import tensorflow.keras)
  • Use tf.keras.Input(shape=(...)) instead of K.placeholder()
  • Access shapes with tensor.shape (static) or tf.shape(tensor) (dynamic)
  • Update custom layers to use input_shape in build() instead of _keras_shape

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

All Rights Reserved.