Tensorflow
tf.Variable
dynamic batch size
neural networks
machine learning

Tensorflow cannot initialize tf.Variable for dynamic batch size

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

If TensorFlow refuses to initialize a tf.Variable because the batch size is dynamic, the real issue is usually a shape-design mistake. Batch size is runtime input metadata, while variables represent persistent model parameters and therefore need a fixed shape when they are created.

Why Dynamic Batch Size and tf.Variable Conflict

A dynamic batch size is commonly written as None in the first dimension of an input shape. That works for placeholders, tensors, and Keras input layers because the batch dimension can vary from one execution to the next.

Variables are different. A variable stores trainable state such as weights or running statistics. TensorFlow needs to know the full variable shape when the variable is created, so a definition like "one weight matrix per current batch size" is usually invalid unless the batch size itself is fixed.

This is the wrong mental model:

python
1import tensorflow as tf
2
3batch_size = tf.shape(inputs)[0]
4weights = tf.Variable(tf.zeros([batch_size, 128]))

batch_size is a tensor computed at runtime, not a concrete dimension available at variable creation time.

What Usually Should Be Dynamic Instead

In most neural network code, the dynamic dimension belongs to the input tensor, not to the weights. For example, if each sample has 64 features and you want a hidden layer of size 128, the weight matrix should be shaped [64, 128], regardless of whether the batch contains 1, 32, or 256 samples.

python
1import tensorflow as tf
2
3inputs = tf.keras.Input(shape=(64,))
4dense = tf.keras.layers.Dense(128)
5outputs = dense(inputs)
6
7model = tf.keras.Model(inputs=inputs, outputs=outputs)
8model.summary()

Here the batch size stays dynamic, but the layer variables are well-defined because only the feature dimension matters for weight creation.

TensorFlow 1 Style Example

The same idea applies in TensorFlow 1 graph code:

python
1import tensorflow as tf
2
3tf.compat.v1.disable_eager_execution()
4
5x = tf.compat.v1.placeholder(tf.float32, shape=[None, 64])
6w = tf.Variable(tf.random.normal([64, 128]))
7b = tf.Variable(tf.zeros([128]))
8
9y = tf.matmul(x, w) + b

The input placeholder has a dynamic first dimension, but the variables do not. That is the normal and correct pattern.

When You Really Need Batch-Dependent State

If you think you need a variable whose size depends on the current batch, step back and check the design. Often one of these is a better fit:

  • use a tensor computed from the current input instead of a variable
  • use tf.TensorArray for per-step dynamic accumulation
  • pad or bucket the inputs so feature dimensions become regular
  • store maximum-size state and slice it at runtime

For example, batch-dependent temporary data should usually be a tensor created inside the forward pass, not a persistent tf.Variable.

python
1import tensorflow as tf
2
3def make_runtime_buffer(inputs):
4    batch_size = tf.shape(inputs)[0]
5    return tf.zeros([batch_size, 128])

This works because tf.zeros creates a tensor during execution. It is not trying to register trainable state in the model.

Keras Layer Building Rules

In Keras, custom layers should create weights in build() using dimensions that are stable across batches. That usually means reading input_shape[-1], not input_shape[0].

python
1import tensorflow as tf
2
3class MyLayer(tf.keras.layers.Layer):
4    def build(self, input_shape):
5        feature_dim = input_shape[-1]
6        self.kernel = self.add_weight(
7            shape=(feature_dim, 128),
8            initializer="glorot_uniform",
9            trainable=True,
10        )
11
12    def call(self, inputs):
13        return tf.matmul(inputs, self.kernel)

The batch dimension may be None, and that is fine. The feature dimension is what matters for variable creation.

Common Pitfalls

The biggest pitfall is confusing data shape with parameter shape. A layer may process dynamic batches, but its trainable weights usually depend only on per-sample feature dimensions.

Another issue is trying to use tf.shape() results inside tf.Variable initialization. tf.shape() returns runtime tensors, which are too late for variable creation.

Developers also sometimes assume eager execution removes the rule. It does not. Eager mode changes when code runs, but variables still need a concrete shape when created.

Finally, if one of your non-batch dimensions is also dynamic, you may need architectural changes such as padding, ragged tensors, or sequence models designed for variable lengths. The fix is usually not a batch-sized variable.

Summary

  • Dynamic batch size is normal for inputs, not for trainable variables.
  • 'tf.Variable needs a concrete shape at creation time.'
  • Weight shapes should usually depend on feature dimensions, not the current batch size.
  • If you need batch-dependent temporary storage, use tensors, not variables.
  • In custom Keras layers, create weights from stable dimensions such as input_shape[-1].

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.