advanced25 min studyLesson 505 in Engineering

A saved tensor needs its historical value, not merely its storage address

Trace mutation hazards through aliases, distinguish detach from copying, and explain version checks without assuming every in-place operation is forbidden or safely detected.

On this page

What you’ll understand

  • Identify when mutation invalidates a saved forward value.
  • Explain version-counter checks and their limited purpose.
  • Separate storage independence from derivative-graph independence.

Reverse mode may use a value long after the statement that computed it. If another alias changes the same storage first, the backward rule can read the present value while needing the past one. A runtime must preserve or verify that dependency. This is why an apparently local in-place optimization can alter derivative correctness even when the final forward output printed before mutation looks entirely reasonable.

Aliasing turns a local write into a history change

A tensor view can share storage with another tensor while having different shape or strides. Writing through either alias can change values seen through the other. A reverse rule that saved the original input for x² needs that original input to compute 2x, not the overwritten value. The hazard depends on what the rule saved and whether the mutation occurs before its last use. Some operations can be handled safely through runtime bookkeeping or a derivative that needs different residuals; there is no mathematical rule that every in-place operation is always invalid.

PyTorch 2.9 documents version counters that change when tensors are marked dirty by tracked in-place operations. Saved values record the relevant version, and retrieving them for backward can detect incompatible modification. Leaf-tensor restrictions and graph-rewriting rules impose additional constraints. These checks protect a defined autograd contract; they are not a general detector for arbitrary external writes or all unsafe storage manipulation. Bypassing tracking or mutating through an unsupported interface can defeat the evidence the runtime relies on. An error should lead to identifying the saved-value dependency, not blindly disabling checks.

ℓ=∑ixi2,∇xℓ=2xforward\ell=\sum_i x_i^2,\qquad \nabla_x\ell=2x_{\rm forward}
For real dimensionless inputs, the gradient depends on the values used in the corresponding forward evaluation. Replacing x_forward by a later mutated alias generally changes the answer; this expression is not an instruction to mutate a live autograd leaf.↔ Scroll wide equations horizontally.

Detaching a graph and copying storage solve different problems

Detaching can remove a derivative dependency while still sharing underlying storage. Cloning can create separate storage while remaining a differentiable copy in the graph. A detached clone combines different choices about history and storage, but using one blindly can also cut a gradient the model actually needs. State the intended ownership and derivative boundary before choosing an operation. A no-grad context suppresses recording of operations under its rules; it does not make overwriting a value already needed by another backward pass harmless.

Aliases also complicate apparent memory savings. Avoiding a copy may retain a much larger backing allocation or extend its lifetime through a saved view. A copy can increase immediate bytes while allowing the original allocation to be released earlier. Peak memory depends on scheduling and ownership, not just the number of clone calls. Compare a functional reference with the proposed mutation using valid inputs, repeated backward where supported, and the actual transformed graph. Correct forward output alone cannot verify that every historical residual remained available for the derivative.

vcurrent=vsavedis a required version check when retrieving that saved valuev_{\rm current}=v_{\rm saved}\quad\text{is a required version check when retrieving that saved value}
v denotes a runtime mutation-version counter, not a tensor value or physical unit. Matching versions supports the runtime's tracked-write contract; it does not prove untracked external memory was unchanged or that the derivative rule itself is correct.↔ Scroll wide equations horizontally.

Test the exact mutation schedule

Write a timeline of forward save, alias creation, mutation, backward read, and storage release. Include optimizer updates and multiple losses sharing a graph: stepping parameters before all intended gradients have been computed can invalidate remaining dependencies. A retained graph preserves access under its contract but does not freeze mutable storage against later writes. Tests should inspect both gradients and expected errors for unsupported schedules. Preserve legitimate dependencies by using a functional formulation or a supported copy/recomputation strategy rather than disguising the mutation as a new graph boundary.

The function lab compares separate evaluations without any tensor aliasing. Reset, explicitly set BOTH a=1 and x=1, predict slope two, then Play or Step. Reset and re-set BOTH a=1 and x=2; predict slope four. These are different selected primal values, not one saved value being safely mutated. Edits retain elapsed t and recompute the analytic display; playback has no autograd storage or version counters. Use the difference to explain why a backward rule must know which forward input it belongs to, not as a demonstration that live in-place mutation is safe.

An unprotected saved alias would differentiate the wrong input

A hypothetical unprotected runtime computes ℓ=x1²+x2² at x=(2,3), then a shared alias overwrites x1 with ten before backward. Assume its square rule reads the aliased storage rather than a preserved value.

  1. The intended forward loss is 4+9=13 and its correct input gradient is (4,6). These correspond to the original evaluation.

  2. Reading the overwritten storage would produce (20,6), the square derivative at a different first input, despite the already computed loss still being thirteen.

  3. A tracked runtime may reject this schedule through version or leaf rules; a functional or correctly preserved residual strategy must recover (4,6). Do not interpret this hypothetical corruption as behavior every PyTorch program is permitted to execute.

The result

The error is a mismatch between forward history and backward residuals, not an error in the elementary derivative 2x.

Explore the model

Interactive lab

Interactive lab · function

Different inputs require different local derivatives

For EACH run Reset, set BOTH a and x, predict, then Play or Step. Compare (1,1), slope two, with (1,2), slope four. Edits retain t and recompute a separate analytic selection. No aliases, saved tensors, in-place writes, or autograd version checks are implemented by this quadratic instrument.

Different inputs require different local derivativesThis lab uses only y = ax². Playback traces the curve within the fixed integration interval from 0 to x; it does not estimate numerical error or change the chosen point. Current readings: y at chosen x: 0.608; Tangent slope · 2ax: 1.35; Signed area · ax³/3: 0.182. All parameters are adjustable with the labeled controls below.−2.4−1.201.22.4−9−4.504.59xy(x, ax²)
y = ax²TangentSigned integral
y at chosen x
0.608
Tangent slope · 2ax
1.35
Signed area · ax³/3
0.182
t = 0 s

This lab uses only y = ax². Playback traces the curve within the fixed integration interval from 0 to x; it does not estimate numerical error or change the chosen point. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forA backward rule needs the derivative of its corresponding forward input; a later value in shared storage is not automatically that input.

Check your understanding

Why does detaching a tensor not necessarily protect an earlier saved value from mutation?

Try it yourself

Two losses share a forward graph that uses one parameter tensor. The optimizer mutates that tensor after the first backward but before the second. Why is retain_graph alone not a sufficient correctness argument?

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Separate preserving graph resources from preserving the historical parameter values that a remaining rule may need.

Compare with the solution

Retaining the graph can preserve saved context for another traversal, but it does not promise that later parameter writes leave every saved dependency valid. Compute intended gradients before the update or use a supported preserved/recomputed-value strategy, and verify the exact runtime schedule and version checks.

Take it with you

  • Alias writes can change residual values required by a later reverse rule.
  • Version checks verify tracked mutation history without proving arbitrary memory safety.
  • Copying storage, detaching history, and retaining a graph solve different requirements.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.