Exercises — Contributing to open-source ML
Two numbers we will re-use throughout, so let us pin them down once, in plain words, before any symbol appears.
- Memory of normal training — how much GPU room the activations eat when you keep every layer's output. We wrote this in the parent, where = number of layers, = batch size (how many examples at once), = hidden size (width of each layer's vector).
- Cosine similarity — a single number between and saying how much two vectors "point the same way". It is the dot product (sum of pairwise products) divided by the two lengths.

The red staircase above is the contribution lifecycle — refer back to it whenever an exercise asks "what step is missing?".
Level 1 — Recognition
Recall Solution
Missing step: Claim It (announcing on the issue that you are working on it).
The lifecycle in the parent is Find Issue → Claim It → Fork → Branch → Code+Tests → Submit PR → Review → Revisions → Merge. "Claim It" is the step whose entire job is preventing duplicate work — you post a comment so nobody else starts the same fix. Because it was skipped, two people wrote the same patch and the bot flagged the second as a duplicate. This is the only step whose stated purpose matches the failure symptom "duplicate", so it is uniquely determined.
Recall Solution
The formula is Each factor is binary (0 or 1). Correct ✅, tested ✅, clear ✅ — but the reviewer cannot see the reasoning for the design choice, so . Because the factors multiply, one zero collapses everything: The PR is unmergeable until you add a justification, even though the code works.
Recall Solution
RAMP = Read, Active, Makes-work-easier, People-tagged-issues. The rule from the parent's mnemonic: 3 or more "yes" → good fit. Here we have R, A, P as "yes" (3 yeses) and only M as "no". , so yes, it is a good fit. The one miss (it doesn't help your daily work) lowers your motivation but does not disqualify it.
Level 2 — Application
Recall Solution
Dot product — multiply matching entries and add: . Length of — square, add, square-root: . Length of : . Close to 1 → the vectors nearly point the same way, which is why the buggy function's answer was fine; only its shape handling was broken.
Recall Solution
A 1-D tensor has exactly one axis, numbered . There is no axis — so .sum(dim=1) asks for a dimension that does not exist, raising RuntimeError: expected 2D.
The original code assumed a batch layout [batch, features] where axis = which example, axis = the feature entries you sum over. With a bare 1-D vector the features live on axis . So you must sum over dim=0. (The parent's fix instead adds a fake batch axis with unsqueeze(0) turning [3] into [1,3], after which the original dim=1 works — both routes are valid.)
Recall Solution
Divide checkpointed by normal — the and cancel because they are identical in both: So the asymptotic model predicts you keep 25% of activation memory (a 75% saving) at . The parent's measured number was 35% kept (65% saved) — real code keeps some non-activation memory (weights, optimizer state) that this simple -model ignores, which is exactly why the parent insisted on empirical benchmarks on top of the derivation.
Level 3 — Analysis
Recall Solution
Memory saved — how much smaller relative to original: Time added — how much slower relative to original: These match the parent's "82% memory, +128% time" to rounding. Worth it when: the model does not fit at all without checkpointing — a 2.3× slowdown is infinitely better than an out-of-memory crash that gives you no result. If it already fits, the slowdown is pure loss.
Recall Solution
- First — the ordinary forward pass: to produce the output you compute all layers once. This happens with or without checkpointing.
- Second — the recomputation during backward. Normally you stored every layer's activation, so backward just reads it (no extra forward work). Checkpointing threw those activations away to save memory, so during backward it must redo the forward pass of the discarded layers to get them back.
- Why and not more: you recompute each discarded layer at most once (during its own backward step), so total forward work is the original plus one extra of recomputation . It is a constant-factor (2×) cost, not a blow-up — that constant factor is precisely the "spare compute cycles" the parent said GPUs can afford.
Recall Solution
Root cause: floating-point arithmetic. computed two different ways (once via cosine_similarity, once via manual torch.sqrt) accumulates tiny rounding differences in the last bits, so exact == returns False even though the values agree to ~7 digits.
Fix: use torch.allclose(result, expected) instead of == — it checks equality within a tolerance. This is exactly why the parent's test used assert torch.allclose(...). Testing a float for exact equality is the bug, not the math.
Level 4 — Synthesis
Recall Solution
## Problem
cosine_similarity crashes on 1-D inputs (RuntimeError: expected 2D)
because it hard-codes .sum(dim=1); 1-D tensors only have axis 0.
Fixes #1234.
## Fix
unsqueeze 1-D inputs to shape [1, N] so the existing dim=1 path works,
leaving all higher-rank behaviour byte-identical.
## Justification
unsqueeze (vs. hard-coding dim=0) keeps the batched API contract intact,
so 2-D and 3-D callers are unaffected. Covered all input ranks.
## Tests
Added test_cosine_similarity_1d comparing against the hand-computed
value 32 / (sqrt(14)*sqrt(77)) via torch.allclose.Mapping: Problem+Fix sentences ⇒ ; "leaving higher-rank byte-identical" ⇒ ; Justification paragraph ⇒ ; Tests ⇒ . All four = 1, so .
Recall Solution
Both GB and GB fit under GB, so both and are feasible. Among feasible options pick the one with least recomputation = fewest checkpoints = largest . Larger means you store activations more often (checkpoint every layers), so you throw away fewer and recompute less, giving lower time cost. From the parent: adds only +36% time (245 ms) vs. 's +128% (410 ms). Pick : it fits the budget and is 1.67× faster than . Rule: choose the largest that still fits.
Level 5 — Mastery
Recall Solution
Plan (all lifecycle steps):
- Find Issue — search for an existing "add gradient checkpointing" request; if none, open one.
- Claim It — comment "I'll take this" to avoid the L1.1 duplicate trap.
- Fork Repo — your own copy to experiment safely.
- Create Branch — e.g.
feat/grad-checkpointing, isolating the change. - Code + Tests — implement the
checkpoint-wrapping module and a correctness test. - Submit PR — with the four-part body from L4.1.
- Code Review — respond to feedback.
- Revisions — iterate.
- Merge.
Evidence for the feature stage: a derivation showing memory drops from to at a compute cost, plus an empirical benchmark table (memory & ms across ) proving the trade-off is real. Derivation alone is not enough (real memory ≠ -model, per L2.3); benchmarks alone don't explain why — you need both.
with a forgotten issue link: correct ✅, tested ✅, justified ✅... but "link the issue" is what lets reviewers verify the change is wanted and understandable in context — its absence drops to . Multiplying: A one-line omission still zeroes the score — add the link and you are back to .
Recall Solution
Expected number: the true cosine similarity Minimal assertion:
a = torch.tensor([1., 2., 3.]); b = torch.tensor([4., 5., 6.])
result = F.cosine_similarity(a, b, dim=0)
expected = 32. / (torch.sqrt(torch.tensor(14.)) * torch.sqrt(torch.tensor(77.)))
assert torch.allclose(result, expected) # ~0.97463- On buggy code: it never reaches the assert — the
.sum(dim=1)line raisesRuntimeErrorfirst. That still counts as a failing test (the run errors out), catching the crash. - On fixed code: the assert compares to the hand-computed via
allclose, so it passes and proves mathematical correctness — not merely "doesn't crash". This single test covers both the shape bug and the value correctness, which is why it is minimal-yet-sufficient.
Connections
- Parent: Contributing to open-source ML
- Code Review Best Practices — what reviewers look for (the / factors)
- Testing ML Systems — why
allclosebeats==, and how tests lock in a fix - Version Control with Git — fork, branch, PR mechanics of the lifecycle
- Software Engineering for ML — API contracts behind the
unsqueeze-vs-dim=0choice - ML Research Paper Implementation — turning a memory/compute derivation into working code
- Collaborative Machine Learning — claiming issues, avoiding duplicate work
- Building ML Portfolios — "your commits are your resume"
- Technical Writing for ML — writing the PR body that scores
Recall Quick self-test
Multiplicative PR score with one factor zero equals? ::: 0 — any missing pillar fails the whole PR. Cosine of (1,2,3) and (4,5,6) to 4 dp? ::: 0.9746 Memory kept at k=4 in the pure -model? ::: 25% (a 75% saving) Largest- rule for choosing checkpoints? ::: pick the biggest that still fits the memory budget (least recomputation).