changh95's picture
Add files using upload-large-folder tool
0190e6b verified
Raw History Blame Contribute Delete
3.44 kB
"""Torch reference of one expert (Qwen3-VL text) decoder layer with HF semantics, used as the oracle for the
device implementation when the golden dump does not contain the full VLM KV cache.
Layer i of the expert attends to [VLM cache rows of layer i (already q/k-normed and roped by the VLM) ; its own
64 tokens], non-causally. Own k gets k_norm + rope, cached keys are used as stored.
"""
from __future__ import annotations
import torch
from .action_proj import rms_norm, rope_cos_sin
from .config import TextConfig
def rotate_half(x: torch.Tensor) -> torch.Tensor:
x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:]
return torch.cat((-x2, x1), dim=-1)
def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
"""x [B, H, L, D], cos/sin [L, D] (broadcast over heads)."""
return x * cos[None, None] + rotate_half(x) * sin[None, None]
def layer_forward(x: torch.Tensor, w: dict[str, torch.Tensor], k_cache: torch.Tensor, v_cache: torch.Tensor,
cos: torch.Tensor, sin: torch.Tensor, tc: TextConfig,
dtype: torch.dtype = torch.float32, return_taps: bool = False):
"""x [1, L, hidden]; w = Checkpoint.text_layer(i, prefix=EXPERT) (HF [out, in] weights);
k_cache/v_cache [1, n_kv, S, head_dim] (HF head order); cos/sin [L, head_dim].
Returns [1, L, hidden] (and intermediate taps if requested)."""
H, KV, D = tc.n_heads, tc.n_kv_heads, tc.head_dim
eps = tc.rms_eps
x = x.to(dtype)
w = {k: v.to(dtype) for k, v in w.items()}
B, L, _ = x.shape
h = rms_norm(x, w["input_layernorm.weight"], eps)
q = (h @ w["self_attn.q_proj.weight"].t()).view(B, L, H, D)
k = (h @ w["self_attn.k_proj.weight"].t()).view(B, L, KV, D)
v = (h @ w["self_attn.v_proj.weight"].t()).view(B, L, KV, D)
q = rms_norm(q, w["self_attn.q_norm.weight"], eps).transpose(1, 2) # [B, H, L, D]
k = rms_norm(k, w["self_attn.k_norm.weight"], eps).transpose(1, 2)
v = v.transpose(1, 2)
q = apply_rope(q, cos.to(dtype), sin.to(dtype))
k = apply_rope(k, cos.to(dtype), sin.to(dtype))
K = torch.cat([k_cache.to(dtype), k], dim=2) # [B, KV, S+L, D]
V = torch.cat([v_cache.to(dtype), v], dim=2)
rep = H // KV
K = K[:, :, None].expand(B, KV, rep, K.shape[2], D).reshape(B, H, -1, D)
V = V[:, :, None].expand(B, KV, rep, V.shape[2], D).reshape(B, H, -1, D)
scores = (q.float() @ K.float().transpose(-1, -2)) * (D ** -0.5)
p = torch.softmax(scores, dim=-1).to(dtype)
attn = (p @ V).transpose(1, 2).reshape(B, L, H * D)
o = attn @ w["self_attn.o_proj.weight"].t()
x1 = x + o
h2 = rms_norm(x1, w["post_attention_layernorm.weight"], eps)
g = torch.nn.functional.silu(h2 @ w["mlp.gate_proj.weight"].t())
u = h2 @ w["mlp.up_proj.weight"].t()
m = (g * u) @ w["mlp.down_proj.weight"].t()
out = x1 + m
if return_taps:
return out, {"h": h, "q": q, "k": k, "v": v, "attn": attn, "o": o, "x1": x1, "h2": h2, "g": g, "u": u, "m": m}
return out
def expert_cos_sin(S: int, rope_delta: int, tc: TextConfig, n_tokens: int = 64):
pos = torch.arange(n_tokens) + int(rope_delta) + int(S)
return rope_cos_sin(pos, tc.head_dim, tc.rope_theta)
def pcc(a: torch.Tensor, b: torch.Tensor) -> float:
a = a.flatten().double()
b = b.flatten().double()
a = a - a.mean()
b = b - b.mean()
return float((a @ b) / (a.norm() * b.norm() + 1e-30))