BRAGA

02 Writing02-02 Notes02-02-236

Update Rule

Published 16 Apr 20268 min read
Contents
  1. 01Deep Breakdown
  2. 02Flowchart
  3. 03Real-world use
  4. 04The formula, piece by piece
  5. 05The formula in words
  6. 06Critical Concepts
  7. 07Step-by-step calculation (tiny example)
  8. 08Code examples
xi+1=xi−γi(∇f(xi))Tx_{i+1} = x_i - \gamma_i (\nabla f(x_i))^T
(1)

(where γi\gamma_i is the learning rate)

Deep Breakdown

What it tells me (Situation Awareness): This is the core iteration of gradient descent: given current parameters xix_i, subtract the gradient of the loss (scaled by learning rate γi\gamma_i) to obtain the next iterate xi+1x_{i+1}. The negative gradient points in the direction of steepest descent, so this step moves xx downhill on the loss surface. The superscript TT converts the gradient from a row vector (Jacobian convention) to a column vector (parameter vector convention) — in practice, for f:Rd→Rf: \mathbb{R}^d \to \mathbb{R}, ∇f(xi)∈Rd\nabla f(x_i) \in \mathbb{R}^d and the update subtracts it directly. This single equation is the foundation of all ML training: SGD, mini-batch GD, Adam, and RMSProp are all modifications of this update rule.

Flowchart

YESNOCurrentparameters: x_iComputegradient: g_i =nabla f(x_i)Choose learningrate: gamma_iUpdate: x_{i+1}= x_i - gamma_i* g_ii = i + 1Converged?||g_i|| < tol?Return x* = x_iFixed: gammaconstantDecaying:gamma_i =gamma_0 /sqrt(i)Line search:choose gammato satisfy Armijocondition

Real-world use

Where it is used

Training every gradient-based ML model: linear regression, logistic regression, SVM (subgradient), neural networks. Also: fine-tuning pre-trained language models, optimizing hyperparameters using gradient through validation loss, training embedding models (word2vec, matrix factorization).

Problem it solves

Given a differentiable loss f(x)f(x), finding x∗x^* that minimizes it. The update rule provides the iterative procedure: compute gradient at current point, step in the negative gradient direction, repeat. Each step is guaranteed to decrease ff (for small enough γi\gamma_i) because the gradient is the direction of steepest ascent.

How it solves the problem

Taylor expansion: f(x−γg)≈f(x)−γ∥g∥2f(x - \gamma g) \approx f(x) - \gamma \|g\|^2 (for small γ\gamma), where g=∇f(x)g = \nabla f(x). Since ∥g∥2≥0\|g\|^2 \ge 0, each gradient step decreases ff by approximately γ∥g∥2\gamma \|g\|^2. The exact decrease depends on curvature, which is why learning rate selection matters. For strongly convex ff with LL-Lipschitz gradient, the optimal fixed step is γ=1/L\gamma = 1/L and convergence is linear: f(xi)−f(x∗)≤(1−m/L)i[f(x0)−f(x∗)]f(x_i) - f(x^*) \le (1 - m/L)^i [f(x_0) - f(x^*)].

Example in a business

A DS trains logistic regression on 100k e-commerce transactions. f(w)=∑ilog⁡(1+e−yiwTxi)+λ∥w∥2f(w) = \sum_i \log(1 + e^{-y_i w^T x_i}) + \lambda \|w\|^2. Gradient: ∇f(w)=−∑iyixiσ(−yiwTxi)+2λw\nabla f(w) = -\sum_i y_i x_i \sigma(-y_i w^T x_i) + 2\lambda w where σ\sigma is sigmoid. Update: w←w−γ∇f(w)w \leftarrow w - \gamma \nabla f(w). With γ=0.01\gamma = 0.01 and 500 iterations, the model converges to AUC 0.87 on the test set.

The formula, piece by piece

  • xi+1x_{i+1}: the new parameter vector after one gradient step
  • xix_i: the current parameter vector at iteration ii
  • γi\gamma_i: the learning rate at iteration ii; scalar that scales the step size; γi>0\gamma_i > 0
  • ∇f(xi)\nabla f(x_i): the gradient of ff evaluated at xix_i; a vector of partial derivatives; points uphill
  • −γi∇f(xi)-\gamma_i \nabla f(x_i): the gradient step; negative because we go downhill
  • (∇f(xi))T(\nabla f(x_i))^T: transpose notation; in ML, gradient is often written as a row vector (Jacobian), transposed to match column vector xx; in practice, numpy computes ∇f\nabla f as a column vector directly
  • ii: iteration index; subscript denotes which iterate (not element-wise indexing)
  • Variable learning rate: γi\gamma_i can change each iteration (learning rate schedules, line search)

The formula in words

"The new parameters equal the old parameters minus the learning rate times the gradient. Move a small step in the direction opposite to the gradient (which points uphill), so we go downhill on the loss surface." Tiny example: f(x)=x2f(x) = x^2, x0=5x_0 = 5, γ=0.3\gamma = 0.3. ∇f(x0)=2⋅5=10\nabla f(x_0) = 2 \cdot 5 = 10. x1=5−0.3⋅10=5−3=2x_1 = 5 - 0.3 \cdot 10 = 5 - 3 = 2. ∇f(x1)=2⋅2=4\nabla f(x_1) = 2 \cdot 2 = 4. x2=2−0.3⋅4=2−1.2=0.8x_2 = 2 - 0.3 \cdot 4 = 2 - 1.2 = 0.8. x3=0.8−0.3⋅1.6=0.32x_3 = 0.8 - 0.3 \cdot 1.6 = 0.32. Converging to x∗=0x^* = 0.

Critical Concepts

  • Too large γ\gamma: overshooting; can diverge. For f(x)=x2f(x) = x^2, instability occurs when γ>1/L=0.5\gamma > 1/L = 0.5 (since L=2L = 2 for ∇2f=2\nabla^2 f = 2). At γ=0.5\gamma = 0.5: x1=x0−0.5⋅2x0=0x_1 = x_0 - 0.5 \cdot 2x_0 = 0. At γ=0.6\gamma = 0.6: x1=x0−1.2x0=−0.2x0x_1 = x_0 - 1.2 x_0 = -0.2 x_0 — still converges but oscillates.
  • Too small γ\gamma: slow convergence; needs many more iterations.
  • Learning rate schedules: constant (γ\gamma), step decay (γ/2\gamma / 2 every kk epochs), cosine annealing, warm restarts, linear warmup + cosine decay (common for transformers).
  • Difference from Newton's method: gradient descent uses −∇f-\nabla f only (first order); Newton uses −(∇2f)−1∇f-(\nabla^2 f)^{-1} \nabla f (second order). Newton converges faster (quadratically near optimum) but requires computing and inverting the Hessian (expensive for dd large).
  • Mini-batch vs full batch: full batch uses the exact gradient (one pass over all nn samples); mini-batch uses the gradient of a random subset of size BB; stochastic (SGD) uses B=1B=1. Noisy gradients add implicit regularization and help escape local minima in deep learning.
  • Gradient clipping: in deep learning, gradients can become very large (exploding gradients in RNNs).

Clipping: g←g⋅min⁡(1,c/∥g∥)g \leftarrow g \cdot \min(1, c/\|g\|) prevents instability without changing direction.

Step-by-step calculation (tiny example)

Run 4 gradient descent steps on f(w1,w2)=w12+4w22f(w_1, w_2) = w_1^2 + 4w_2^2 starting at (3,2)(3, 2) with γ=0.2\gamma = 0.2. ∇f=(2w1,8w2)\nabla f = (2w_1, 8w_2).

Step w1w_1 w2w_2 g1=2w1g_1 = 2w_1 g2=8w2g_2 = 8w_2 f(w)f(w)
0 3.000 2.000 6.000 16.000 25.000
1 1.800 -1.200 3.600 -9.600 9.000
2 1.080 0.720 2.160 5.760 3.240
3 0.648 -0.432 1.296 -3.456 1.166
4 0.389 0.259 0.778 2.074 0.420

Converging to (0,0)(0, 0). Note: large step for w2w_2 term (8w28w_2) causes oscillation; the w1w_1 term converges faster.

Code examples

Beginner example (Python)

python
import numpy as np

def gradient_descent_step(
    x: np.ndarray,
    grad: np.ndarray,
    learning_rate: float,
) -> np.ndarray:
    """Single gradient descent update: x_{i+1} = x_i - gamma * grad."""
    return x - learning_rate * grad


# f(w) = w1^2 + 4*w2^2; gradient = (2w1, 8w2)
def f(w: np.ndarray) -> float:
    return w[0]**2 + 4 * w[1]**2


def grad_f(w: np.ndarray) -> np.ndarray:
    return np.array([2 * w[0], 8 * w[1]])


LEARNING_RATE = 0.2
N_STEPS = 10
w = np.array([3.0, 2.0])
print(f"Gradient descent: f(w) = w1^2 + 4*w2^2, gamma={LEARNING_RATE}")
print(f"{'Step':>4} {'w1':>8} {'w2':>8} {'f(w)':>10} {'||grad||':>10}")
for step in range(N_STEPS):
    g = grad_f(w)
    print(f"{step:>4} {w[0]:>8.4f} {w[1]:>8.4f} {f(w):>10.4f} {np.linalg.norm(g):>10.4f}")
    w = gradient_descent_step(w, g, LEARNING_RATE)
print(f"\nFinal w* = {w}, f(w*) = {f(w):.2e}")

Beginner example (R)

r
# Gradient descent update rule: x_{i+1} = x_i - gamma_i * grad_f(x_i)
gd_step <- function(x, grad, learning_rate) x - learning_rate * grad
f <- function(w) w[1]^2 + 4*w[2]^2
grad_f <- function(w) c(2*w[1], 8*w[2])
LEARNING_RATE <- 0.2
N_STEPS <- 10
w <- c(3, 2)
cat(sprintf("%-4s %-8s %-8s %-10s %-10s\n", "Step", "w1", "w2", "f(w)", "||grad||"))
for (step in 0:(N_STEPS-1)) {
  g <- grad_f(w)
  cat(sprintf("%-4d %-8.4f %-8.4f %-10.4f %-10.4f\n", step, w[1], w[2], f(w), sqrt(sum(g^2))))
  w <- gd_step(w, g, LEARNING_RATE)
}
cat(sprintf("Final w* = (%.4e, %.4e), f(w*) = %.2e\n", w[1], w[2], f(w)))

Professional example (Python)

python
import numpy as np
from sklearn.datasets import make_classification
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score
np.random.seed(42)

# -- Full gradient descent with learning rate schedule for logistic regression --
N_SAMPLES = 800
X_raw, y_raw = make_classification(n_samples=N_SAMPLES, n_features=8, n_informative=5, random_state=42)
scaler = StandardScaler()
X = scaler.fit_transform(X_raw)
y = y_raw * 2 - 1   # {-1, +1} encoding
N = len(y)
D = X.shape[1]
LAMBDA_REG = 0.01

def log_loss_with_reg(w: np.ndarray) -> float:
    margins = y * (X @ w)
    return float(np.mean(np.log1p(np.exp(-margins)))) + LAMBDA_REG * np.sum(w**2)

def log_loss_gradient(w: np.ndarray) -> np.ndarray:
    margins = y * (X @ w)
    sigmoid_neg = 1 / (1 + np.exp(margins))   # sigmoid(-margin)
    return -1/N * X.T @ (y * sigmoid_neg) + 2 * LAMBDA_REG * w


# Learning rate schedules to compare
def constant_lr(step: int, lr0: float = 0.5) -> float:
    return lr0


def step_decay(step: int, lr0: float = 0.5, decay: float = 0.5, drop_every: int = 50) -> float:
    return lr0 * (decay ** (step // drop_every))


def sqrt_decay(step: int, lr0: float = 0.5) -> float:
    return lr0 / np.sqrt(step + 1)


schedules = {
    "constant": constant_lr,
    "step decay": step_decay,
    "sqrt decay": sqrt_decay,
}
N_ITER = 300
results = {}
for name, lr_func in schedules.items():
    w = np.zeros(D)
    losses = []
    for step in range(N_ITER):
        gamma = lr_func(step)
        g = log_loss_gradient(w)
        # Core update rule: x_{i+1} = x_i - gamma_i * grad f(x_i)
        w = w - gamma * g
        losses.append(log_loss_with_reg(w))
    y_score = X @ w
    auc = roc_auc_score(y_raw, y_score)
    results[name] = {"w": w, "final_loss": losses[-1], "auc": auc, "losses": losses}
print(f"{'Schedule':<15} {'Final Loss':>12} {'AUC':>8}")
for name, res in results.items():
    print(f"{name:<15} {res['final_loss']:>12.6f} {res['auc']:>8.4f}")
print(f"\nUpdate rule: x_{{i+1}} = x_i - gamma_i * grad_f(x_i)")
print(f"gamma_i is the learning rate — the single most important hyperparameter.")

Source: MML1 p. 234

#calculus#continuous-optimisation#gradient-descent

Keyboard shortcuts

Single keys, ignored while typing in a field or with Cmd, Ctrl or Alt held.

0
Home
1 2 3 4
Work, Writing, Tools, About
j / k
Next / previous section on this page
n / p
Next / previous project or essay
u
Up one level
l
Deutsch
t
Light or dark theme
?
This list
Esc
Close