Lecture 3 — Backpropagation and Stochastic Optimization
Project connection. Project Step 3 first asks you to build a scalar reverse-mode automatic-differentiation engine. You will validate it on a graph with fan-out, against finite differences, and on the hand-wired diamond network from Step 2. You will then use PyTorch’s tensor-valued version of the same algorithm to train the neural -gram model introduced in Lecture 2.
Chapter overview. The cross-entropy objective of a neural network is a scalar function of many parameters. We will represent its evaluation as a computational graph and use the chain rule in two orders. Forward mode carries one input perturbation toward the output. Reverse mode carries one output sensitivity toward every input, which is exactly the shape needed for neural-network training. We then turn from differentiation to optimization: a minibatch supplies a cheap random estimate of the full-corpus gradient, and stochastic gradient descent uses that estimate to update the parameters. These are distinct algorithms. Backpropagation computes a gradient; stochastic gradient descent decides which loss to differentiate and how to use the result.
0. The computational task at hand
We begin with a review of our setting. Let
collect all trainable parameters of a model. A parameter is a number whose value is chosen during training, such as an entry of a weight matrix, bias vector, or embedding matrix. Suppose the training set contains examples and the loss of example is . As in Lecture 2, the empirical loss is the average
The word empirical means that the average is taken over observed data. The function being minimized is also called the objective function, or simply the objective.
The gradient is the column vector of first partial derivatives
Lecture 2 defined gradient descent. Written with a possibly changing learning rate , one update is
The superscript is an index, not an exponent. One execution of the update is an iteration, and is the learning rate, or step size. If the gradient uses all examples, the method is full-batch gradient descent.
There are now two computational problems.
- Differentiation: given a particular loss computation and a particular value of , compute its gradient.
- Optimization: choose which examples to use, choose a learning rate, and update so that the objective becomes small.
Reverse-mode automatic differentiation solves the first problem. Full-batch gradient descent or stochastic gradient descent solves the second.
Think of differentiation as drawing an accurate local arrow on a map: the gradient says which infinitesimal direction raises the loss fastest. Optimization is the travel policy that uses those arrows. It decides how far to move, when to draw a new arrow, and whether to estimate the arrow from the entire dataset or from a small random sample.
Why not differentiate every formula by hand, as we did for logistic and softmax regression in Lecture 2? A neural network may contain millions or billions of parameters, but the more important difficulty is structural: loss along many paths, and the program changes as the architecture changes. We want an algorithm that differentiates the program itself.
1. Computational graphs
A directed graph consists of nodes and directed edges. A directed path is a sequence of directed edges, followed in their arrow directions. A directed cycle is a nonempty directed path that returns to its starting node. A directed graph is acyclic if it has no directed cycle; a finite directed acyclic graph is abbreviated DAG.
A node with no incoming edges is a source. A node with no outgoing edges is a sink. The parents of a node are the nodes with edges into it, and its children are the nodes with edges out of it. A topological ordering of a finite DAG is an ordered list of its nodes so that every parent occurs before its child. Every finite DAG has at least one topological ordering, though it need not have only one.
An edge says that the value at node directly depends on the value at node .
Definition 1.1 (scalar computational graph). A scalar computational graph is a finite DAG with topologically ordered nodes . Each source node is labelled by a variable. Every non-source node is labelled by a differentiable elementary function of its parents:
Here is the set of parent indices of node , and is called the node’s primitive operation, or primitive. The last scalar sink is designated as the output.
A computational graph is designed to model the process of computing a complicated function. Assigning values (typically scalars) to the source variables first and then evaluating the primitives in topological order is called the forward pass. The values created between the inputs and output are intermediate values.
A neural network evaluating its loss on one example is exactly such a computational graph. The source nodes hold the inputs and the parameters: the example’s features and target, together with every entry of each weight matrix, bias vector, and embedding matrix. The primitives are the elementary operations the network performs: the multiplications and additions inside each affine layer, the coordinatewise nonlinearity such as , and the exponentials, sums, and divisions inside softmax and the loss. Each layer is a subgraph, composing layers chains those subgraphs together, and the scalar loss is the output node . In a neural network the intermediates are often called activations, meaning values produced by units or layers during the forward pass.
It’s also possible to assign vectors, matrices or higher-dimensional tensors to the source variables (a tensor is a multidimensional rectangular array of numbers). Expanding every tensor entry into its own scalar node recovers a more complicated computational graph, so without loss of generality, we can consider assigning scalars to the variables labelling the source nodes.
We will use one expression throughout the lecture as a running example.
Introduce the intermediate values
The following picture is the corresponding computational graph. The formula is nested, but the graph makes every direct dependence explicit.
A node has fan-out if it has more than one child. Fan-out means that one value influences the output along more than one directed path. In the figure, both and have fan-out.
At the Project Step 3 test point , the forward pass gives
The local derivative of a primitive is its derivative with respect to one parent while its other parents are held fixed. The local derivative of the primitive with respect to parent is naturally associated to the edge , so we can think of this as a labelling of the directed edges. The local derivatives in this graph are
and
These are deliberately small facts. Automatic differentiation obtains a large derivative by composing local derivatives according to the graph.
Here is the graph with the local derivatives labelling the edges:
2. The chain rule and forward-mode automatic differentiation
The multivariable chain rule says that if a scalar influences a scalar through intermediate variables , then
The sum is essential: it combines every immediate route from through the to .
Automatic differentiation (AD) is the algorithmic evaluation of derivatives by applying exact local derivative rules and the chain rule to a numerical computation. It is termed automatic because a program constructs and traverses the derivative computation.
Our principal application is to compute the gradient of with respect to the parameters for gradient descent.
Fix one parameter coordinate . For every graph node define its tangent
We initialize the input tangents by
where the indicator equals when statement is true and otherwise. More concretely, this sets to when and to otherwise.
This initialization is called a seed. It selects the input variable (in this case ) or combination of variables whose influence we want to follow.
Algorithm 2.1 (forward-mode automatic differentiation). Visit the nodes of the graph in topological order. At non-source node , compute the non-source value
and also compute the tangent
The process is called forward mode because values and their tangents move together in the forward topological direction.
By design, at the output node,
The rule in the definition, of course, is just the chain rule at each node. Its computational efficiency comes from storing the tangent of every intermediate, so each shared subcomputation is performed exactly once per seed.
Example 2.2 (two forward sweeps). For , we perform the full computation needed for the gradient of at point . This gradient has two entries, namely and . To compute the first of these, we use seed , . For the second, we use seed , .
Here is the full computation of , shown on the computational graph. Each node is labelled with its corresponding value and tangent.
The following table shows all the computations for both seeds. The third column corresponds to the computation with seed , and the fourth column for the other seed.
Therefore
Forward mode need not seed only one coordinate. For an input direction , seed . The output tangent becomes
This scalar is the directional derivative of in direction .
To compare algorithmic costs, for positive functions and we write when is bounded above for all sufficiently large , and when it is bounded both above and below by positive constants for all sufficiently large . These notations ignore fixed multiplicative constants and describe growth as the problem size increases.
Proposition 2.3 (cost of a full forward-mode gradient). Suppose the output is scalar and the input has coordinates. Computing by coordinatewise forward mode requires forward sweeps and therefore costs times one evaluation, where the hidden constants are determined by the primitive operations.
Proof
One seed , the th standard basis vector, produces only . Here a standard basis vector has one coordinate equal to and every other coordinate equal to . Repeating for produces all gradient coordinates. Each sweep performs a constant amount of derivative work beside each forward primitive, so its cost is a constant multiple of evaluation.
Forward mode is excellent when there are few inputs and many outputs. The proposition above treats a single scalar output, but notice that one forward sweep computes the tangent of every node in the graph, not only of . If a computation has many outputs — say sink nodes rather than one — then a single sweep with seed delivers the derivatives of all outputs with respect to simultaneously. The cost of forward mode therefore scales with the number of inputs that must be seeded, and is independent of the number of outputs.
Neural-network training has the opposite shape: perhaps billions of input parameters and one scalar loss. This mismatch motivates reverse mode, which we will cover shortly.
3. Reverse mode and backpropagation
Forward mode asks, “Fix an input. How does this node change with respect to that input?” In other words, we compute for a fixed input at each node . Reverse mode asks the complementary question, “Fix an output. How does this node affect that output?” In other words, we compute for a fixed output at each node .
For the fixed scalar output , define the adjoint of node by
The bar marks sensitivity of the output to that node. Some software calls the same quantity the node’s gradient.
Theorem 3.1 (reverse accumulation). Set and initialize every other adjoint . Traverse the nodes in reverse topological order. For every edge from parent to child , perform
Here the equals sign denotes assignment: the right-hand side is computed and stored as the new value of . After the reverse sweep,
for every node . In particular, the adjoints of the parameter nodes are the complete gradient .
Proof
The output seed is correct because . Suppose that the adjoints of all children of node are correct. The multivariable chain rule gives
Reverse topological order guarantees that each child’s adjoint is ready before is processed. The additive updates give exactly the terms in this sum. Induction backward through the ordering proves the claim.
The algorithm is called reverse accumulation because derivative contributions accumulate while moving opposite the forward edges. It is called reverse-mode automatic differentiation because it applies AD in that direction. Backpropagation is the customary neural-network name for reverse-mode AD applied to a loss.
Imagine that the loss sends a one-unit “responsibility message” backward. At an operation, the incoming message is multiplied by each local derivative before being sent to the corresponding parent. If a value was used in several places, several messages return to it and must be added.
The local derivative answers how strongly the child reacts to its parent. The child’s adjoint answers how strongly the loss reacts to the child. Their product answers how strongly the loss reacts along that one edge.
Let us revisit our running example, the graph from Section 1. At , the values were
(This is computed by one simple forward pass.)
Seed . We think of each node sending a message back along each of its incoming edges: the message from child to parent is the child’s adjoint times the local derivative on that edge, and a node’s adjoint is the sum of all the messages it receives. From the primitive , the messages are
(Each of and has only one child, so a single message is the whole adjoint.) The node sends the message
The addition has local derivative toward each parent, so it sends its adjoint unchanged to both: the message goes to , and the same message goes to . Since is the only child of ,
Finally, sends the message
and sends the message
Both inputs received two messages, and each adjoint is the sum of the messages received. The two contributions to cancel:
while
Thus reverse mode reproduces the two-sweep forward-mode answer
but it obtains both coordinates in one reverse sweep.
Real neural-network primitives act on tensors rather than scalars. A tensor is just a finite multidimensional array of numbers: a vector is a -tensor, and a matrix is a -tensor. The shape of the tensor isn’t as important as the number of scalar entries, so let us consider nodes to be labelled with vectors. Let be one directed edge, where and are the vector labels of the nodes. We write
with differentiable. The Jacobian of at is the matrix of partial derivatives
In forward mode, we compute from . If the input tangent is known, the output tangent is
called a Jacobian—vector product (JVP). This is exactly the directional derivative of at in direction (Section 2).
In reverse mode, we compute from . If is known, the chain rule gives
Equivalently, in row-vector notation, is a vector—Jacobian product (VJP). Reverse-mode libraries implement a VJP rule for each primitive.
An arithmetic circuit is a computational graph whose primitives are arithmetic operations such as addition, multiplication, and division where the denominator is nonzero. Its size is its number of operation nodes. We first state the classical result for such circuits; standard AD systems extend the same idea with derivative rules for primitives such as exponential, logarithm, and .
Theorem 3.2 (cheap-gradient principle; Baur—Strassen). Suppose a scalar function is evaluated by an arithmetic circuit of size . Its full gradient with respect to every input can be evaluated by a circuit of size . Consequently, reverse-mode differentiation costs a constant multiple of the forward evaluation, independent of the number of inputs.
Proof sketch
Store the value produced by every node during the forward evaluation. In reverse topological order, visit each edge once. Each visit multiplies a child adjoint by one local derivative and adds the result to a parent adjoint. An arithmetic primitive has a fixed number of parents, and computing each local derivative takes a fixed amount of work. The total reverse work is therefore at most a constant times the number of forward operations. The Baur—Strassen theorem makes this construction precise for rational arithmetic circuits, circuits whose operation nodes use addition, subtraction, multiplication, and division.
4. Backpropagation through softmax and a neural -gram
We now write every step of backpropagation for a concrete model: the Bengio-style neural -gram language model of Lecture 2, Section 4.4, which is also the model trained in Project Step 3. The model predicts the next token from the previous tokens. It looks up a learned embedding vector for each context token, concatenates the embeddings into one vector , passes through a single hidden layer, and applies one more affine map followed by softmax to produce a probability for each of the tokens in the vocabulary. The loss is the negative log of the probability assigned to the token that actually came next. Schematically,
Section 4.1 derives the reverse rule for the softmax cross-entropy loss, a short interlude records the local rules for affine maps and coordinatewise , and Section 4.2 states the architecture precisely and assembles the rules into the complete backward pass.
4.1 Backpropagation: loss with respect to logit
Let be the logit vector: one unconstrained score for each of possible target tokens. Our goal in this subsection is to compute .
Lecture 2 defined
Let be the target-token index. Its one-hot vector has a in coordinate and zeros elsewhere. Define the log-sum-exp function
The single-example categorical cross-entropy loss (Lecture 2, Definition 3.2) is
Proposition 4.1 (softmax cross-entropy gradient). With ,
The coordinates of this gradient sum to zero.
Proof
For coordinate ,
while
Thus , which is the th coordinate of . Both and have coordinate sum one, so their difference has sum zero.
The vector is often called the output error signal, meaning the adjoint at the logit vector from which the rest of backpropagation starts. It is not the difference between a predicted token and a token number; token indices have no numerical distance. It is a difference between two probability vectors.
Example 4.2 (reading the error signal). Take
and suppose the second label is correct. Then
A negative-gradient update lowers logits and and raises target logit . The amount assigned to each wrong label is its current probability, while the target coordinate receives . The update is largest when the model assigns little probability to the target.
Backpropagation: derivatives of affine maps and tanh
We need only affine maps and coordinatewise to propagate the output signal through the Step 3 network. Here are some useful equations for relevant derivatives.
First suppose
where , , and . If the incoming adjoint is , then
The matrix is an outer product: the product of a column vector and a row vector. Its entry is , as required because .
Next suppose coordinatewise. Since ,
The symbol denotes the Hadamard product, or elementwise product: . The in the formula denotes the vector of ones of the appropriate length.
These rules illustrate a general principle: the forward shapes determine the backward shapes. If is , then its gradient must also be ; if has length , then has length . Checking shapes catches many backpropagation mistakes before checking numbers.
4.2 One complete backward pass
Let us recall the notation of the neural -gram model of Lecture 2 (Example 4.7). Let be an embedding matrix. An embedding is a learned vector representation, and row represents token . For a context , an embedding lookup reads those rows and concatenates them:
Concatenation joins vectors end to end. The network then computes
Here
The vector is the hidden preactivation, the value before the nonlinear is applied. The vector is the hidden representation of this context.
The logit gradient, also called the output error signal, is
Applying the tanh and affine rules above gives
and
Finally, split into consecutive blocks of length . The block for position is added to the gradient of row . If the same token occurs in two context positions, both blocks are added to the same row. This operation is often called a scatter-add: values associated with indices are added back into the corresponding locations of a larger array.
The embedding matrix exhibits parameter sharing. Parameter sharing means that the same parameter participates in several parts of the computation. Every occurrence of token reads the same row , so the row has fan-out across positions and examples. Its gradient is the sum of all returning contributions.
For a minibatch, each example acquires a leading batch coordinate. The same equations then use batched matrix multiplication, and parameter gradients sum or average over the batch coordinate.
Backpropagation is modular. Softmax cross-entropy knows only how to turn a target and logits into . The second affine layer knows only how to turn into gradients for , and its input. Tanh knows only its local derivative. The first affine layer and embedding lookup do the same. Each module receives the sensitivity of its output and returns the sensitivity of its inputs.
This modularity is why changing one layer does not require re-deriving the entire network by hand. The AD library needs one correct local rule for the new primitive.
5. Autograd in practice
Autograd is the common name for a software system that records differentiable operations and automatically computes their derivatives. It is an implementation of automatic differentiation, not a different calculus algorithm.
5.1 What a scalar Value object stores
Project Step 3 wraps every scalar in a Value object.
Conceptually, each object stores four things:
data: the node’s forward value;grad: its adjoint, initialized to zero;_parents: the nodes on which it directly depends; and_backward: the local VJP rule that adds this node’s contribution to its parents.
A leaf node is a source node created directly by the user, such as a parameter or input. A root for a backward pass is the output node from which reverse traversal begins—normally the scalar loss. A topological sort is an algorithm that constructs a topological ordering. A depth-first search follows one unvisited dependency path as far as it can before returning to try another. The small engine performs such a search from the loss, appends a node after visiting its parents, and reverses the resulting list for backpropagation.
For multiplication
the closure stored on implements
A closure is a function that retains access to values from the scope in which it was created. Here the local backward closure remembers , , and even after the forward multiplication has returned.
Calling loss.backward() then performs exactly Theorem 3.1:
- topologically order the reachable graph;
- set the loss gradient to ;
- traverse the order backward; and
- call each node’s local backward rule.
5.2 Why gradients accumulate and must be cleared
AD systems use addition for two different kinds of accumulation.
- Within one graph, fan-out creates several paths to the same node.
- Across several backward calls, a user may intentionally sum gradients from several losses or microbatches. A microbatch is one small part of a larger logical batch processed separately to save memory.
For this reason, PyTorch normally adds into .grad rather than
replacing it. A training loop that wants a fresh minibatch gradient must
clear old parameter gradients before the next backward pass. Forgetting
this step silently changes the update into a sum over the current and
previous minibatches.
The parameter update itself should not become part of the differentiated
model computation. PyTorch’s torch.no_grad() context
temporarily disables graph recording. A tensor is detached when it
shares numerical data with a computation but is treated as having no
derivative connection to that computation. Detaching in the middle of a
model accidentally cuts the backward path; detaching a diagnostic value
or disabling recording during the parameter update is intentional.
Algorithm 5.1 (the autograd training cycle). Repeat:
- Forward: evaluate a scalar minibatch loss while recording the graph.
- Clear: set parameter gradients from the previous iteration to zero or to “not yet allocated.”
- Backward: seed the loss adjoint with and reverse the graph.
- Update: with graph recording disabled, replace each parameter by parameter minus learning rate times gradient.
The next forward pass builds a new graph from the updated parameter values.
5.3 Three independent checks
A derivative implementation is most convincing when independent methods agree.
- Hand calculation: derive a small graph with the chain rule.
- Finite differences: compare selected coordinates with nearby loss evaluations.
- Independent systems: compare your scalar engine, finite differences, and PyTorch on one input to the Step 2 diamond network.
For scalars and , the absolute error is . A scale-aware comparison often uses the relative error
The denominator avoids declaring two tiny, harmless numbers wildly
different. No single numerical error threshold fits every data type and
graph, but Step 3’s small float64 checks should agree much
more closely than a
large low-precision training run.
Common failure patterns are diagnostic:
- correct chain graphs but incorrect fan-out graphs suggest assignment instead of addition;
- gradients missing from an early node suggest a wrong traversal order or an accidental detach;
- gradients exactly multiplied by the number of iterations suggest that old gradient values were not cleared;
- plausible AD gradients that disagree with finite differences only at extreme step sizes suggest numerical, rather than chain-rule, error.
6. Connection to Project Step 3
6.1 From the scalar engine to the neural model
Step 3 first validates Theorem 3.1 on . It then rebuilds one forward pass of the Step 2 diamond classifier from scalar objects and differentiates its loss with respect to the input. The closed formula, finite differences, your engine, and PyTorch all produce the same gradient. This closes the loop between a network assembled by hand and an algorithm that differentiates its computational graph.
The project next trains the neural -gram model of Section 4 with minibatch SGD. Its parameter count is
The five terms count the embedding matrix , first-layer weights , first-layer bias , second-layer weights , and second-layer bias . The count grows linearly with context length , whereas an unrestricted next-token matrix indexed by every length- context has entries.
The training set is the set used to update parameters. A disjoint validation set is held out from parameter updates and used to estimate performance on unseen examples during model development. Training loss and validation loss are the same loss formula averaged over these two sets.
6.2 Reading the context-length sweep
The reference run in Project Step 3 reports:
| context length | parameters | training loss | validation loss | bits/character |
|---|---|---|---|---|
| 1 | 11,601 | 2.4706 | 2.4805 | 3.579 |
| 3 | 15,697 | 1.9171 | 1.9821 | 2.860 |
| 5 | 19,793 | 1.9054 | 2.0018 | 2.888 |
| 8 | 25,937 | 1.9372 | 2.0329 | 2.933 |
A nat is the information unit obtained when cross-entropy uses natural logarithms. A bit is the corresponding unit for base- logarithms; dividing loss in nats by gives bits per predicted character.
When , the model sees only the immediately preceding character, just as a bigram model does. The hidden network is wide enough to assign an independent logit vector to each of the finitely many input characters, so its model family contains the bigram behavior. Its validation loss should therefore be near the earlier bigram result when optimization succeeds. A model family, or hypothesis class, is the set of functions obtainable as its parameters vary.
Moving from to supplies useful context and sharply improves validation loss. Moving to or does not help at the fixed hidden width and training budget. Model capacity is the range and complexity of functions a model family can represent. Flattening the longer vector spreads the fixed hidden capacity across more inputs and also makes the optimization problem harder.
An optimization bottleneck occurs when the training procedure fails to find a low-loss member of the model family. A representational bottleneck occurs when the family itself cannot express the needed function. Both can produce underfitting, meaning that important patterns remain unfitted even in the training data. Overfitting has a different signature: training loss improves while validation performance worsens because the model has specialized too strongly to the training set.
The run has worse training loss than the run, so its poorer validation result cannot be explained by classic overfitting alone. The small training improvement leaves room for a mixture of effects, but the matrix as a whole points toward representational and optimization bottlenecks rather than a large train—validation gap.
More available context is not automatically more usable context. The Step 3 model flattens all embeddings into one fixed vector and gives every context the same pattern of connections. A longer window increases the amount the first layer must disentangle, but does not give it a way to select which position matters for this particular prediction.
Lecture 4 introduces attention: a mechanism that forms content-dependent weighted combinations of context positions. The weights can change from one input to another, allowing the model to retrieve relevant earlier information without treating the entire context as one undifferentiated address.
6.3 What to carry forward
The scalar engine is intentionally small, but it exposes principles that remain true in large systems:
- computations form a DAG for one forward pass;
- local VJP rules compose into a global gradient;
- fan-out requires addition;
- reverse order is a dependency requirement, not a coding preference;
- tensor backpropagation avoids materializing giant Jacobians;
- minibatching changes the gradient estimator, not the chain rule; and
- clearing gradients and excluding updates from the graph are explicit parts of the training algorithm.
Summary
A differentiable program can be represented as a computational graph. Forward mode propagates a tangent from selected inputs toward all later nodes and naturally computes Jacobian—vector products. Computing every coordinate of a scalar-output gradient this way requires one sweep per parameter.
Reverse mode seeds the scalar loss with adjoint and propagates sensitivities in reverse topological order. Each edge contributes a child adjoint times a local derivative, and fan-out contributions are added. This produces every parameter derivative in one reverse sweep. The cheap-gradient principle says the arithmetic cost is a constant multiple of the forward computation, while stored or recomputed activations account for the memory cost.
For softmax cross-entropy, the logit adjoint is . Affine, , and
embedding-lookup VJPs route this signal through the Bengio neural
-gram model. The same rules operate on scalars in your
Value engine and on tensors in PyTorch.
In practice, training pairs backpropagation with minibatch stochastic gradient descent: a random batch of examples supplies a cheap estimate of the full-corpus gradient that is correct on average, and the parameters move against it by a learning rate. Batch size, learning-rate schedule, and hardware throughput are tuned together; Lecture 6 takes up these practical choices.
Exercises (paired with Step 3)
A star marks a Project Step 3 task.
- ★ For at , reproduce Example 2.2 and the reverse sweep of Section 3 without looking at the tables. Label every local derivative and every accumulated contribution.
- ★ Implement scalar
Valueprimitives for addition, multiplication, , exponential, logarithm, and powers. Explain which forward values each backward closure must retain. - Construct the smallest computational graph you can find on which replacing addition by assignment during reverse accumulation gives a wrong derivative. Give values for which the wrong answer does not accidentally equal the right one.
- Give a DAG with two valid topological orderings. Reverse one valid ordering and verify Theorem 3.1. Then exhibit an invalid reverse order and identify the contribution it loses.
- Compute the directional derivative of the running example at in direction in two ways: one forward-mode sweep and the dot product .
- ★ Prove Proposition 4.1. Verify it with your scalar engine and with PyTorch for a randomly chosen five-logit vector.
- Derive the affine-layer VJP coordinate by coordinate. Check the shapes of all three outputs , , and .
- For a context , write explicitly how the three blocks of scatter-add into the embedding-matrix gradient. Which rows are exactly zero for this one example?
- Count the activations stored by an -layer width- MLP on a batch of size . Propose a checkpointing scheme and state what it stores and recomputes.
- Show that permuting the hidden coordinates of a one-hidden-layer network, together with the corresponding rows and columns of its weight matrices, preserves the represented function.
- ★ At and , use the Step 2 diamond network with target “inside.” Derive . Reproduce both derivatives with your scalar engine, central finite differences, and PyTorch autograd, and report the maximum absolute disagreement.
- In Algorithm 5.1, deliberately omit gradient clearing for three identical forward/backward passes without updating parameters. Predict the result before running it, then explain the observed gradient values.
- The reference model has worse training and validation loss than the model. Explain why this evidence points toward underfitting rather than overfitting, and name at least two interventions that would distinguish an optimization bottleneck from a representational bottleneck.
Pointers
Baydin, Pearlmutter, Radul, and Siskind, Automatic Differentiation in Machine Learning: a Survey; Griewank and Walther, Evaluating Derivatives; Baur and Strassen (1983); Robbins and Monro (1951); Bengio, Ducharme, Vincent, and Jauvin, A Neural Probabilistic Language Model (2003); and Olah’s backpropagation essay. See the resources page, Project Step 3, and Lecture 2.