Paper · 2023-06-06 · cs.LG, cs.AI, cs.CL
Inference-Time Intervention: Eliciting Truthful Answers from a Language Model
Inference-Time Intervention (ITI) finds attention heads whose linear probes separate true from false answers, then shifts those heads' outputs along a truthful direction at every decoding step, with no retraining. On TruthfulQA, Alpaca's true*informative score rises from 32.5% to 65.1%, author-reported; a strength knob trades truthfulness for informativeness.
Activation steeringHallucination mitigationLinear probingMechanistic interpretabilityactivation-steeringtruthfulnessinterpretabilityinference-timeprobing
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Language models often "know" the right answer and still write a false one. This paper looks inside the model while it reads a question and a candidate answer. It finds a small set of internal components, called attention heads, whose signals tell true answers from false ones. Then, while the model writes, it nudges those components a little in the "true" direction at every step. No retraining and no new training text are needed. The directions come from a few hundred labelled examples.
The authors test this on a benchmark of questions where people often answer with a common misconception. The nudge makes the models more truthful, and the gains are largest on an instruction-tuned model. The cost is a trade-off: a stronger nudge makes the model more truthful but less informative, because it answers "I have no comment" more often. The gains are measured on one benchmark, and the nudge strength has to be tuned.
TL;DR
- Find heads whose activations linearly predict truthfulness, keep the top K, and add a scaled truthful direction to each kept head's output at every generated token §3.3 eq. 2.
- Needs white-box access, labelled true and false answers, and a fine-tuned GPT judge for evaluation §4.1 [README.md#L104-L106].
- On TruthfulQA, author-reported true*informative scores: Alpaca 32.5% to 65.1%, Vicuna 51.5% to 74.0% tab. 2.
- Strength alpha trades truthfulness against informativeness; K=48 and alpha=15 were chosen on a sweep for LLaMA-7B §4.3 fig. 4.
- The released code and the paper differ in several places (tuning set for sigma, bias baking, normalisation). Read Paper vs code before implementing edit_weight.py.
Problem
Models can produce fluent false answers even when probes on their hidden states show the truth is represented. On LLaMA-7B and TruthfulQA the authors measure a gap of about 40 points between probe accuracy and generation accuracy §1. Existing fixes such as RLHF and RLAIF need large annotation or compute budgets, and the training can reward pleasing a rater rather than being truthful §1.
Method
The method has two parts. First, a probing step on labelled answers picks a sparse set of attention heads and a direction per head §3.2 §3.3. Second, at inference time the chosen heads' outputs are shifted along those directions eq. 2.
Data. TruthfulQA has 817 questions in 38 categories. It is built so that some humans answer wrongly because of false beliefs or misconceptions §3.1. The authors split it into N = 5,918 question-answer pairs, each labelled 1 if the answer is truthful and 0 if not §3.1. Each pair is formatted as the question followed by the answer §3.2.
Architecture notation. Each attention head h in layer l writes a vector through its own output matrix Q. The paper writes the attention block as a sum over heads, with the activation read after attention and before Q §3.1.
x_{l+1} = x_l + \sum_{h=1}^{H} Q_l^h \, \mathrm{Att}_l^h(P_l^h x_l) eq. 1
Probing. For each head in each layer, the authors fit a linear probe, p(x) = sigmoid(<theta, x>), on the head's activation at the last token of each pair §3.2. The probe is trained on a 4:1 train/validation split. Validation accuracy measures how truth-related each head is §3.2 app. C. The highest single-head accuracy is 83.3%, for the 18th head in the 14th layer, and most of the signal sits in early to middle layers §3.2 fig. 2. Concatenating all heads reaches 84.4% §5.5.
Direction. The paper compares three directions per head §3.3 §4.2:
- the probe weight direction, theta, normalised §3.2;
- the mass mean shift: the vector from the mean of false activations to the mean of true activations §3.3 §4.2;
- the direction found by Contrast-Consistent Search, with its sign set using labels §4.2.
Mass mean shift performs best and is used in all later experiments unless stated otherwise tab. 3 §4.3.
Intervention. Only the top K heads, ranked by validation accuracy, are edited §3.3. For each kept head, sigma is the standard deviation of the activations along the chosen direction. The edit adds alpha times sigma times theta to that head's output before Q. Unselected heads have theta = 0 §3.3:
x_{l+1} = x_l + \sum_{h=1}^{H} Q_l^h \left( \mathrm{Att}_l^h(P_l^h x_l) + \alpha \sigma_l^h \theta_l^h \right) eq. 2
The edit is repeated for every next-token prediction and does not depend on the decoding algorithm §3.3.
Baking into weights. Because the edit is one fixed vector per layer, it can be stored as the output-projection bias of that layer, with Q applied to each head's shift and summed over the intervened heads §5.2:
\mathrm{Bias}_l = \alpha \sum_{h=1}^{H} Q_l^h \left( \sigma_l^h \theta_l^h \right) eq. 3
The authors report close to zero run-time overhead §5.2.
Pseudocode. The fit and the decoding-time edit, following the paper. The normalisation of theta is taken from code, because the paper does not state it for mass mean shift.
# Fit the intervention from labelled QA pairs [§3.2] [§3.3] [app. C]
# Input: activations (N, layers, heads, d) at the last token; labels (N,)
# Output: for each kept head, a unit direction and a scale sigma
def fit_iti(activations, labels, questions, num_heads_k, alpha):
# Split the questions 4:1 into train and validation [app. C]
train_idx, val_idx = split_by_question(questions, train_fraction=0.8)
# One linear probe per head, fit on the training split [§3.2]
probe_accuracy = {}
for layer, head in all_layer_head_pairs():
probe = fit_logistic_regression(
activations[train_idx, layer, head], labels[train_idx])
probe_accuracy[layer, head] = score(
probe, activations[val_idx, layer, head], labels[val_idx])
# Keep the K heads with the highest validation accuracy [§3.3]
kept_heads = top_k_by_accuracy(probe_accuracy, num_heads_k)
# Mass mean shift: true mean minus false mean [§3.3] [tab. 3]
# Unit-length normalisation is from the code, not the paper
# [code: validation/validate_2fold.py#L171]
for layer, head in kept_heads:
true_mean = mean(activations[labels == 1, layer, head])
false_mean = mean(activations[labels == 0, layer, head])
theta[layer, head] = unit_vector(true_mean - false_mean)
# Sigma: std of the activations projected on theta [§3.3]
# The tuning set for this std is not clear; see Paper vs code
projections = activations_tuning[:, layer, head] @ theta[layer, head]
sigma[layer, head] = std(projections)
return theta, sigma, kept_heads# Shift one head's output during decoding [eq. 2] [§3.3]
# Input: head output at the last position, shape (d,)
# Output: shifted head output, same shape
def shift_head_output(head_output, theta, sigma, alpha):
# Add alpha * sigma * theta; the next step applies Q [eq. 2]
return head_output + alpha * sigma * thetaHyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| heads intervened, K | 48 | §4.3 validate_2fold.py | Ranked over all layer-head pairs by validation accuracy utils.py. Chosen from a sweep fig. 4. |
| strength, alpha | 15 | §4.3 validate_2fold.py | Table 3 gives alpha = 20 as optimal for mass mean shift. See Pitfalls. |
| scale per head, sigma | std of projections on the unit direction | §3.3 utils.py | Paper says "std along the direction" §3.3. |
| train:validation split | 4:1 | §3.2 app. C | Split unit is questions in the code validate_2fold.py. |
| probe | one logistic regression per head | §3.2 | Regularisation not stated. Code uses sklearn defaults with max_iter=1000 utils.py. |
| probe input | last token of "Q: question A: answer" | §3.2 | Exact format not stated in paper; code uses "Q: {q} A: {a}" utils.py. |
| evaluation protocol, main | 2-fold CV over questions | §4.3 app. C | Used for Table 2, Table 3 and Table 5 tab. 2 tab. 3 tab. 5. |
| evaluation protocol, sweep | 5% of questions for training, 5% for validation | §4.3 fig. 4 | Used for Table 1 and the sweep. The repo has no script for this protocol. |
| decoding | greedy, autoregressive | §4.1 | Code uses top_k=1 utils.py. Max new tokens = prompt length + 50 utils.py. |
| instruction prefix | "Interpret each question literally..." | app. F | Code uses the same prefix utils.py. |
| few-shot baseline | 10% of TruthfulQA samples appended to prompt | app. F | Text also says 50-shot §4.2 and 5% for fair comparison §4.3. See Pitfalls. |
| judge | fine-tuned GPT-judge and GPT-info | §4.1 | Paper says GPT-3-13B; repo notebook fine-tunes davinci-002 finetune_gpt.ipynb. |
| CE and KL corpus | OpenWebText subset | §4.1 | Code: 100 random samples, first 128 tokens utils.py utils.py. |
| runs | 3 averaged (Table 1); 5 for std (App. D); seed 1 (App. A) | tab. 1 app. D app. A | |
| heads per layer | 32 | interveners.py | Hard-coded in the collector. Paper text does not state model dimensions. |
| alpha and K for Alpaca and Vicuna | not stated | tab. 2 | |
| SFT settings | not stated | §4.2 | Learning rate, steps and batch size not given. |
Results
Author-reported. The setup differs between tables; see the Source column and the protocol rows above.
Main TruthfulQA results on LLaMA-7B with 5% of questions used for training and validation. Baselines were reproduced by the authors tab. 1 §4.3:
| Setting | True*Info (%) | True (%) | MC acc. (%) | CE | KL | Source |
|---|---|---|---|---|---|---|
| Baseline | 30.5 | 31.6 | 25.7 | 2.16 | 0.0 | tab. 1 |
| Supervised finetuning | 36.1 | 47.1 | 24.2 | 2.10 | 0.01 | tab. 1 |
| Few-shot prompting | 49.5 | 49.5 | 32.5 | - | - | tab. 1 |
| Baseline + ITI | 43.5 | 49.1 | 25.9 | 2.48 | 0.40 | tab. 1 |
| Few-shot prompting + ITI | 51.4 | 53.5 | 32.5 | - | - | tab. 1 |
Instruction-tuned models, 2-fold cross-validation tab. 2:
| Setting | True*Info (%) | True (%) | MC acc. (%) | CE | KL | Source |
|---|---|---|---|---|---|---|
| Alpaca | 32.5 | 32.7 | 27.8 | 2.56 | 0.0 | tab. 2 |
| Alpaca + ITI | 65.1 | 66.6 | 31.9 | 2.92 | 0.61 | tab. 2 |
| Vicuna | 51.5 | 55.6 | 33.3 | 2.63 | 0.0 | tab. 2 |
| Vicuna + ITI | 74.0 | 88.6 | 38.9 | 3.36 | 1.41 | tab. 2 |
Direction comparison on LLaMA-7B, 2-fold cross-validation, with each direction's own grid-searched alpha tab. 3 §4.3:
| Direction | alpha | True*Info (%) | True (%) | MC acc. (%) | CE | KL | Source |
|---|---|---|---|---|---|---|---|
| Baseline | - | 30.5 | 31.6 | 25.7 | 2.16 | 0.0 | tab. 3 |
| Random direction | 20 | 31.2 | 32.3 | 25.8 | 2.19 | 0.02 | tab. 3 |
| CCS direction | 5 | 33.4 | 34.7 | 26.2 | 2.21 | 0.06 | tab. 3 |
| Probe weight direction | 15 | 34.8 | 36.3 | 27.0 | 2.21 | 0.06 | tab. 3 |
| Mass mean shift | 20 | 42.3 | 45.1 | 28.8 | 2.41 | 0.27 | tab. 3 |
Head selection on LLaMA-7B, 2-fold cross-validation tab. 5 §5.5:
| Selection | alpha | True*Info (%) | True (%) | MC acc. (%) | CE | KL | Source |
|---|---|---|---|---|---|---|---|
| Without selection (all heads) | 5 | 35.4 | 37.1 | 28.3 | 2.19 | 0.08 | tab. 5 |
| Point-wise (top coefficients) | 15 | 39.2 | 55.3 | 28.7 | 4.01 | 1.95 | tab. 5 |
| Head-wise (ours) | 20 | 42.3 | 45.1 | 28.8 | 2.41 | 0.27 | tab. 5 |
Transfer without tuning on other QA and knowledge benchmarks, multiple-choice accuracy, with directions and hyperparameters learned on TruthfulQA tab. 4 §5.3:
| Model | Natural Questions | TriviaQA | MMLU | Source |
|---|---|---|---|---|
| LLaMA-7B | 46.6 | 89.6 | 35.71 | tab. 4 |
| LLaMA-7B + ITI | 51.3 | 91.1 | 40.16 | tab. 4 |
The differences are 4.7, 1.5 and 4.45 points, computed from tab. 4. The authors call this "somewhat better" and note that MMLU's question format is closest to TruthfulQA §5.3.
Per-category gains: ITI raises the true*informative score across most subcategories with 10 or more questions, with no single category driving the average §5.1 fig. 5.
Trade-off: the number of "I have no comment" answers rises with alpha, and the authors frame this as truthfulness traded against helpfulness §5.4 fig. 6.
Stability: the authors report that results are quite stable across random seeds. Standard deviations over 5 runs are in tab. 6 app. D.
| Setting | True*Info (%) | True (%) | MC acc. (%) | CE | KL | Source |
|---|---|---|---|---|---|---|
| Baseline | 1.80e-05 | 1.90e-05 | 2.26e-05 | 2.07e-02 | 0.00e+00 | tab. 6 |
| Probe weight direction | 1.09e-02 | 1.16e-02 | 3.90e-03 | 2.04e-02 | 5.36e-03 | tab. 6 |
| Random direction | 4.10e-03 | 3.32e-03 | 2.42e-03 | 2.09e-02 | 1.59e-03 | tab. 6 |
| ITI (ours) | 4.95e-02 | 5.32e-02 | 4.35e-03 | 8.14e-02 | 6.57e-02 | tab. 6 |
Qualitative answers for every TruthfulQA question, with and without ITI, are in the appendix app. A. In that 2-fold run, overall truthfulness goes from 31.6% to 54.5% and informativeness from 96.7% to 93.3% app. A.
Paper vs code
Code: https://github.com/likenneth/honest_llama at commit
2c6b2179be7b5aa8f0a171688cf9e01b812ca327, MIT licence. The repository is
linked from the abstract and belongs to the first author. The README says the
pyvene scripts are an updated implementation with similar results, and that the
legacy scripts may suit paper replication better [README.md#L118]. The legacy
code is the paper-era version; it is cited below where it differs.
- Tuning set for sigma. Paper: sigma is estimated from the training and validation activations §3.3. Code: sigma is estimated from label-free activations of the
tqa_gen_end_qset validate_2fold.py validate_2fold.py. Those prompts end with a random extra question, so the captured position is the last token of that question utils.py utils.py. - Direction normalisation. Paper does not state normalisation for mass mean shift. Code divides each direction by its norm before scaling utils.py validate_2fold.py.
- Edit positions. Paper writes the edit per token eq. 2 §3.3. Code adds the edit only at the last position of each forward call interveners.py. In the prefill this is the last prompt token; the earlier prompt tokens are not shifted. The legacy code does the same with
head_output[:, -1, ...]llama_validate_2fold.py. The repo README says the same [README.md#L39]. - Baked-in bias. Eq. 3 applies Q to the shift eq. 3. The active code sets the bias to the raw displacement and leaves the Q multiplication commented out edit_weight.py. The README warns that baked models and edits on base models are not exactly comparable [README.md#L2].
- Required flag.
--use_center_of_massis off by default, but the intervention loop indexescom_directionsunconditionally, which isNonewithout the flag validate_2fold.py validate_2fold.py. The README command includes the flag [README.md#L112]. Without it the script fails as written. - Probe fit. Paper gives no regularisation §3.2. Code uses scikit-learn logistic regression with default L2 regularisation and max_iter=1000 utils.py.
- Mass mean shift set. Code computes the mean difference over the train and validation questions combined utils.py, which matches app. C's "whole development set" app. C.
- Judge model. Paper says the GPT-judge is a fine-tuned GPT-3-13B model §4.1. The repo notebook fine-tunes davinci-002 finetune_gpt.ipynb finetune_gpt.ipynb. The judge has to be trained with the user's own OpenAI key [README.md#L104-L106].
- MC scoring. Paper: candidate answers are compared by conditional probability §4.1. Code sums the log-probabilities of the answer tokens after dropping the first three, with no length normalisation utils.py.
- KL and CE sample. Paper: a subset of OpenWebText §4.1. Code: 100 samples truncated to 128 tokens; KL is divided by sequence length utils.py utils.py utils.py.
- 5% protocol. Table 1 and the sweep use 5% of questions §4.3. The repo has no script for this split. Its validation script uses 2-fold cross-validation validate_2fold.py.
- Few-shot prompt. Paper gives three different sizes for the few-shot baseline (see Pitfalls). The repo's utilities accept a many-shot prefix, but no script sets it utils.py.
Pitfalls
- Alpha and K are not consistent across the paper. Section 4.3 picks K=48 and alpha=15 §4.3. Table 3 lists alpha=20 as optimal for mass mean shift, and says mass mean shift is used everywhere else tab. 3. Alpha is not stated for Table 1 or Table 2. Code defaults are K=48 and alpha=15 validate_2fold.py. Tune alpha on your own model.
- Alpha is in units of sigma. The edit is alpha times sigma times a unit vector eq. 2. An alpha of 20 means 20 standard deviations along the direction app. B. Because sigma depends on the model's activations, it has to be recomputed for each model.
- Random-direction control. Random directions at alpha=20 leave LLaMA-7B nearly unchanged app. B tab. 3. Keep the random baseline to check that gains come from the learned direction.
- Sigma's data. See Paper vs code. A different tuning set changes the scale of every edit.
- Truthfulness vs informativeness. An answer of "I have no comment" counts as true but not informative, so the true*informative metric penalises it §4.1. Stronger alpha raises truthfulness and lowers informativeness §4.3 §5.4 fig. 6. Report both.
- Judge bias and cost. The GPT-judge slightly overestimates truthfulness and underestimates informativeness in the authors' human check, with no numbers given §4.1. The judge is a fine-tuned OpenAI model, so results are not reproducible without access to it.
- Few-shot baseline sizes conflict. Section 4.2 cites a 50-shot baseline from prior work §4.2. Appendix F says the authors appended 10% of TruthfulQA app. F. Section 4.3 says 5% was used "for a fair comparison" §4.3. Check which one a reproduction should use.
- Protocols are not comparable. Table 1 (5% train, 5% validation) and Tables 2 to 5 (2-fold) use different splits. Baseline+ITI is 43.5% in Table 1 and 42.3% in Table 3 for the same model tab. 1 tab. 3.
- Data needs are stated loosely. The abstract says "few hundred examples" (abstract). Section 2 says "as few as 40" §2. Section 4.3 uses 5% of 817 questions for training (about 41) §4.3. Probes need labelled true and false answers for every head.
- Probe position. Each probe reads only the last token of each pair §3.2. The paper does not examine other positions.
- Cost. Run time is nearly free: one vector added per layer §5.2. Fitting needs a forward pass over all labelled pairs with hooks on every head. Baking the edit into weights is cheaper at inference but changes the prompt tokens too, which the code does not match exactly (see Paper vs code).
- Generalisation is narrow. Gains on the transfer benchmarks are small (4.7, 1.5 and 4.45 points) tab. 4. The authors list testing on wider benchmarks and real chat settings as open work §6.
- Repo replication notes are not the authors' results. The repo has a community replication note on Llama-2 and Llama-3 models with mixed truth and informativeness changes iti_replication_results.md. It is not part of the paper.
Lineage
- Builds on: LLaMA for the base models; the probing and activation-editing idea follows prior interpretability work §2 §3.2. Contrast-Consistent Search, arXiv:2212.03827 (no card), is the main unsupervised comparison §2 §4.2.
- Uses the TruthfulQA benchmark, arXiv:2109.07958 (no card) §3.1.
- Compared against: supervised finetuning and RLHF-style training (no card) §4.2; few-shot prompting §4.2; random and probe-weight directions tab. 3; contrast-consistent search tab. 3.
- Superseded by: not stated on this wiki.
Source and license
Adapted from “Inference-Time Intervention: Eliciting Truthful Answers from a Language Model” by Kenneth Li, Oam Patel, Fernanda Viégas, Hanspeter Pfister, Martin Wattenberg (arXiv:2306.03341), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/likenneth/honest_llama at commit 2c6b2179be7b (MIT). Any quoted code is shown under that license.