TensorFlow Fundamentals · lesson 04/27
Layers API
tf.layers was the middle chapter in TensorFlow's history: after hand-built graphs, before Keras owned everything. It gave you layer functions such as dense and conv2d that created their own variables and could be reused. In TF 2.x tf.layers no longer exists — the API was folded into tf.keras.layers — but the concepts carry over directly.
The idea
The core value of a layers API is that a layer owns its variables. Instead of writing tf.get_variable("weights", [784, 128]) and remembering a scope name, you call a function that allocates what it needs:
import tensorflow as tf
h = tf.layers.dense(x, 128, activation=tf.nn.relu, name="hidden") # TF 1.x only
logits = tf.layers.dense(h, 10, name="logits")Two knobs mattered:
reuse— TF 1.x neededreuse=Trueto share weights across calls, for example in a siamese network or a decoder. Getting this wrong either doubled your parameters or raised "variable already exists".name— the scope prefix under which variables were stored, which decided how checkpoint keys lined up.
tf.layers.dropout, tf.layers.batch_normalization, and tf.layers.conv2d followed the same pattern.
Worked example
A small MNIST network with the old API:
| Call | Output shape | Variables created |
|---|---|---|
tf.layers.dense(x, 128) | [?, 128] | dense/kernel [784, 128], dense/bias [128] |
tf.layers.dense(h, 10) | [?, 10] | dense_1/kernel [128, 10], dense_1/bias [10] |
Note the automatic numbering: call the same function twice and you get dense then dense_1, each with independent weights. That silent behavior is exactly why reuse=True had to exist, and why the API confused people who expected a second call to reuse the first call's weights.
In code
The TF 2.x equivalent, with explicit layer objects:
import tensorflow as tf
class Mlp(tf.keras.Model):
def __init__(self, hidden=128, classes=10):
super().__init__()
self.hidden = tf.keras.layers.Dense(hidden, activation="relu")
self.out = tf.keras.layers.Dense(classes)
def call(self, x):
return self.out(self.hidden(x))
model = Mlp()
model(tf.zeros([1, 784])) # build: creates the variables
print([v.shape for v in model.weights])Status: tf.layers is removed as a public API in TensorFlow 2.x. If you must run graph-mode TF 1.x code, tf.compat.v1.layers still provides the old functions inside a tf.compat.v1.disable_v2_behavior() block, but that is a compatibility path, not a place for new work.
Check yourself
- Why did
reuse=Trueexist in TF 1.x, and what did Keras replace it with? - In the table above, why are the variables named
dense/kernelanddense_1/kernelrather than bothdense/kernel? - What does a Keras layer object add that a
tf.layersfunction call did not?
Key takeaways
- A layers API makes each layer own its variables — that was the real innovation.
reuseandnamewere TF 1.x machinery that Keras replaced with object identity.- Use
tf.keras.layers; reach fortf.compat.v1.layersonly to run old checkpoints.