entoscope.patching

Activation patching.

The operator, its input, configs, enums, collectors, method base and factory are importable from entoscope.patching; the operator, its input, configs and enums from entoscope too.

class entoscope.patching.config.PatchingType(*values)[source]

Bases: StrEnum

What a patch writes, named on its source element. A target element is always replace.

replace writes the source run’s activation and value_scale the same times value. value_replace writes the constant value, zero ablation with the default 0.0, and gaussian_noise the target run’s own activation plus noise at three times its spread, the corruption of causal tracing. Neither of those two reads the source run.

class entoscope.patching.config.PatchingMetric(*values)[source]

Bases: StrEnum

The scalar read off one run’s output distribution.

r and r' are the first and second of the tokens a metric names in its tokens. kl_divergence reads none: it scores the whole distribution against the source run’s.

class entoscope.patching.config.MetricComparison(*values)[source]

Bases: StrEnum

How a patched run’s metric is reported against the two runs it sits between.

Chosen per metric: a divergence is normally read as an indirect_effect and a logit difference as normalized.

An ineffective patch reproduces the target run, and one that takes over entirely reproduces the source run, so normalized reads 0 and 1 at those two ends whichever of the pair is the clean prompt - that choice is expressed by the tokens a metric reads and their order, not here.

class entoscope.patching.config.PatchingElementConfig(*, activation: ModelLayerLocation | None = None, location: list[ModelAxis] = <factory>, filter: Filter = <factory>, group: Group = <factory>, incremental: Incremental = <factory>, combination: Combination = <factory>, coordinates: Coordinates | None = None, type: PatchingType, value: float = 0.0)[source]

Bases: ModelLocatorConfig

One side of a patch element: a locator, plus what the patch writes.

Variables:
  • type (entoscope.patching.config.PatchingType) – What the patch writes, on a source element, and replace on a target one.

  • value (float) – The constant value_replace writes and the factor value_scale multiplies by.

class entoscope.patching.config.SinglePatchingConfig(source: PatchingElementConfig, target: PatchingElementConfig)[source]

Bases: object

A patch element: what is read at row i of the source index is written at row i of the target’s.

An index of a single row pairs with every row of the other. A config lists several elements to patch them together: patched run i applies row i of every element, so their indices have the same number of rows, or one. The result is laid out along the element with the most rows.

Variables:
class entoscope.patching.config.PatchingMetricConfig(*, type: PatchingMetric, tokens: list[str] = <factory>, comparison: MetricComparison)[source]

Bases: object

A metric a run reads off every patched run, the tokens it reads and what it is reported against.

The config’s metric names it, so several metrics of one type can each read tokens of their own, or be reported under a comparison of their own.

Variables:
class entoscope.patching.config.PatchingMethodConfig(*, type: str)[source]

Bases: MethodConfig

The method config of a patching method that takes no fields, such as exact.

A method with fields of its own names a dataclass extending this one.

classmethod factory() → type[PatchingMethodFactory][source]

Name the factory patching methods register with.

Returns:

PatchingMethodFactory.

class entoscope.patching.config.PatchingConfig(*, config_id: str | None = None, metric: dict[str, ~entoscope.patching.config.PatchingMetricConfig], method: ~entoscope.patching.config.PatchingMethodConfig, patch: list[~entoscope.patching.config.SinglePatchingConfig] = <factory>)[source]

Bases: AbstractConfig

What a patching run patches, and what it reads off every patched run.

Every metric is read off the same patched run, so a second metric costs no extra forward pass. Each metric’s result column is drawn as its own plot on the result page.

Variables:
  • metric (dict[str, entoscope.patching.config.PatchingMetricConfig]) – Each metric, by a name of the config’s choosing, which names its result column too.

  • method (entoscope.patching.config.PatchingMethodConfig) – How the change in a metric is obtained, its type naming the method. exact patches and runs the model once per index row, while attribution and integrated_gradients estimate every row from gradients instead, trading a linearity assumption for a cost that does not grow with the index. In a YAML file it is the mapping read strictly into the method’s config class, or just the method’s type for its defaults.

  • patch (list[entoscope.patching.config.SinglePatchingConfig]) – The patch elements, applied together in every patched run.

class entoscope.patching.input.PatchingInput(config: PatchingConfig, source_prompt: str | None, target_prompt: str, tokens: dict[str, str | int]=<factory>)[source]

Bases: OperationInput

The two prompts a patching run sits between, and the tokens its metrics read.

The target run is the one patched, and the source run is what replace and value_scale read. A source element that reads no source run, a value or noise on the target’s own activation, counts its positions in the target prompt.

Variables:
  • config (entoscope.patching.config.PatchingConfig) – The patching config.

  • source_prompt (str | None) – The prompt patched from. None when no element reads it, and then kl_divergence and normalized, which score against the source run, are unavailable.

  • target_prompt (str) – The prompt patched into.

  • tokens (dict[str, str | int]) – The tokens the config’s metrics read, by the names the metrics give them, each a string that tokenizes to a single token or a token id. A string of several tokens is read as its first, with a warning. Only the tokens a metric reads are checked and resolved, so one mapping can serve configs that read different ones.

Raises:

ValueError – If the config has no patch element, names a patch type, metric or comparison nothing implements, reads or scores against the source run without a source prompt, or a metric names other than as many tokens as its type reads, or a token that tokens does not name or that is neither a string nor a token id.

prepare(model: TransformerBridge | CachedModel, batch_size: int = 1, *, index_only: bool = False) → PatchingInput[source]

Resolve the index and the tokens, then run both prompts, caching what it reads, as wide as the patched runs.

A backend may round differently at different batch shapes (MPS does, by up to half a logit in bfloat16), so a patched run has to be compared with, and patched from, runs of its own shape: only then does a patch that writes back what was there reproduce the target run exactly. Each prompt is therefore run batch_width copies abreast and kept at its first row.

Parameters:
  • model – The model to run.

  • batch_size – How many patched runs each forward pass holds.

  • index_only – Whether to stop after the index and the tokens, before any forward pass.

Returns:

The input itself, prepared in place.

Raises:

ValueError – If the elements’ row counts cannot be paired, the model computes no transformer activation an element names, or a token a metric reads is a string that holds no token or an id outside the vocabulary.

rows() → list[tuple][source]

Group the index’s rows as a run patches them.

Returns:

One tuple per patched run, in result row order, holding that run’s row of every element.

activation_names() → list[str][source]

Name every activation the run touches, on either side of the patch.

The target’s own names are read only by the gradient methods, which need the clean activation a patch would overwrite and the value they interpolate towards. The two sides name the same activation in the common case, so caching both usually costs nothing.

Returns:

The transformer activation names, sorted.

target_activation_names() → list[str][source]

Name every activation a patch writes.

Returns:

The transformer activation names, sorted.

property source_tokens: Int[Tensor, 'batch pos']

The source prompt’s tokens, once the index is prepared.

property target_tokens: Int[Tensor, 'batch pos']

The target prompt’s tokens, once the index is prepared.

property source_logits: Float[Tensor, 'batch pos d_vocab']

The unpatched source run’s logits, once the input is prepared.

property target_logits: Float[Tensor, 'batch pos d_vocab']

The unpatched target run’s logits, once the input is prepared.

property source_generation: int

The token the unpatched source run predicts, once the input is prepared.

property target_generation: int

The token the unpatched target run predicts, once the input is prepared.

property source_cache: ActivationCache

The source run’s activations, once the input is prepared.

property target_cache: ActivationCache

The target run’s activations, once the input is prepared.

property index: list[Index[PatchingElementConfig]]

One index per patch element, once the index is prepared.

property layout_index: Index[PatchingElementConfig]

the first with the most rows, so every run has a row.

Type:

The element index the result is laid out along

property batch_width: int

How many rows every patched run holds, the width the cached runs were made at.

property metrics: dict[str, AbstractMetric]

Every metric of the config by its name, built on the ids of the tokens it reads when first read.

Built again once the prompts are re-run, or the input moved, so what a metric keeps of the unpatched runs is always of the runs the input holds.

noise_scale(name: str) → Tensor[source]

Find the standard deviation gaussian_noise draws at for an activation.

Measured once per activation rather than once per row that noises it.

Parameters:

name – The transformer activation name.

Returns:

Three times the target run’s own spread there, as in causal tracing (Meng et al. 2022).

to(device: str | device = 'cpu') → PatchingInput[source]

Move the tensors the input holds to device.

Parameters:

device – The device to move to.

Returns:

The input itself, moved in place.

class entoscope.patching.patcher.Patcher(model: TransformerBridge | CachedModel, collector_class: type[PatchingCollector] = PatchingVisualizationCollector, batch_size: int = 1)[source]

Bases: Operator

Patches the source run into the target run and reports how the metrics moved, one row per patched run.

Backends round differently at different batch shapes, so the cached source and target runs are made at the same width as the patched runs.

Parameters:
  • model – The model to patch.

  • collector_class – The collector to write the results with. The default also draws result.html, and PatchingCollector writes the CSV files alone.

  • batch_size – How many patched runs one forward pass of exact holds. The gradient methods score the whole index from passes of their own and ignore it.

apply(patching_input: PatchingInput, save_dir: str | None = None, return_collector: bool = False, verbose: bool = True) → PatchingCollector | None[source]

Patch every row of the input’s index and read the metrics off each patched run.

The method is built before any forward pass, so a config that names something the run cannot do fails before the model is touched. Where the results were saved is logged through the entoscope logger.

Parameters:
  • patching_input – The config, prompts and answer tokens to run, prepared in place.

  • save_dir – The directory to write result.csv, one index-{i}.csv per patch element, config.json and the page into, created if missing. None writes nothing.

  • return_collector – Whether to return the collector.

  • verbose – Whether to show a progress bar.

Returns:

The collector, whose result holds every patched run’s metric columns, if return_collector is set, and None otherwise.

plan(patching_input: PatchingInput, save_dir: str | None = None, verbose: bool = True) → OperationPlan[source]

Describe the run apply would make, without a forward pass and without touching save_dir.

The input’s index is resolved exactly as the run resolves it, and kept for apply.

Parameters:
  • patching_input – The config, prompts and answer tokens the run would use.

  • save_dir – The directory the run would write into.

  • verbose – Whether to also log the plan.

Returns:

The plan – one step per patched run, and the forward and backward passes the method makes on top of one cached run per prompt.

class entoscope.patching.collector.PatchingCollector(operation_input: InputT, model: TransformerBridge | CachedModel, save_dir: str | None = None)[source]

Bases: FrameCollector[PatchingInput]

Writes the metrics of every patched run as result.csv, plus one index file per element.

A run that patched the model also records what it then predicts next, as generation (the token id, exact) and str_generation (its string, see str_token on reading it back).

Each metric’s column is its name with _metric appended, such as logit_diff_metric, so no metric can take a column the run writes itself.

This base collector writes the CSV files only, and PatchingVisualizationCollector also draws result.html.

collect(row: RowResult)[source]

Take one patched run’s metrics.

Parameters:

row – The run’s result row and metric columns, and its next token when the method ran the patched model.

class entoscope.patching.collector.PatchingVisualizationCollector(operation_input: InputT, model: TransformerBridge | CachedModel, save_dir: str | None = None)[source]

Bases: VisualizationCollector[PatchingInput, PatchingFigure], PatchingCollector

A PatchingCollector that also draws its result as an interactive Plotly result.html.

Its figures are keyed by the metric names the config gives.

figure_class

alias of PatchingFigure

class entoscope.patching.metric.AbstractMetric(tokens: list[int])[source]

Bases: ABC

One scalar read off a run’s final-position logits, on the tokens it reads.

The input, which holds the two unpatched runs, is handed to it with every run it reads. The input also builds it, and builds it again whenever it re-runs its prompts, so a metric belongs to one pair of unpatched runs: what it reads off them, which every row is reported against, it reads once and keeps.

Parameters:

tokens – The ids of the tokens the metric reads, in the order its config names them.

Variables:
  • effect_sign (ClassVar[int]) – The sign an indirect_effect is reported with, -1 for a metric that falls as the patch restores the source run.

  • n_tokens (ClassVar[int]) – How many tokens the metric reads, which its config names in tokens.

  • needs_source_run (ClassVar[bool]) – Whether the metric scores against the source run.

  • tokens – The ids of the tokens the metric reads.

source(patching_input: PatchingInput) → torch.Tensor[source]

Read the metric off the unpatched source run, the first time it is asked.

Parameters:

patching_input – The prepared input that built the metric, which has a source prompt.

Returns:

The metric, a scalar.

target(patching_input: PatchingInput) → torch.Tensor[source]

Read the metric off the unpatched target run, the first time it is asked.

Parameters:

patching_input – The prepared input that built the metric.

Returns:

The metric, a scalar.

abstractmethod compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

static get(metric: PatchingMetric) → type[AbstractMetric][source]

Pick the implementation of a metric.

Parameters:

metric – The metric.

Returns:

The metric’s class.

Raises:

ValueError – If the metric is unknown.

class entoscope.patching.metric.LogitDiffMetric(tokens: list[int])[source]

Bases: AbstractMetric

logit(r) - logit(r').

compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

class entoscope.patching.metric.ProbabilityMetric(tokens: list[int])[source]

Bases: AbstractMetric

P(r).

compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

class entoscope.patching.metric.LogProbMetric(tokens: list[int])[source]

Bases: AbstractMetric

log P(r).

compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

class entoscope.patching.metric.LogProbDiffMetric(tokens: list[int])[source]

Bases: AbstractMetric

log P(r) - log P(r').

Numerically the same thing as LogitDiffMetric, since the softmax’s normalizer is shared by the two tokens and cancels, and kept apart because the papers that use it call it by this name.

compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

class entoscope.patching.metric.KLDivergenceMetric(tokens: list[int])[source]

Bases: AbstractMetric

D_KL(P_source || P_run), how far a run’s whole distribution sits from the source run’s.

Needs no answer token, which is what makes it the task-agnostic choice. kl_div(input, target) reads input as log-probabilities and computes D_KL(target || exp(input)), so the run being scored is the one passed as input.

compute(patching_input: PatchingInput, logits: Logits) → torch.Tensor[source]

Read the metric off one run, differentiably, so the gradient methods can back-propagate through it.

Parameters:
  • patching_input – The prepared input, whose unpatched runs a metric may score against.

  • logits – The run’s logits at the final position.

Returns:

The metric, a scalar.

class entoscope.patching.metric.AbstractComparison[source]

Bases: ABC

Turns the change a patch made to a metric into the number a run reports.

Every method produces the same primitive, delta, the patched run’s metric minus the unpatched target run’s: exactly for exact and as a gradient estimate for the others.

Variables:

needs_source_run (bool) – Whether the comparison scores against the source run.

abstractmethod classmethod apply(metric: AbstractMetric, patching_input: PatchingInput, delta: torch.Tensor) → torch.Tensor[source]

Report a change in a metric.

Parameters:
  • metric – The metric that changed.

  • patching_input – The prepared input, whose unpatched runs the change is reported against.

  • delta – The patched run’s metric minus the unpatched target run’s.

Returns:

The reported number.

static get(comparison: MetricComparison) → type[AbstractComparison][source]

Pick the implementation of a comparison.

Parameters:

comparison – The comparison.

Returns:

The comparison’s class.

Raises:

ValueError – If the comparison is unknown.

class entoscope.patching.metric.AbsoluteComparison[source]

Bases: AbstractComparison

The patched run’s metric itself.

classmethod apply(metric: AbstractMetric, patching_input: PatchingInput, delta: torch.Tensor) → torch.Tensor[source]

Report a change in a metric.

Parameters:
  • metric – The metric that changed.

  • patching_input – The prepared input, whose unpatched runs the change is reported against.

  • delta – The patched run’s metric minus the unpatched target run’s.

Returns:

The reported number.

class entoscope.patching.metric.IndirectEffectComparison[source]

Bases: AbstractComparison

The patched run’s metric minus the unpatched target run’s, signed by the metric’s effect_sign.

classmethod apply(metric: AbstractMetric, patching_input: PatchingInput, delta: torch.Tensor) → torch.Tensor[source]

Report a change in a metric.

Parameters:
  • metric – The metric that changed.

  • patching_input – The prepared input, whose unpatched runs the change is reported against.

  • delta – The patched run’s metric minus the unpatched target run’s.

Returns:

The reported number.

class entoscope.patching.metric.NormalizedComparison[source]

Bases: AbstractComparison

0 where the patch changed nothing, 1 where it reproduced the source run.

The same ratio circuit work calls faithfulness, with the unpatched target run playing the part of the fully-ablated model. Two runs that score the same leave no gap to divide by: every row then reads inf, or nan where the patch changed nothing, and a warning is logged.

classmethod apply(metric: AbstractMetric, patching_input: PatchingInput, delta: torch.Tensor) → torch.Tensor[source]

Report a change in a metric.

Parameters:
  • metric – The metric that changed.

  • patching_input – The prepared input, whose unpatched runs the change is reported against.

  • delta – The patched run’s metric minus the unpatched target run’s.

Returns:

The reported number.

class entoscope.patching.processor.SourceStrategy[source]

Bases: ABC

What a patch writes, by the type of its source element.

Variables:

needs_source_run (bool) – Whether the strategy reads the source run.

abstractmethod static extract(index: IndexElement[PatchingElementConfig], patching_input: PatchingInput) → torch.Tensor[source]

Produce the value a patch writes.

Parameters:
  • index – The source element.

  • patching_input – The prepared input.

Returns:

The value, shaped as the source element’s slice or broadcastable to the target’s.

static get(patching_type: PatchingType) → type[SourceStrategy][source]

Pick the strategy of a source element’s type.

Parameters:

patching_type – The source element’s type.

Returns:

The strategy.

Raises:

ValueError – If the type is unknown.

class entoscope.patching.processor.TargetStrategy[source]

Bases: ABC

How a patch is written, by the type of its target element.

abstractmethod static set(index: IndexElement[PatchingElementConfig], target_activation: Tensor, extracted_source: Tensor, batch: int) → Tensor[source]

Write a value into the patched run’s activation.

Parameters:
  • index – The target element.

  • target_activation – The patched run’s activation, written in place unless it is a sublayer’s input.

  • extracted_source – What the source strategy produced.

  • batch – The batch row of the patched run.

Returns:

The patched activation, the one the hook hands on.

static get(patching_type: PatchingType) → type[TargetStrategy][source]

Pick the strategy of a target element’s type.

Parameters:

patching_type – The target element’s type.

Returns:

The strategy.

Raises:

ValueError – If the type is not replace.

class entoscope.patching.processor.ReplaceSourceStrategy[source]

Bases: SourceStrategy

The source run’s activation.

static extract(source_index: IndexElement[PatchingElementConfig], patching_input: PatchingInput) → torch.Tensor[source]

Produce the value a patch writes.

Parameters:
  • index – The source element.

  • patching_input – The prepared input.

Returns:

The value, shaped as the source element’s slice or broadcastable to the target’s.

class entoscope.patching.processor.ValueReplaceSourceStrategy[source]

Bases: SourceStrategy

The element’s constant value.

static extract(source_index: IndexElement[PatchingElementConfig], patching_input: PatchingInput) → torch.Tensor[source]

Produce the value a patch writes.

Parameters:
  • index – The source element.

  • patching_input – The prepared input.

Returns:

The value, shaped as the source element’s slice or broadcastable to the target’s.

class entoscope.patching.processor.ValueScaleSourceStrategy[source]

Bases: SourceStrategy

The source run’s activation times the element’s value.

static extract(source_index: IndexElement[PatchingElementConfig], patching_input: PatchingInput) → torch.Tensor[source]

Produce the value a patch writes.

Parameters:
  • index – The source element.

  • patching_input – The prepared input.

Returns:

The value, shaped as the source element’s slice or broadcastable to the target’s.

class entoscope.patching.processor.GaussianNoiseSourceStrategy[source]

Bases: SourceStrategy

The target’s own activation plus N(0, noise_scale) noise, the corruption of causal tracing.

Seeded the same on every call, so each row of a sweep is corrupted by the same draw and differs from the others only in where it lands, and a rerun repeats the sweep exactly.

Variables:

SEED – The seed every draw starts from.

static extract(source_index: IndexElement[PatchingElementConfig], patching_input: PatchingInput) → torch.Tensor[source]

Produce the value a patch writes.

Parameters:
  • index – The source element.

  • patching_input – The prepared input.

Returns:

The value, shaped as the source element’s slice or broadcastable to the target’s.

class entoscope.patching.processor.ReplaceTargetStrategy[source]

Bases: TargetStrategy

Overwrites the target element’s slice with the value.

static set(index: IndexElement[PatchingElementConfig], target_activation: Tensor, extracted_source: Tensor, batch: int) → Tensor[source]

Write a value into the patched run’s activation.

Parameters:
  • index – The target element.

  • target_activation – The patched run’s activation, written in place unless it is a sublayer’s input.

  • extracted_source – What the source strategy produced.

  • batch – The batch row of the patched run.

Returns:

The patched activation, the one the hook hands on.

class entoscope.patching.processor.PatchProcessor[source]

Bases: object

Applies one patch, source strategy then target strategy.

static patch(index: SingleIndex[PatchingElementConfig], patching_input: PatchingInput, target_activation: torch.Tensor, batch: int) → torch.Tensor[source]

Write a row’s source into one batch row of the patched run’s activation.

Parameters:
  • index – The row to patch.

  • patching_input – The prepared input.

  • target_activation – The patched run’s activation, written in place unless it is a sublayer’s input.

  • batch – The batch row of the patched run.

Returns:

The patched activation, the one the hook hands on.

class entoscope.patching.processor.HookedPatchProcessor[source]

Bases: PatchProcessor

Applies a patch as a transformer_lens forward hook.

classmethod hook(index: SingleIndex[PatchingElementConfig], patching_input: PatchingInput, batch: int) → tuple[str, Callable][source]

Build the forward hook that patches one row.

Parameters:
  • index – The row to patch.

  • patching_input – The prepared input.

  • batch – The batch row of the patched run.

Returns:

The hook name and the hook function, as run_with_hooks takes them.

Interactive Plotly rendering of a patching run.

The grid is the run’s own index, layer up and position across for the canonical sweep, coloured by the metric on a diverging scale centred on the value an ineffective patch would produce, so warm cells are the locations that moved the model toward the source. One such grid is drawn per column the config names, each centred on its own comparison’s ineffective value. A cell’s hover names the token its patched run predicts next, and the subtitle the token each unpatched prompt does and the tokens the grid’s metric reads.

class entoscope.patching.visualization.PatchingFigure(result: DataFrame, operation_input: PatchingInput, model: TransformerBridge | CachedModel, index: Index)[source]

Bases: GridFigure[PatchingInput, str]

The page of a patching run: one grid per result column, each panel a metric, by its name.

The patch elements the result is not laid out along are described in the subtitle when they patch the same location in every run, and in each cell’s hover when they vary with the row.

Parameters:
  • result – The run’s result frame.

  • operation_input – The input the run iterated.

  • model – The model the run patched.

  • index – The index that laid the result out.

property title: str

The page’s title.

panels() → list[str][source]

Name the page’s panels.

Returns:

The panels, in page order, each drawn as its own plot.

heading(panel: str) → str[source]

Name one panel’s plot.

Parameters:

panel – One of panels().

Returns:

The plot’s heading, shown above the subtitle line.

subtitle(panel: str) → str[source]

Describe what one panel’s plot holds, under its heading.

Parameters:

panel – One of panels().

Returns:

The subtitle, as HTML.

value_column(panel: str) → str[source]

Name the result column one panel colours its cells by.

Parameters:

panel – One of panels().

Returns:

A column of the result frame.

value_range(panel: str, values: Sequence[float]) → tuple[float, float][source]

Choose the range one panel’s colours span.

Parameters:
  • panel – One of panels().

  • values – The panel’s values, one per result row.

Returns:

The low and high ends of the colour range, by default 0 and 1.

str_tokens() → dict[str, list[str]][source]

Read the prompt’s token strings, to label the position axes with.

Read once at construction. A figure whose index carries no position, or a run with no prompt to read one from, labels its ticks with the coordinates alone.

Returns:

The token strings for each position column of the result, keyed by that column. Empty by default.

hover(panel: str, position: int, coordinate: str) → str[source]

Write what one cell shows on hover.

Parameters:
  • panel – One of panels().

  • position – The result row the cell is built from.

  • coordinate – The cell’s laid-out coordinates, from cell_labels.

Returns:

The hover text, as HTML.

entoscope.patching.method.patching.metric_column(metric: str) → str[source]

Name the result column a metric is written to.

Every metric’s column carries the same suffix, so none is a column the run writes itself: generation, str_generation or one of the index’s.

Parameters:

metric – The metric’s name in the config.

Returns:

<name>_metric, such as logit_diff_metric.

class entoscope.patching.method.patching.RowResult(output: int, metrics: dict[str, float], generation: int | None = None)[source]

Bases: object

What a method reports for one index row.

Variables:
  • output (int) – The result row the collector files the report under.

  • metrics (dict[str, float]) – The row’s value of every metric, keyed by the metric’s name; the collector writes each to its metric_column.

  • generation (int | None) – The token the patched run predicts next. Only a method that actually runs the patched model has one, and the gradient methods, which estimate the metric without such a run, leave it unset.

class entoscope.patching.method.patching.AbstractPatchingMethod(model: TransformerBridge | CachedModel, config: PatchingConfig)[source]

Bases: ABC, Generic[PatchingMethodConfigT]

The base of every patching method: how a run turns its index into one number per row.

Every method produces the same primitive for each row, delta, the metric of the patched run minus the metric of the unpatched target run, one per metric, and _report reports each under its metric’s comparison. What separates the methods is only how delta is reached: by patching and running the model, or by estimating from gradients.

A method is built once per run. Its type argument names the dataclass of its method config, extending PatchingMethodConfig, which a config file’s method is read into. A method naming none takes PatchingMethodConfig itself, which no field fits.

Parameters:
  • model – The model the method patches.

  • config – The run’s config.

Variables:
  • batched (ClassVar[bool]) – Whether the method runs the patched model batch_size rows at a time, so a plan reports its batches.

  • method_config – The run’s method config, typed by the method’s type argument.

abstractmethod scores(patching_input: PatchingInput, verbose: bool = True) → Iterator[RowResult][source]

Score every patched run.

Parameters:
  • patching_input – The prepared input.

  • verbose – Whether to show a progress bar.

Yields:

One RowResult per patched run, its metric values keyed by the metric’s name.

abstractmethod passes(patching_input: PatchingInput, batches: int) → tuple[int, int][source]

Count the passes the sweep costs, over and above the input’s cached runs.

Parameters:
  • patching_input – The input, its index resolved: rows() lists the patched runs and target_activation_names() the transformer activations a patch writes.

  • batches – The number of batches the patched runs fall into, for a batched method.

Returns:

The forward and the backward passes.

check(patching_input: PatchingInput) → None[source]

Reject an input the method cannot score, before either prompt is run.

Called once the input’s index is resolved, by plan as well as by the run, so a plan never promises a run the method would refuse. Nothing is rejected by default.

Parameters:

patching_input – The input, its index resolved and no forward pass made.

Raises:

ValueError – If the method cannot score the input.

class entoscope.patching.method.exact.ExactPatchingMethod(model: TransformerBridge | CachedModel, config: PatchingConfig)[source]

Bases: AbstractPatchingMethod[PatchingMethodConfigT]

Patches and runs the model, once per index row.

The reference the other methods approximate: nothing is assumed about how the model responds to the intervention, at the price of a forward pass per row. Its last batch is padded to the full width, so every patched run has the shape the cached runs were made at.

scores(patching_input: PatchingInput, verbose: bool = True) → Iterator[RowResult][source]

Score every patched run.

Parameters:
  • patching_input – The prepared input.

  • verbose – Whether to show a progress bar.

Yields:

One RowResult per patched run, its metric values keyed by the metric’s name.

passes(patching_input: PatchingInput, batches: int) → tuple[int, int][source]

Count the passes the sweep costs, over and above the input’s cached runs.

Parameters:
  • patching_input – The input, its index resolved: rows() lists the patched runs and target_activation_names() the transformer activations a patch writes.

  • batches – The number of batches the patched runs fall into, for a batched method.

Returns:

The forward and the backward passes.

class entoscope.patching.method.attribution.AttributionPatchingMethod(model: TransformerBridge | CachedModel, config: PatchingConfig)[source]

Bases: AbstractPatchingMethod[PatchingMethodConfigT]

Attribution patching, the first-order Taylor expansion of the true indirect effect.

IE ~= grad(m) . (a_patch - a_clean), with the gradient taken on the target run at the activation a patch would overwrite. One backward pass scores the whole index, so the cost is flat in the number of rows rather than linear in it. The price is that the estimate is only as good as the model’s local linearity, which a large intervention (zero ablation of a whole layer, say) leaves well behind.

The intervention itself is unchanged: the replacement value comes from the same SourceStrategy the exact method uses, so resampling, zero ablation, scaling and noise are all scored the same way.

scores(patching_input: PatchingInput, verbose: bool = True) → Iterator[RowResult][source]

Score every patched run.

Parameters:
  • patching_input – The prepared input.

  • verbose – Whether to show a progress bar.

Yields:

One RowResult per patched run, its metric values keyed by the metric’s name.

passes(patching_input: PatchingInput, batches: int) → tuple[int, int][source]

Count the passes the sweep costs, over and above the input’s cached runs.

Parameters:
  • patching_input – The input, its index resolved: rows() lists the patched runs and target_activation_names() the transformer activations a patch writes.

  • batches – The number of batches the patched runs fall into, for a batched method.

Returns:

The forward and the backward passes.

class entoscope.patching.method.attribution.IntegratedGradientsConfig(*, type: str, interpolation_steps: int = 10)[source]

Bases: PatchingMethodConfig

The method config of integrated_gradients.

Variables:

interpolation_steps (int) – The number of points the gradient is averaged over.

Raises:

ValueError – If interpolation_steps is below 1.

class entoscope.patching.method.attribution.IntegratedGradientsMethod(model: TransformerBridge | CachedModel, config: PatchingConfig)[source]

Bases: AttributionPatchingMethod[IntegratedGradientsConfig]

Integrated gradients in activation space (Marks et al. 2024, Eq. 4).

Averages the gradient over interpolation_steps points on the straight path from the source activation to the target one instead of reading it once at the target. That recovers the effects a flat gradient at the clean point hides, at a forward and a backward pass per step for each transformer activation, still flat in the rows of the index.

Each patched activation is interpolated on passes of its own, one per step but the clean one, which a single pass serves for all of them. The cost so grows with the number of transformer activations the index patches, such as the layers it sweeps, and never with its rows: every position, head or neuron of one is scored from the same passes.

The path runs between the two runs’ activations, so it is the path the intervention actually traverses only when the source resamples from the source run. A value-based source (zero ablation, scaling, noise) writes something off that path, and the averaged gradient then belongs to a change other than the one being scored. Attribution patching, whose expansion point does not depend on a path at all, is the estimate to reach for there.

check(patching_input: PatchingInput) → None[source]

Reject an input the method cannot score, before either prompt is run.

Called once the input’s index is resolved, by plan as well as by the run, so a plan never promises a run the method would refuse. Nothing is rejected by default.

Parameters:

patching_input – The input, its index resolved and no forward pass made.

Raises:

ValueError – If the method cannot score the input.

class entoscope.patching.method.factory.PatchingMethodFactory[source]

Bases: RegistryFactory[AbstractPatchingMethod]

Resolves the patching method a config’s method.type names and builds it for a run.

It holds exact, attribution, integrated_gradients and every method registered with register.

classmethod create(model: TransformerBridge | CachedModel, config: PatchingConfig) → AbstractPatchingMethod[source]

Build the method a config names, once per run.

Parameters:
  • model – The model the method patches.

  • config – The run’s config, whose method.type names the method.

Returns:

The method.

Raises:

ValueError – If no method is registered under the name, or the method rejects the config.