Attention head intervention¶
attention_add adds a direction to attention head outputs after attention
aggregation and before the output projection. This is the intervention point
used by Inference-Time Intervention (ITI).
It changes the representation passed to the output projection; it does not edit
attention scores, attention probabilities, or stored keys and values directly.
Supported component¶
Use standard decoder MHA or GQA with tensor_parallel_size=1. MLA, encoder
attention, and cross-attention are outside this component's current scope.
The engine discovers attention modules from the model itself and checks that
the hook target is available.
For a layer with H query heads and value-output dimension D, the captured
and steered tensor has shape (rows, H * D). This width need not equal the
residual hidden size. GQA still uses the query head count at this point,
not the number of KV heads.
Capture the representation¶
The same capture API and selectors work for attention heads:
from vllm import LLM
from vllm.capture import SelectSpec
import easysteer.hidden_states as hs
llm = LLM(
model="Qwen/Qwen2.5-1.5B-Instruct",
tensor_parallel_size=1,
enable_steer_vector=True,
steer_algorithms=["attention_add"],
)
prompts = ["The capital of France is"]
heads = hs.capture(
llm,
prompts,
layers=[10],
stream="attention_heads",
select=SelectSpec(prompt_positions=[-1]),
max_tokens=1,
steering=False,
)
layout = heads.layouts[10]
last_token_heads = heads.sample(0)[10].reshape(
-1, layout["num_heads"], layout["head_size"],
)
This raw completion prompt illustrates the component layout. For chat applications, render the model's chat template as in the quickstart. For a paper replication, keep the paper's prompt format.
Apply selected head directions¶
Use a concatenated DirectionVector or an EasySteer direction GGUF. Leave
unselected head slices zero. The same per-request ApplySpec, prefix caching,
and graph rules apply:
from vllm.steer_vectors import ApplySpec, SteeringSpec, VectorSpec
# This file must contain attention directions for the model loaded above.
attention = SteeringSpec(vectors=[VectorSpec(
source="vectors/my_attention_direction.gguf",
algorithm="attention_add",
scale=3.0,
normalize=False,
apply=ApplySpec(prompt_positions=[-1], generation="all"),
)])
outputs = llm.generate(prompts, steering=attention)
The last prompt position computes the first answer-token prediction; including
it is useful when steering a whole answer. generation="all" covers subsequent
decode forward passes. To steer all prompt processing as well, use
ApplySpec(prompt="all", generation="all").
attention_add supports eager, split, and in_graph; a single-vector
declaration selects in_graph under the default auto mode. Normalization is
not supported, so keep normalize=False.
ITI example¶
The ITI notebook uses Llama-2-7B-Chat, the authors' QA prompt, 48 selected heads, and intervention strength 15. It loads a supplied direction, evaluates one fixed TruthfulQA set, and displays baseline/steered accuracy and generated examples as executed notebook outputs.
To learn directions from your own data, use
ITIExtractor. It fits
per-head probes on training captures, selects heads by validation accuracy, and
returns the scaled head directions in the common control-vector format.
Choose held-out data by question before collecting answer representations.
The bundled replication direction uses the official implementation's separate
GEN calibration bank for the projection standard deviation. The general
extractor uses the provided training and validation captures. Both use
attention_add for inference; the extraction guide describes the distinction.