NLP probing
Requires the [nlp] extra (pip install igl[nlp]).
igl.nlp
Language-model probing helpers. Requires the [nlp] extra.
Verified plumbing for head-in-the-loop measurements on HuggingFace causal
LMs: :func:resolve_head probes the head convention instead of assuming
it, :class:ContinueFrom builds identity-gated patch-and-continue
closures, :func:extract_activations produces disjoint fit/eval splits,
and :func:requirement_dimension sweeps projection arms against the
frozen head. Importing any submodule raises :class:igl.IGLDependencyError
when transformers is missing, so the failure is loud and actionable.
Projector = Callable[[torch.Tensor, int], torch.Tensor]
module-attribute
A projection arm: (states [N, C], k) -> reconstructed states [N, C].
ActivationSet
dataclass
Extracted hidden states, split into disjoint fit and eval sets.
Attributes:
| Name | Type | Description |
|---|---|---|
fit |
Tensor
|
Subsampled states |
eval_batches |
list[tuple[Tensor, Tensor]]
|
One |
layer |
int
|
The zero-based block index the states come out of. |
ctx |
int
|
The context length used for chunking. |
ContinueFrom
Run blocks layer+1 .. end plus final norm and head on given states.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM (GPT-2, LLaMA/Qwen, GPT-NeoX
families: block lists at |
required |
layer
|
int
|
Zero-based block index the states come out of; the closure
applies blocks |
required |
probe_tokens
|
int
|
Length of the identity-gate forward. |
16
|
atol
|
float
|
Absolute logits tolerance of the identity gate, in fp32. |
0.01
|
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When the architecture is not recognized, |
__call__(states)
Continue from states [T, C] to fp32 logits [T, V].
HeadClosure
dataclass
A verified hidden-states-to-logits closure.
Attributes:
| Name | Type | Description |
|---|---|---|
post_norm |
bool
|
Whether the final hidden state is already normalized (the unembedding applies to it directly). |
lm_head |
Module
|
The unembedding module. |
norm |
Module | None
|
The final normalization module, when |
__call__(states)
Map states [..., C] to fp32 logits, casting through the head's dtype.
RequirementReport
dataclass
Result of a required-dimension sweep.
Attributes:
| Name | Type | Description |
|---|---|---|
intact_perplexity |
float
|
Perplexity of the head reading the raw states. |
curves |
dict[str, dict[int, float]]
|
Per arm, the perplexity at each swept k. |
required |
dict[str, dict[float, int | None]]
|
Per arm, the smallest swept k whose perplexity is within
|
extract_activations(model, input_ids, *, layer, ctx=512, n_eval_chunks=20, n_fit=20000, seed=42)
Extract layer activations with disjoint fit and eval splits.
The token stream is cut into ctx-length chunks. The first
n_eval_chunks become the evaluation set; the fit pool is sampled
(without replacement, seeded) from all positions of the remaining
chunks, so fit and eval tokens never overlap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM with hidden-state outputs. |
required |
input_ids
|
Tensor
|
A 1-D token-id tensor. |
required |
layer
|
int
|
Zero-based block index; states are the residual stream
after block |
required |
ctx
|
int
|
Context length per forward. |
512
|
n_eval_chunks
|
int
|
Number of leading chunks reserved for evaluation. |
20
|
n_fit
|
int
|
Number of fit positions to sample; capped at the pool size. |
20000
|
seed
|
int
|
Seed of the fit subsampling. |
42
|
Returns:
| Name | Type | Description |
|---|---|---|
An |
ActivationSet
|
|
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When |
require_transformers()
Raise :class:igl.IGLDependencyError unless the nlp extra is installed.
requirement_dimension(projectors, head, batches, *, grid, tolerances=_DEFAULT_TOLERANCES)
Sweep projection arms over k and report per-tolerance requirements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
projectors
|
Mapping[str, Projector]
|
Named arms mapping |
required |
head
|
Callable[[Tensor], Tensor]
|
The frozen consumer mapping states to logits: an
:class: |
required |
batches
|
Iterable[tuple[Tensor, Tensor]]
|
|
required |
grid
|
Sequence[int]
|
The k values to sweep, in increasing order. |
required |
tolerances
|
Sequence[float]
|
Multiplicative perplexity tolerances; for each, the
report records the smallest swept k within
|
_DEFAULT_TOLERANCES
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
RequirementReport
|
|
RequirementReport
|
perplexity curve per arm, and per-tolerance required dimensions. |
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When |
resolve_head(model, *, probe_tokens=8, atol=0.01)
Probe and verify how the unembedding reads the final hidden state.
Runs a short forward with hidden states, then checks which convention
reproduces the model's own logits: the head on the raw last hidden
state, or the head on the final norm of it (candidate norms:
model.norm, transformer.ln_f, gpt_neox.final_layer_norm).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM exposing |
required |
probe_tokens
|
int
|
Length of the verification forward. |
8
|
atol
|
float
|
Absolute tolerance of the logits comparison, in fp32. |
0.01
|
Returns:
| Type | Description |
|---|---|
HeadClosure
|
A verified :class: |
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When no unembedding module is found, or neither convention reproduces the model's logits. |
igl.nlp.resolve_head(model, *, probe_tokens=8, atol=0.01)
Probe and verify how the unembedding reads the final hidden state.
Runs a short forward with hidden states, then checks which convention
reproduces the model's own logits: the head on the raw last hidden
state, or the head on the final norm of it (candidate norms:
model.norm, transformer.ln_f, gpt_neox.final_layer_norm).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM exposing |
required |
probe_tokens
|
int
|
Length of the verification forward. |
8
|
atol
|
float
|
Absolute tolerance of the logits comparison, in fp32. |
0.01
|
Returns:
| Type | Description |
|---|---|
HeadClosure
|
A verified :class: |
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When no unembedding module is found, or neither convention reproduces the model's logits. |
igl.nlp.HeadClosure
dataclass
A verified hidden-states-to-logits closure.
Attributes:
| Name | Type | Description |
|---|---|---|
post_norm |
bool
|
Whether the final hidden state is already normalized (the unembedding applies to it directly). |
lm_head |
Module
|
The unembedding module. |
norm |
Module | None
|
The final normalization module, when |
__call__(states)
Map states [..., C] to fp32 logits, casting through the head's dtype.
igl.nlp.ContinueFrom
Run blocks layer+1 .. end plus final norm and head on given states.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM (GPT-2, LLaMA/Qwen, GPT-NeoX
families: block lists at |
required |
layer
|
int
|
Zero-based block index the states come out of; the closure
applies blocks |
required |
probe_tokens
|
int
|
Length of the identity-gate forward. |
16
|
atol
|
float
|
Absolute logits tolerance of the identity gate, in fp32. |
0.01
|
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When the architecture is not recognized, |
__call__(states)
Continue from states [T, C] to fp32 logits [T, V].
igl.nlp.extract_activations(model, input_ids, *, layer, ctx=512, n_eval_chunks=20, n_fit=20000, seed=42)
Extract layer activations with disjoint fit and eval splits.
The token stream is cut into ctx-length chunks. The first
n_eval_chunks become the evaluation set; the fit pool is sampled
(without replacement, seeded) from all positions of the remaining
chunks, so fit and eval tokens never overlap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
object
|
A HuggingFace causal LM with hidden-state outputs. |
required |
input_ids
|
Tensor
|
A 1-D token-id tensor. |
required |
layer
|
int
|
Zero-based block index; states are the residual stream
after block |
required |
ctx
|
int
|
Context length per forward. |
512
|
n_eval_chunks
|
int
|
Number of leading chunks reserved for evaluation. |
20
|
n_fit
|
int
|
Number of fit positions to sample; capped at the pool size. |
20000
|
seed
|
int
|
Seed of the fit subsampling. |
42
|
Returns:
| Name | Type | Description |
|---|---|---|
An |
ActivationSet
|
|
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When |
igl.nlp.ActivationSet
dataclass
Extracted hidden states, split into disjoint fit and eval sets.
Attributes:
| Name | Type | Description |
|---|---|---|
fit |
Tensor
|
Subsampled states |
eval_batches |
list[tuple[Tensor, Tensor]]
|
One |
layer |
int
|
The zero-based block index the states come out of. |
ctx |
int
|
The context length used for chunking. |
igl.nlp.requirement_dimension(projectors, head, batches, *, grid, tolerances=_DEFAULT_TOLERANCES)
Sweep projection arms over k and report per-tolerance requirements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
projectors
|
Mapping[str, Projector]
|
Named arms mapping |
required |
head
|
Callable[[Tensor], Tensor]
|
The frozen consumer mapping states to logits: an
:class: |
required |
batches
|
Iterable[tuple[Tensor, Tensor]]
|
|
required |
grid
|
Sequence[int]
|
The k values to sweep, in increasing order. |
required |
tolerances
|
Sequence[float]
|
Multiplicative perplexity tolerances; for each, the
report records the smallest swept k within
|
_DEFAULT_TOLERANCES
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
RequirementReport
|
|
RequirementReport
|
perplexity curve per arm, and per-tolerance required dimensions. |
Raises:
| Type | Description |
|---|---|
IGLConfigError
|
When |
igl.nlp.RequirementReport
dataclass
Result of a required-dimension sweep.
Attributes:
| Name | Type | Description |
|---|---|---|
intact_perplexity |
float
|
Perplexity of the head reading the raw states. |
curves |
dict[str, dict[int, float]]
|
Per arm, the perplexity at each swept k. |
required |
dict[str, dict[float, int | None]]
|
Per arm, the smallest swept k whose perplexity is within
|