Finite-Sum Structure#
The ERMEmpirical Risk Minimization objective has a special structure explicitly exploited by variance reduction:
Each is the loss on a single training example (or a minibatch, for batched VR methods). The gradient is:
Computing the full gradient costs per iteration — cheap for , expensive for . SGD samples one index uniformly and uses as a stochastic gradient:
The estimator is unbiased, but its variance is the root problem.
Variance of SGD#
Even at the optimum where , a stochastic gradient is generally nonzero — different data points pull in different directions, and only their average cancels. The gradient variance at is:
SGD's convergence rate under strong convexity is:
where bounds the variance. The second term is an asymptotic noise floor that does not vanish as . To converge to the exact minimiser, SGD must decay the learning rate . A constant learning rate converges only to a neighbourhood of the optimum, with radius proportional to .
The core tension: SGD is cheap per iteration ( vs for full-batch) but requires decaying learning rates and many epochs for high accuracy. Variance reduction retains the per-iteration cost (amortised) while achieving linear convergence to the exact solution — no learning rate decay needed.
SVRG#
Stochastic Variance Reduced Gradient (SVRG) uses a control variate — a correlated, zero-mean correction that reduces the effective variance of the stochastic gradient.
SVRG operates in outer loops (epochs). At the start of each outer loop , a full gradient is computed at a snapshot :
Then for inner iterations ( in the original paper), the stochastic gradient is:
where is sampled uniformly at random. The key insight: , so the term has zero mean but is correlated with . When , the two gradients are similar, and their difference is small — the variance is dramatically reduced.
After inner steps, the snapshot is updated: (or a random/average of the inner iterates), and a new full gradient is computed.
Convergence. Under strong convexity and smoothness, SVRG converges linearly (geometric rate) to the exact minimiser with a constant step size — no decay needed. The amortised cost per iteration is roughly , balancing the occasional full gradient against cheap inner steps.
SAGA#
SAGA is a close cousin that avoids the outer-loop structure by maintaining a table of historical gradients:
At all times, the algorithm stores for each , where is the most recent iterate at which was evaluated. Each iteration:
- Sample index uniformly.
- Form the SAGA gradient:
- Update the table entry for : , and update the running average of stored gradients.
The running average acts as an approximation of the full gradient, updated incrementally (no full-batch pass needed). SAGA requires memory to store the gradient table (one vector per training example), which limits its use to moderate . SVRG's memory is beyond storing a single snapshot gradient.
Practice Notes#
Where VR methods shine:
- Moderate (thousands to low millions of examples) where computing full gradients periodically is affordable.
- Convex or strongly convex problems where linear convergence matters.
- Logistic regression, SVMs, and classical ML with explicit finite-sum structure.
Where VR methods are rarely used:
- Deep learning with : storing per-example gradients (SAGA) or computing periodic full gradients (SVRG) is impractical.
- Nonconvex objectives: the control variate argument relies on convexity; the theory is weaker in the nonconvex case, though some extensions exist.
- When the computational bottleneck is per gradient computation rather than the number of gradient evaluations — VR reduces the number of gradient calls, not the cost per call.
Legacy and influence. While classical SVRG/SAGA are not standard in deep learning pipelines, the control-variate idea has influenced modern methods: momentum-based variance reduction, stochastic Newton sketches, and the analysis of large-batch training all draw on the same variance decomposition.
At the snapshot $\tilde{x}_s$, the SVRG control variate $-\nabla f_i(\tilde{x}_s) + \tilde{\mu}_s$ has expected value ____.
The finite-sum objective $f(x) = rac{1}{n}sum_{i=1}^n f_i(x)$ appears in ERM, where each $f_i$ is the ____ on a single example.
Browser lab#
On synthetic finite-sum least squares (, ), compare the gradient error norm for SGD vs SVRG over epochs. Demonstrate that SVRG drives the gradient to zero while SGD stagnates at the noise floor.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
n, d = 1000, 20
X = np.random.randn(n, d)
w_opt = np.random.randn(d)
y = X @ w_opt + 0.1 * np.random.randn(n)
# Precompute gradients for speed
grads = np.array([X[i] * (X[i] @ w - y[i]) for i, w in enumerate([np.zeros(d)] * n)])
def compute_gradient(w):
return X.T @ (X @ w - y) / n
def compute_gradient_i(w, i):
return X[i] * (X[i] @ w - y[i])
# SGD with constant step size
eta = 0.05
n_epochs = 60
w_sgd = np.zeros(d)
sgd_grad_norms = []
for epoch in range(n_epochs):
perm = np.random.permutation(n)
for i in perm:
g = compute_gradient_i(w_sgd, i)
w_sgd = w_sgd - eta * g
sgd_grad_norms.append(np.linalg.norm(compute_gradient(w_sgd)))
# SVRG
m = 2 * n # inner iterations per outer loop
w_svrg = np.zeros(d)
svrg_grad_norms = []
for outer in range(n_epochs // 2):
w_snap = w_svrg.copy()
full_grad_snap = compute_gradient(w_snap)
for inner in range(m):
i = np.random.randint(n)
g = compute_gradient_i(w_svrg, i) - compute_gradient_i(w_snap, i) + full_grad_snap
w_svrg = w_svrg - eta * g
if inner % n == 0:
svrg_grad_norms.append(np.linalg.norm(compute_gradient(w_svrg)))
fig, ax = plt.subplots(figsize=(8, 5))
ax.semilogy(sgd_grad_norms, "b-", linewidth=1.2, alpha=0.8, label="SGD (constant $\eta$)")
ax.semilogy(svrg_grad_norms, "r-", linewidth=1.5, label="SVRG")
ax.axhline(0.1, color="gray", linestyle="--", alpha=0.5, label="Noise floor (SGD)")
ax.set_xlabel("Epoch")
ax.set_ylabel("$\|\|\\nabla f(w)\|\|$ (log scale)")
ax.set_title("Gradient norm: SGD vs SVRG on finite-sum least squares")
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()
print(f"Final SGD gradient norm: {sgd_grad_norms[-1]:.6f}")
print(f"Final SVRG gradient norm: {svrg_grad_norms[-1]:.6f}")