tensorflow
gradient accumulation
tensorflow 2.0
machine learning
deep learning

How to accumulate gradients in tensorflow 2.0?

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

Gradient accumulation lets you simulate a larger batch size by summing gradients across several smaller mini-batches before applying an optimizer step. This is useful when the model fits only small batches in GPU or CPU memory. In TensorFlow 2.x, the cleanest implementation uses a custom training loop with GradientTape.

Why Gradient Accumulation Helps

Normally, a training step looks like this:

  • Run forward pass on one batch.
  • Compute gradients.
  • Apply optimizer update immediately.

With accumulation, you delay the update:

  • Run several mini-batches.
  • Sum or average their gradients.
  • Apply one optimizer step after N mini-batches.

If your physical batch size is 8 and you accumulate for 4 steps, the optimizer effectively sees batch size 32. This does not perfectly reproduce every property of a real larger batch, but it is often close enough for practical training.

That last caveat matters when the model contains layers whose behavior depends on the micro-batch itself, such as batch normalization. Accumulation changes optimizer updates, but it does not magically turn four separate forward passes into one true large-batch forward pass.

Build a Simple TensorFlow 2.x Accumulation Loop

The key idea is to keep gradient buffers that match model variables.

python
1import tensorflow as tf
2
3model = tf.keras.Sequential([
4    tf.keras.layers.Dense(16, activation="relu"),
5    tf.keras.layers.Dense(1),
6])
7
8optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
9loss_fn = tf.keras.losses.MeanSquaredError()
10
11accum_steps = 4
12
13x = tf.random.normal((64, 10))
14y = tf.reduce_sum(x, axis=1, keepdims=True)
15dataset = tf.data.Dataset.from_tensor_slices((x, y)).batch(8)
16
17gradient_accumulators = [
18    tf.Variable(tf.zeros_like(var), trainable=False)
19    for var in model.trainable_variables
20]
21
22step = 0
23
24for batch_x, batch_y in dataset:
25    with tf.GradientTape() as tape:
26        preds = model(batch_x, training=True)
27        loss = loss_fn(batch_y, preds) / accum_steps
28
29    grads = tape.gradient(loss, model.trainable_variables)
30
31    for acc, grad in zip(gradient_accumulators, grads):
32        acc.assign_add(grad)
33
34    step += 1
35
36    if step % accum_steps == 0:
37        optimizer.apply_gradients(zip(gradient_accumulators, model.trainable_variables))
38
39        for acc in gradient_accumulators:
40            acc.assign(tf.zeros_like(acc))

Dividing the loss by accum_steps ensures the accumulated update approximates the average gradient rather than multiplying it by the number of accumulation steps.

Handle the Last Partial Accumulation Window

Datasets do not always divide evenly by accum_steps. If the loop ends with some accumulated gradients still pending, you should apply them.

python
1remainder = step % accum_steps
2
3if remainder != 0:
4    optimizer.apply_gradients(zip(gradient_accumulators, model.trainable_variables))
5
6    for acc in gradient_accumulators:
7        acc.assign(tf.zeros_like(acc))

If you skip this, the final mini-batches in an epoch may never influence the model.

Add tf.function for Speed

Once the logic is correct, wrapping the train step in tf.function often improves performance.

python
1@tf.function
2def train_micro_batch(batch_x, batch_y, gradient_accumulators, model, optimizer):
3    with tf.GradientTape() as tape:
4        preds = model(batch_x, training=True)
5        loss = loss_fn(batch_y, preds) / accum_steps
6
7    grads = tape.gradient(loss, model.trainable_variables)
8
9    for acc, grad in zip(gradient_accumulators, grads):
10        acc.assign_add(grad)

Keep correctness first. Optimize only after validating that accumulation produces the expected number of optimizer updates.

If you later move this into a custom Keras model, the same accumulation idea usually lives inside an overridden train_step method. The underlying math stays the same even if the outer training API changes.

Monitor the Difference Between Micro-Step and Optimizer Step

When accumulation is enabled, the number of micro-batches and the number of optimizer updates differ. That affects:

  • Learning-rate schedules.
  • Logging cadence.
  • Checkpoint naming if it depends on optimizer.iterations.

If your training loop assumes one dataset batch equals one optimizer step, update that assumption explicitly. Otherwise warmup schedules and logging can look wrong even when training math is correct.

Common Pitfalls

  • Forgetting to divide loss by accum_steps, which makes updates too large.
  • Not applying leftover gradients at the end of an epoch.
  • Reusing accumulation buffers without resetting them after optimizer step.
  • Assuming callbacks and schedulers still operate per micro-batch when they now operate per optimizer update.
  • Trying to force accumulation into model.fit without custom training logic when the workflow really needs a manual loop.
  • Expecting batch-normalization statistics to behave exactly like a real larger batch.

Summary

  • Gradient accumulation simulates larger effective batches without increasing per-step memory use.
  • In TensorFlow 2.x, implement it with GradientTape and explicit gradient buffers.
  • Average gradients by dividing the loss or gradients by the accumulation count.
  • Flush leftover accumulated gradients at epoch end.
  • Update your logging and scheduling assumptions because optimizer steps happen less often than micro-batches.

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.