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 over parameters , find that makes small. Gradient descent does not search this space randomly. It exploits local first-order information — the gradient — 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.
- 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 for training examples and 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.
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 (all weights and biases flattened together). Given a training set , define the empirical loss
where is the network's forward function and is a per-example loss such as cross-entropy or squared error. Training means finding that minimizes . Because is a smooth (almost everywhere differentiable) function of , and is enormous, we cannot solve in closed form; instead we search iteratively.
Full-batch gradient descent is the iterative rule
where is the learning rate (step size) and 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 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 : a vector with one entry per parameter, each entry being the partial derivative . This vector, by a basic fact of multivariable calculus, points in the direction of steepest increase of at . Moving in the opposite direction — hence the minus sign — is therefore the direction of steepest decrease, at least locally. The learning rate 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.
Complexity & correctness
Time complexity. Let be the number of training examples, the number of parameters, and assume a forward pass through the network costs 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, , since reverse-mode automatic differentiation performs one extra pass of comparable cost to the forward pass — a classical and important fact, since naively computing separate directional derivatives would cost . Therefore:
- One full-batch gradient computation costs .
- Over iterations, full-batch gradient descent costs .
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 using a subset of size , reducing per-step cost to at the price of noisier gradient estimates.
Space complexity. Training requires storing:
- the parameters : ,
- the gradient accumulator: ,
- all intermediate activations needed by backpropagation for the current batch: where 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 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 around for a small step :
We want to choose (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 of fixed norm , the inner product is minimized (most negative) when points exactly opposite to — a direct consequence of the Cauchy–Schwarz inequality, since
with equality exactly when for . Choosing realizes this optimal direction. Substituting back,
which is strictly less than whenever and is small enough that the linear approximation is trustworthy. This gives a descent guarantee: each step decreases the loss, provided is chosen appropriately. Making this rigorous (rather than just "approximately") requires an assumption on the smoothness of . If is -smooth (its gradient is -Lipschitz — a standard, mild assumption for the loss surfaces induced by common activations and losses on bounded domains), then a classical descent lemma gives
Choosing (in practice ) makes the coefficient of strictly positive, guaranteeing monotonic decrease at every step until a stationary point () 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 , 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: , , . Loss: . Gradient: . Let and .
| residuals | |||||
|---|---|---|---|---|---|
| 0 | 0.000 | 0.000, 0.000, 0.000 | −2, −3, −5 | ||
| 1 | 0.7667 | 0.767, 1.533, 2.300 | −1.233, −1.467, −2.700 | ||
| 2 | 1.1856 | 1.186, 2.371, 3.557 | −0.814, −0.629, −1.443 | ||
| 3 | 1.3990 | 1.399, 2.798, 4.197 | −0.601, −0.202, −0.803 |
Notice the loss is falling each step (residuals shrink in magnitude) and the gradient magnitude is shrinking (), exactly as predicted by the descent lemma: as approaches the least-squares optimum, . The exact least-squares solution here is , and the iterates are visibly converging toward it geometrically. This single-parameter trace scales conceptually to a full deep network: the same update rule, applied to a -dimensional using gradients from backpropagation, drives every weight toward a stationary point of the loss.
Common pitfalls & traps
| Pitfall | What goes wrong | Fix |
|---|---|---|
| Learning rate too large | Loss oscillates or diverges to infinity because the linear (Taylor) approximation of the descent argument breaks down | Shrink ; use a rate below the smoothness bound, or a schedule |
| Learning rate too small | Training is technically correct but converges impractically slowly, wasting compute | Tune empirically, or use adaptive methods (foreshadowing later optimizers) |
| Forgetting to zero/re-accumulate gradients | Gradients from a previous iteration silently add onto the new ones, corrupting the update direction | Explicitly reset the gradient accumulator to zero every iteration, as shown in the pseudocode |
| Treating a stationary point as "the" minimum | Non-convex losses have many stationary points (saddles, local minima); gradient descent offers no global-optimality guarantee | Use good initialization, over-parameterization, or momentum-based optimizers empirically shown to escape poor saddles |
| Assuming full-batch is "the standard" method | Full-batch gradient descent is rarely used at real scale because of its per-step cost and memory footprint for activations | Understand it as the conceptual baseline; use mini-batch/SGD in practice |
| Ignoring feature/parameter scaling | Badly-scaled inputs create elongated, ill-conditioned loss surfaces where a single works poorly in different directions | Normalize 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
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 -smooth, choosing guarantees monotonic decrease, but is rarely known exactly for deep networks. In practice, 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 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 over training, and adaptive optimizers that implicitly rescale 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 per step. Stochastic gradient descent uses a single randomly-sampled example per update, costing 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 (e.g., 32–512 examples) per step, balancing gradient accuracy against computational and memory cost. All three share the identical update rule ; 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 , the directional derivative of at in a unit direction equals . By the Cauchy–Schwarz inequality, this inner product is maximized, over all unit vectors , exactly when points in the same direction as , giving the maximum possible rate of increase . Symmetrically, the inner product is minimized (most negative) when points opposite to . This is why moving along produces the fastest local decrease in the loss.
What happens if the learning rate is too large?
If exceeds roughly where 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 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 time per example, while gradient descent answers "given the gradient, how should I change ?" Every training loop in deep learning interleaves the two: one backpropagation pass produces the gradient, then one gradient-descent step consumes it.