Qwen patches
Browse files- generate_imatrix.py +155 -7
generate_imatrix.py
CHANGED
|
@@ -137,17 +137,35 @@ class GGUFModel:
|
|
| 137 |
|
| 138 |
def get_config(self):
|
| 139 |
arch = self.get_arch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
return {
|
| 141 |
'arch': arch,
|
| 142 |
'n_layers': self.kv.get(f'{arch}.block_count', 0),
|
| 143 |
-
'n_embd':
|
| 144 |
-
'n_head':
|
| 145 |
-
'n_head_kv':
|
| 146 |
'n_ff': self.kv.get(f'{arch}.feed_forward_length', 0),
|
| 147 |
'vocab_size': self.kv.get(f'{arch}.vocab_size', 0),
|
| 148 |
'rms_eps': self.kv.get(f'{arch}.attention.layer_norm_rms_epsilon', 1e-6),
|
| 149 |
'rope_base': self.kv.get(f'{arch}.rope.freq_base', 10000.0),
|
| 150 |
'swa_window': self.kv.get(f'{arch}.attention.sliding_window', 0),
|
|
|
|
| 151 |
}
|
| 152 |
|
| 153 |
def get_tensor_f32(self, name):
|
|
@@ -458,7 +476,7 @@ class TransformerRunner:
|
|
| 458 |
self.model = model
|
| 459 |
self.cfg = config
|
| 460 |
self.verbose = verbose
|
| 461 |
-
self.head_dim = config['n_embd'] // config['n_head']
|
| 462 |
self.act_fn = ACTIVATION_MAP.get(config['arch'], silu)
|
| 463 |
|
| 464 |
# Importance accumulators: tensor_name β (sum_x2, count)
|
|
@@ -701,6 +719,131 @@ class TransformerRunner:
|
|
| 701 |
|
| 702 |
return hidden
|
| 703 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 704 |
def forward(self, token_ids):
|
| 705 |
"""Full forward pass, collecting importance statistics."""
|
| 706 |
cfg = self.cfg
|
|
@@ -718,14 +861,19 @@ class TransformerRunner:
|
|
| 718 |
|
| 719 |
# Process each layer
|
| 720 |
for layer_idx in range(cfg['n_layers']):
|
| 721 |
-
# Check if this layer has DeltaNet tensors (Qwen 3.6 hybrid)
|
| 722 |
pfx = f"blk.{layer_idx}"
|
|
|
|
|
|
|
| 723 |
has_linear_attn = f'{pfx}.ssm_in_qkv.weight' in self.model.tensor_infos
|
| 724 |
-
has_full_attn = f'{pfx}.attn_q.weight' in self.model.tensor_infos
|
| 725 |
|
| 726 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 727 |
hidden = self.forward_linear_attn_layer(hidden, layer_idx)
|
| 728 |
else:
|
|
|
|
| 729 |
hidden = self.forward_layer(hidden, layer_idx, cos_f, sin_f)
|
| 730 |
if self.verbose and (layer_idx + 1) % 4 == 0:
|
| 731 |
print(f" Layer {layer_idx + 1}/{cfg['n_layers']}", end='\r')
|
|
|
|
| 137 |
|
| 138 |
def get_config(self):
|
| 139 |
arch = self.get_arch()
|
| 140 |
+
n_embd = self.kv.get(f'{arch}.embedding_length', 0)
|
| 141 |
+
n_head = self.kv.get(f'{arch}.attention.head_count', 0)
|
| 142 |
+
n_head_kv = self.kv.get(f'{arch}.attention.head_count_kv', 0)
|
| 143 |
+
|
| 144 |
+
# Auto-detect head_dim: prefer fused QKV tensor dims over n_embd/n_head
|
| 145 |
+
# (Qwen 3.6 has n_embd=5120, n_head=24 β non-integer ratio;
|
| 146 |
+
# real head_dim=320, computed from QKV: 10240 / (24+2*4) = 320)
|
| 147 |
+
head_dim = 0
|
| 148 |
+
qkv_name = 'blk.0.attn_qkv.weight'
|
| 149 |
+
if qkv_name in self.tensor_infos:
|
| 150 |
+
qkv_cols = self.tensor_infos[qkv_name]['dims'][1] # output dim
|
| 151 |
+
n_head_total = n_head + 2 * n_head_kv
|
| 152 |
+
if n_head_total > 0:
|
| 153 |
+
head_dim = qkv_cols // n_head_total
|
| 154 |
+
if head_dim == 0 and n_head > 0:
|
| 155 |
+
head_dim = n_embd // n_head
|
| 156 |
+
|
| 157 |
return {
|
| 158 |
'arch': arch,
|
| 159 |
'n_layers': self.kv.get(f'{arch}.block_count', 0),
|
| 160 |
+
'n_embd': n_embd,
|
| 161 |
+
'n_head': n_head,
|
| 162 |
+
'n_head_kv': n_head_kv,
|
| 163 |
'n_ff': self.kv.get(f'{arch}.feed_forward_length', 0),
|
| 164 |
'vocab_size': self.kv.get(f'{arch}.vocab_size', 0),
|
| 165 |
'rms_eps': self.kv.get(f'{arch}.attention.layer_norm_rms_epsilon', 1e-6),
|
| 166 |
'rope_base': self.kv.get(f'{arch}.rope.freq_base', 10000.0),
|
| 167 |
'swa_window': self.kv.get(f'{arch}.attention.sliding_window', 0),
|
| 168 |
+
'head_dim': head_dim,
|
| 169 |
}
|
| 170 |
|
| 171 |
def get_tensor_f32(self, name):
|
|
|
|
| 476 |
self.model = model
|
| 477 |
self.cfg = config
|
| 478 |
self.verbose = verbose
|
| 479 |
+
self.head_dim = config.get('head_dim', config['n_embd'] // config['n_head'])
|
| 480 |
self.act_fn = ACTIVATION_MAP.get(config['arch'], silu)
|
| 481 |
|
| 482 |
# Importance accumulators: tensor_name β (sum_x2, count)
|
|
|
|
| 719 |
|
| 720 |
return hidden
|
| 721 |
|
| 722 |
+
def forward_qwen35_layer(self, hidden, layer_idx, cos_f, sin_f):
|
| 723 |
+
"""Forward pass through a Qwen 3.6 hybrid layer (attention + SSM).
|
| 724 |
+
|
| 725 |
+
Qwen 3.6 uses:
|
| 726 |
+
- Fused attn_qkv.weight (Q+K+V in one tensor)
|
| 727 |
+
- attn_gate.weight (gated attention output, not attn_output)
|
| 728 |
+
- SSM tensors: ssm_alpha, ssm_beta, ssm_conv1d, ssm_out
|
| 729 |
+
- post_attention_norm.weight (instead of ffn_norm)
|
| 730 |
+
"""
|
| 731 |
+
pfx = self._layer_prefix(layer_idx)
|
| 732 |
+
cfg = self.cfg
|
| 733 |
+
n_head = cfg['n_head']
|
| 734 |
+
n_head_kv = cfg['n_head_kv']
|
| 735 |
+
head_dim = self.head_dim
|
| 736 |
+
seq_len = hidden.shape[0]
|
| 737 |
+
|
| 738 |
+
# ββ Attention norm ββ
|
| 739 |
+
attn_norm_w = self._get_weight(f'{pfx}.attn_norm.weight')
|
| 740 |
+
if attn_norm_w is None:
|
| 741 |
+
return hidden
|
| 742 |
+
normed = rms_norm(hidden, attn_norm_w, cfg['rms_eps'])
|
| 743 |
+
|
| 744 |
+
# ββ Fused QKV projection ββ
|
| 745 |
+
qkv_w = self._get_weight(f'{pfx}.attn_qkv.weight')
|
| 746 |
+
gate_w = self._get_weight(f'{pfx}.attn_gate.weight')
|
| 747 |
+
|
| 748 |
+
attn_out_vec = np.zeros_like(hidden)
|
| 749 |
+
if qkv_w is not None:
|
| 750 |
+
self._record(f'{pfx}.attn_qkv.weight', normed)
|
| 751 |
+
|
| 752 |
+
qkv = normed @ qkv_w.T # [seq, (n_head + 2*n_head_kv) * head_dim]
|
| 753 |
+
|
| 754 |
+
# Split into Q, K, V
|
| 755 |
+
q_dim = n_head * head_dim
|
| 756 |
+
kv_dim = n_head_kv * head_dim
|
| 757 |
+
q = qkv[:, :q_dim].reshape(seq_len, n_head, head_dim)
|
| 758 |
+
k = qkv[:, q_dim:q_dim + kv_dim].reshape(seq_len, n_head_kv, head_dim)
|
| 759 |
+
v = qkv[:, q_dim + kv_dim:q_dim + 2 * kv_dim].reshape(seq_len, n_head_kv, head_dim)
|
| 760 |
+
|
| 761 |
+
# RoPE
|
| 762 |
+
q = apply_rope(q, cos_f, sin_f)
|
| 763 |
+
k = apply_rope(k, cos_f, sin_f)
|
| 764 |
+
|
| 765 |
+
# GQA: repeat KV heads
|
| 766 |
+
if n_head_kv < n_head:
|
| 767 |
+
rep = n_head // n_head_kv
|
| 768 |
+
k = np.repeat(k, rep, axis=1)
|
| 769 |
+
v = np.repeat(v, rep, axis=1)
|
| 770 |
+
|
| 771 |
+
# Scaled dot-product attention
|
| 772 |
+
q_t = q.transpose(1, 0, 2) # [n_head, seq, head_dim]
|
| 773 |
+
k_t = k.transpose(1, 0, 2)
|
| 774 |
+
v_t = v.transpose(1, 0, 2)
|
| 775 |
+
|
| 776 |
+
scale = 1.0 / np.sqrt(head_dim)
|
| 777 |
+
attn = np.matmul(q_t, k_t.transpose(0, 2, 1)) * scale
|
| 778 |
+
|
| 779 |
+
# Causal mask (with optional SWA)
|
| 780 |
+
mask = np.triu(np.full((seq_len, seq_len), -1e9, dtype=np.float32), k=1)
|
| 781 |
+
swa = cfg.get('swa_window', 0)
|
| 782 |
+
if swa and swa > 0:
|
| 783 |
+
for i in range(seq_len):
|
| 784 |
+
for j in range(0, max(0, i - swa)):
|
| 785 |
+
mask[i, j] = -1e9
|
| 786 |
+
attn = attn + mask[np.newaxis, :, :]
|
| 787 |
+
attn = softmax(attn, axis=-1)
|
| 788 |
+
|
| 789 |
+
out = np.matmul(attn, v_t)
|
| 790 |
+
attn_result = out.transpose(1, 0, 2).reshape(seq_len, -1) # [seq, n_head*head_dim]
|
| 791 |
+
|
| 792 |
+
# Gated attention output
|
| 793 |
+
if gate_w is not None:
|
| 794 |
+
self._record(f'{pfx}.attn_gate.weight', attn_result)
|
| 795 |
+
attn_out_vec = attn_result @ gate_w.T
|
| 796 |
+
|
| 797 |
+
# ββ SSM path ββ
|
| 798 |
+
ssm_alpha_w = self._get_weight(f'{pfx}.ssm_alpha.weight')
|
| 799 |
+
ssm_beta_w = self._get_weight(f'{pfx}.ssm_beta.weight')
|
| 800 |
+
ssm_conv_w = self._get_weight(f'{pfx}.ssm_conv1d.weight')
|
| 801 |
+
ssm_out_w = self._get_weight(f'{pfx}.ssm_out.weight')
|
| 802 |
+
|
| 803 |
+
ssm_out_vec = np.zeros_like(hidden)
|
| 804 |
+
if ssm_alpha_w is not None:
|
| 805 |
+
self._record(f'{pfx}.ssm_alpha.weight', normed)
|
| 806 |
+
if ssm_beta_w is not None:
|
| 807 |
+
self._record(f'{pfx}.ssm_beta.weight', normed)
|
| 808 |
+
if ssm_conv_w is not None:
|
| 809 |
+
# ssm_conv1d input is the QKV projection (reuse from attention)
|
| 810 |
+
if qkv_w is not None:
|
| 811 |
+
qkv_for_ssm = normed @ qkv_w.T
|
| 812 |
+
self._record(f'{pfx}.ssm_conv1d.weight', qkv_for_ssm)
|
| 813 |
+
if ssm_out_w is not None:
|
| 814 |
+
# SSM output projection β use normed as proxy for recurrent output
|
| 815 |
+
ssm_proxy = normed[:, :ssm_out_w.shape[1]] if normed.shape[-1] >= ssm_out_w.shape[1] else normed
|
| 816 |
+
self._record(f'{pfx}.ssm_out.weight', ssm_proxy)
|
| 817 |
+
ssm_out_vec = ssm_proxy @ ssm_out_w.T
|
| 818 |
+
|
| 819 |
+
# Combine attention + SSM
|
| 820 |
+
hidden = hidden + attn_out_vec + ssm_out_vec
|
| 821 |
+
|
| 822 |
+
# ββ FFN (uses post_attention_norm instead of ffn_norm) ββ
|
| 823 |
+
ffn_norm_w = self._get_weight(f'{pfx}.post_attention_norm.weight')
|
| 824 |
+
if ffn_norm_w is None:
|
| 825 |
+
ffn_norm_w = self._get_weight(f'{pfx}.ffn_norm.weight')
|
| 826 |
+
if ffn_norm_w is None:
|
| 827 |
+
return hidden
|
| 828 |
+
|
| 829 |
+
normed_ff = rms_norm(hidden, ffn_norm_w, cfg['rms_eps'])
|
| 830 |
+
|
| 831 |
+
gate_fw = self._get_weight(f'{pfx}.ffn_gate.weight')
|
| 832 |
+
up_w = self._get_weight(f'{pfx}.ffn_up.weight')
|
| 833 |
+
down_w = self._get_weight(f'{pfx}.ffn_down.weight')
|
| 834 |
+
|
| 835 |
+
if gate_fw is not None and up_w is not None and down_w is not None:
|
| 836 |
+
self._record(f'{pfx}.ffn_gate.weight', normed_ff)
|
| 837 |
+
self._record(f'{pfx}.ffn_up.weight', normed_ff)
|
| 838 |
+
gate_out = self.act_fn(normed_ff @ gate_fw.T)
|
| 839 |
+
up_out = normed_ff @ up_w.T
|
| 840 |
+
ff_mid = gate_out * up_out
|
| 841 |
+
self._record(f'{pfx}.ffn_down.weight', ff_mid)
|
| 842 |
+
ff_out = ff_mid @ down_w.T
|
| 843 |
+
hidden = hidden + ff_out
|
| 844 |
+
|
| 845 |
+
return hidden
|
| 846 |
+
|
| 847 |
def forward(self, token_ids):
|
| 848 |
"""Full forward pass, collecting importance statistics."""
|
| 849 |
cfg = self.cfg
|
|
|
|
| 861 |
|
| 862 |
# Process each layer
|
| 863 |
for layer_idx in range(cfg['n_layers']):
|
|
|
|
| 864 |
pfx = f"blk.{layer_idx}"
|
| 865 |
+
has_fused_qkv = f'{pfx}.attn_qkv.weight' in self.model.tensor_infos
|
| 866 |
+
has_separate_q = f'{pfx}.attn_q.weight' in self.model.tensor_infos
|
| 867 |
has_linear_attn = f'{pfx}.ssm_in_qkv.weight' in self.model.tensor_infos
|
|
|
|
| 868 |
|
| 869 |
+
if has_fused_qkv:
|
| 870 |
+
# Qwen 3.6 hybrid: fused QKV + SSM
|
| 871 |
+
hidden = self.forward_qwen35_layer(hidden, layer_idx, cos_f, sin_f)
|
| 872 |
+
elif has_linear_attn and not has_separate_q:
|
| 873 |
+
# DeltaNet-only layers
|
| 874 |
hidden = self.forward_linear_attn_layer(hidden, layer_idx)
|
| 875 |
else:
|
| 876 |
+
# Standard transformer (LLaMA, Mistral, Gemma, etc.)
|
| 877 |
hidden = self.forward_layer(hidden, layer_idx, cos_f, sin_f)
|
| 878 |
if self.verbose and (layer_idx + 1) % 4 == 0:
|
| 879 |
print(f" Layer {layer_idx + 1}/{cfg['n_layers']}", end='\r')
|