Skip to main content
© 2026 ePowerAI. All rights reserved.
CoursesContact
eAI
CoursesContact
Week 9: Variance Reduction
Adv. Optimization
01Week 1: Optimality Conditions Revisited
02Week 2: Lagrangian Duality in Depth
03Week 3: KKT and Sensitivity
04Week 4: LP, QP, and Conic Form
05Week 5: Semidefinite Programming
06Week 6: Interior-Point Methods
07Week 7: Proximal Methods and ADMM
08Week 8: Newton and Quasi-Newton
09Week 9: Variance Reduction
10Week 10: Large-Batch and Distributed Training
11Week 11: Automatic Differentiation
12Week 12: Sharpness and SAM
13Week 13: Landscapes and Implicit Bias
14Week 14: Capstone — Structure Meets Scale
Week 09· Adv. Optimization· Second-order & scale22 min read

Week 9: Variance Reduction

✦Learning Outcomes
  • Write the finite-sum objective f(x)=1n∑i=1nfi(x)f(x) = \frac{1}{n}\sum_{i=1}^n f_i(x)f(x)=n1​∑i=1n​fi​(x) and its full gradient
  • Explain gradient variance as the obstacle that forces SGDStochastic Gradient Descent to decay its learning rate
  • Describe SVRGStochastic Variance Reduced Gradient's snapshot gradient control variate and why it reduces variance
  • State when variance-reduced methods beat minibatch SGD
◆Prerequisites

Background: Week 8 (convergence rates, Newton vs first-order trade-offs). Intermediate Optim Weeks 6–7 (SGDStochastic Gradient Descent, momentum, learning rate schedules). The concept that SGD's noise comes from sampling a random fif_ifi​ at each step.

Later weeks: Variance reduction is the conceptual foundation for Week 10 (large-batch training, where variance scales inversely with batch size) and explains why large-batch training can increase variance if not paired with learning rate adjustments.

Finite-Sum Structure#

The ERMEmpirical Risk Minimization objective has a special structure explicitly exploited by variance reduction:

f(x)=1n∑i=1nfi(x)f(x) = \frac{1}{n}\sum_{i=1}^n f_i(x)f(x)=n1​∑i=1n​fi​(x)

Each fif_ifi​ is the loss on a single training example (or a minibatch, for batched VR methods). The gradient is:

∇f(x)=1n∑i=1n∇fi(x)\nabla f(x) = \frac{1}{n}\sum_{i=1}^n \nabla f_i(x)∇f(x)=n1​∑i=1n​∇fi​(x)

Computing the full gradient costs O(n)O(n)O(n) per iteration — cheap for n=104n = 10^4n=104, expensive for n=107n = 10^7n=107. SGD samples one index iii uniformly and uses ∇fi(x)\nabla f_i(x)∇fi​(x) as a stochastic gradient:

Ei[∇fi(x)]=∇f(x)\mathbb{E}_i[\nabla f_i(x)] = \nabla f(x)Ei​[∇fi​(x)]=∇f(x)

The estimator is unbiased, but its variance is the root problem.

Variance of SGD#

Even at the optimum x∗x^*x∗ where ∇f(x∗)=0\nabla f(x^*) = 0∇f(x∗)=0, a stochastic gradient ∇fi(x∗)\nabla f_i(x^*)∇fi​(x∗) is generally nonzero — different data points pull in different directions, and only their average cancels. The gradient variance at xxx is:

σ2(x)=1n∑i=1n∥∇fi(x)−∇f(x)∥2\sigma^2(x) = \frac{1}{n}\sum_{i=1}^n \|\nabla f_i(x) - \nabla f(x)\|^2σ2(x)=n1​∑i=1n​∥∇fi​(x)−∇f(x)∥2

SGD's convergence rate under strong convexity is:

E[∥xk−x∗∥2]≤(1−ημ)k∥x0−x∗∥2+ησ2μ\mathbb{E}[\|x_k - x^*\|^2] \leq (1 - \eta\mu)^k \|x_0 - x^*\|^2 + \frac{\eta \sigma^2}{\mu}E[∥xk​−x∗∥2]≤(1−ημ)k∥x0​−x∗∥2+μησ2​

where σ2\sigma^2σ2 bounds the variance. The second term is an asymptotic noise floor that does not vanish as k→∞k \to \inftyk→∞. To converge to the exact minimiser, SGD must decay the learning rate ηk→0\eta_k \to 0ηk​→0. A constant learning rate converges only to a neighbourhood of the optimum, with radius proportional to ησ2\sqrt{\eta \sigma^2}ησ2​.

The core tension: SGD is cheap per iteration (O(1)O(1)O(1) vs O(n)O(n)O(n) for full-batch) but requires decaying learning rates and many epochs for high accuracy. Variance reduction retains the O(1)O(1)O(1) 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 sss, a full gradient is computed at a snapshot x~s\tilde{x}_sx~s​:

μ~s=∇f(x~s)=1n∑i=1n∇fi(x~s)\tilde{\mu}_s = \nabla f(\tilde{x}_s) = \frac{1}{n}\sum_{i=1}^n \nabla f_i(\tilde{x}_s)μ~​s​=∇f(x~s​)=n1​∑i=1n​∇fi​(x~s​)

Then for mmm inner iterations (m≈2nm \approx 2nm≈2n in the original paper), the stochastic gradient is:

vk=∇fi(xk)−∇fi(x~s)+μ~sv_k = \nabla f_i(x_k) - \nabla f_i(\tilde{x}_s) + \tilde{\mu}_svk​=∇fi​(xk​)−∇fi​(x~s​)+μ~​s​

where iii is sampled uniformly at random. The key insight: Ei[∇fi(x~s)]=μ~s\mathbb{E}_i[\nabla f_i(\tilde{x}_s)] = \tilde{\mu}_sEi​[∇fi​(x~s​)]=μ~​s​, so the term −∇fi(x~s)+μ~s-\nabla f_i(\tilde{x}_s) + \tilde{\mu}_s−∇fi​(x~s​)+μ~​s​ has zero mean but is correlated with ∇fi(xk)\nabla f_i(x_k)∇fi​(xk​). When xk≈x~sx_k \approx \tilde{x}_sxk​≈x~s​, the two gradients are similar, and their difference is small — the variance is dramatically reduced.

After mmm inner steps, the snapshot is updated: x~s+1=xm\tilde{x}_{s+1} = x_mx~s+1​=xm​ (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 O(1+n/m)O(1 + n/m)O(1+n/m), balancing the occasional O(n)O(n)O(n) full gradient against mmm cheap O(1)O(1)O(1) 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 ∇fi(x^i)\nabla f_i(\hat{x}_i)∇fi​(x^i​) for each iii, where x^i\hat{x}_ix^i​ is the most recent iterate at which ∇fi\nabla f_i∇fi​ was evaluated. Each iteration:

  1. Sample index iii uniformly.
  2. Form the SAGA gradient: vk=∇fi(xk)−∇fi(x^i)+1n∑j=1n∇fj(x^j)v_k = \nabla f_i(x_k) - \nabla f_i(\hat{x}_i) + \frac{1}{n}\sum_{j=1}^n \nabla f_j(\hat{x}_j)vk​=∇fi​(xk​)−∇fi​(x^i​)+n1​∑j=1n​∇fj​(x^j​)
  3. Update the table entry for iii: x^i=xk\hat{x}_i = x_kx^i​=xk​, 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 O(n)O(n)O(n) memory to store the gradient table (one vector per training example), which limits its use to moderate nnn. SVRG's memory is O(1)O(1)O(1) beyond storing a single snapshot gradient.

Practice Notes#

Where VR methods shine:

  • Moderate nnn (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 n≫107n \gg 10^7n≫107: 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 O(d)O(d)O(d) 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.

Exercise · Fill in the blank

At the snapshot $\tilde{x}_s$, the SVRG control variate $-\nabla f_i(\tilde{x}_s) + \tilde{\mu}_s$ has expected value ____.

Question 1 of 3

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 (n=1000n = 1000n=1000, d=20d = 20d=20), compare the gradient error norm ∥∇f(xk)∥\|\nabla f(x_k)\|∥∇f(xk​)∥ for SGD vs SVRG over epochs. Demonstrate that SVRG drives the gradient to zero while SGD stagnates at the noise floor.

python · runs in browser
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}")
← Previous
Week 8: Newton and Quasi-Newton
Next →
Week 10: Large-Batch and Distributed Training
On this page
  • Finite-Sum Structure
  • Variance of SGD
  • SVRG
  • SAGA
  • Practice Notes
  • Browser lab