TensorFlow
TensorFlow 2.0
tf.keras.layers
Conv2D
Dense

TensorFlow 2.0 How to get trainable variables from tf.keras.layers layers, like Conv2D or Dense

Master System Design with Codemia

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

Introduction

In TensorFlow 2, Keras layers expose their trainable parameters directly once the layer has been built. The main thing to understand is that a Dense or Conv2D layer does not create its weights until it has seen an input shape, so asking for trainable variables too early often returns an empty list.

What Counts as a Trainable Variable

For common Keras layers, the trainable variables are usually the kernel weights and, if enabled, the bias.

For example:

  • 'Dense usually has kernel and bias'
  • 'Conv2D usually has convolution kernel and optional bias'
  • custom layers may register any tf.Variable marked as trainable

TensorFlow collects these variables under properties such as:

  • 'layer.trainable_variables'
  • 'layer.trainable_weights'
  • 'model.trainable_variables'

In most modern code, trainable_variables is the clearest property to use.

The Layer Must Be Built First

A layer that has not been built yet does not know its input dimensionality, so it cannot allocate weights.

python
1import tensorflow as tf
2
3layer = tf.keras.layers.Dense(4)
4print(layer.trainable_variables)  # []
5
6x = tf.ones((2, 3))
7y = layer(x)
8print([v.name for v in layer.trainable_variables])

After the call layer(x), the layer is built and the variables exist.

This is the most common source of confusion. The API is correct; the layer simply has not created its variables yet.

Accessing Variables on Dense

python
1import tensorflow as tf
2
3layer = tf.keras.layers.Dense(5, use_bias=True)
4_ = layer(tf.random.normal((1, 8)))
5
6print("all trainable variables:")
7for var in layer.trainable_variables:
8    print(var.name, var.shape)
9
10print("kernel shape:", layer.kernel.shape)
11print("bias shape:", layer.bias.shape)

For Dense, the kernel shape is usually (input_dim, units) and the bias shape is (units,).

Accessing Variables on Conv2D

python
1import tensorflow as tf
2
3layer = tf.keras.layers.Conv2D(filters=16, kernel_size=3, use_bias=True)
4_ = layer(tf.random.normal((1, 32, 32, 3)))
5
6for var in layer.trainable_variables:
7    print(var.name, var.shape)
8
9print("kernel shape:", layer.kernel.shape)
10print("bias shape:", layer.bias.shape)

For Conv2D, the kernel shape is typically (kernel_height, kernel_width, in_channels, filters).

From Layers to Models

If you want all trainable variables in a whole network, ask the model instead of each layer individually.

python
1import tensorflow as tf
2
3model = tf.keras.Sequential([
4    tf.keras.layers.Dense(8, activation="relu"),
5    tf.keras.layers.Dense(1)
6])
7
8_ = model(tf.random.normal((4, 10)))
9
10for var in model.trainable_variables:
11    print(var.name, var.shape)

This is especially useful in custom training loops, where you pass model.trainable_variables to a gradient tape and optimizer.

trainable = False Changes the List

If a layer is frozen, its variables still exist, but they are no longer returned as trainable variables for optimization.

python
1import tensorflow as tf
2
3layer = tf.keras.layers.Dense(4)
4_ = layer(tf.ones((1, 3)))
5
6print(len(layer.trainable_variables))
7layer.trainable = False
8print(len(layer.trainable_variables))
9print(len(layer.variables))

This matters during transfer learning. layer.variables still includes the weights, but layer.trainable_variables becomes empty for that layer.

Why This Matters in Custom Training

A manual training step usually looks like this:

python
1import tensorflow as tf
2
3model = tf.keras.Sequential([
4    tf.keras.layers.Dense(8, activation="relu"),
5    tf.keras.layers.Dense(1)
6])
7optimizer = tf.keras.optimizers.Adam()
8loss_fn = tf.keras.losses.MeanSquaredError()
9
10x = tf.random.normal((4, 3))
11y = tf.random.normal((4, 1))
12
13with tf.GradientTape() as tape:
14    preds = model(x)
15    loss = loss_fn(y, preds)
16
17grads = tape.gradient(loss, model.trainable_variables)
18optimizer.apply_gradients(zip(grads, model.trainable_variables))

If the layer or model was not built, or if you accidentally froze it, that variable list may be empty and nothing will update.

Common Pitfalls

The most common mistake is inspecting trainable_variables before the layer has been built. Call the layer once or define the input shape first.

Another mistake is confusing variables with trainable_variables. The first includes all variables, while the second includes only those that should be optimized.

A third issue is freezing a layer with trainable = False and then expecting its variables to appear in the optimizer step.

Finally, when debugging nested models, remember that model.trainable_variables is usually easier to reason about than crawling every sublayer manually.

Summary

  • Use layer.trainable_variables to get a layer's trainable parameters.
  • Keras layers do not create weights until they are built.
  • Call the layer once or provide input shape before inspecting variables.
  • 'Dense and Conv2D usually expose kernel and optional bias.'
  • Use model.trainable_variables for whole-model training logic.
  • If trainable = False, the weights still exist but drop out of the trainable list.

Course illustration
Course illustration

All Rights Reserved.