interlens.interp.grad¶
interlens.interp.grad
¶
Gradient-enabled forward passes for backprop through a model.
The rest of interp is read-only: capture_activations runs under torch.inference_mode and detaches, which
is right for logit-lens / probe readouts but throws away the graph. This module is the escape hatch for
optimization through a (usually frozen) model: differentiable soft-prompt tuning into a target, and end-to-end
backprop across two stacked models (model A's relaxed tokens feed model B via bridge.soft_embed). Nothing here
touches ModelParticipant.generate — these are standalone functions on the raw .model, matching the style of
token_logprobs/decoder_layers.
Two design points:
- Accept inputs_embeds (soft embeddings) as a first-class input, not just input_ids — that is how a
continuous, differentiable signal enters a transformer. Residual-stream capture is grad-connected (via
output_hidden_states on the same forward, not the separate detached pass capture_activations uses).
- checkpoint=True turns on HF gradient checkpointing for the pass, so backprop through a frozen B recomputes
activations instead of storing them — the memory lever that lets a 0.5B+1.5B two-model stack fit one GPU.
GradCaptureSpec
dataclass
¶
What grad-connected activations to pull from forward_with_grad (mirrors CaptureSpec minus offload).
sites is any of "residual"/"attn"/"mlp"; layers selects decoder-layer indices (None =
all). Unlike CaptureSpec there is no offload — the whole point is to keep tensors on-device and in the
autograd graph, so an intermediate-layer objective (e.g. project onto a concept direction) can be backpropagated.
GradForwardOutput
¶
Bases: NamedTuple
Result of forward_with_grad: grad-connected logits ([batch, seq, vocab]) and, if a
GradCaptureSpec was passed, hidden mapping (site, layer) -> tensor[batch, seq, d_model] (also
grad-connected). hidden is empty when no capture was requested.
continuation_logprob
¶
continuation_logprob(
model: "PreTrainedModel",
*,
target_ids: Tensor,
prefix_ids: Tensor | None = None,
prefix_embeds: Tensor | None = None,
attention_mask: Tensor | None = None,
reduction: str = "mean",
checkpoint: bool = False
) -> torch.Tensor
Differentiable teacher-forced logprob of target_ids continuing a prefix, under model.
The grad-enabled analog of train_to_steer/rl_elicit.py::cont_logprob (which is @torch.no_grad and returns
a float). Supply the prefix as prefix_ids ([batch, P] long) OR prefix_embeds ([batch, P, d_model]
float — e.g. a learnable soft prompt, or soft embeddings bridged from model A). target_ids is [batch, T]
long (or [T] broadcast to batch 1). The prefix and target are embedded via model.get_input_embeddings()
and run in one forward; the returned scalar (or per-example vector, see reduction) is the logprob the model
assigns to the exact target tokens, still attached to the graph.
reduction: "mean" (mean over target tokens, mean over batch -> scalar; the reward used by rl_elicit),
"sum" (sum over target tokens, mean over batch), or "none" ([batch, T] per-token logprobs). Use
"mean" as a length-normalized elicitation reward, "sum" when total sequence likelihood matters, "none"
for custom weighting. checkpoint is forwarded to save memory when backpropagating through a large frozen B.
Source code in src/interlens/interp/grad.py
forward_with_grad
¶
forward_with_grad(
model: "PreTrainedModel",
*,
input_ids: Tensor | None = None,
inputs_embeds: Tensor | None = None,
attention_mask: Tensor | None = None,
capture: GradCaptureSpec | None = None,
checkpoint: bool = False
) -> GradForwardOutput
One grad-connected forward pass over input_ids XOR inputs_embeds.
Exactly one of input_ids ([batch, seq] long) or inputs_embeds ([batch, seq, d_model] float,
typically the output of bridge.soft_embed or a learnable soft prompt) must be given. attention_mask is
optional ([batch, seq]). Returns logits and any requested hidden activations, all still attached to the
graph so .backward() flows back to inputs_embeds / soft-prompt params / an upstream model A.
checkpoint=True enables HF gradient checkpointing for this call (use_reentrant=False) and restores the
prior setting afterwards; it recomputes layer activations on the backward pass to trade compute for memory, and
produces identical gradients. Residual sites come from output_hidden_states (grad-connected); attn/mlp
sites come from non-detaching forward hooks on the submodules.