fix: address CodeRabbit feedback — QuaRot error surface, LoRA shape validation, Sylvester bug fix, cache cap

This commit is contained in:
JWLHS 2026-07-10 11:07:08 +08:00
parent b5d471f5ff
commit 3e5738da24
2 changed files with 43 additions and 18 deletions

View File

@ -74,18 +74,15 @@ class TINT4Linear(nn.Module):
# ── QuaRot activation rotation (online) ────────────────── # ── QuaRot activation rotation (online) ──────────────────
if self._use_quarot and self._hadamard_H is not None: if self._use_quarot and self._hadamard_H is not None:
try: if x_flat.shape[-1] % self._group_size != 0:
H_dev = self._hadamard_H.to( raise ValueError(
device=x.device, f"activation dim {x_flat.shape[-1]} not divisible "
dtype=(x.dtype if x.dtype in ( f"by QuaRot group_size {self._group_size}"
torch.float16, torch.bfloat16)
else torch.float16),
) )
n_groups = x_flat.shape[-1] // self._group_size H_dev = self._hadamard_H.to(device=x.device, dtype=x.dtype)
x_g = x_flat.view(-1, n_groups, self._group_size) n_groups = x_flat.shape[-1] // self._group_size
x_flat = torch.matmul(x_g, H_dev).view(-1, x_g.shape[2] * n_groups) x_g = x_flat.view(-1, n_groups, self._group_size)
except Exception: x_flat = torch.matmul(x_g, H_dev).view(-1, n_groups * self._group_size)
pass
dev = x.device dev = x.device
@ -105,6 +102,7 @@ class TINT4Linear(nn.Module):
else torch.float16) else torch.float16)
for lora_entries in entries.values(): for lora_entries in entries.values():
for e in lora_entries: for e in lora_entries:
# ── LoKr path ────────────────────────────────
if isinstance(e[0], str) and e[0] == "lokr": if isinstance(e[0], str) and e[0] == "lokr":
_, w1, w2, mult, factor = e[:5] _, w1, w2, mult, factor = e[:5]
sl = e[5] if len(e) > 5 else None sl = e[5] if len(e) > 5 else None
@ -118,13 +116,23 @@ class TINT4Linear(nn.Module):
dw = (w1x * w2d).mul_(mult) dw = (w1x * w2d).mul_(mult)
lo = x_flat.to(cd) @ dw.T lo = x_flat.to(cd) @ dw.T
if sl is not None: if sl is not None:
if lo.shape[1] == (se - sl): if lo.shape[1] != (se - sl):
out[:, sl:se] += lo raise ValueError(
f"LoKr output width {lo.shape[1]} "
f"doesn't match target slice width "
f"{se - sl}"
)
out[:, sl:se] += lo
elif lo.shape == out.shape: elif lo.shape == out.shape:
out += lo out += lo
else:
raise ValueError(
f"LoKr output shape {lo.shape} "
f"incompatible with {out.shape}"
)
continue continue
# standard LoRA # ── Standard LoRA path ────────────────────────
A, B, mult = e[:3] A, B, mult = e[:3]
sl = e[3] if len(e) > 3 else None sl = e[3] if len(e) > 3 else None
se = e[4] if len(e) > 4 else None se = e[4] if len(e) > 4 else None
@ -132,10 +140,20 @@ class TINT4Linear(nn.Module):
Bd = B.to(device=dev, dtype=cd) Bd = B.to(device=dev, dtype=cd)
lo = (x_flat.to(cd) @ Ad.T) @ Bd.T * mult lo = (x_flat.to(cd) @ Ad.T) @ Bd.T * mult
if sl is not None: if sl is not None:
if lo.shape[1] == (se - sl): if lo.shape[1] != (se - sl):
out[:, sl:se] += lo raise ValueError(
f"LoRA output width {lo.shape[1]} "
f"doesn't match target slice width "
f"{se - sl}"
)
out[:, sl:se] += lo
elif lo.shape[1] == out.shape[1]: elif lo.shape[1] == out.shape[1]:
out += lo out += lo
else:
raise ValueError(
f"LoRA output shape {lo.shape} "
f"incompatible with {out.shape}"
)
if self.bias is not None: if self.bias is not None:
out += self.bias.to(device=dev, dtype=out.dtype) out += self.bias.to(device=dev, dtype=out.dtype)

View File

@ -14,6 +14,7 @@ except ImportError:
_SCIPY_AVAILABLE = False _SCIPY_AVAILABLE = False
_HADAMARD_CACHE: dict = {} _HADAMARD_CACHE: dict = {}
_HADAMARD_MAX_SIZE = 64 # Maximum number of cached (size, device, dtype) entries
def build_hadamard( def build_hadamard(
@ -38,14 +39,20 @@ def build_hadamard(
H_np = _scipy_hadamard(size) H_np = _scipy_hadamard(size)
H = torch.tensor(H_np, dtype=dtype, device=device) / (size ** 0.5) H = torch.tensor(H_np, dtype=dtype, device=device) / (size ** 0.5)
else: else:
# Pure-PyTorch Sylvester construction
H = torch.tensor([[1.0]], dtype=dtype, device=device) H = torch.tensor([[1.0]], dtype=dtype, device=device)
n = 1 n = 1
while n < size: while n < size:
H = torch.cat([torch.cat([H, H], dim=1), top = torch.cat([H, H], dim=1)
torch.cat([H, -H], dim=0)], dim=0) bottom = torch.cat([H, -H], dim=1)
H = torch.cat([top, bottom], dim=0)
n *= 2 n *= 2
H = H / (size ** 0.5) H = H / (size ** 0.5)
# Enforce cache size cap (evict oldest entry)
if len(_HADAMARD_CACHE) >= _HADAMARD_MAX_SIZE:
_HADAMARD_CACHE.pop(next(iter(_HADAMARD_CACHE)))
_HADAMARD_CACHE[cache_key] = H _HADAMARD_CACHE[cache_key] = H
return H return H