Skip to content

Extracting steering vectors

Two complementary routes turn captured hidden states into interventions.

Analysis-based: easysteer.steer

Derives directions from captured activations while keeping model weights fixed. Available extractors: DiffMean, PCA, LAT, linear probe, ITI attention head directions, and SAE feature vectors.

from easysteer.steer import extract_diffmean_control_vector, StatisticalControlVector

control_vector = extract_diffmean_control_vector(
    all_hidden_states=capture_result,  # CaptureResult from hs.capture(...)
    positive_indices=[0, 1, 2, 3],
    negative_indices=[4, 5, 6, 7],
    token_pos=-1,       # which token's activation to use
    normalize=True,
)

control_vector.export_gguf("vectors/diffmean.gguf")
# ... later
control_vector = StatisticalControlVector.import_gguf("vectors/diffmean.gguf")

The exported GGUF file is what VectorSpec(source=...) consumes at inference time. Pass CaptureResult directly to preserve true layer IDs. Legacy nested [sample][layer][token] inputs are also accepted, but their layer keys are inferred from list positions starting at zero. The selected rows must include every sample needed by positive_indices and negative_indices.

Extractor normalize=True normalizes the extracted direction. This differs from inference-time VectorSpec.normalize=True, which rescales the transformed hidden state to its original norm.

Sibling functions follow the same shape: extract_pca_control_vector, extract_lat_control_vector, extract_linear_probe_control_vector, and the generic extract_statistical_control_vector. SAE helpers (search_sae_features, get_sae_feature_explanation, extract_sae_decoder_vector) locate and export interpretable SAE decoder directions. See the API reference.

ITI attention head directions

ITIExtractor takes training and validation captures from stream="attention_heads". Split data by question before capture so different answers to one question cannot cross the training, validation, and test boundaries.

from pathlib import Path

from easysteer.steer import ITIExtractor
from easysteer.vectors import from_control_vector

iti = ITIExtractor.extract(
    train_capture,
    positive_indices=train_true_answer_indices,
    validation_hidden_states=validation_capture,
    validation_positive_indices=validation_true_answer_indices,
    num_heads={
        layer: layout["num_heads"]
        for layer, layout in train_capture.layouts.items()
    },
    top_k=1,
)
payload = from_control_vector(iti)  # VectorSpec(data=payload, algorithm="attention_add", ...)
Path(".runtime/iti").mkdir(parents=True, exist_ok=True)
iti.export_gguf(".runtime/iti/iti.gguf")

Unlisted answer indices are negatives unless explicit negative indices are provided. Probes fit the training set and rank heads by validation accuracy. Following ITI, the selected head directions use the truthful-minus-false means from both development splits, normalized and multiplied by their projection standard deviation. Unselected head slices remain zero. The returned vector preserves this scale; apply it with algorithm="attention_add", normalize=False, and scale set to the desired intervention strength. The generic extraction interface also accepts method="iti" with the same keyword arguments.

The ITI notebook demonstrates Llama-2-7B-Chat with a supplied direction: 48 heads, strength 15, one fixed TruthfulQA evaluation set, an accuracy comparison, and generated examples. It uses the authors' QA prompt and loads the model once. The supplied direction uses the official code's separate, unlabeled GEN calibration bank; the extractor above uses its training and validation captures for that scale.

Learning-based: easysteer.reft

Reimplements pyreft: trains a parameterized intervention (e.g. BiasIntervention, LoReFT) on a frozen HuggingFace model with a standard transformers trainer, then saves the learned representation for inference.

Use easysteer.reft.train.train_reft for the shared training pipeline. Bias checkpoints are loaded through easysteer.vectors.from_pyreft and applied with algorithm="direct"; LoReFT checkpoints use the same adapter with algorithm="loreft". These checkpoints are passed through VectorSpec(data=...), rather than as a third-party checkpoint path in source.

The complete training walkthrough (data module, trainer, saving) is in ReFT training; see the LoReFT replication for a complete train-then-steer notebook.