Skip to content

backward

backward

Read and edit gradients during a backward pass.

Writing with tensor.backward(): runs the real backward pass interleaved with the body of the with block, so the block can read and replace the .grad of any tensor as the gradient reaches it.

A backward trace is almost always nested inside a forward trace, so the tensors whose gradients you want are the real tensors produced during the run::

with model.trace("The Eiffel Tower is in"):
    a1   = model.transformer.h[0].output
    loss = model.output.logits.sum()
    with loss.backward():
        g = a1.grad.save()          # capture the gradient flowing into a1
        a1.grad = a1.grad * 2       # ...and replace it
print(g)

Gradients flow in the reverse of the forward pass, so .grad must be requested in reverse-forward order — requesting an earlier-forward tensor's gradient before a later one raises OutOfOrderError.

Only .grad is served inside the block; the forward is over by the time autograd runs, so capture any .output or .input you want gradients for beforehand. A forward that carried no gradient tracking has no graph to walk and opening a backward trace over it raises NotImplementedError — that covers a frozen model, and every model.generate() trace, since HuggingFace decorates generate with @torch.no_grad().

BackwardTracer

BackwardTracer(tensor: Tensor, fn: Callable, *args: Any, backend: Any = None, **kwargs: Any)

Bases: Tracer

Read and edit gradients inside a with loss.backward(): block.

Opened by with loss.backward(): (almost always nested inside a forward trace). Inside the block, read a tensor's incoming gradient with t.grad, replace it with t.grad = ..., and capture a value for use after the trace with .save(). Gradients must be requested in reverse-forward order.

Examples:

>>> with model.trace("The Eiffel Tower is in"):
...     a1   = model.transformer.h[0].output
...     loss = model.output.logits.sum()
...     with loss.backward():
...         g = a1.grad.save()     # capture the gradient
...         a1.grad = a1.grad * 2  # ...and replace it
>>> print(g)
PARAMETER DESCRIPTION
tensor

The tensor whose .backward(...) was invoked.

TYPE: Tensor

fn

The real, unpatched Tensor.backward to call during execution.

TYPE: Callable

*args

Positional arguments forwarded to fn (e.g. gradient).

TYPE: Any DEFAULT: ()

backend

Optional execution backend passed to the base Tracer.

TYPE: Any DEFAULT: None

**kwargs

Keyword arguments forwarded to fn (e.g. retain_graph).

TYPE: Any DEFAULT: {}

tensor instance-attribute

tensor = tensor

fn instance-attribute

fn = fn

args instance-attribute

args = args

kwargs instance-attribute

kwargs = kwargs

execute

execute(code: CodeType) -> None

Run the real backward, serving the block's .grad reads and writes.

Compiles the with block into an intervention mediator, installs the _grad_property on torch.Tensor, and drives the real backward under an interleaver: as autograd produces each gradient, its hook hands the value to the block, which may read or replace it. Cleans up the patched property and every registered hook afterwards, then pushes the results back with save-gating (see Tracer.execute).

PARAMETER DESCRIPTION
code

Compiled code object for the body of the with block.

TYPE: CodeType

install

install() -> None

Patch Tensor.backward so with t.backward(): enters a BackwardTracer.

Idempotent: replaces torch.Tensor.backward with _backward only if it isn't already installed. Called once at import time so the context-manager form is available everywhere, while plain t.backward() keeps working unchanged.