Extract, analyze, and visualize entropy profiles from transformer models using the logit-lens technique.
Project description
entropy-profiler
Extract, analyse, and visualize entropy profiles from transformer models using the logit-lens technique.
entropy-profiler computes per-layer Shannon or Rényi entropy by passing
hidden states through the model's own unembedding head (layer norm + lm_head).
It works on any HuggingFace CausalLM without architecture-specific hooks.
from entropy_profiler import EntropyProfiler, plot_profile
import torch
profiler = EntropyProfiler("gpt2", dtype=torch.float32)
profile = profiler.profile_text("The meaning of life is", max_new_tokens=32)
plot_profile(profile)
Installation
From source (recommended for development)
git clone https://github.com/TODO/entropy-profiler
cd entropy-profiler
# Using uv (fast, handles venvs automatically)
uv sync # core dependencies
uv sync --extra notebook # + Jupyter support
uv sync --extra dev # + pytest, ruff
# Or using pip
pip install -e .
pip install -e ".[quantize]" # + 8-bit/4-bit quantization (bitsandbytes, accelerate)
pip install -e ".[notebook]"
pip install -e ".[dev]"
From PyPI (once published)
pip install entropy-profiler
Quick Start
Profile a single prompt
from entropy_profiler import EntropyProfiler, plot_profile
import torch
profiler = EntropyProfiler("gpt2", dtype=torch.float32)
profile = profiler.profile_text("The capital of France is", max_new_tokens=32)
print(profile.entropy.shape) # (n_tokens, n_layers)
print(profile.mean_profile()) # (n_layers,) tensor
plot_profile(profile)
Profile multiple prompts
from entropy_profiler import plot_aggregated
agg = profiler.profile_batch([
"The stock market experienced significant",
"In quantum mechanics, the wave function",
"Modern neural networks learn by",
], max_new_tokens=24)
print(agg.to_matrix().shape) # (3, n_layers)
plot_aggregated(agg)
Compare prompts with distances
from entropy_profiler import profile_distance
p1 = profiler.profile_text("Water boils at", max_new_tokens=24)
p2 = profiler.profile_text("Once upon a time", max_new_tokens=24)
result = profile_distance(p1, p2, metric="jsd")
print(f"JSD distance: {result.aggregate:.4f}")
Analyse layer dynamics
from entropy_profiler import LayerAnalyzer
profile, hidden_states = profiler.profile_text_with_states(
"Hello world", max_new_tokens=32
)
analyzer = LayerAnalyzer(profiler, profile, hidden_states=hidden_states)
print(analyzer.layer_entropy()) # (n_layers,)
print(analyzer.information_velocity()) # (n_layers,)
print(analyzer.layer_mi(method="cka")) # (n_layers,)
Core Concepts
Logit-Lens Decoding
At each transformer layer, the hidden state is projected through the model's final layer norm and language model head to produce a vocabulary distribution. The entropy of this distribution measures how "decided" the model is at that layer — low entropy means a peaked distribution (confident prediction), high entropy means a flat distribution (uncertain).
Entropy Profiles
An entropy profile is a matrix of shape (n_tokens, n_layers) where each
entry is the entropy of the vocabulary distribution at that token position and
layer depth. The mean profile (n_layers,) averages across tokens to give
a single curve showing how entropy evolves through the network.
Why Rényi Entropy?
Shannon entropy (alpha=1) is the default, but Rényi entropy at other orders
provides complementary views:
alpha < 1— emphasises rare events (tail sensitivity)alpha = 1— Shannon entropy (standard)alpha = 2— collision entropy (sensitive to mode)alpha > 2— increasingly dominated by the most probable token
API Reference
Core Module (entropy_profiler.profiler)
| Symbol | Description |
|---|---|
EntropyProfiler(model, dtype, alpha, layer_stride, load_in_8bit, load_in_4bit) |
Main class. Loads model, runs generation, computes entropy. Use load_in_8bit or load_in_4bit for quantized loading (requires bitsandbytes). |
EntropyProfile |
Dataclass: entropy, token_ids, layer_indices, alpha, model_name, metadata. |
AggregatedProfile |
Collection of profiles with mean_profile() and to_matrix(). |
shannon_entropy(probs) |
H(p) = -sum(p log p) on the last dimension. |
renyi_entropy(probs, alpha) |
Rényi entropy of order α. Falls back to Shannon when α ≈ 1. |
EntropyProfiler methods:
| Method | Returns | Description |
|---|---|---|
profile_text(prompt, max_new_tokens, ...) |
EntropyProfile |
Profile generated text. |
profile_text_with_states(prompt, ...) |
(EntropyProfile, Tensor) |
Profile + raw hidden states. |
profile_batch(prompts, ...) |
AggregatedProfile |
Profile multiple prompts. |
unload() |
None |
Free model memory. |
EntropyProfile attributes and methods:
| Member | Type | Description |
|---|---|---|
entropy |
Tensor (n_tokens, n_layers) |
Per-token, per-layer entropy. |
token_ids |
Tensor (n_tokens,) |
Generated token IDs. |
n_layers |
int |
Number of profiled layers. |
n_tokens |
int |
Number of profiled tokens. |
mean_profile() |
Tensor (n_layers,) |
Mean entropy at each layer. |
to_numpy() |
ndarray |
Convert to NumPy (float32). |
Distances (entropy_profiler.distances)
| Function | Type | Description |
|---|---|---|
profile_distance(p1, p2, metric, aggregation) |
DistanceResult |
Unified entry point. |
pairwise_distances(profiles, metric) |
ndarray (N, N) |
Symmetric distance matrix. |
jsd_layer(p1, p2, n_bins) |
ndarray (n_layers,) |
Per-layer Jensen-Shannon divergence. |
wasserstein_layer(p1, p2) |
ndarray (n_layers,) |
Per-layer Wasserstein-1 distance. |
fisher_rao_distance(p1, p2) |
float |
Geodesic on probability simplex. |
srvf_distance(p1, p2) |
float |
Elastic SRVF curve distance. |
Available metrics for profile_distance: "jsd", "wasserstein", "fisher_rao", "srvf".
Aggregation methods: "mean", "max", "sum" (for layer-wise metrics).
Layer Analysis (entropy_profiler.analysis)
| Symbol | Description |
|---|---|
LayerAnalyzer(profiler, profile, hidden_states) |
Per-layer metric computation. |
Additional functions available via from entropy_profiler.analysis import ...:
compare_models, plot_layer_importance, plot_information_plane, plot_velocity_entropy.
LayerAnalyzer methods:
| Method | Returns | Description |
|---|---|---|
layer_entropy() |
ndarray (n_layers,) |
Mean Shannon entropy per layer. |
information_velocity() |
ndarray (n_layers,) |
Wasserstein between consecutive layers. |
distance_to_output() |
ndarray (n_layers,) |
Fisher-Rao distance to final layer. |
jsd_to_output(n_bins) |
ndarray (n_layers,) |
JSD from each layer to final. |
layer_mi(method) |
ndarray (n_layers,) |
MI with final layer (Rényi or CKA). |
layer_importance() |
dict |
All four non-MI metrics. |
Visualization (entropy_profiler.viz)
| Function | Description |
|---|---|
plot_profile(profile, ax, ...) |
Line plot with ±1 std fill. |
plot_profiles(profiles, labels, ...) |
Overlay multiple profiles. |
plot_heatmap(profile, ax, ...) |
Token × layer entropy heatmap. |
plot_aggregated(agg, ax, ...) |
Aggregated mean ± std curve. |
plot_cluster(profiles, labels, method, feature, metric, ...) |
2D scatter via t-SNE/UMAP/PCA. |
Estimators (entropy_profiler.estimators)
| Symbol | Description |
|---|---|
MatrixRenyiMI(alpha, device) |
Matrix-based Rényi MI via Gram matrices. Used by LayerAnalyzer.layer_mi(). |
Supported Models
Any HuggingFace AutoModelForCausalLM is supported. The profiler automatically
detects the unembedding architecture:
| Model Family | Layer Norm Path | Status |
|---|---|---|
| GPT-2 | transformer.ln_f |
Tested |
| LLaMA / LLaMA 2 / LLaMA 3 | model.norm |
Tested |
| Mistral | model.norm |
Tested |
| Gemma / Gemma 2 | model.norm |
Tested |
| Qwen / Qwen 2 | model.norm |
Tested |
| OPT | model.norm (fallback) |
Tested |
To add a new architecture, add a resolution pattern to
_get_unembedding() in entropy_profiler/profiler.py.
Tips for large models
# Use float16 for 7B+ models to fit in GPU memory
profiler = EntropyProfiler("meta-llama/Llama-2-7b-hf", dtype=torch.float16)
# Load in 8-bit or 4-bit to fit even larger models (requires: pip install bitsandbytes accelerate)
profiler = EntropyProfiler("meta-llama/Llama-2-7b-hf", load_in_8bit=True)
profiler = EntropyProfiler("meta-llama/Llama-2-7b-hf", load_in_4bit=True)
# Profile every other layer to reduce computation
profiler = EntropyProfiler("gpt2", layer_stride=2)
# Use context manager to auto-unload
with EntropyProfiler("gpt2") as profiler:
profile = profiler.profile_text("Hello world")
Design Decisions
No hooks. HuggingFace's output_hidden_states=True returns all hidden
states without hook infrastructure. This works across all CausalLM
architectures with zero architecture-specific code.
Logit-lens, not probing. The unembedding head is the model's own decoder. No training of linear probes is needed — the entropy values are directly interpretable as "how peaked is the vocabulary distribution at this layer."
Float32 entropy. Entropy is always computed in float32 regardless of model dtype, avoiding numerical issues with half-precision softmax.
Dataclass outputs. EntropyProfile and DistanceResult are plain
dataclasses — easy to inspect, serialize, and compose.
Notebooks
| Notebook | Description |
|---|---|
exploration.ipynb |
Multi-model exploration: entropy curves, heatmaps, velocities, distances, clustering. Requires GPU and gated-model access. |
api_tour.ipynb |
Complete API tour exercising every public function with GPT-2. |
uv sync --extra notebook
uv run jupyter notebook notebooks/
Development
# Install dev dependencies
uv sync --extra dev
# Lint
uv run ruff check .
uv run ruff check --fix .
# Test
uv run pytest
# Run a script without activating venv
uv run python your_script.py
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file entropy_profiler-0.2.1.tar.gz.
File metadata
- Download URL: entropy_profiler-0.2.1.tar.gz
- Upload date:
- Size: 1.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9db9b2b4ca1665becc7b513bcf9fdc7fc28aae4eb1fbe8d012ba12b8760f860b
|
|
| MD5 |
31da9e515ef042d33375248e7b895c1e
|
|
| BLAKE2b-256 |
d458881d985b412b3cb8eebf78c4dc9061822cd1da07f1702dd7221820af47a0
|
Provenance
The following attestation bundles were made for entropy_profiler-0.2.1.tar.gz:
Publisher:
publish.yml on TheGitCommit/entropy-profiler
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
entropy_profiler-0.2.1.tar.gz -
Subject digest:
9db9b2b4ca1665becc7b513bcf9fdc7fc28aae4eb1fbe8d012ba12b8760f860b - Sigstore transparency entry: 965024384
- Sigstore integration time:
-
Permalink:
TheGitCommit/entropy-profiler@46b14ba6106c6b4fd21597bca6291c20cfbf6ab6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/TheGitCommit
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@46b14ba6106c6b4fd21597bca6291c20cfbf6ab6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file entropy_profiler-0.2.1-py3-none-any.whl.
File metadata
- Download URL: entropy_profiler-0.2.1-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
213d534e09c0c0e88da9f2ca6c99c6200783a6a491ca17390939df1aee3b33c7
|
|
| MD5 |
acd948f656e7dfada56fa401a4130d34
|
|
| BLAKE2b-256 |
257694b6058a2766304313d752818730a286c5246833c21a1f5492f9fca0e919
|
Provenance
The following attestation bundles were made for entropy_profiler-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on TheGitCommit/entropy-profiler
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
entropy_profiler-0.2.1-py3-none-any.whl -
Subject digest:
213d534e09c0c0e88da9f2ca6c99c6200783a6a491ca17390939df1aee3b33c7 - Sigstore transparency entry: 965024465
- Sigstore integration time:
-
Permalink:
TheGitCommit/entropy-profiler@46b14ba6106c6b4fd21597bca6291c20cfbf6ab6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/TheGitCommit
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@46b14ba6106c6b4fd21597bca6291c20cfbf6ab6 -
Trigger Event:
release
-
Statement type: