Skip to content

interlens.interp.logprobs

interlens.interp.logprobs

token_logprobs

token_logprobs(
    scores: tuple[Tensor, ...], generated_ids: Tensor
) -> dict

Compute per-token logprobs / surprisal / entropy for a generation.

scores is the tuple of per-step logit tensors from model.generate(..., output_scores=True, return_dict_in_generate=True) (one [vocab] per generated token); generated_ids are the sampled token ids. Returns lists suitable to drop into Message.metadata — scalar-per-token, so they stay small and don't violate the "no heavy tensors in metadata" invariant.

Surprisal = -logprob (nats); entropy is the full next-token distribution entropy at each step (a readout of the model's uncertainty, distinct from the surprisal of the token it actually emitted).

Source code in src/interlens/interp/logprobs.py
def token_logprobs(scores: tuple[torch.Tensor, ...], generated_ids: torch.Tensor) -> dict:
	"""Compute per-token logprobs / surprisal / entropy for a generation.

	``scores`` is the tuple of per-step logit tensors from ``model.generate(..., output_scores=True,
	return_dict_in_generate=True)`` (one ``[vocab]`` per generated token); ``generated_ids`` are the sampled
	token ids. Returns lists suitable to drop into ``Message.metadata`` — scalar-per-token, so they stay small
	and don't violate the "no heavy tensors in metadata" invariant.

	Surprisal = -logprob (nats); entropy is the full next-token distribution entropy at each step (a readout of
	the model's uncertainty, distinct from the surprisal of the token it actually emitted).
	"""
	if len(scores) == 0:
		return {"logprobs": [], "surprisal": [], "entropy": []}
	# Stack once -> [steps, vocab]; batch all ops so there is a single GPU->CPU sync at the end.
	logits = torch.stack([s[0] for s in scores]).float()
	logp = torch.log_softmax(logits, dim=-1)
	toks = torch.as_tensor(generated_ids, device=logp.device, dtype=torch.long)[: logp.shape[0]]
	lp = logp.gather(1, toks.unsqueeze(1)).squeeze(1)
	ent = -(logp.exp() * logp).sum(dim=-1)
	logprobs = lp.tolist()
	return {
		"logprobs": logprobs,
		"surprisal": (-lp).tolist(),
		"entropy": ent.tolist(),
	}