Stochastic gradient descent
Replace the full gradient with a stochastic estimate and understand the noise versus step-size tradeoff.
Machine-learning objectives are averages over millions of examples, and computing the full gradient every step is prohibitive. Stochastic gradient descent replaces it with the gradient of a small random batch: a noisy but unbiased and cheap estimate. The noise changes the convergence story, and managing it through the step-size schedule is the central practical skill.
The finite-sum objective
Training minimizes an average loss over examples,
where is the loss on example and are the parameters. The full gradient costs a pass over all examples.
The stochastic update
SGD samples an index (or a small minibatch) uniformly at random and steps along that single gradient:
The key property is unbiasedness: averaging over the random draw,
[1]The stochastic gradient points in the right direction on average, but any single step is corrupted by variance .
The noise floor with a constant step size
The variance changes convergence fundamentally. For -strongly convex, -smooth with a constant step size , the expected suboptimality satisfies a recursion of the form
The first term contracts as in deterministic GD; the second injects noise every step. At the fixed point, contraction balances injection and the iterates hover at a noise floor:
Constant-step SGD converges fast to a neighborhood of the optimum, then bounces around it. The floor height is proportional to : a smaller step gives a lower floor but slower approach.
Decaying step sizes
To reach the exact optimum, the step size must shrink so the noise term vanishes. The Robbins-Monro conditions are
[2]A schedule like satisfies both and gives . In practice, step decay or cosine schedules implement the same idea: large steps early to move fast, small steps late to settle.
Why minibatches
A minibatch of size averages independent gradient estimates, cutting the variance to . This lowers the noise floor and lets a larger step size be used, while exploiting vectorized hardware. The minibatch size trades gradient quality against per-step cost.
Check it on arrays
import numpy as np
rng = np.random.default_rng(0)
# Least squares as a finite sum: f_i(x) = 0.5 (a_i^T x - b_i)^2
n, d = 2000, 10
A = rng.normal(size=(n, d))
x_true = rng.normal(size=d)
b = A @ x_true + 0.1 * rng.normal(size=n)
x_star = np.linalg.lstsq(A, b, rcond=None)[0]
def full_loss(x):
return 0.5 * np.mean((A @ x - b)**2)
def sgd(eta, batch=1, steps=20_000, decay=False):
x = np.zeros(d)
for t in range(steps):
idx = rng.integers(0, n, size=batch)
g = A[idx].T @ (A[idx] @ x - b[idx]) / batch
lr = eta / (1 + 0.001 * t) if decay else eta
x = x - lr * g
return full_loss(x) - full_loss(x_star)
# Constant step: lower eta -> lower noise floor
print("const eta=0.05, B=1 :", round(sgd(0.05), 6))
print("const eta=0.01, B=1 :", round(sgd(0.01), 6))
# Bigger batch lowers the floor at the same step size
print("const eta=0.05, B=32:", round(sgd(0.05, batch=32), 6))
# Decaying step reaches lower suboptimality
print("decaying eta0=0.05 :", round(sgd(0.05, decay=True), 6))A smaller constant step and a larger batch both lower the noise floor, and the decaying schedule drives suboptimality further down.
Exercises
Exercise 1. A constant-step SGD run with step settles at a noise floor of height . If halving the step is observed to halve the floor, explain why, and predict the floor after dividing the step by relative to the original.
Solution
Step 1. The floor formula is linear in : the factors , , and are properties of the problem, fixed across runs, so only varies.
Step 2. Halving multiplies the whole expression by , which halves the floor. That linear dependence is exactly the observation.
Step 3. Dividing the step by multiplies the floor by , so the new floor is one tenth of the original. The cost is a slower approach: the contraction factor moves closer to , so reaching the lower floor takes more steps.
Exercise 2. Show that the minibatch gradient is unbiased and that a batch of size drawn with replacement has variance . Let with the independent and uniform.
Solution
Step 1. Each draw is unbiased: . By linearity of expectation, , so the batch estimate is unbiased.
Step 2. Write the error of one draw as , with and . The batch error is .
Step 3. The draws are independent, so cross terms vanish in expectation, for :
The variance drops as : a batch of cuts gradient noise by a factor of .
Exercise 3 (code). Estimate the noise floor empirically. Run constant-step SGD on a least-squares finite sum, and verify that halving the step size roughly halves the measured suboptimality floor, matching the linear dependence of Exercise 1.
Solution
import numpy as np
rng = np.random.default_rng(0)
n, d = 2000, 10
A = rng.normal(size=(n, d))
x_true = rng.normal(size=d)
b = A @ x_true + 0.1 * rng.normal(size=n)
x_star = np.linalg.lstsq(A, b, rcond=None)[0]
def full_loss(x):
return 0.5 * np.mean((A @ x - b)**2)
def floor(eta, steps=40_000, tail=5_000):
# average suboptimality over the last `tail` steps = noise-floor estimate
x = np.zeros(d)
acc = 0.0
for t in range(steps):
i = rng.integers(0, n)
g = A[i] * (A[i] @ x - b[i])
x = x - eta * g
if t >= steps - tail:
acc += full_loss(x) - full_loss(x_star)
return acc / tail
f1 = floor(0.02)
f2 = floor(0.01) # half the step
ratio = f2 / f1
print("floor(eta=0.02):", round(f1, 6))
print("floor(eta=0.01):", round(f2, 6))
print("ratio:", round(ratio, 3))
assert 0.35 < ratio < 0.75 # near 0.5, the linear predictionThe measured floor at half the step is roughly half the original, the linear scaling predicted by the recursion's fixed point. Sampling noise keeps the ratio near but not exactly .
The next lesson speeds up the deterministic part with momentum.
References
- [1]Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning, ch. 8. MIT Press. Minibatch stochastic gradient estimates and their unbiasedness.↩
- [2]Robbins, H., & Monro, S. (1951). "A stochastic approximation method". Annals of Mathematical Statistics, 22, 400-407. The step-size conditions for convergence (doi:10.1214/aoms/1177729586).↩