Advanced Features#
Attention Probabilities#
For supported models, access attention probabilities. You must enable them when loading the model by setting enable_attention_probs=True:
from nnterp import StandardizedTransformer
# Load model with attention probabilities enabled
model = StandardizedTransformer("gpt2", enable_attention_probs=True)
with model.trace("The cat sat on the mat"):
# Access attention probabilities for layer 5
attn_probs = model.attention_probabilities[5].save()
# Shape: (batch, heads, seq_len, seq_len)
# Modify attention patterns
attn_probs[:, :, :, 0] = 0 # Remove attention to first token
attn_probs /= attn_probs.sum(dim=-1, keepdim=True) # Renormalize
modified_logits = model.logits.save()
Check what’s happening:
model.attention_probabilities.print_source()
Using nnterp’s renaming with LanguageModel or NNsight classes#
Even if it is recommended to use StandardizedTransformer, as it abstract some of the quirks of HuggingFace implementations, nnterp still provides a low level renaming functionality that allows you to rename LanguageModel or NNsight classes and even to have unified access to the model’s PyTorch modules..
Renaming a LanguageModel or NNsight class#
get_rename_dict combined with the rename feature of NNsight will allow you to access the modules of the model with the same names as the StandardizedTransformer class:
your_model
├── embed_tokens
├── layers
│ ├── self_attn
│ └── mlp
├── ln_final
└── lm_head
For that, you just need to pass the rename dictionary to the LanguageModel or NNsight class:
from nnterp import get_rename_dict
from nnsight import LanguageModel, NNsight
from transformers import AutoModelForCausalLM
rename_dict = get_rename_dict()
renamed_model = LanguageModel("gpt2", rename=rename_dict)
# or
hf_model = AutoModelForCausalLM.from_pretrained("gpt2")
renamed_model = LanguageModel(hf_model, rename=rename_dict)
# or
renamed_model = NNsight(hf_model, rename=rename_dict) # NNsight works with any PyTorch model
print(renamed_model.layers, renamed_model.ln_final, renamed_model.lm_head)
Accessing the PyTorch modules using renaming#
For some reason, you might want a universal way to access the PyTorch modules of your model, without using NNsight interventions. For example if you want to attach them a hook, or just want to access the weights.
If you embed your model using the NNsight class with nnterp’s renaming, you get universal access to the PyTorch modules with no overhead:
from nnsight import NNsight
from nnterp import get_rename_dict
from transformers import AutoModelForCausalLM
import torch.nn as nn
hf_model = AutoModelForCausalLM.from_pretrained("gpt2")
renamed_model = NNsight(hf_model, rename=get_rename_dict())
layers_modules = renamed_model.layers._module
assert isinstance(layers_modules, nn.ModuleList)
However, the ._module syntax is not very convenient, so you can use the ModuleAccessor class to access the modules directly:
from nnterp import ModuleAccessor
module_accessor = ModuleAccessor(hf_model)
layers_modules = module_accessor.get_layers()
# or
layers_modules = module_accessor.layers
assert isinstance(layers_modules, nn.ModuleList)
Prompt Utilities#
Track probabilities of specific tokens:
from nnterp.prompt_utils import Prompt, run_prompts
# Create prompt with target tokens
targets = {
"correct_answer": "Paris",
"traps": ["London", "Madrid"],
"longstring": "the country of France",
}
prompt = Prompt.from_strings(
"The capital of France (not England or Spain) is",
targets,
model.tokenizer,
)
# Check what tokens are tracked for each target category
for name, tokens in prompt.target_tokens.items():
print(f"{name}: {model.tokenizer.convert_ids_to_tokens(tokens)}")
# Get probabilities
results = run_prompts(model, [prompt])
for target, probs in results.items():
print(f"{target}: {probs.shape}") # Shape: (batch_size,)
results is a dictionary that, for each target category, returns the sum of the probabilities of the first tokens of the target strings with and without a space at the beginning. For example for “traps”, depending on the tokenizer, it could return the sum of the probabilities of “_London”, “Lon”, “_Mad” and “Ma”.
Combined with Interventions#
from nnterp.interventions import logit_lens
# Use interventions with target tracking
results = run_prompts(model, prompts, get_probs_func=logit_lens)
# Returns probabilities for each target category across all layers
Visualization#
Plot top tokens at each layer:
from nnterp.display import plot_topk_tokens, prompts_to_df
probs = logit_lens(model, "The capital of France is")
# Interactive plot
plot_topk_tokens(
probs[0], # First prompt
model.tokenizer,
k=5,
title="Top 5 tokens at each layer"
)
Prompt Analysis#
# Convert prompts to DataFrame
df = prompts_to_df(prompts, model.tokenizer)
display(df)
Plot Target Evolution#
import plotly.graph_objects as go
# From logit lens results with target tracking
results = run_prompts(model, prompts, get_probs_func=logit_lens)
fig = go.Figure()
for category, probs in results.items():
fig.add_trace(go.Scatter(
x=list(range(len(probs[0]))),
y=probs[0].tolist(),
mode="lines+markers",
name=category
))
fig.update_layout(
title="Target Token Probabilities Across Layers",
xaxis_title="Layer",
yaxis_title="Probability"
)
fig.show()