Gradient accumulation helps train models with big batches using small memory. Zeroing clears old gradients so new learning is correct.
Gradient accumulation and zeroing in PyTorch
optimizer.zero_grad() for i, data in enumerate(dataloader): outputs = model(data) loss = loss_fn(outputs, labels) loss.backward() if (i + 1) % accumulation_steps == 0: optimizer.step() optimizer.zero_grad()
zero_grad() clears old gradients before new backward pass.
Accumulate gradients over several batches before calling optimizer.step().
optimizer.zero_grad() loss.backward() optimizer.step()
optimizer.zero_grad() for i, data in enumerate(dataloader): outputs = model(data) loss = loss_fn(outputs, labels) loss.backward() if (i + 1) % 4 == 0: optimizer.step() optimizer.zero_grad()
This example shows training a simple linear model on 8 samples. It accumulates gradients over 4 samples before updating weights. We print predictions before and after one epoch to see the change.
import torch import torch.nn as nn import torch.optim as optim # Simple model class SimpleModel(nn.Module): def __init__(self): super().__init__() self.linear = nn.Linear(2, 1) def forward(self, x): return self.linear(x) # Data: 8 samples, 2 features inputs = torch.tensor([[1.0, 2.0], [2.0, 1.0], [3.0, 4.0], [4.0, 3.0], [5.0, 6.0], [6.0, 5.0], [7.0, 8.0], [8.0, 7.0]]) labels = torch.tensor([[3.0], [3.0], [7.0], [7.0], [11.0], [11.0], [15.0], [15.0]]) model = SimpleModel() loss_fn = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=0.01) accumulation_steps = 4 print('Before training:') with torch.no_grad(): preds = model(inputs) print(preds.squeeze().tolist()) optimizer.zero_grad() for i in range(len(inputs)): input_i = inputs[i].unsqueeze(0) label_i = labels[i].unsqueeze(0) output = model(input_i) loss = loss_fn(output, label_i) loss.backward() if (i + 1) % accumulation_steps == 0: optimizer.step() optimizer.zero_grad() print('\nAfter one epoch with gradient accumulation:') with torch.no_grad(): preds = model(inputs) print(preds.squeeze().tolist())
Always call optimizer.zero_grad() before starting to accumulate gradients.
Gradient accumulation lets you use bigger batch sizes without needing more memory.
Forget to zero gradients and you add old gradients again, which can confuse learning.
Gradient accumulation adds gradients over multiple batches before updating model.
Zeroing gradients clears old information to keep learning correct.
This technique helps train with limited memory and control update frequency.
Practice
Solution
Step 1: Understand gradient accumulation process
Gradients accumulate by default in PyTorch, so old gradients must be cleared before new backward passes.Step 2: Purpose of zeroing gradients
Zeroing gradients resets them to zero to avoid mixing old and new gradient information.Final Answer:
To clear old gradients before computing new ones -> Option AQuick Check:
Zeroing gradients = clear old gradients [OK]
- Thinking zeroing resets model weights
- Confusing zeroing with saving model
- Believing zeroing changes learning rate
model in PyTorch?Solution
Step 1: Recall PyTorch API for zeroing gradients
The official method to zero gradients iszero_grad()on the model or optimizer.Step 2: Check method names
Onlymodel.zero_grad()is a valid PyTorch method; others are incorrect or do not exist.Final Answer:
model.zero_grad() -> Option AQuick Check:
Correct method name = zero_grad() [OK]
- Using non-existent methods like clear_grad()
- Confusing zero_grad with reset_gradients
- Adding extra letters to method names
optimizer.zero_grad()
for i, batch in enumerate(data_loader):
outputs = model(batch)
loss = loss_fn(outputs, targets)
loss.backward()
if (i + 1) % 4 == 0:
optimizer.step()
optimizer.zero_grad()What is the effect of this code on gradient updates?
Solution
Step 1: Analyze the loop and optimizer calls
Gradients are zeroed before loop and after every 4 batches. Backward is called every batch, so gradients accumulate for 4 batches.Step 2: Understand optimizer.step() timing
Optimizer updates weights every 4 batches, using accumulated gradients, then zeroes gradients to start fresh.Final Answer:
Accumulates gradients over 4 batches before updating -> Option DQuick Check:
Step every 4 batches = gradient accumulation [OK]
- Assuming update happens every batch
- Forgetting gradients accumulate without zeroing
- Thinking zero_grad inside loop clears gradients too early
for batch in data_loader:
optimizer.zero_grad()
outputs = model(batch)
loss = loss_fn(outputs, targets)
loss.backward()
optimizer.step()
loss.backward()What is wrong here?
Solution
Step 1: Review order of backward and optimizer calls
Loss.backward() is called twice without zeroing gradients in between, causing gradient accumulation unintentionally.Step 2: Understand consequences of double backward
Second backward adds gradients again, which is usually a mistake unless intentional accumulation is desired.Final Answer:
Calling loss.backward() twice without zeroing gradients -> Option CQuick Check:
Double backward without zero_grad causes wrong gradients [OK]
- Calling backward multiple times without zeroing
- Forgetting zero_grad before backward
- Misplacing optimizer.step() call
Solution
Step 1: Understand gradient accumulation logic
Gradients must be zeroed once before accumulation starts, then loss.backward() called every batch to accumulate gradients.Step 2: Timing optimizer step and zero_grad
After accumulating gradients over 8 batches, call optimizer.step() to update weights, then zero_grad() to reset gradients for next accumulation.Final Answer:
Call optimizer.zero_grad() once before loop, call loss.backward() every batch, call optimizer.step() and zero_grad() every 8 batches -> Option BQuick Check:
Zero once, backward every batch, step and zero every 8 batches [OK]
- Zeroing gradients every batch breaks accumulation
- Calling backward only every N batches misses gradients
- Stepping optimizer too often or too rarely
Ready to prove it?
Pass the test and this pattern moves unknown
