TensorFlow’s model pruning doesn’t just make models smaller; it fundamentally alters how computations are performed by introducing structured sparsity.

Let’s see it in action. Imagine you have a dense, trained TensorFlow model. We’ll use a simple convolutional layer as an example.

import tensorflow as tf
import tensorflow_model_optimization as tfmot

# Assume 'model' is a pre-trained TensorFlow Keras model
# For demonstration, let's create a dummy model
input_shape = (28, 28, 1)
inputs = tf.keras.Input(shape=input_shape)
x = tf.keras.layers.Conv2D(32, 3, activation='relu')(inputs)
x = tf.keras.layers.Flatten()(x)
outputs = tf.keras.layers.Dense(10, activation='softmax')(x)
model = tf.keras.Model(inputs=inputs, outputs=outputs)

# Define pruning parameters
pruning_params = {
    'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay(
        initial_sparsity=0.50,  # Start with 50% sparsity
        final_sparsity=0.80,  # End with 80% sparsity
        begin_step=0,
        end_step=1000  # Prune over 1000 training steps
    )
}

# Apply pruning wrapper to the model
# We'll apply it to the Conv2D and Dense layers
pruned_model = tfmot.sparsity.keras.prune_low_magnitude(model, **pruning_params)

# Compile the pruned model
pruned_model.compile(optimizer='adam',
                     loss='sparse_categorical_crossentropy',
                     metrics=['accuracy'])

pruned_model.summary()

Notice the prune_low_magnitude wrapper. This isn’t just zeroing out weights post-training; it’s adding a mechanism to dynamically adjust weights during training. The PolynomialDecay schedule dictates how the sparsity target increases over training steps.

The core idea is to identify and remove weights that contribute least to the model’s output. Instead of simply setting these weights to zero, pruning introduces a mask for each weight tensor. During the forward pass, the input is multiplied by this mask, effectively skipping computations involving zeroed-out weights. During backpropagation, gradients are only applied to unmasked weights. This process is iterative, gradually increasing sparsity while fine-tuning the remaining weights to compensate.

The pruning_params are your primary levers.

  • initial_sparsity: The percentage of weights that will be zero from the start of the pruning process.
  • final_sparsity: The target sparsity level at the end of the pruning schedule.
  • begin_step: The training step at which pruning starts. If set to 0, it begins immediately.
  • end_step: The training step at which the final_sparsity is reached. The sparsity will then remain constant.
  • pruning_schedule: This defines how sparsity increases. PolynomialDecay is common, but others like ConstantSparsity or LinearDecay exist. The schedule determines the rate of sparsity increase.

After pruning and fine-tuning, you’ll typically strip the pruning wrappers to get a smaller, sparse model.

# After training the pruned_model
final_model = tfmot.sparsity.keras.strip_pruning(pruned_model)
final_model.summary()

The final_model’s summary will look like the original model, but its underlying weight matrices will be structurally sparse, meaning many weights are effectively zero and can be skipped during inference.

The surprising part is that these masks, while enabling sparsity, add a small overhead. The actual inference speedup comes not just from skipping multiplications but from leveraging specialized hardware or libraries that can exploit this structured sparsity efficiently. Without such optimizations, a pruned model might be smaller but not necessarily faster.

The next step is understanding how to export this sparse model for efficient inference on edge devices.

Want structured learning?

Take the full Tensorflow course →