AQIT
0.1.0
Toggle main menu visibility
Loading...
Searching...
No Matches
weight_rank.py
Go to the documentation of this file.
1
# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2
# This file is part of the Aquin Engine. Unauthorized copying, modification,
3
# distribution, or use of this file, via any medium, is strictly prohibited.
4
# Proprietary and confidential. See LICENSE for terms.
5
6
"""Stable rank analysis for transformer weight matrices."""
7
from
__future__
import
annotations
8
9
import
re
10
11
import
torch
12
13
14
_LAYER_RE = re.compile(
r"(?:^|\.)(?:layers?|layer|block)\.?(\d+)(?:\.|$)"
, re.I)
15
_EXPERT_WEIGHT_RE = re.compile(
r"\.experts?\.\d+"
, re.I)
16
17
18
def
_is_heavy_weight_model
(model) -> bool:
19
"""Large / MoE models fill VRAM — weight analysis must run on CPU and skip experts."""
20
cfg = getattr(model,
"cfg"
,
None
)
21
d_model = int(getattr(cfg,
"d_model"
, 0)
or
0)
22
if
d_model >= 4096:
23
return
True
24
n_layers = int(getattr(cfg,
"n_layers"
, 0)
or
0)
25
if
d_model >= 2048
and
n_layers >= 24:
26
return
True
27
# Avoid iterating every MoE expert parameter — that can hang or exhaust host RAM.
28
return
False
29
30
31
def
_should_skip_weight_param
(name: str, *, heavy: bool) -> bool:
32
"""Skip tensors that are redundant or too large for in-GPU analysis."""
33
if
not
heavy:
34
return
False
35
kl = name.lower()
36
if
_EXPERT_WEIGHT_RE.search(name):
37
return
True
38
if
"word_embeddings"
in
kl
or
kl.endswith(
"lm_head.weight"
):
39
return
True
40
if
"router"
in
kl
or
"gate"
in
kl
and
"mlp"
in
kl:
41
return
False
# keep MoE router/gate — small and informative
42
return
False
43
44
45
def
_layer_from_key
(key: str) -> int:
46
m = _LAYER_RE.search(key.replace(
"_"
,
"."
))
47
return
int(m.group(1))
if
m
else
-1
48
49
50
def
_hf_matrix_label
(key: str) -> str:
51
kl = key.lower()
52
if
"self_attn"
in
kl
or
".attn."
in
kl:
53
for
tag, label
in
(
54
(
"q_proj"
,
"Q"
),
55
(
"k_proj"
,
"K"
),
56
(
"v_proj"
,
"V"
),
57
(
"o_proj"
,
"O"
),
58
(
"out_proj"
,
"O"
),
59
):
60
if
tag
in
kl:
61
return
label
62
if
"mlp"
in
kl
or
"feed_forward"
in
kl:
63
if
"gate"
in
kl:
64
return
"MLP_gate"
65
if
"down"
in
kl:
66
return
"MLP_down"
67
if
"up"
in
kl:
68
return
"MLP_up"
69
return
"MLP"
70
if
"conv"
in
kl
or
"shortconv"
in
kl:
71
if
"in_proj"
in
kl:
72
return
"Conv_in"
73
if
"out_proj"
in
kl:
74
return
"Conv_out"
75
if
re.search(
r"\.conv\.|conv\d*\.weight$"
, kl):
76
return
"Conv_dw"
77
for
tag, label
in
(
78
(
"q_proj"
,
"Q"
),
79
(
"k_proj"
,
"K"
),
80
(
"v_proj"
,
"V"
),
81
(
"o_proj"
,
"O"
),
82
(
"out_proj"
,
"O"
),
83
(
"gate_proj"
,
"MLP_gate"
),
84
(
"up_proj"
,
"MLP_up"
),
85
(
"down_proj"
,
"MLP_down"
),
86
(
"query_key_value"
,
"QKV"
),
87
(
"c_attn"
,
"Attn"
),
88
(
"c_fc"
,
"MLP_up"
),
89
(
"c_proj"
,
"MLP_down"
),
90
):
91
if
tag
in
kl:
92
return
label
93
parts = key.split(
"."
)
94
return
parts[-2]
if
len(parts) >= 2
else
parts[-1]
95
96
97
def
_weight_root
(model) -> object:
98
if
hasattr(model,
"hf_model"
):
99
return
model.hf_model
100
return
model
101
102
103
def
_is_hf_weight_model
(model) -> bool:
104
if
hasattr(model,
"hf_model"
):
105
return
True
106
if
not
hasattr(model,
"blocks"
):
107
return
True
108
try
:
109
block = model.blocks[0]
110
return
not
(hasattr(block,
"attn"
)
and
hasattr(block.attn,
"W_Q"
))
111
except
(IndexError, TypeError, AttributeError):
112
return
True
113
114
115
def
_scalar
(t: torch.Tensor) -> float:
116
return
t.detach().float().reshape(-1)[0].item()
117
118
119
def
_to_2d
(w: torch.Tensor) -> torch.Tensor:
120
"""HookedTransformer attn weights are often 3D (n_heads, d_in, d_out)."""
121
f = w.detach().float()
122
if
f.ndim == 1:
123
return
f.unsqueeze(0)
124
if
f.ndim == 2:
125
return
f
126
return
f.reshape(-1, f.shape[-1])
127
128
129
def
_matrix_ranks
(w: torch.Tensor) -> tuple[float, float]:
130
# SVD on CPU — model may already occupy nearly all VRAM (e.g. Sarvam 30B MoE).
131
f =
_to_2d
(w).detach().float().cpu()
132
if
f.numel() < 2:
133
return
1.0, 1.0
134
try
:
135
sv = torch.linalg.svdvals(f)
136
except
Exception:
137
return
0.0, 0.0
138
if
sv.numel() == 0:
139
return
0.0, 0.0
140
s0 =
_scalar
(sv.reshape(-1)[0])
141
if
s0 < 1e-12:
142
return
0.0, 0.0
143
ssum =
_scalar
(sv.sum())
144
s2sum =
_scalar
((sv ** 2).sum())
145
stable = (ssum ** 2) / max(s2sum, 1e-12)
146
nuclear_ratio = ssum / (s0 * sv.numel())
147
return
round(stable, 4), round(nuclear_ratio, 4)
148
149
150
def
run_weight_rank_hf
(model, collapse_threshold: float = 0.1) -> dict:
151
"""Stable rank over HuggingFace parameter names (LFM, Sarvam, etc.)."""
152
root =
_weight_root
(model)
153
cfg = getattr(model,
"cfg"
,
None
)
154
n_layers = int(getattr(cfg,
"n_layers"
, 0)
or
0)
155
heavy =
_is_heavy_weight_model
(model)
156
matrices: list[dict] = []
157
seen_layers: set[int] = set()
158
159
for
name, param
in
root.named_parameters():
160
if
param.dim() < 2
or
param.numel() < 256:
161
continue
162
if
_should_skip_weight_param
(name, heavy=heavy):
163
continue
164
layer =
_layer_from_key
(name)
165
if
layer < 0:
166
continue
167
seen_layers.add(layer)
168
matrix =
_hf_matrix_label
(name)
169
stable, nuclear =
_matrix_ranks
(param)
170
matrices.append({
171
"layer"
: layer,
172
"matrix"
: matrix,
173
"module"
: name,
174
"stable_rank"
: stable,
175
"nuclear_norm_ratio"
: nuclear,
176
"collapsed"
: stable < collapse_threshold,
177
})
178
179
if
not
n_layers
and
seen_layers:
180
n_layers = max(seen_layers) + 1
181
182
collapsed_count = sum(1
for
m
in
matrices
if
m[
"collapsed"
])
183
return
{
184
"n_layers"
: n_layers,
185
"matrices"
: matrices,
186
"collapse_threshold"
: collapse_threshold,
187
"collapsed_count"
: collapsed_count,
188
"backend"
:
"hf"
,
189
"heavy_model"
: heavy,
190
}
191
192
193
def
run_weight_rank_tl
(model, collapse_threshold: float = 0.1) -> dict:
194
"""Compute stable rank for Q/K/V/O and MLP matrices across all layers."""
195
n_layers = int(getattr(model.cfg,
"n_layers"
, 0)
or
len(model.blocks))
196
matrices: list[dict] = []
197
198
for
layer
in
range(n_layers):
199
block = model.blocks[layer]
200
attn = block.attn
201
mlp = block.mlp
202
specs: list[tuple[str, torch.Tensor]] = [
203
(
"Q"
, attn.W_Q),
204
(
"K"
, attn.W_K),
205
(
"V"
, attn.W_V),
206
(
"O"
, attn.W_O),
207
(
"MLP_up"
, mlp.W_in),
208
(
"MLP_down"
, mlp.W_out),
209
]
210
if
hasattr(mlp,
"W_gate"
):
211
specs.append((
"MLP_gate"
, mlp.W_gate))
212
213
for
matrix_name, weight
in
specs:
214
stable, nuclear =
_matrix_ranks
(weight.detach())
215
matrices.append({
216
"layer"
: layer,
217
"matrix"
: matrix_name,
218
"stable_rank"
: stable,
219
"nuclear_norm_ratio"
: nuclear,
220
"collapsed"
: stable < collapse_threshold,
221
})
222
223
collapsed_count = sum(1
for
m
in
matrices
if
m[
"collapsed"
])
224
return
{
225
"n_layers"
: n_layers,
226
"matrices"
: matrices,
227
"collapse_threshold"
: collapse_threshold,
228
"collapsed_count"
: collapsed_count,
229
"backend"
:
"transformer_lens"
,
230
}
231
232
233
def
run_weight_rank_analysis
(model, collapse_threshold: float = 0.1) -> dict:
234
"""Compute stable rank for attention/MLP (TL) or HF-native weights."""
235
if
_is_hf_weight_model
(model):
236
return
run_weight_rank_hf
(model, collapse_threshold=collapse_threshold)
237
return
run_weight_rank_tl
(model, collapse_threshold=collapse_threshold)
aquin.compute.weight_rank._scalar
float _scalar(torch.Tensor t)
Definition
weight_rank.py:119
aquin.compute.weight_rank.run_weight_rank_hf
dict run_weight_rank_hf(model, float collapse_threshold=0.1)
Definition
weight_rank.py:154
aquin.compute.weight_rank.run_weight_rank_analysis
dict run_weight_rank_analysis(model, float collapse_threshold=0.1)
Definition
weight_rank.py:237
aquin.compute.weight_rank.run_weight_rank_tl
dict run_weight_rank_tl(model, float collapse_threshold=0.1)
Definition
weight_rank.py:197
aquin.compute.weight_rank._to_2d
torch.Tensor _to_2d(torch.Tensor w)
Definition
weight_rank.py:123
aquin.compute.weight_rank._layer_from_key
int _layer_from_key(str key)
Definition
weight_rank.py:49
aquin.compute.weight_rank._is_heavy_weight_model
bool _is_heavy_weight_model(model)
Definition
weight_rank.py:22
aquin.compute.weight_rank._weight_root
object _weight_root(model)
Definition
weight_rank.py:101
aquin.compute.weight_rank._matrix_ranks
tuple[float, float] _matrix_ranks(torch.Tensor w)
Definition
weight_rank.py:133
aquin.compute.weight_rank._hf_matrix_label
str _hf_matrix_label(str key)
Definition
weight_rank.py:54
aquin.compute.weight_rank._should_skip_weight_param
bool _should_skip_weight_param(str name, *, bool heavy)
Definition
weight_rank.py:35
aquin.compute.weight_rank._is_hf_weight_model
bool _is_hf_weight_model(model)
Definition
weight_rank.py:107
aquin
compute
weight_rank.py
AQIT · Aquin Labs Private Limited · Apache 2.0 · Generated by
1.18.0