Gradient Descent for Deep Learning

Imagine you are lost on a foggy mountain and want to reach the valley floor. You cannot see the terrain. But you can feel which direction the ground slopes beneath your feet. The natural strategy is to take a step downhill, feel the new slope, and repeat. Gradient descent formalizes exactly this strategy for training deep networks. Here, the "mountain" is a loss function of millions of parameters. The "slope" is the gradient computed by Backpropagation. This idea matters because every deep network is trained this way. This includes everything from a two-layer classifier to a billion-parameter transformer. Each is trained by iteratively nudging its weights in the direction that most reduces the loss. Gradient descent is the mathematical skeleton underneath every optimizer used in practice: SGD, momentum, RMSProp, Adam. Without a precise understanding of the basic full-batch update rule, none of the later refinements make sense. They are all patches, accelerations, or approximations of this one core idea. The big picture is that learning in a neural network is cast as numerical optimization. Given a differentiable loss L(θ)L(\theta) over parameters θ\theta, find θ\theta that makes LL small. Gradient descent does not search this space randomly. It exploits local first-order information — the gradient ∇θL\nabla_\theta L — to always move in the steepest locally-decreasing direction. Deep loss landscapes are non-convex, high-dimensional, and enormous in scale. Because of this, the simple local rule, repeated millions of times, is the only computationally tractable strategy available. This article delivers a precise, self-contained treatment of full-batch gradient descent. It covers the exact update rule, and why it provably decreases the loss under reasonable assumptions. It also covers its time and space cost, a fully worked numerical trace, and common failure modes. Finally, it traces the historical lineage of the idea, before foreshadowing the stochastic and mini-batch variants that make it scale to modern datasets.

Key takeaways
  • Gradient descent updates parameters by moving opposite to the loss gradient, scaled by a learning rate.
  • The full-batch version computes the gradient using the entire training set at every step.
  • Correctness rests on a first-order Taylor approximation: the gradient is the direction of steepest ascent, so its negative locally decreases the loss.
  • Convergence is guaranteed to a stationary point for smooth losses with a small-enough learning rate; convexity is not required for this guarantee, but it is required for guaranteeing a global minimum.
  • Per-step cost is O(n⋅p)O(n \cdot p) for nn training examples and pp parameters, dominated by one forward and one backward pass over the whole dataset.
  • It is the ancestor of Stochastic Gradient Descent and Mini-Batch Gradient Descent, which trade exactness of the gradient for speed and noise that can even help generalization.
The core insight

Gradient descent trains a network by repeatedly subtracting a small multiple of the loss gradient from every parameter, because that gradient points in the direction of steepest local increase.

The idea

Consider a deep network parameterized by a vector θ∈Rp\theta \in \mathbb{R}^p (all weights and biases flattened together). Given a training set {(x(i),y(i))}i=1n\{(x^{(i)}, y^{(i)})\}_{i=1}^n, define the empirical loss

L(θ)=1n∑i=1nℓ(fθ(x(i)),y(i)),L(\theta) = \frac{1}{n}\sum_{i=1}^{n} \ell\big(f_\theta(x^{(i)}), y^{(i)}\big),

where fθf_\theta is the network's forward function and ℓ\ell is a per-example loss such as cross-entropy or squared error. Training means finding θ⋆\theta^\star that minimizes LL. Because LL is a smooth (almost everywhere differentiable) function of θ\theta, and pp is enormous, we cannot solve ∇θL(θ)=0\nabla_\theta L(\theta) = 0 in closed form; instead we search iteratively.

Full-batch gradient descent is the iterative rule

θt+1=θt−η ∇θL(θt),\theta_{t+1} = \theta_t - \eta \, \nabla_\theta L(\theta_t),

where η>0\eta > 0 is the learning rate (step size) and ∇θL(θt)\nabla_\theta L(\theta_t) is the gradient of the entire dataset's average loss, evaluated at the current parameters. The word "full-batch" specifically means every one of the nn training examples contributes to the gradient at every single step — as opposed to stochastic or mini-batch variants, which use a random subset. The gradient itself is computed efficiently via Backpropagation, which applies the chain rule layer-by-layer through the Computational Graph of the network. The key conceptual object is the gradient vector ∇θL(θ)\nabla_\theta L(\theta): a vector with one entry per parameter, each entry being the partial derivative ∂L/∂θj\partial L / \partial \theta_j. This vector, by a basic fact of multivariable calculus, points in the direction of steepest increase of LL at θ\theta. Moving in the opposite direction — hence the minus sign — is therefore the direction of steepest decrease, at least locally. The learning rate η\eta controls how far we trust this local linear approximation before recomputing it.

How it works

The algorithm alternates two phases at every iteration: (1) a forward-and-backward pass over the whole dataset to compute the exact gradient, and (2) a parameter update using that gradient. This repeats until some stopping criterion — a fixed iteration budget, a small gradient norm, or a validation-loss plateau — is met. ``` ALGORITHM FullBatchGradientDescent Input: theta_0 : initial parameter vector (e.g., random init) D = {(x_i,y_i)} : full training set, i = 1..n eta : learning rate T : maximum number of iterations tol : gradient-norm stopping tolerance theta <- theta_0 for t = 1 to T:

# --- Forward pass: compute predictions and loss for ALL n examples --- total_loss <- 0 for i = 1 to n: y_hat_i <- forward(theta, x_i) total_loss <- total_loss + loss(y_hat_i, y_i) total_loss <- total_loss / n

# --- Backward pass: accumulate gradient over ALL n examples --- grad <- zero_vector(size(theta)) for i = 1 to n: grad_i <- backprop(theta, x_i, y_i) # dL_i/d theta via chain rule grad <- grad + grad_i grad <- grad / n

# --- Stopping check --- if norm(grad) < tol: break

# --- Parameter update: the actual gradient-descent step --- theta <- theta - eta * grad return theta ``` Two implementation details matter. First, the loop `for i = 1 to n` for the backward pass is, in practice, vectorized: frameworks compute the gradient over the whole batch as one tensor operation rather than a Python-level loop, but the mathematical content — averaging per-example gradients — is identical. Second, the per-example gradients `grad_i` are computed by a single application of reverse-mode automatic differentiation (backpropagation) per example, reusing intermediate activations from the forward pass; this is what keeps the cost linear rather than quadratic in the network depth. The following diagram shows the computational loop as a state machine: the two big phases (accumulate gradient over the full dataset, then take one step) followed by a stopping check.

Figure
The full-batch gradient descent cycle: one iteration consumes the entire dataset before a single parameter update
θ_t current params Forward + Backward over ALL n examples x₁,y₁ x₂,y₂ … xₙ,yₙ accumulate ∂ℓᵢ/∂θ → ∇L(θ_t) = (1/n)Σ ∇ℓᵢ ∇L(θ_t) full-batch gradient θ_t+1 = θ_t − η∇L(θ_t) one parameter update ‖∇L(θ_t)‖ < tol ? yes → stop no: loop back to θ_t+1 θ_t+1 becomes new θ_t

Complexity & correctness

Time complexity. Let nn be the number of training examples, pp the number of parameters, and assume a forward pass through the network costs O(p)O(p) floating-point operations per example (true for standard feedforward and convolutional architectures, where compute is dominated by matrix multiplications whose total size scales with the parameter count). Backpropagation for one example costs the same order as the forward pass, O(p)O(p), since reverse-mode automatic differentiation performs one extra pass of comparable cost to the forward pass — a classical and important fact, since naively computing pp separate directional derivatives would cost O(p2)O(p^2). Therefore:

  • One full-batch gradient computation costs O(n⋅p)O(n \cdot p).
  • Over TT iterations, full-batch gradient descent costs O(T⋅n⋅p)O(T \cdot n \cdot p).

This is the honest cost: to take even one single parameter-update step, you must touch every training example. This is the central tradeoff that motivates Mini-Batch Gradient Descent and Stochastic Gradient Descent — they approximate ∇θL(θ)\nabla_\theta L(\theta) using a subset of size m≪nm \ll n, reducing per-step cost to O(m⋅p)O(m \cdot p) at the price of noisier gradient estimates.

Space complexity. Training requires storing:

  • the parameters θ\theta: O(p)O(p),
  • the gradient accumulator: O(p)O(p),
  • all intermediate activations needed by backpropagation for the current batch: O(n⋅h)O(n \cdot h) where hh is the total width of hidden layers, since full-batch descent must forward-propagate the entire dataset before it can backpropagate.

This last term is often the true bottleneck in practice — it is why full-batch gradient descent is rarely used at scale for deep networks: keeping every activation for every training example in memory simultaneously is infeasible once nn is large (this is precisely why mini-batching exists, quite apart from any statistical benefit of noise).

Correctness argument. Why does subtracting the gradient decrease the loss? Consider a first-order Taylor expansion of LL around θt\theta_t for a small step Δθ\Delta\theta:

L(θt+Δθ)≈L(θt)+∇L(θt)⊤Δθ.L(\theta_t + \Delta\theta) \approx L(\theta_t) + \nabla L(\theta_t)^\top \Delta\theta.

We want to choose Δθ\Delta\theta (subject to a bound on its size, since the approximation is only locally valid) to make the right-hand side as negative as possible. Among all vectors Δθ\Delta\theta of fixed norm rr, the inner product ∇L(θt)⊤Δθ\nabla L(\theta_t)^\top \Delta\theta is minimized (most negative) when Δθ\Delta\theta points exactly opposite to ∇L(θt)\nabla L(\theta_t) — a direct consequence of the Cauchy–Schwarz inequality, since

∇L(θt)⊤Δθ≥−∥∇L(θt)∥ ∥Δθ∥,\nabla L(\theta_t)^\top \Delta\theta \ge -\|\nabla L(\theta_t)\|\, \|\Delta\theta\|,

with equality exactly when Δθ=−c ∇L(θt)\Delta\theta = -c\,\nabla L(\theta_t) for c>0c>0. Choosing Δθ=−η∇L(θt)\Delta\theta = -\eta \nabla L(\theta_t) realizes this optimal direction. Substituting back,

L(θt+1)≈L(θt)−η ∥∇L(θt)∥2,L(\theta_{t+1}) \approx L(\theta_t) - \eta \, \|\nabla L(\theta_t)\|^2,

which is strictly less than L(θt)L(\theta_t) whenever ∇L(θt)≠0\nabla L(\theta_t) \ne 0 and η\eta is small enough that the linear approximation is trustworthy. This gives a descent guarantee: each step decreases the loss, provided η\eta is chosen appropriately. Making this rigorous (rather than just "approximately") requires an assumption on the smoothness of LL. If LL is β\beta-smooth (its gradient is β\beta-Lipschitz — a standard, mild assumption for the loss surfaces induced by common activations and losses on bounded domains), then a classical descent lemma gives

L(θt+1)≤L(θt)−η(1−ηβ2)∥∇L(θt)∥2.L(\theta_{t+1}) \le L(\theta_t) - \eta\Big(1 - \frac{\eta\beta}{2}\Big)\|\nabla L(\theta_t)\|^2.

Choosing η<2/β\eta < 2/\beta (in practice η≤1/β\eta \le 1/\beta) makes the coefficient of ∥∇L(θt)∥2\|\nabla L(\theta_t)\|^2 strictly positive, guaranteeing monotonic decrease at every step until a stationary point (∇L(θt)=0\nabla L(\theta_t) = 0) is reached. Since deep-network losses are non-convex, this argument only guarantees convergence to a stationary point — a local minimum, saddle point, or (in pathological cases) a local maximum — not the global minimum. This is the honest tradeoff: gradient descent's correctness guarantee is about local improvement, not global optimality, and in practice saddle points and flat regions are the dominant obstacle in high-dimensional non-convex loss landscapes, not local minima being globally suboptimal (empirically, most local minima found by gradient descent in deep nets are of comparable quality).

Worked example

To make the mechanics completely concrete, consider a tiny network: a single linear neuron with no bias, predicting y^=θx\hat{y} = \theta x, trained with squared error on three data points. This is small enough to trace by hand, yet non-trivial (asymmetric, non-zero-centered data) so no error can hide. Dataset: (x1,y1)=(1,2)(x_1,y_1)=(1,2), (x2,y2)=(2,3)(x_2,y_2)=(2,3), (x3,y3)=(3,5)(x_3,y_3)=(3,5). Loss: L(θ)=13∑i=13(θxi−yi)2L(\theta) = \frac{1}{3}\sum_{i=1}^3 (\theta x_i - y_i)^2. Gradient: dLdθ=23∑i=13xi(θxi−yi)\dfrac{dL}{d\theta} = \dfrac{2}{3}\sum_{i=1}^3 x_i(\theta x_i - y_i). Let η=0.05\eta = 0.05 and θ0=0\theta_0 = 0.

ttθt\theta_ty^1,y^2,y^3\hat y_1,\hat y_2,\hat y_3residuals (y^i−yi)(\hat y_i-y_i)∇L(θt)\nabla L(\theta_t)θt+1=θt−η∇L\theta_{t+1}=\theta_t-\eta\nabla L
00.0000.000, 0.000, 0.000−2, −3, −523(1(−2)+2(−3)+3(−5))=23(−23)=−15.333\frac{2}{3}(1(-2)+2(-3)+3(-5)) = \frac{2}{3}(-23)=-15.3330−0.05(−15.333)=0.76670 - 0.05(-15.333) = 0.7667
10.76670.767, 1.533, 2.300−1.233, −1.467, −2.70023(1(−1.233)+2(−1.467)+3(−2.700))=23(−12.567)=−8.378\frac{2}{3}(1(-1.233)+2(-1.467)+3(-2.700)) = \frac{2}{3}(-12.567)=-8.3780.7667+0.4189=1.18560.7667 + 0.4189 = 1.1856
21.18561.186, 2.371, 3.557−0.814, −0.629, −1.44323(1(−0.814)+2(−0.629)+3(−1.443))=23(−6.401)=−4.267\frac{2}{3}(1(-0.814)+2(-0.629)+3(-1.443)) = \frac{2}{3}(-6.401)=-4.2671.1856+0.2134=1.39901.1856 + 0.2134 = 1.3990
31.39901.399, 2.798, 4.197−0.601, −0.202, −0.80323(1(−0.601)+2(−0.202)+3(−0.803))=23(−3.415)=−2.277\frac{2}{3}(1(-0.601)+2(-0.202)+3(-0.803)) = \frac{2}{3}(-3.415)=-2.2771.3990+0.1138=1.51281.3990 + 0.1138 = 1.5128

Notice the loss is falling each step (residuals shrink in magnitude) and the gradient magnitude is shrinking (15.33→8.38→4.27→2.2815.33 \to 8.38 \to 4.27 \to 2.28), exactly as predicted by the descent lemma: as θt\theta_t approaches the least-squares optimum, ∇L(θt)→0\nabla L(\theta_t) \to 0. The exact least-squares solution here is θ⋆=∑xiyi∑xi2=2+6+151+4+9=2314≈1.643\theta^\star = \frac{\sum x_iy_i}{\sum x_i^2} = \frac{2+6+15}{1+4+9}=\frac{23}{14}\approx 1.643, and the iterates 0→0.767→1.186→1.399→1.5130 \to 0.767 \to 1.186 \to 1.399 \to 1.513 are visibly converging toward it geometrically. This single-parameter trace scales conceptually to a full deep network: the same update rule, applied to a pp-dimensional θ\theta using gradients from backpropagation, drives every weight toward a stationary point of the loss.

Common pitfalls & traps

PitfallWhat goes wrongFix
Learning rate too largeLoss oscillates or diverges to infinity because the linear (Taylor) approximation of the descent argument breaks downShrink η\eta; use a rate below the 2/β2/\beta smoothness bound, or a schedule
Learning rate too smallTraining is technically correct but converges impractically slowly, wasting computeTune η\eta empirically, or use adaptive methods (foreshadowing later optimizers)
Forgetting to zero/re-accumulate gradientsGradients from a previous iteration silently add onto the new ones, corrupting the update directionExplicitly reset the gradient accumulator to zero every iteration, as shown in the pseudocode
Treating a stationary point as "the" minimumNon-convex losses have many stationary points (saddles, local minima); gradient descent offers no global-optimality guaranteeUse good initialization, over-parameterization, or momentum-based optimizers empirically shown to escape poor saddles
Assuming full-batch is "the standard" methodFull-batch gradient descent is rarely used at real scale because of its O(n⋅p)O(n\cdot p) per-step cost and memory footprint for activationsUnderstand it as the conceptual baseline; use mini-batch/SGD in practice
Ignoring feature/parameter scalingBadly-scaled inputs create elongated, ill-conditioned loss surfaces where a single η\eta works poorly in different directionsNormalize inputs, use well-conditioned initializations, or per-parameter adaptive learning rates

Pioneers & history

The method traces back to 19th-century numerical analysis, long before neural networks existed, and was repurposed by 20th-century optimization and machine-learning researchers into the workhorse of deep learning.

Origins · 1 minAugustin-Louis Cauchy and how this was discovered
AC
Augustin-Louis Cauchy
1789–1857 · French mathematician who first proposed the method of steepest descent in 1847 as a technique for solving systems of equations arising in astronomical calculations, laying the analytic foundation later reused for training neural networks

History
1847
— Cauchy proposes the method of steepest descent for solving nonlinear equations.
1951
— Robbins and Monro introduce stochastic approximation, the statistical ancestor of stochastic gradient descent.
1986
— Rumelhart, Hinton, and Williams popularize backpropagation for training multilayer networks with gradient descent.
2012
— Deep convolutional networks trained via gradient-descent variants (AlexNet) win ImageNet, sparking the deep learning boom.
2015
— Adam and related adaptive gradient methods become the default optimizers built atop the gradient-descent update rule.

Where this matters

Every major deep learning framework — PyTorch, TensorFlow, JAX — implements gradient descent and its stochastic/mini-batch descendants as the core `optimizer.step()` primitive that all training loops call. Large-scale systems such as image classifiers, large language models, recommendation engines, and reinforcement learning agents are all trained by variants of this same update rule, layered with momentum, adaptive scaling, weight decay, and learning-rate schedules. Understanding the plain full-batch rule is the prerequisite for understanding why these more sophisticated optimizers were invented: each addresses a specific weakness (noise, curvature, scale, cost) of the basic gradient-descent step described here. It also underlies numerical methods far beyond deep learning, including classical regression, matrix factorization, and physics-based simulation, wherever a smooth objective must be minimized over continuous parameters.

Ask the tutor: How do momentum and adaptive learning rates (like Adam) modify this basic gradient descent update, and why do they train deep networks faster in practice?

Is full-batch gradient descent guaranteed to find the global minimum of a deep network's loss?

No. The descent argument only guarantees that the loss decreases (or reaches a stationary point) at each step, using a local, first-order Taylor approximation. Since deep network losses are generally non-convex, there can be many stationary points: local minima, saddle points, and flat regions. Gradient descent has no mechanism to distinguish a good local minimum from a bad one, or to guarantee escaping a saddle point quickly, since gradients vanish at both. In practice, empirical evidence suggests most local minima found in over-parameterized deep networks are of comparable quality, but this is an empirical observation, not a guarantee provided by the algorithm itself.

How is the learning rate η chosen in practice?

Theoretically, if the loss is β\beta-smooth, choosing η<2/β\eta < 2/\beta guarantees monotonic decrease, but β\beta is rarely known exactly for deep networks. In practice, η\eta is tuned empirically: start with a value from a standard range (e.g., 0.1, 0.01, 0.001), monitor the training loss curve, and reduce η\eta if the loss oscillates or diverges, or increase it if convergence is too slow. Modern practice typically also uses a learning-rate schedule that decays η\eta over training, and adaptive optimizers that implicitly rescale η\eta per parameter based on gradient history.

What is the difference between full-batch, mini-batch, and stochastic gradient descent?

Full-batch gradient descent computes the exact gradient using every training example before each update, costing O(n⋅p)O(n \cdot p) per step. Stochastic gradient descent uses a single randomly-sampled example per update, costing O(p)O(p) per step but with a noisy, high-variance gradient estimate. Mini-batch gradient descent is the practical middle ground, using a small random subset of size mm (e.g., 32–512 examples) per step, balancing gradient accuracy against computational and memory cost. All three share the identical update rule θ←θ−η∇L(θ)\theta \leftarrow \theta - \eta \nabla L(\theta); they differ only in how much data is used to estimate the gradient at each step.

Why does the gradient point in the direction of steepest increase, mathematically?

For a differentiable function LL, the directional derivative of LL at θ\theta in a unit direction uu equals ∇L(θ)⊤u\nabla L(\theta)^\top u. By the Cauchy–Schwarz inequality, this inner product is maximized, over all unit vectors uu, exactly when uu points in the same direction as ∇L(θ)\nabla L(\theta), giving the maximum possible rate of increase ∥∇L(θ)∥\|\nabla L(\theta)\|. Symmetrically, the inner product is minimized (most negative) when uu points opposite to ∇L(θ)\nabla L(\theta). This is why moving along −∇L(θ)-\nabla L(\theta) produces the fastest local decrease in the loss.

What happens if the learning rate is too large?

If η\eta exceeds roughly 2/β2/\beta where β\beta is the smoothness constant of the loss, the descent lemma's guarantee breaks down: the linear (first-order) approximation used to justify the step is no longer accurate over a step of that size. In practice this manifests as the loss oscillating wildly, growing instead of shrinking, or the parameters diverging to very large values, often producing `NaN` losses once activations overflow. This is one of the most common and most easily misdiagnosed training bugs, because the code itself is often perfectly correct — only the hyperparameter choice is wrong.

Does gradient descent require the loss function to be convex?

No. The descent guarantee — that the loss decreases at each step for a small enough learning rate — only requires the loss to be smooth (Lipschitz-gradient), not convex. Convexity would additionally guarantee that any stationary point is a global minimum, which is a much stronger and generally false assumption for deep networks. Deep learning practitioners rely on gradient descent's weaker, non-convex guarantee (convergence to some stationary point) combined with empirical evidence that, for sufficiently over-parameterized networks, most such points found in practice generalize well.

How does full-batch gradient descent relate to backpropagation?

Backpropagation is the algorithm that computes the gradient ∇θL(θ)\nabla_\theta L(\theta) efficiently via the chain rule through the network's computational graph; gradient descent is the optimization rule that uses that gradient to update parameters. They are complementary: backpropagation answers "what is the gradient right now?" in O(p)O(p) time per example, while gradient descent answers "given the gradient, how should I change θ\theta?" Every training loop in deep learning interleaves the two: one backpropagation pass produces the gradient, then one gradient-descent step consumes it.

Before this

Where this leads