Gradient descent batch processing
Visualize how different gradient descent strategies process data to update weights.
Batch
Update @ Epoch End
Stochastic
Update Per Point
Mini-Batch
Update Per Batch
Overview
Three regimes
- Batch size 1 (stochastic). One sample per update. Very noisy gradient, very many updates. And the noise it sometimes helps escape bad regions.
- Full batch. The whole dataset per update. The gradient is exact. But you get one update per epoch and it is slow.
- Mini-batch (32–256). What in effect everyone uses. Enough samples for a usable gradient estimate, enough updates to make progress. And it fits on a GPU.
The batch size controls a three-way trade-off between gradient accuracy, update frequency and hardware utilisation. A batch of 1 gives the noisiest gradient and the most updates per epoch. A full-batch gradient is exact but gives just one update per epoch. Mini-batches of 32 to 256 sit at the sweet spot: the gradient estimate is close enough to the true gradient for stable convergence, updates are frequent enough for fast progress. And the batch is large enough to saturate the GPU's parallel cores.
Gradient noise is not purely harmful. The stochastic noise from small batches helps the optimiser escape sharp local minima and find flatter minima that generalise better. This is why raising batch size often needs raising the learning rate proportionally (linear scaling rule) to keep the same training dynamics. And why very large batch sizes sometimes give nets that generalise worse still though the training loss is less.
The real upper bound on batch size is GPU memory. A batch of pics must fit in VRAM along with the net params, activations and gradients. When the batch does not fit, the usual fixes are to reduce the batch size, use gradient accumulation (run a few less forward passes and sum the gradients before updating), use mixed-precision training (float16 halves the memory per activation). Or distribute across multiple GPUs. Gradient accumulation is the simplest — it simulates a large batch without needing more memory.
Visualize how different gradient descent strategies process data to update weights.
In this guide
- Three regimes
- How the counts work out
- Why mini-batch won
- Batch size and learning rate move together
- Shuffling is not optional
- Three sizes of step
- Why the noise is useful
- Shuffling, and why it is not optional
- Try this above
- What often goes wrong
- In one line
- Choosing a batch size in practice
- Gradient accumulation
- Full batch, one row, and all between
- Questions people ask
- Recap in one screen
Gradient Descent Batch Processing: A Practical Guide
Batch size decides how many examples the model looks at before it changes its mind. It is the quietest hyperparameter with the widest effect on how training feels.
Three regimes
- Batch size 1 (stochastic). One sample per update. Very noisy gradient, very many updates. And the noise it sometimes helps escape bad regions.
- Full batch. The whole dataset per update. The gradient is exact. But you get one update per epoch and it is slow.
- Mini-batch (32–256). What in effect everyone uses. Enough samples for a usable gradient estimate, enough updates to make progress. And it fits on a GPU.
The batch size controls a three-way trade-off between gradient accuracy, update frequency and hardware utilisation. A batch of 1 gives the noisiest gradient and the most updates per epoch. A full-batch gradient is exact but gives just one update per epoch. Mini-batches of 32 to 256 sit at the sweet spot: the gradient estimate is close enough to the true gradient for stable convergence, updates are frequent enough for fast progress. And the batch is large enough to saturate the GPU's parallel cores.
Gradient noise is not purely harmful. The stochastic noise from small batches helps the optimiser escape sharp local minima and find flatter minima that generalise better. This is why raising batch size often needs raising the learning rate proportionally (linear scaling rule) to keep the same training dynamics. And why very large batch sizes sometimes give nets that generalise worse still though the training loss is less.
The real upper bound on batch size is GPU memory. A batch of pics must fit in VRAM along with the net params, activations and gradients. When the batch does not fit, the usual fixes are to reduce the batch size, use gradient accumulation (run a few less forward passes and sum the gradients before updating), use mixed-precision training (float16 halves the memory per activation). Or distribute across multiple GPUs. Gradient accumulation is the simplest — it simulates a large batch without needing more memory.
How the counts work out
With 1,024 training samples:
- Batch size 32 → 32 updates per epoch
- Batch size 256 → 4 updates per epoch
Gradient noise falls roughly with the square root of batch size. Going from 32 to 128 — a 4× increase — halves the noise, not quarters it. That diminishing give is why batch sizes stop growing well before memory runs out.
Why mini-batch won
Batch gradient descent computes an exact gradient and needs a full pass over the data for a single update. On a million samples that is a million forward passes to move the weights once — exact and unusable.
Stochastic gradient descent updates after each sample. So it makes a million updates per epoch. But each gradient is a single-sample estimate and the path jitters badly. It also wastes the hardware: one sample cannot occupy a GPU.
Mini-batches of 32 to 256 sit at the point where both problems disappear. The gradient estimate is close enough to the true one for stable progress, updates are frequent. And the batch is large enough to fill the device. This is not a compromise nobody is happy with — it is better than either extreme on both axes that matter.
Batch size and learning rate move together
These two cannot be tuned independently. The gradient of a batch is an average. So a more batch gives a less-variance estimate — and a more reliable estimate can support a more step.
The linear scaling rule is the standard heuristic: multiply the batch size by k and multiply the learning rate by k. Going from batch 32 at learning rate 0.01 to batch 256 suggests roughly 0.08. Very large batches often need a warmup period as well, ramping the rate up over the first few epochs, since the scaled rate is unstable while the weights are still random.
The corollary is that checking two runs at other batch sizes with the same learning rate checks nothing useful.
Shuffling is not optional
Mini-batches must be drawn from a shuffled dataset, reshuffled each epoch. If the data arrives sorted by class — all the cats then all the dogs — then each batch holds one class, each gradient points toward predicting that class. And the net oscillates instead of converging.
Reshuffling each epoch also means the net never sees the same batch composition twice. It adds a small amount of useful noise. The exception is time series. There shuffling destroys the temporal ordering the net is supposed to learn from. There the batches must be contiguous windows.
Three sizes of step
The gradient is an average over training cases. How many you average over before updating sets three variants of the same algorithm.
| Variant | Examples per update | Gradient quality | Hardware use |
|---|---|---|---|
| Batch (full) | All of them | Exact | Poor — one update per pass |
| Stochastic | 1 | Very noisy | Poor — no parallelism |
| Mini-batch | 32–256 | Good estimate | Excellent |
Full-batch descent takes the true gradient and one step per pass through the data. On a million rows that is a million forward and backward passes for a single weight update, which is unusable.
Stochastic descent updates after each case. Steps are cheap and the gradient is a very noisy estimate. Worse, a single 784-item vector uses a fraction of a percent of a GPU's capacity.
Mini-batch is the compromise everyone uses. And it wins on all three counts at once: a usable gradient estimate, a matrix multiplication large enough to saturate the hardware. And enough residual noise to escape saddle points.
Why the noise is useful
It is tempting to treat gradient noise as a defect to be minimised. It is not.
It escapes saddle points. In high dimensions almost each point with zero gradient is a saddle rather than a min. An exact gradient at a saddle is zero and full-batch descent stalls. A noisy one is not. And the net moves on.
It biases towards flat minima. Noise makes it hard to settle into a narrow crevice in the loss surface. And flat minima generalise better since a small shift in the data costs less. This is the main reason very large batches sometimes generalise slightly worse.
It acts as a regulariser. Part of why deep networks generalise better than their param counts suggest is this implicit regularisation from stochastic updates.
So the goal is not the cleanest possible gradient. It is a gradient good enough to point downhill, computed on a batch large enough to use the hardware.
Shuffling, and why it is not optional
Mini-batch descent assumes each batch is a representative sample. If the data arrives sorted — by class, by date, by source — then it is not.
Unshuffled sorted data gives batches holding a single class. The net spends one batch learning "all is class A", the next unlearning it in favour of class B. And the gradients pull in contradictory ways. Batch normalisation makes it worse, since its statistics are computed per batch and turn wildly unrepresentative.
Reshuffling each epoch matters too: it means the net never sees the same partition of cases twice. It adds useful variation.
loader = DataLoader(dataset, batch_size=64, shuffle=True, drop_last=True)drop_last=True discards a final partial batch. With batch normalisation that matters — a last batch of one or two cases gives meaningless statistics and a visibly worse update.
The exception is time series, where shuffling destroys the temporal ordering the net needs. There, batches are contiguous windows and the split is chronological.
Try this above
- Drag Batch Size to its min. The descent path visibly jitters — each step is reacting to one or two samples.
- Raise it toward the max. The path straightens out. But notice it also takes fewer, more steps to cover the same ground.
- Now hold batch size high and less Learning Rate. Progress stalls — a big smooth gradient with a tiny step size wastes the smoothness.
What often goes wrong
Raising batch size without touching the learning rate. You get fewer updates per epoch, each no more than before. So training slows down and people conclude the more batch "trains worse". The usual remedy is the linear scaling rule: double the batch, double the learning rate, within reason.Checking runs by epoch count. An epoch at batch size 512 holds a fraction of the updates an epoch at batch size 32 does. Check by count of updates, or by wall-clock time, not by epochs.
In one line
Batch size trades gradient noise against how many times per epoch you get to move.
Choosing a batch size in practice
Start with the largest that fits comfortably in memory, up to about 256, then scale the learning rate to match.
The linear scaling rule is the standard guidance: double the batch size, double the learning rate. The reasoning is that a batch twice as large gives a gradient with roughly half the noise. So a step twice as long is equally safe. It holds well up to a few thousand cases per batch, with a warm-up period to survive the first large steps.
Beyond that, the noise that was giving implicit regularisation has largely gone. And specialised optimisers (LARS, LAMB) are needed to keep large-batch training strong. This is why the papers that trained ImageNet in minutes needed more than just more GPUs.
Constraints that override the "as large as fits" rule:
- Batch normalisation needs at least about 8 cases per batch for usable statistics. Below that, use GroupNorm.
- Very large inputs — high-resolution pics, long sequences — may force a batch of 1 or 2. Gradient accumulation recovers the effective batch size.
- Small datasets give few updates per epoch at a large batch size. So a less batch and more updates often trains better.
Gradient accumulation
When the batch you want does not fit, accumulate gradients over a few small batches and update once:
accum = 8 # effective batch = 8 x batch_size
opt.zero_grad()
for i, batch in enumerate(loader):
loss = criterion(model(batch.x), batch.y) / accum # keep the average right
loss.backward() # accumulates into .grad
if (i + 1) % accum == 0:
opt.step()
opt.zero_grad()Dividing the loss by accum is key — without it the accumulated gradient is eight times too large and the effective learning rate is eight times what you set.
This is the same accumulation behaviour that makes a forgotten zero_grad() a bug. Here it is the feature.
The cost is wall-clock time: eight small batches take about as long as one large one would have. You get the large-batch statistics, not the large-batch speed.
Full batch, one row, and all between
The same problem solved by batch, stochastic and mini-batch gradient descent, with the noise each one injects measured right -- since that noise is the whole gap.
Building on the same setup, the next block shows the left cases.
The next block covers the left steps.
The next block continues with the left demonstrations.
Questions people ask
How many updates does one epoch give? dataset size / batch size. With 50,000 cases and a batch of 64, that is 782 — not 1.
Why did accuracy drop when I raised the batch size? Fewer updates per epoch and an unscaled learning rate. Scale the rate up and add warm-up.
Is a power of two needed? No, though it aligns well with hardware and is marginally faster.
Should I use full-batch descent on a small dataset? Rarely — you lose the noise that helps generalisation. And get very few updates. Still on 1,000 rows, mini-batches often train better.
Does batch size affect the final accuracy? Somewhat. Very large batches tend to generalise slightly worse. Very small ones are slow and noisy.
What if my dataset does not divide evenly? The last batch is less. Drop it if batch normalisation is in use.
Recap in one screen
- Full batch is exact and unusable. Single-case updates are noisy and waste hardware. Mini-batches win on both.
- The residual noise is useful — it escapes saddles and favours flat minima.
- Shuffle each epoch, and drop the final partial batch when with batch normalisation.
- Scale the learning rate linearly with batch size, with warm-up.
- Gradient accumulation buys a large effective batch on small hardware, at the cost of time.
Recall check
0 of 3Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.
What is meant by “Batch normalisation” here?
What is meant by “Very large inputs” here?
What is meant by “Small datasets” here?
Gradient Descent Batch Processing
Change one control at a time and watch how batch size reshapes the path gradient descent takes toward the minimum.