DebasishDhal99 commited on
Commit
4ed3b70
·
1 Parent(s): c9744fe

feat: add explanation on `hidden_size = num_attention_heads * dim_per_head`

Browse files
Files changed (3) hide show
  1. README.md +2 -1
  2. app.py +31 -12
  3. src/extract.py +19 -6
README.md CHANGED
@@ -27,7 +27,8 @@ learning notes only and are **not** imported by the app.
27
  1. **Random matrix** — sample even-width Q (and K) tensors, apply numpy RoPE,
28
  inspect heatmaps, pairwise 2D rotation, `QK^T`, and additive sinusoidal PE.
29
  2. **Real model** — lazy-load an ungated Llama-like checkpoint (default
30
- `HuggingFaceTB/SmolLM2-135M`), take `embed_tokens`, first-layer `q_proj` /
 
31
  `k_proj` (GQA-aware), and compare educational numpy RoPE (`llama` pairing)
32
  to the model's `rotary_emb`. No Hugging Face token is required. Gated models
33
  are not used.
 
27
  1. **Random matrix** — sample even-width Q (and K) tensors, apply numpy RoPE,
28
  inspect heatmaps, pairwise 2D rotation, `QK^T`, and additive sinusoidal PE.
29
  2. **Real model** — lazy-load an ungated Llama-like checkpoint (default
30
+ `HuggingFaceTB/SmolLM2-135M`; `HuggingFaceM4/tiny-random-LlamaForCausalLM`
31
+ is included for a very small test model), take `embed_tokens`, first-layer `q_proj` /
32
  `k_proj` (GQA-aware), and compare educational numpy RoPE (`llama` pairing)
33
  to the model's `rotary_emb`. No Hugging Face token is required. Gated models
34
  are not used.
app.py CHANGED
@@ -14,7 +14,7 @@ from src.extract import (
14
  MODEL_CHOICES,
15
  expand_kv_heads,
16
  extract_from_model,
17
- get_model_head_dim,
18
  random_qk,
19
  select_head,
20
  )
@@ -93,18 +93,28 @@ def _safe_slider_max(n: int) -> int:
93
 
94
  def update_dimension(source: str, model_name: str):
95
  if source.startswith("Random"):
96
- return gr.update(minimum=4, maximum=128, value=32, step=2, interactive=True)
 
 
 
 
 
97
  try:
98
- model_dim = get_model_head_dim(model_name)
99
- return gr.update(
100
- minimum=4,
101
- maximum=max(128, model_dim),
102
- value=model_dim,
103
- step=2,
104
- interactive=False,
 
 
 
 
 
105
  )
106
  except Exception:
107
- return gr.update()
108
 
109
 
110
  def compute(
@@ -306,6 +316,15 @@ with gr.Blocks(title="RoPE Explorer") as demo:
306
  seq_len = gr.Slider(2, MAX_SEQ_LEN, value=16, step=1, label="Sequence length")
307
  dim = gr.Slider(4, 128, value=32, step=2, label="Dimension (even; per attention head)")
308
  seed = gr.Number(value=42, label="Seed", precision=0)
 
 
 
 
 
 
 
 
 
309
  base = gr.Number(
310
  value=10000,
311
  label="RoPE base (overridden by config.rope_theta for real models)",
@@ -355,12 +374,12 @@ with gr.Blocks(title="RoPE Explorer") as demo:
355
  source.change(
356
  update_dimension,
357
  inputs=[source, model_name],
358
- outputs=[dim],
359
  )
360
  model_name.change(
361
  update_dimension,
362
  inputs=[source, model_name],
363
- outputs=[dim],
364
  )
365
 
366
  bulk_inputs = [state, which, head, mod_2pi]
 
14
  MODEL_CHOICES,
15
  expand_kv_heads,
16
  extract_from_model,
17
+ get_model_dimensions,
18
  random_qk,
19
  select_head,
20
  )
 
93
 
94
  def update_dimension(source: str, model_name: str):
95
  if source.startswith("Random"):
96
+ return (
97
+ gr.update(minimum=4, maximum=128, value=32, step=2, interactive=True),
98
+ gr.update(value=32),
99
+ gr.update(value=1),
100
+ gr.update(value=32),
101
+ )
102
  try:
103
+ total_dim, n_heads, head_dim = get_model_dimensions(model_name)
104
+ return (
105
+ gr.update(
106
+ minimum=4,
107
+ maximum=max(128, head_dim),
108
+ value=head_dim,
109
+ step=2,
110
+ interactive=False,
111
+ ),
112
+ gr.update(value=total_dim),
113
+ gr.update(value=n_heads),
114
+ gr.update(value=head_dim),
115
  )
116
  except Exception:
117
+ return gr.update(), gr.update(), gr.update(), gr.update()
118
 
119
 
120
  def compute(
 
316
  seq_len = gr.Slider(2, MAX_SEQ_LEN, value=16, step=1, label="Sequence length")
317
  dim = gr.Slider(4, 128, value=32, step=2, label="Dimension (even; per attention head)")
318
  seed = gr.Number(value=42, label="Seed", precision=0)
319
+ with gr.Row():
320
+ total_dim = gr.Number(value=32, label="Total dimension", precision=0, interactive=False)
321
+ attention_heads = gr.Number(value=1, label="Attention heads", precision=0, interactive=False)
322
+ head_dim = gr.Number(value=32, label="Dimension per attention head", precision=0, interactive=False)
323
+ gr.Markdown(
324
+ "**Why these numbers differ:** `total dimension = attention heads × dimension per head`. "
325
+ "RoPE rotates each query/key head separately, so its Dimension slider uses "
326
+ "the per-head value, not the model's total dimension."
327
+ )
328
  base = gr.Number(
329
  value=10000,
330
  label="RoPE base (overridden by config.rope_theta for real models)",
 
374
  source.change(
375
  update_dimension,
376
  inputs=[source, model_name],
377
+ outputs=[dim, total_dim, attention_heads, head_dim],
378
  )
379
  model_name.change(
380
  update_dimension,
381
  inputs=[source, model_name],
382
+ outputs=[dim, total_dim, attention_heads, head_dim],
383
  )
384
 
385
  bulk_inputs = [state, which, head, mod_2pi]
src/extract.py CHANGED
@@ -108,20 +108,33 @@ def _require_hf():
108
  return torch, AutoModelForCausalLM, AutoTokenizer
109
 
110
 
111
- def get_model_head_dim(model_name: str) -> int:
112
- """Return the Q/K dimension per attention head without loading model weights."""
113
  if model_name not in _config_cache:
114
  try:
115
  from transformers import AutoConfig
116
  except ImportError as exc:
117
  raise RuntimeError("Real-model mode needs `transformers`.") from exc
118
  _config_cache[model_name] = AutoConfig.from_pretrained(model_name)
 
119
 
120
- config = _config_cache[model_name]
 
 
 
 
 
121
  configured_head_dim = getattr(config, "head_dim", None)
122
- if configured_head_dim is not None:
123
- return int(configured_head_dim)
124
- return int(config.hidden_size) // int(config.num_attention_heads)
 
 
 
 
 
 
 
 
125
 
126
 
127
  def get_model(model_name: str):
 
108
  return torch, AutoModelForCausalLM, AutoTokenizer
109
 
110
 
111
+ def _get_model_config(model_name: str):
 
112
  if model_name not in _config_cache:
113
  try:
114
  from transformers import AutoConfig
115
  except ImportError as exc:
116
  raise RuntimeError("Real-model mode needs `transformers`.") from exc
117
  _config_cache[model_name] = AutoConfig.from_pretrained(model_name)
118
+ return _config_cache[model_name]
119
 
120
+
121
+ def get_model_dimensions(model_name: str) -> tuple[int, int, int]:
122
+ """Return total hidden size, attention heads, and per-head Q/K size."""
123
+ config = _get_model_config(model_name)
124
+ total_dim = int(config.hidden_size)
125
+ n_heads = int(config.num_attention_heads)
126
  configured_head_dim = getattr(config, "head_dim", None)
127
+ head_dim = (
128
+ int(configured_head_dim)
129
+ if configured_head_dim is not None
130
+ else total_dim // n_heads
131
+ )
132
+ return total_dim, n_heads, head_dim
133
+
134
+
135
+ def get_model_head_dim(model_name: str) -> int:
136
+ """Return the Q/K dimension per attention head without loading model weights."""
137
+ return get_model_dimensions(model_name)[2]
138
 
139
 
140
  def get_model(model_name: str):