Jaber Notes · 4 of 16
Optimization
GD convergence, SGD, momentum, Adam/AdamW, LR schedules, Newton/L-BFGS.
How models actually get trained: convergence theory for gradient descent, the variance of SGD, why momentum accelerates, the full Adam/AdamW update rules, and learning-rate schedules that matter in practice.
Visual reference
Gradient descent & learning rate
How we actually find model parameters: gradient descent and its variants, convergence theory, and practical tricks.
Table of contents
- First-order vs second-order methods
- Gradient descent (batch)
- Stochastic and mini-batch SGD
- Momentum and Nesterov
- Adaptive learning rate methods
- Second-order methods (Newton, quasi-Newton)
- Learning rate schedules
- Convergence theory (overview)
- Practical optimization tips
- Coordinate descent
1. First-order vs second-order methods
| Property | First-order | Second-order |
|---|---|---|
| Info used | Gradients | Gradients + Hessian |
| Cost per step | Cheap () | Expensive ( to ) |
| Convergence rate | Linear | Quadratic (near optimum) |
| Scalability | billions of parameters | Up to thousands |
| Examples | GD, SGD, Adam | Newton, BFGS, L-BFGS |
Most deep learning uses first-order. Classical ML in medium-scale uses L-BFGS or IRLS.
2. Gradient descent (batch)
2.1 Algorithm
Initialize . At each step :
where is the learning rate (step size).
Intuition: negative gradient points in the direction of steepest decrease. GD takes a step of size in that direction.
2.2 Why step in the negative gradient direction?
First-order Taylor expansion:
To decrease most per unit of : choose (steepest descent direction, from Cauchy-Schwarz).
2.3 Convergence for -smooth convex functions
A function is -smooth if (Lipschitz gradients, bounded curvature).
With :
This is convergence — to get -accurate, need iterations.
2.4 Convergence for strongly convex functions
A function is -strongly convex if everywhere ().
With step , GD converges linearly (exponentially fast):
Condition number : higher → slower convergence. Ridge regression adds which increases and reduces → faster convergence.
2.5 Learning rate sensitivity
| Learning rate | Behavior |
|---|---|
| Too large () | Diverges (oscillates then explodes) |
| Too small | Converges but very slowly |
| Just right () | Optimal convergence |
Optimal requires knowing (Lipschitz constant of gradient), which is usually estimated via line search or set empirically.
3. Stochastic and mini-batch SGD
3.1 Motivation
Batch GD evaluates over all samples per step — expensive for large .
Stochastic GD (SGD): use gradient of one random sample as an estimate:
is an unbiased estimator of : .
3.2 Mini-batch SGD
Compromise: use a batch of samples (batch size):
- Gradient variance: . Larger batch → less noise → more stable updates.
- Cost per step: . Number of steps to see all data once (epoch) = .
- Hardware: larger batches allow better GPU parallelism, but may require adjusting learning rate.
Linear scaling rule: if you multiply batch size by , multiply learning rate by (empirically works in certain regimes).
3.3 SGD vs batch GD tradeoffs
| Batch GD | Mini-batch SGD | |
|---|---|---|
| Gradient quality | Exact | Noisy but unbiased |
| Convergence | Smooth | Noisy (may oscillate near optimum) |
| Generalization | Sometimes slightly worse | Noisy updates can regularize (implicit regularization) |
| Memory | Need full dataset | Only batch size |
| Parallelism | Easy | Best with moderate batch |
Noise as regularizer: SGD's gradient noise prevents convergence to sharp minima (which tend to generalize poorly) — an implicit regularization effect observed empirically.
3.4 Epoch vs iteration
- Iteration: one gradient update.
- Epoch: one full pass through the training data ( iterations).
4. Momentum and Nesterov
4.1 SGD with momentum
Problem with vanilla SGD: slow convergence in directions with low curvature (gradient zig-zags across narrow valleys).
Momentum accumulates a velocity vector:
is the momentum coefficient (typically 0.9). Velocity is an exponentially weighted moving average of past gradients.
Effect: accelerates in consistent directions, dampens oscillations across the valley.
Effective step size in consistent direction: if gradient is always the same , velocity converges to , so effective step = times the learning rate.
4.2 Nesterov accelerated gradient (NAG)
Improvement: compute gradient at the look-ahead position:
Theoretical convergence for smooth convex functions: vs for vanilla GD.
5. Adaptive learning rate methods
5.1 Motivation
Different parameters can have very different scales and gradient magnitudes. A single learning rate is suboptimal. Adaptive methods maintain per-parameter learning rates.
5.2 AdaGrad
Accumulate squared gradients:
Effect: large learning rate for infrequent (sparse) features, small for frequent ones. Good for sparse data (NLP, embeddings). Problem: only grows → learning rate monotonically decreases → may stop learning.
5.3 RMSProp
Fix AdaGrad's vanishing lr with exponentially decaying average:
5.4 Adam (Adaptive Moment Estimation)
Most widely used in practice. Maintains both first and second moment estimates:
First moment (mean): Second moment (variance): (elementwise)
Bias correction (accounts for initialization at zero):
Update:
Default hyperparameters: , , , .
Intuition: like momentum (first moment), but adapts step size by second moment (variance of recent gradients). Large gradient variance → small step (cautious in noisy directions).
AdamW: Adam + decoupled weight decay. Standard in modern DL training. Adds to update directly (not through gradient), properly implementing L2 regularization.
5.5 Comparison table
| Optimizer | Formula type | When to use |
|---|---|---|
| SGD | Fixed lr | Simple convex, well-tuned |
| SGD+Momentum | Accelerated | Vision tasks with good tuning |
| AdaGrad | Adaptive | Sparse features, NLP |
| RMSProp | Adaptive decay | RNNs, non-stationary |
| Adam/AdamW | Adaptive + momentum | General default, DL |
| L-BFGS | Second-order quasi-Newton | Classical ML, small/medium |
6. Second-order methods (Newton, quasi-Newton)
6.1 Newton's method
Use quadratic approximation:
Minimize over : .
Update: .
Quadratic convergence near optimum: number of correct digits roughly doubles per step.
Problem: inverting costs — impractical for large .
6.2 L-BFGS (Limited-memory BFGS)
BFGS approximates via rank-1 updates using gradient differences. L-BFGS stores only the last (typically 5–20) gradient/position pairs, costing per step.
Used in sklearn's LogisticRegression(solver='lbfgs') and scipy.optimize.minimize(method='L-BFGS-B').
6.3 IRLS (Iteratively Reweighted Least Squares)
Newton's method applied to GLMs (logistic regression) can be rewritten as a weighted least squares problem at each iteration. sklearn's LogisticRegression(solver='newton-cg').
7. Learning rate schedules
7.1 Constant learning rate
Simple, often used with Adam. May oscillate near optimum.
7.2 Step decay
Reduce lr by a factor every epochs: .
7.3 Exponential decay
.
7.4 Cosine annealing
Smoothly decays from \eta_\max to \eta_\min. Often paired with warm restarts (restart at peak periodically).
7.5 Linear warmup + decay
Ramp learning rate up for first few steps/epochs, then decay. Important for large-batch training and transformers.
7.6 Reduce on plateau
Reduce lr when monitored metric stops improving. keras.callbacks.ReduceLROnPlateau.
8. Convergence theory (overview)
8.1 Summary for different function classes
| Function class | Optimal rate | Algorithm |
|---|---|---|
| Convex, -smooth | GD | |
| -strongly convex, -smooth | GD (linear) | |
| Convex, -smooth (Nesterov) | NAG | |
| Non-convex (DL) | SGD (to stationary point) |
8.2 Saddle points in non-convex optimization
Non-convex functions (neural nets) have saddle points — zero gradient, not a minimum. Second-order methods can get stuck; SGD's noise helps escape. Saddle points are less problematic in practice than once feared (high-dimensional saddles are usually surrounded by directions that lead downward).
8.3 Local minima in neural networks
Modern evidence: many local minima in deep networks have similar (near-optimal) loss values. The harder problem is plateaus and saddle points early in training.
9. Practical optimization tips
9.1 Gradient checking
Numerically verify gradient implementation:
Use . Match to within relative error .
9.2 Feature scaling and conditioning
GD on unscaled features can be very slow: gradient points toward steepest descent direction in feature space, which may zig-zag across poorly scaled dimensions. Always scale features (StandardScaler, MinMaxScaler) before gradient-based optimization.
9.3 Gradient clipping
For exploding gradients (especially in RNNs): if , scale it: .
9.4 Initialization
Weights initialized at zero → symmetric neurons learn the same features in neural nets. Use random initialization; scale to control gradient magnitude.
9.5 Batch size and learning rate tradeoff
| Batch size | Effect |
|---|---|
| Smaller (e.g., 32) | More noise, may generalize better, slower per epoch |
| Larger (e.g., 2048) | Less noise, faster epochs, may need LR warmup |
Large-batch training is studied in "large-batch optimization" literature.
10. Coordinate descent
Optimize one parameter (or block of parameters) at a time:
Convergence: for convex functions with separable non-smooth terms (like L1), coordinate descent with cyclic or random updates converges.
Used for: Lasso (per-coordinate soft-thresholding has closed form), SVM dual (SMO algorithm updates one pair of dual variables at a time).
# Coordinate descent for Lasso (simplified)
import numpy as np
def lasso_coordinate_descent(X, y, lam, n_iter=1000):
n, p = X.shape
w = np.zeros(p)
for _ in range(n_iter):
for j in range(p):
r = y - X @ w + X[:, j] * w[j] # partial residual
z = X[:, j] @ r / n # unconstrained update
w[j] = np.sign(z) * max(abs(z) - lam / (2*n), 0) # soft threshold
return w*File: notes/04_optimization.md — next: notes/05_evaluation_and_validation.md*
From-scratch code
Runnable implementations, each checked against its scikit-learn equivalent.
"""
Gradient descent for linear regression (MSE) — compare to closed-form OLS.
Loss: J(w) = (1/2n) ||y - Xw||^2
Gradient: (1/n) X^T (Xw - y)
"""
from __future__ import annotations
import numpy as np
from sklearn.datasets import make_regression
from sklearn.metrics import mean_squared_error
def add_intercept(X: np.ndarray) -> np.ndarray:
return np.column_stack([np.ones(len(X)), X])
def ols_normal_equations(X: np.ndarray, y: np.ndarray) -> np.ndarray:
return np.linalg.solve(X.T @ X, X.T @ y)
def mse_grad(X: np.ndarray, y: np.ndarray, w: np.ndarray) -> np.ndarray:
n = X.shape[0]
return (X.T @ (X @ w - y)) / n
def gd_linear_regression(
X: np.ndarray, y: np.ndarray, lr: float, n_iter: int
) -> np.ndarray:
w = np.zeros(X.shape[1])
for _ in range(n_iter):
w -= lr * mse_grad(X, y, w)
return w
def main() -> None:
X_raw, y = make_regression(n_samples=300, n_features=4, noise=8.0, random_state=2)
X_raw = np.asarray(X_raw, dtype=np.float64)
y = np.asarray(y, dtype=np.float64)
X = add_intercept(X_raw)
w_closed = ols_normal_equations(X, y)
w_gd = gd_linear_regression(X, y, lr=0.15, n_iter=5000)
print("Closed-form w:", w_closed)
print("GD w :", w_gd)
print("||diff|| :", np.linalg.norm(w_closed - w_gd))
print("MSE closed :", mean_squared_error(y, X @ w_closed))
print("MSE GD :", mean_squared_error(y, X @ w_gd))
if __name__ == "__main__":
main()