build() creates a Keras layer’s weights once the input shape is known. This deferred setup matters because weight shapes depend on the incoming tensor, so a layer can be configured before it receives data. section 3.6.2
Building a custom dense layer
A custom layer defines its weights in build() and its computation in call(): build(input_shape) extracts the input dimension, creates W with shape (input_dim, units), and creates a bias with shape (units,). Calling my_dense on a tensor shaped (2, 784) triggers this setup and produces output shaped (2, 32). section 3.6.2
Use build() when you need predictable model setup:
- Call model.build(input_shape=(None, 3)) to create a Sequential model’s weights without passing actual data. section 7.2.1
- Inspect the resulting architecture with summary(); this displays layers, output shapes, and parameter counts after building. section 7.2.1
- Declare keras.Input(shape=(3,)) when incrementally adding layers so the model builds as its structure grows. section 7.2.1
Listing 3.33 A simple dense layer from scratch in Keras
import keras class SimpleDense(keras.Layer): #1 def __init__(self, units, activation=None): super().__init__() self.units = units self.activation = activation def build(self, input_shape): #2 batch_dim, input_dim = input_shape self.W = self.add_weight( #3 shape=(input_dim, self.units), initializer="random_normal" #3 ) #3 self.b = self.add_weight(shape=(self.units,), initializer="zeros") def call(self, inputs): #4 y = keras.ops.matmul(inputs, self.W) + self.b if self.activation is not None: y = self.activation(y) return y
The custom SimpleDense implementation shows the division between shape-dependent weight creation in build() and tensor computation in call(). section 3.6.2

