vlmprobe 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. vlmprobe-0.9.0/MANIFEST.in +19 -0
  2. vlmprobe-0.9.0/PKG-INFO +257 -0
  3. vlmprobe-0.9.0/README.md +217 -0
  4. vlmprobe-0.9.0/pyproject.toml +51 -0
  5. vlmprobe-0.9.0/setup.cfg +4 -0
  6. vlmprobe-0.9.0/src/vlm_probing/__init__.py +17 -0
  7. vlmprobe-0.9.0/src/vlm_probing/_vendor/__init__.py +1 -0
  8. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/LICENSE +21 -0
  9. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/README.md +22 -0
  10. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/__init__.py +1 -0
  11. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/layer_helpers.py +21 -0
  12. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/layers.py +280 -0
  13. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/vit.py +372 -0
  14. vlmprobe-0.9.0/src/vlm_probing/_vendor/chefer2021/weight_init.py +60 -0
  15. vlmprobe-0.9.0/src/vlm_probing/adapters/__init__.py +7 -0
  16. vlmprobe-0.9.0/src/vlm_probing/adapters/auto.py +35 -0
  17. vlmprobe-0.9.0/src/vlm_probing/adapters/base.py +15 -0
  18. vlmprobe-0.9.0/src/vlm_probing/adapters/hf_attention.py +70 -0
  19. vlmprobe-0.9.0/src/vlm_probing/adapters/huggingface.py +184 -0
  20. vlmprobe-0.9.0/src/vlm_probing/adapters/spec.py +198 -0
  21. vlmprobe-0.9.0/src/vlm_probing/adapters/torch.py +201 -0
  22. vlmprobe-0.9.0/src/vlm_probing/adapters/vit.py +103 -0
  23. vlmprobe-0.9.0/src/vlm_probing/api/__init__.py +1 -0
  24. vlmprobe-0.9.0/src/vlm_probing/api/attention.py +214 -0
  25. vlmprobe-0.9.0/src/vlm_probing/api/attention_maps.py +146 -0
  26. vlmprobe-0.9.0/src/vlm_probing/api/causal.py +371 -0
  27. vlmprobe-0.9.0/src/vlm_probing/api/common.py +145 -0
  28. vlmprobe-0.9.0/src/vlm_probing/api/lenses.py +248 -0
  29. vlmprobe-0.9.0/src/vlm_probing/attention/__init__.py +22 -0
  30. vlmprobe-0.9.0/src/vlm_probing/attention/_attribution_utils.py +45 -0
  31. vlmprobe-0.9.0/src/vlm_probing/attention/attribution.py +46 -0
  32. vlmprobe-0.9.0/src/vlm_probing/attention/base.py +68 -0
  33. vlmprobe-0.9.0/src/vlm_probing/attention/beyond_intuition.py +103 -0
  34. vlmprobe-0.9.0/src/vlm_probing/attention/chefer.py +53 -0
  35. vlmprobe-0.9.0/src/vlm_probing/attention/grad_cam.py +55 -0
  36. vlmprobe-0.9.0/src/vlm_probing/attention/head_logit_attribution.py +54 -0
  37. vlmprobe-0.9.0/src/vlm_probing/attention/lrp.py +195 -0
  38. vlmprobe-0.9.0/src/vlm_probing/attention/profile.py +51 -0
  39. vlmprobe-0.9.0/src/vlm_probing/attention/relevance.py +47 -0
  40. vlmprobe-0.9.0/src/vlm_probing/attention/reweight.py +49 -0
  41. vlmprobe-0.9.0/src/vlm_probing/attention/rollout.py +54 -0
  42. vlmprobe-0.9.0/src/vlm_probing/attention/tam.py +58 -0
  43. vlmprobe-0.9.0/src/vlm_probing/attention/temperature.py +46 -0
  44. vlmprobe-0.9.0/src/vlm_probing/causal/__init__.py +15 -0
  45. vlmprobe-0.9.0/src/vlm_probing/causal/activation_patching.py +29 -0
  46. vlmprobe-0.9.0/src/vlm_probing/causal/attention_knockout.py +47 -0
  47. vlmprobe-0.9.0/src/vlm_probing/causal/attribution_patching.py +47 -0
  48. vlmprobe-0.9.0/src/vlm_probing/causal/base.py +94 -0
  49. vlmprobe-0.9.0/src/vlm_probing/causal/eap_ig.py +81 -0
  50. vlmprobe-0.9.0/src/vlm_probing/causal/path_patching.py +185 -0
  51. vlmprobe-0.9.0/src/vlm_probing/causal/steering.py +63 -0
  52. vlmprobe-0.9.0/src/vlm_probing/causal/transformer_eap_ig.py +397 -0
  53. vlmprobe-0.9.0/src/vlm_probing/causal/visual_steering.py +99 -0
  54. vlmprobe-0.9.0/src/vlm_probing/core/__init__.py +4 -0
  55. vlmprobe-0.9.0/src/vlm_probing/core/base.py +16 -0
  56. vlmprobe-0.9.0/src/vlm_probing/core/types.py +41 -0
  57. vlmprobe-0.9.0/src/vlm_probing/lenses/__init__.py +10 -0
  58. vlmprobe-0.9.0/src/vlm_probing/lenses/attention.py +116 -0
  59. vlmprobe-0.9.0/src/vlm_probing/lenses/base.py +144 -0
  60. vlmprobe-0.9.0/src/vlm_probing/lenses/embed.py +74 -0
  61. vlmprobe-0.9.0/src/vlm_probing/lenses/jacobian.py +127 -0
  62. vlmprobe-0.9.0/src/vlm_probing/lenses/logit.py +28 -0
  63. vlmprobe-0.9.0/src/vlm_probing/lenses/patchscope.py +65 -0
  64. vlmprobe-0.9.0/src/vlm_probing/lenses/tuned.py +76 -0
  65. vlmprobe-0.9.0/src/vlm_probing/metrics.py +91 -0
  66. vlmprobe-0.9.0/src/vlm_probing/prober.py +129 -0
  67. vlmprobe-0.9.0/src/vlm_probing/training/__init__.py +8 -0
  68. vlmprobe-0.9.0/src/vlm_probing/training/cache.py +49 -0
  69. vlmprobe-0.9.0/src/vlm_probing/training/distribution.py +187 -0
  70. vlmprobe-0.9.0/src/vlm_probing/training/jacobian.py +139 -0
  71. vlmprobe-0.9.0/src/vlm_probing/training/model_bound.py +83 -0
  72. vlmprobe-0.9.0/src/vlm_probing/training/tuned_import.py +98 -0
  73. vlmprobe-0.9.0/src/vlm_probing/visualization/__init__.py +22 -0
  74. vlmprobe-0.9.0/src/vlm_probing/visualization/_common.py +112 -0
  75. vlmprobe-0.9.0/src/vlm_probing/visualization/attention.py +238 -0
  76. vlmprobe-0.9.0/src/vlm_probing/visualization/causal.py +222 -0
  77. vlmprobe-0.9.0/src/vlm_probing/visualization/lenses.py +84 -0
  78. vlmprobe-0.9.0/src/vlm_probing/visualization/path_graphs.py +254 -0
  79. vlmprobe-0.9.0/src/vlmprobe/__init__.py +38 -0
  80. vlmprobe-0.9.0/src/vlmprobe.egg-info/PKG-INFO +257 -0
  81. vlmprobe-0.9.0/src/vlmprobe.egg-info/SOURCES.txt +82 -0
  82. vlmprobe-0.9.0/src/vlmprobe.egg-info/dependency_links.txt +1 -0
  83. vlmprobe-0.9.0/src/vlmprobe.egg-info/requires.txt +21 -0
  84. vlmprobe-0.9.0/src/vlmprobe.egg-info/top_level.txt +2 -0
@@ -0,0 +1,19 @@
1
+ # Keep source distributions as small and portable as the installed library.
2
+ include README.md pyproject.toml MANIFEST.in
3
+ recursive-include src *.py
4
+ include src/vlm_probing/_vendor/chefer2021/LICENSE
5
+ include src/vlm_probing/_vendor/chefer2021/README.md
6
+ prune build
7
+ prune dist
8
+ prune docs
9
+ prune demos
10
+ prune examples
11
+ prune tests
12
+ prune paper
13
+ prune papers
14
+ prune research
15
+ prune report
16
+ prune slides
17
+ prune outputs
18
+ prune results
19
+ global-exclude __pycache__ *.py[cod]
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.4
2
+ Name: vlmprobe
3
+ Version: 0.9.0
4
+ Summary: Lens, attention, and causal probes for language, vision, and vision-language models
5
+ Author: Gexin Huang, Haisheng Fu, Ruinan Jin, Xiaoxiao Li
6
+ Project-URL: Homepage, https://gexinh.github.io/vlm-probing/
7
+ Project-URL: Documentation, https://github.com/gexinh/vlm-probing/tree/main/docs
8
+ Project-URL: Source, https://github.com/gexinh/vlm-probing
9
+ Project-URL: Issues, https://github.com/gexinh/vlm-probing/issues
10
+ Project-URL: Report, https://github.com/gexinh/vlm-probing-report
11
+ Keywords: interpretability,probing,vision-language-models,mechanistic-interpretability
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: torch>=2.2
23
+ Provides-Extra: visualization
24
+ Requires-Dist: matplotlib>=3.8; extra == "visualization"
25
+ Requires-Dist: Pillow>=10; extra == "visualization"
26
+ Requires-Dist: numpy>=1.26; extra == "visualization"
27
+ Provides-Extra: transformers
28
+ Requires-Dist: transformers<5.4,>=5.3; extra == "transformers"
29
+ Requires-Dist: torch>=2.4; extra == "transformers"
30
+ Requires-Dist: torchvision>=0.19; extra == "transformers"
31
+ Requires-Dist: accelerate>=1.0; extra == "transformers"
32
+ Requires-Dist: Pillow>=10; extra == "transformers"
33
+ Provides-Extra: notebooks
34
+ Requires-Dist: jupyterlab>=4; extra == "notebooks"
35
+ Requires-Dist: nbclient>=0.10; extra == "notebooks"
36
+ Requires-Dist: matplotlib>=3.8; extra == "notebooks"
37
+ Requires-Dist: Pillow>=10; extra == "notebooks"
38
+ Requires-Dist: numpy>=1.26; extra == "notebooks"
39
+ Requires-Dist: tokenizers>=0.22; extra == "notebooks"
40
+
41
+ <p align="center">
42
+ <img src="https://raw.githubusercontent.com/gexinh/vlm-probing/main/docs/assets/vlm-probing-icon.svg" width="112" alt="VLM Probing icon">
43
+ </p>
44
+
45
+ <h1 align="center">VLM Probing</h1>
46
+
47
+ <p align="center"><strong>Exploring Model Internals Beyond the Answer</strong></p>
48
+
49
+ <p align="center">
50
+ <a href="https://gexinh.github.io/vlm-probing/"><img src="https://img.shields.io/badge/Project%20Page-Visit%20Website-3266e5?style=for-the-badge" alt="Visit the VLM Probing project page"></a>
51
+ </p>
52
+
53
+ VLM Probing brings lens readouts, attention explanations, and causal
54
+ interventions into one model-bound interface. It supports vision-language
55
+ models, language models, and a ViT image-classification control. Bind a loaded
56
+ model once, select layers and token groups, and inspect how its internal
57
+ computation relates to a chosen answer.
58
+
59
+ - **Lens:** read vocabulary predictions from residual states, attention heads,
60
+ or projected visual embeddings.
61
+ - **Attention:** compare attention patterns and target-conditioned spatial
62
+ attributions on the same input.
63
+ - **Causal:** replace activations, isolate paths, block attention routes, or steer
64
+ residual states and measure the resulting change.
65
+
66
+ Explicit adapters handle hook locations, expanded visual-token coordinates,
67
+ and model readouts. Results retain tensors and experiment metadata; shared
68
+ visualization functions turn them into heatmaps, image overlays, curves, and
69
+ connection graphs. Capability checks distinguish an unsupported operation from
70
+ a measured zero effect.
71
+
72
+ <p align="center">
73
+ <img src="https://raw.githubusercontent.com/gexinh/vlm-probing/main/docs/assets/library-protocol.svg" width="100%" alt="Model inputs, adapter configuration, lens/attention/causal tools, and structured results">
74
+ </p>
75
+
76
+ [Project Page](https://gexinh.github.io/vlm-probing/) · [Documentation](https://github.com/gexinh/vlm-probing/blob/main/docs/README.md) · [Demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md) ·
77
+ [Visualization](https://github.com/gexinh/vlm-probing/blob/main/docs/VISUALIZATION.md) ·
78
+ [Technical report](https://github.com/gexinh/vlm-probing-report/blob/main/technical-report.pdf) ·
79
+ [Papers and datasets](https://github.com/gexinh/vlm-probing/blob/main/docs/REFERENCES.md)
80
+
81
+ ## Installation
82
+
83
+ Python 3.10+ is required. Install directly from GitHub; no manual clone or
84
+ `PYTHONPATH` setup is needed:
85
+
86
+ ```bash
87
+ python -m pip install "vlmprobe[transformers,visualization] @ git+https://github.com/gexinh/vlm-probing.git"
88
+ ```
89
+
90
+ For tensor methods and custom PyTorch adapters, install the base package:
91
+
92
+ ```bash
93
+ python -m pip install "vlmprobe @ git+https://github.com/gexinh/vlm-probing.git"
94
+ ```
95
+
96
+ The Python import name is **`vlmprobe`**. Existing `vlm_probing` imports remain
97
+ compatible, including adapter registration and saved Python objects.
98
+
99
+ ```python
100
+ import vlmprobe
101
+
102
+ probe = vlmprobe.Prober(model, processor) # use your loaded model
103
+ result = probe.lens.logit(tokens="last_prompt").run(inputs)
104
+ ```
105
+
106
+ The base package requires only PyTorch. The `transformers` extra targets
107
+ Transformers 5.3.x and includes Accelerate, Torchvision, and Pillow;
108
+ `visualization` provides the plotting dependencies. Add `notebooks` when using
109
+ Jupyter. Model weights and complete datasets are downloaded separately.
110
+
111
+ To develop the library in a checkout, use
112
+ `python -m pip install -e ".[transformers,visualization,notebooks]"`.
113
+ The [package guide](https://github.com/gexinh/vlm-probing/blob/main/docs/PACKAGING.md) covers wheels and PyPI publication.
114
+ The short PyPI command `pip install vlmprobe` becomes available after the first
115
+ PyPI release; the GitHub installation above works independently of PyPI.
116
+
117
+ ## Quick Start
118
+
119
+ This example loads **Qwen3-VL-2B-Instruct** on one CUDA GPU. Replace
120
+ `example.jpg` with your image, or use an image from [the bundled demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md).
121
+
122
+ ```python
123
+ import torch
124
+ from PIL import Image
125
+ from transformers import AutoModelForImageTextToText, AutoProcessor
126
+ from vlmprobe import Prober
127
+
128
+ model_id = "Qwen/Qwen3-VL-2B-Instruct"
129
+ model = AutoModelForImageTextToText.from_pretrained(
130
+ model_id, dtype=torch.bfloat16, device_map={"": "cuda:0"},
131
+ attn_implementation="eager",
132
+ )
133
+ processor = AutoProcessor.from_pretrained(model_id)
134
+ probe = Prober(model, processor)
135
+
136
+ messages = [{"role": "user", "content": [
137
+ {"type": "image"},
138
+ {"type": "text", "text": "What is in this image?"},
139
+ ]}]
140
+ prompt = processor.apply_chat_template(
141
+ messages, tokenize=False, add_generation_prompt=True,
142
+ )
143
+ inputs = probe.prepare(
144
+ prompt=prompt, image=Image.open("example.jpg").convert("RGB"), device="cuda:0",
145
+ )
146
+
147
+ print(probe.describe()["methods"]["lens.logit"])
148
+ result = probe.lens.logit(layers=[0, 13, 27], tokens="last_prompt").run(inputs)
149
+ token_id = result.tensors["logits"][-1, 0].argmax().item()
150
+ print(processor.tokenizer.decode([token_id]))
151
+ result.save("outputs/logit_lens")
152
+
153
+ # Read projected image tokens in the input embedding space.
154
+ visual = probe.lens.embed(tokens="visual").run(inputs, top_k=5)
155
+
156
+ # Observe the attention actually used by the language decoder.
157
+ attention = probe.attention.profile(
158
+ layers=[27], queries="last_prompt", include_attention=True,
159
+ ).run(inputs)
160
+ ```
161
+
162
+ Use a model you have already loaded, and pass its native processor dictionary
163
+ directly to `.run(...)` when convenient. Layer indices are zero-based; tokens
164
+ refer to the expanded sequence, including image positions. The
165
+ [model guides](https://github.com/gexinh/vlm-probing/blob/main/docs/models/README.md) explain each architecture's input contract.
166
+
167
+ ```python
168
+ from vlmprobe import visualization as viz
169
+
170
+ answer_id = processor.tokenizer.encode(" yellow", add_special_tokens=False)[0]
171
+ ax = viz.plot_lens_heatmap(result, target_token_id=answer_id)
172
+ ax.figure.savefig("outputs/lens.png", bbox_inches="tight")
173
+ ```
174
+
175
+ This plots one vocabulary token's rank across selected layers and positions.
176
+ A complete answer can contain several tokens; choose the intended subword
177
+ explicitly, or use complete-answer metrics for interventions.
178
+
179
+ | Common operation | Purpose |
180
+ | --- | --- |
181
+ | `Prober(model, processor=None)` | Bind a caller-owned model and resolve its adapter. |
182
+ | `probe.describe()` | Inspect available methods, sites, and missing capabilities. |
183
+ | `probe.prepare(prompt=..., image=..., device=...)` | Convert formatted text and images into native inputs. |
184
+ | `probe.lens / probe.attention / probe.causal` | Configure a method in one of three families. |
185
+ | `method.run(inputs, ...)` | Execute a probe and return `ProbeResult`. |
186
+ | `method.fit_batches(...)` / `method.load(...)` | Calibrate or restore Tuned/Attention Lens; Jacobian Lens has a separate estimator. |
187
+ | `result.save(directory)` | Save CPU tensors and JSON metadata. |
188
+
189
+ [The API guide](https://github.com/gexinh/vlm-probing/blob/main/docs/API.md) covers selectors, complete-answer metrics,
190
+ paired-input alignment, and result shapes. [Visualization](https://github.com/gexinh/vlm-probing/blob/main/docs/VISUALIZATION.md)
191
+ describes reusable plotting functions. The [demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md) include
192
+ saved real measurements and input images for offline replay; rerunning a model
193
+ is an explicit option. A [small CPU example](https://github.com/gexinh/vlm-probing/blob/main/examples/prober_quickstart.py)
194
+ exercises the API without downloading pretrained weights.
195
+
196
+ ## Model Support
197
+
198
+ Eight VLM families have explicit adapters. Text-only GPT-2/Llama and a ViT
199
+ classifier provide complementary controls. Availability is method-specific;
200
+ see the [capability matrix](https://github.com/gexinh/vlm-probing/blob/main/docs/models/README.md).
201
+
202
+ | Family | Example checkpoint | Guide |
203
+ | --- | --- | --- |
204
+ | Qwen3-VL | Qwen3-VL-2B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen3_vl.md) |
205
+ | Qwen2.5-VL | Qwen2.5-VL-3B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen2_5_vl.md) |
206
+ | Qwen2-VL | Qwen2-VL-2B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen2_vl.md) |
207
+ | Qwen3.5 | Qwen3.5-4B; hybrid attention restrictions | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen3_5.md) |
208
+ | SmolVLM | SmolVLM-500M-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/smolvlm.md) |
209
+ | InternVL | InternVL3-1B-hf | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/internvl.md) |
210
+ | LLaVA | llava-1.5-7b-hf | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/llava.md) |
211
+ | Pixtral | Pixtral-12B-2409 | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/pixtral.md) |
212
+ | Language models | GPT-2 / Llama | [GPT-2](https://github.com/gexinh/vlm-probing/blob/main/docs/models/gpt2.md) · [Llama](https://github.com/gexinh/vlm-probing/blob/main/docs/models/llama.md) |
213
+ | Vision classifier | ViT-B/16 | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/vit.md) |
214
+
215
+ ## Methods
216
+
217
+ Each guide explains the implemented computation, parameters, outputs, and
218
+ relationship to its original paper. Reusable primitives are labeled separately
219
+ from paper methods.
220
+
221
+ | Family | Method guides |
222
+ | --- | --- |
223
+ | Lens | [Logit](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/logit_lens.md) · [Tuned](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/tuned_lens.md) · [Attention Lens](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_lens.md) · [Jacobian](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/jacobian_lens.md) · [EmbedLens](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/embed_lens.md) · [Patchscopes](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/patchscope.md) |
224
+ | Attention | [Rollout](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_rollout.md) · [Grad-CAM, ATTATTR, TAM, Beyond Intuition](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_maps.md) · [Generic relevance](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_relevance.md) · [ViT DTD/LRP](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/chefer_lrp.md) |
225
+ | Causal | [Activation patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/activation_patching.md) · [IOI-style path patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/path_patching.md) · [Attribution patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attribution_patching.md) · [Attention Knockout](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_knockout.md) · [EAP / EAP-IG](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/eap_ig.md) · [Residual steering / VSV](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/steering.md) |
226
+ | Supporting tools | [Attention profile](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_profile.md) · [Head logit attribution](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/head_logit_attribution.md) · [Probability reweighting](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_reweight.md) · [Temperature](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_temperature.md) |
227
+
228
+ ## Package Architecture
229
+
230
+ ```text
231
+ src/vlmprobe/ # public import name; shares the implementation below
232
+ src/vlm_probing/
233
+ prober.py # shared model-bound entry point
234
+ api/ # method factories, capture and replay
235
+ adapters/ # explicit architecture and token-layout contracts
236
+ lenses/ # BaseLens and concrete readout methods
237
+ attention/ # BaseAttention and concrete attention methods
238
+ causal/ # BaseCausal and concrete interventions/estimators
239
+ training/ # calibration, checkpoint import and resume
240
+ visualization/ # shared heatmaps, overlays, curves and graphs
241
+ core/ # results and execution traces
242
+ demos/ # runnable notebooks, compact measurements and input images
243
+ docs/ # model, method, API and source guides
244
+ ```
245
+
246
+ Family base classes keep algorithm implementations small; adapters let them
247
+ share model execution. [Architecture](https://github.com/gexinh/vlm-probing/blob/main/docs/ARCHITECTURE.md) and
248
+ [adapter contracts](https://github.com/gexinh/vlm-probing/blob/main/docs/ADAPTERS.md) describe these boundaries.
249
+
250
+ ## References
251
+
252
+ The [paper and dataset index](https://github.com/gexinh/vlm-probing/blob/main/docs/REFERENCES.md) links the original method
253
+ papers, author implementations, and datasets used for demonstrations and lens
254
+ calibration. The
255
+ [technical report](https://github.com/gexinh/vlm-probing-report/blob/main/technical-report.pdf)
256
+ compares real readouts, explanations, and interventions on shared cases across
257
+ five pretrained VLMs, with language and vision controls.
@@ -0,0 +1,217 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/gexinh/vlm-probing/main/docs/assets/vlm-probing-icon.svg" width="112" alt="VLM Probing icon">
3
+ </p>
4
+
5
+ <h1 align="center">VLM Probing</h1>
6
+
7
+ <p align="center"><strong>Exploring Model Internals Beyond the Answer</strong></p>
8
+
9
+ <p align="center">
10
+ <a href="https://gexinh.github.io/vlm-probing/"><img src="https://img.shields.io/badge/Project%20Page-Visit%20Website-3266e5?style=for-the-badge" alt="Visit the VLM Probing project page"></a>
11
+ </p>
12
+
13
+ VLM Probing brings lens readouts, attention explanations, and causal
14
+ interventions into one model-bound interface. It supports vision-language
15
+ models, language models, and a ViT image-classification control. Bind a loaded
16
+ model once, select layers and token groups, and inspect how its internal
17
+ computation relates to a chosen answer.
18
+
19
+ - **Lens:** read vocabulary predictions from residual states, attention heads,
20
+ or projected visual embeddings.
21
+ - **Attention:** compare attention patterns and target-conditioned spatial
22
+ attributions on the same input.
23
+ - **Causal:** replace activations, isolate paths, block attention routes, or steer
24
+ residual states and measure the resulting change.
25
+
26
+ Explicit adapters handle hook locations, expanded visual-token coordinates,
27
+ and model readouts. Results retain tensors and experiment metadata; shared
28
+ visualization functions turn them into heatmaps, image overlays, curves, and
29
+ connection graphs. Capability checks distinguish an unsupported operation from
30
+ a measured zero effect.
31
+
32
+ <p align="center">
33
+ <img src="https://raw.githubusercontent.com/gexinh/vlm-probing/main/docs/assets/library-protocol.svg" width="100%" alt="Model inputs, adapter configuration, lens/attention/causal tools, and structured results">
34
+ </p>
35
+
36
+ [Project Page](https://gexinh.github.io/vlm-probing/) · [Documentation](https://github.com/gexinh/vlm-probing/blob/main/docs/README.md) · [Demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md) ·
37
+ [Visualization](https://github.com/gexinh/vlm-probing/blob/main/docs/VISUALIZATION.md) ·
38
+ [Technical report](https://github.com/gexinh/vlm-probing-report/blob/main/technical-report.pdf) ·
39
+ [Papers and datasets](https://github.com/gexinh/vlm-probing/blob/main/docs/REFERENCES.md)
40
+
41
+ ## Installation
42
+
43
+ Python 3.10+ is required. Install directly from GitHub; no manual clone or
44
+ `PYTHONPATH` setup is needed:
45
+
46
+ ```bash
47
+ python -m pip install "vlmprobe[transformers,visualization] @ git+https://github.com/gexinh/vlm-probing.git"
48
+ ```
49
+
50
+ For tensor methods and custom PyTorch adapters, install the base package:
51
+
52
+ ```bash
53
+ python -m pip install "vlmprobe @ git+https://github.com/gexinh/vlm-probing.git"
54
+ ```
55
+
56
+ The Python import name is **`vlmprobe`**. Existing `vlm_probing` imports remain
57
+ compatible, including adapter registration and saved Python objects.
58
+
59
+ ```python
60
+ import vlmprobe
61
+
62
+ probe = vlmprobe.Prober(model, processor) # use your loaded model
63
+ result = probe.lens.logit(tokens="last_prompt").run(inputs)
64
+ ```
65
+
66
+ The base package requires only PyTorch. The `transformers` extra targets
67
+ Transformers 5.3.x and includes Accelerate, Torchvision, and Pillow;
68
+ `visualization` provides the plotting dependencies. Add `notebooks` when using
69
+ Jupyter. Model weights and complete datasets are downloaded separately.
70
+
71
+ To develop the library in a checkout, use
72
+ `python -m pip install -e ".[transformers,visualization,notebooks]"`.
73
+ The [package guide](https://github.com/gexinh/vlm-probing/blob/main/docs/PACKAGING.md) covers wheels and PyPI publication.
74
+ The short PyPI command `pip install vlmprobe` becomes available after the first
75
+ PyPI release; the GitHub installation above works independently of PyPI.
76
+
77
+ ## Quick Start
78
+
79
+ This example loads **Qwen3-VL-2B-Instruct** on one CUDA GPU. Replace
80
+ `example.jpg` with your image, or use an image from [the bundled demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md).
81
+
82
+ ```python
83
+ import torch
84
+ from PIL import Image
85
+ from transformers import AutoModelForImageTextToText, AutoProcessor
86
+ from vlmprobe import Prober
87
+
88
+ model_id = "Qwen/Qwen3-VL-2B-Instruct"
89
+ model = AutoModelForImageTextToText.from_pretrained(
90
+ model_id, dtype=torch.bfloat16, device_map={"": "cuda:0"},
91
+ attn_implementation="eager",
92
+ )
93
+ processor = AutoProcessor.from_pretrained(model_id)
94
+ probe = Prober(model, processor)
95
+
96
+ messages = [{"role": "user", "content": [
97
+ {"type": "image"},
98
+ {"type": "text", "text": "What is in this image?"},
99
+ ]}]
100
+ prompt = processor.apply_chat_template(
101
+ messages, tokenize=False, add_generation_prompt=True,
102
+ )
103
+ inputs = probe.prepare(
104
+ prompt=prompt, image=Image.open("example.jpg").convert("RGB"), device="cuda:0",
105
+ )
106
+
107
+ print(probe.describe()["methods"]["lens.logit"])
108
+ result = probe.lens.logit(layers=[0, 13, 27], tokens="last_prompt").run(inputs)
109
+ token_id = result.tensors["logits"][-1, 0].argmax().item()
110
+ print(processor.tokenizer.decode([token_id]))
111
+ result.save("outputs/logit_lens")
112
+
113
+ # Read projected image tokens in the input embedding space.
114
+ visual = probe.lens.embed(tokens="visual").run(inputs, top_k=5)
115
+
116
+ # Observe the attention actually used by the language decoder.
117
+ attention = probe.attention.profile(
118
+ layers=[27], queries="last_prompt", include_attention=True,
119
+ ).run(inputs)
120
+ ```
121
+
122
+ Use a model you have already loaded, and pass its native processor dictionary
123
+ directly to `.run(...)` when convenient. Layer indices are zero-based; tokens
124
+ refer to the expanded sequence, including image positions. The
125
+ [model guides](https://github.com/gexinh/vlm-probing/blob/main/docs/models/README.md) explain each architecture's input contract.
126
+
127
+ ```python
128
+ from vlmprobe import visualization as viz
129
+
130
+ answer_id = processor.tokenizer.encode(" yellow", add_special_tokens=False)[0]
131
+ ax = viz.plot_lens_heatmap(result, target_token_id=answer_id)
132
+ ax.figure.savefig("outputs/lens.png", bbox_inches="tight")
133
+ ```
134
+
135
+ This plots one vocabulary token's rank across selected layers and positions.
136
+ A complete answer can contain several tokens; choose the intended subword
137
+ explicitly, or use complete-answer metrics for interventions.
138
+
139
+ | Common operation | Purpose |
140
+ | --- | --- |
141
+ | `Prober(model, processor=None)` | Bind a caller-owned model and resolve its adapter. |
142
+ | `probe.describe()` | Inspect available methods, sites, and missing capabilities. |
143
+ | `probe.prepare(prompt=..., image=..., device=...)` | Convert formatted text and images into native inputs. |
144
+ | `probe.lens / probe.attention / probe.causal` | Configure a method in one of three families. |
145
+ | `method.run(inputs, ...)` | Execute a probe and return `ProbeResult`. |
146
+ | `method.fit_batches(...)` / `method.load(...)` | Calibrate or restore Tuned/Attention Lens; Jacobian Lens has a separate estimator. |
147
+ | `result.save(directory)` | Save CPU tensors and JSON metadata. |
148
+
149
+ [The API guide](https://github.com/gexinh/vlm-probing/blob/main/docs/API.md) covers selectors, complete-answer metrics,
150
+ paired-input alignment, and result shapes. [Visualization](https://github.com/gexinh/vlm-probing/blob/main/docs/VISUALIZATION.md)
151
+ describes reusable plotting functions. The [demos](https://github.com/gexinh/vlm-probing/blob/main/demos/README.md) include
152
+ saved real measurements and input images for offline replay; rerunning a model
153
+ is an explicit option. A [small CPU example](https://github.com/gexinh/vlm-probing/blob/main/examples/prober_quickstart.py)
154
+ exercises the API without downloading pretrained weights.
155
+
156
+ ## Model Support
157
+
158
+ Eight VLM families have explicit adapters. Text-only GPT-2/Llama and a ViT
159
+ classifier provide complementary controls. Availability is method-specific;
160
+ see the [capability matrix](https://github.com/gexinh/vlm-probing/blob/main/docs/models/README.md).
161
+
162
+ | Family | Example checkpoint | Guide |
163
+ | --- | --- | --- |
164
+ | Qwen3-VL | Qwen3-VL-2B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen3_vl.md) |
165
+ | Qwen2.5-VL | Qwen2.5-VL-3B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen2_5_vl.md) |
166
+ | Qwen2-VL | Qwen2-VL-2B-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen2_vl.md) |
167
+ | Qwen3.5 | Qwen3.5-4B; hybrid attention restrictions | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/qwen3_5.md) |
168
+ | SmolVLM | SmolVLM-500M-Instruct | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/smolvlm.md) |
169
+ | InternVL | InternVL3-1B-hf | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/internvl.md) |
170
+ | LLaVA | llava-1.5-7b-hf | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/llava.md) |
171
+ | Pixtral | Pixtral-12B-2409 | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/pixtral.md) |
172
+ | Language models | GPT-2 / Llama | [GPT-2](https://github.com/gexinh/vlm-probing/blob/main/docs/models/gpt2.md) · [Llama](https://github.com/gexinh/vlm-probing/blob/main/docs/models/llama.md) |
173
+ | Vision classifier | ViT-B/16 | [Usage](https://github.com/gexinh/vlm-probing/blob/main/docs/models/vit.md) |
174
+
175
+ ## Methods
176
+
177
+ Each guide explains the implemented computation, parameters, outputs, and
178
+ relationship to its original paper. Reusable primitives are labeled separately
179
+ from paper methods.
180
+
181
+ | Family | Method guides |
182
+ | --- | --- |
183
+ | Lens | [Logit](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/logit_lens.md) · [Tuned](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/tuned_lens.md) · [Attention Lens](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_lens.md) · [Jacobian](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/jacobian_lens.md) · [EmbedLens](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/embed_lens.md) · [Patchscopes](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/patchscope.md) |
184
+ | Attention | [Rollout](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_rollout.md) · [Grad-CAM, ATTATTR, TAM, Beyond Intuition](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_maps.md) · [Generic relevance](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_relevance.md) · [ViT DTD/LRP](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/chefer_lrp.md) |
185
+ | Causal | [Activation patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/activation_patching.md) · [IOI-style path patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/path_patching.md) · [Attribution patching](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attribution_patching.md) · [Attention Knockout](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_knockout.md) · [EAP / EAP-IG](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/eap_ig.md) · [Residual steering / VSV](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/steering.md) |
186
+ | Supporting tools | [Attention profile](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_profile.md) · [Head logit attribution](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/head_logit_attribution.md) · [Probability reweighting](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_reweight.md) · [Temperature](https://github.com/gexinh/vlm-probing/blob/main/docs/methods/attention_temperature.md) |
187
+
188
+ ## Package Architecture
189
+
190
+ ```text
191
+ src/vlmprobe/ # public import name; shares the implementation below
192
+ src/vlm_probing/
193
+ prober.py # shared model-bound entry point
194
+ api/ # method factories, capture and replay
195
+ adapters/ # explicit architecture and token-layout contracts
196
+ lenses/ # BaseLens and concrete readout methods
197
+ attention/ # BaseAttention and concrete attention methods
198
+ causal/ # BaseCausal and concrete interventions/estimators
199
+ training/ # calibration, checkpoint import and resume
200
+ visualization/ # shared heatmaps, overlays, curves and graphs
201
+ core/ # results and execution traces
202
+ demos/ # runnable notebooks, compact measurements and input images
203
+ docs/ # model, method, API and source guides
204
+ ```
205
+
206
+ Family base classes keep algorithm implementations small; adapters let them
207
+ share model execution. [Architecture](https://github.com/gexinh/vlm-probing/blob/main/docs/ARCHITECTURE.md) and
208
+ [adapter contracts](https://github.com/gexinh/vlm-probing/blob/main/docs/ADAPTERS.md) describe these boundaries.
209
+
210
+ ## References
211
+
212
+ The [paper and dataset index](https://github.com/gexinh/vlm-probing/blob/main/docs/REFERENCES.md) links the original method
213
+ papers, author implementations, and datasets used for demonstrations and lens
214
+ calibration. The
215
+ [technical report](https://github.com/gexinh/vlm-probing-report/blob/main/technical-report.pdf)
216
+ compares real readouts, explanations, and interventions on shared cases across
217
+ five pretrained VLMs, with language and vision controls.
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vlmprobe"
7
+ dynamic = ["version"]
8
+ description = "Lens, attention, and causal probes for language, vision, and vision-language models"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = ["torch>=2.2"]
12
+ authors = [
13
+ {name = "Gexin Huang"},
14
+ {name = "Haisheng Fu"},
15
+ {name = "Ruinan Jin"},
16
+ {name = "Xiaoxiao Li"},
17
+ ]
18
+ keywords = ["interpretability", "probing", "vision-language-models", "mechanistic-interpretability"]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Science/Research",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://gexinh.github.io/vlm-probing/"
32
+ Documentation = "https://github.com/gexinh/vlm-probing/tree/main/docs"
33
+ Source = "https://github.com/gexinh/vlm-probing"
34
+ Issues = "https://github.com/gexinh/vlm-probing/issues"
35
+ Report = "https://github.com/gexinh/vlm-probing-report"
36
+
37
+ [project.optional-dependencies]
38
+ visualization = ["matplotlib>=3.8", "Pillow>=10", "numpy>=1.26"]
39
+ transformers = ["transformers>=5.3,<5.4", "torch>=2.4", "torchvision>=0.19", "accelerate>=1.0", "Pillow>=10"]
40
+ notebooks = ["jupyterlab>=4", "nbclient>=0.10", "matplotlib>=3.8", "Pillow>=10", "numpy>=1.26", "tokenizers>=0.22"]
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+ include = ["vlmprobe*", "vlm_probing*"]
45
+ namespaces = false
46
+
47
+ [tool.setuptools.dynamic]
48
+ version = {attr = "vlm_probing.__version__"}
49
+
50
+ [tool.setuptools.package-data]
51
+ "vlm_probing._vendor.chefer2021" = ["LICENSE", "README.md"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,17 @@
1
+ """Three method families: causal interventions, attention analysis, and lenses."""
2
+ from .core import BaseMethod, ForwardTrace, ProbeResult
3
+ from .adapters import BaseModelAdapter, HookPoint, ModelReadout, TorchModelAdapter
4
+ from .adapters import CapabilityError, HeadSite, ModelSpec, ProbeInputs, TokenLayout, register_adapter
5
+ from .prober import Prober
6
+ from .api.lenses import LensMethods
7
+ from .api.attention import AttentionMethods
8
+ from .api.causal import CausalMethods
9
+ from .metrics import TokenMargin, SequenceLogProb, ClassScore
10
+ from . import visualization
11
+
12
+ __version__ = "0.9.0"
13
+ __all__ = ["BaseMethod", "ProbeResult", "ForwardTrace", "BaseModelAdapter",
14
+ "HookPoint", "ModelReadout", "TorchModelAdapter", "Prober", "ModelSpec",
15
+ "ProbeInputs", "TokenLayout", "HeadSite", "CapabilityError", "register_adapter",
16
+ "LensMethods", "AttentionMethods", "CausalMethods", "TokenMargin", "SequenceLogProb", "ClassScore",
17
+ "visualization"]
@@ -0,0 +1 @@
1
+ """Pinned third-party research kernels, imported only by explicit backends."""
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Hila Chefer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ # Pinned Chefer CVPR 2021 ViT relevance implementation
2
+
3
+ Source: [hila-chefer/Transformer-Explainability](https://github.com/hila-chefer/Transformer-Explainability),
4
+ commit [`c3e578f76b954e8528afeaaee26de3f07e3fe559`](https://github.com/hila-chefer/Transformer-Explainability/tree/c3e578f76b954e8528afeaaee26de3f07e3fe559).
5
+ The source is MIT licensed; the complete license is retained in [LICENSE](LICENSE).
6
+
7
+ | Local file | Upstream file | Changes |
8
+ | --- | --- | --- |
9
+ | `layers.py` | `modules/layers_ours.py` | Byte-identical. |
10
+ | `vit.py` | `baselines/ViT/ViT_LRP.py` | Relative imports; equivalent native PyTorch reshapes replace `einops.rearrange`; remove model URL tables, pretrained factories and conversion helpers. All forward and relevance rules remain intact. |
11
+ | `weight_init.py` | `baselines/ViT/weight_init.py` | Byte-identical. |
12
+ | `layer_helpers.py` | `baselines/ViT/layer_helpers.py` | Byte-identical. |
13
+
14
+ The public wrapper does not implicitly download weights. It can copy a native
15
+ Hugging Face ViT classifier into this architecture with strict state-dict loading.
16
+ The wrapper evaluates one image per relevance propagation, replaces hard-coded
17
+ CUDA class seeds with device-aware tensors, leaves parameter gradient buffers
18
+ unchanged, and restores the caller's training flags.
19
+
20
+ The separate upstream `ViT_orig_LRP.py` / `layers_lrp.py` implementation is not
21
+ included. Do not describe the optional `full` or `last_layer` modes of this
22
+ Chefer-rule model as that separate conventional LRP baseline.
@@ -0,0 +1 @@
1
+ """Chefer et al. CVPR 2021 source; see README.md and LICENSE."""
@@ -0,0 +1,21 @@
1
+ """ Layer/Module Helpers
2
+ Hacked together by / Copyright 2020 Ross Wightman
3
+ """
4
+ from itertools import repeat
5
+ import collections.abc
6
+
7
+
8
+ # From PyTorch internals
9
+ def _ntuple(n):
10
+ def parse(x):
11
+ if isinstance(x, collections.abc.Iterable):
12
+ return x
13
+ return tuple(repeat(x, n))
14
+ return parse
15
+
16
+
17
+ to_1tuple = _ntuple(1)
18
+ to_2tuple = _ntuple(2)
19
+ to_3tuple = _ntuple(3)
20
+ to_4tuple = _ntuple(4)
21
+ to_ntuple = _ntuple