gemma4-e2b-exp-quant / src /quantization.py
toxzak's picture
Initial upload: mixed-budget sub-4-bit artifacts + perplexity result
9c41926 verified
Raw
History Blame Contribute Delete
3.02 kB
import torch
import numpy as np
def ternary_quantize(x: torch.Tensor):
scale = x.abs().max()
if scale == 0:
scale = 1.0
normalized = x / scale
ternary = torch.sign(normalized)
ternary[ternary == 0] = 1
return ternary.to(torch.int8), scale
def ternary_pack(t: torch.Tensor) -> torch.Tensor:
encoded = t.to(torch.int8) + 1
n = encoded.numel()
pad = (5 - n % 5) % 5
if pad:
encoded = torch.cat([encoded.flatten(), torch.zeros(pad, dtype=torch.int8, device=encoded.device)])
weights = torch.tensor([81, 27, 9, 3, 1], dtype=torch.int32, device=encoded.device)
packed = (encoded.reshape(-1, 5).to(torch.int32) * weights).sum(dim=1)
return packed.to(torch.uint8)
def ternary_unpack(packed: torch.Tensor, original_shape: tuple) -> torch.Tensor:
weights = torch.tensor([81, 27, 9, 3, 1], dtype=torch.int32, device=packed.device)
expanded = packed.to(torch.int32).unsqueeze(-1) // weights % 3
flat = (expanded - 1).flatten()
return flat[:np.prod(original_shape)].reshape(original_shape).to(torch.int8)
def sigma_quantize(s: torch.Tensor, num_bits: int = 2):
max_val = s.abs().max()
if max_val == 0:
max_val = 1.0
qmax = 2 ** (num_bits - 1) - 1
scale = max_val / qmax
quantized = (s / scale).round().clamp(-qmax, qmax)
return quantized.to(torch.int8), scale
def sigma_dequantize(q: torch.Tensor, scale: torch.Tensor) -> torch.Tensor:
return q.float() * scale
def quantize_factor(U: torch.Tensor, S: torch.Tensor, Vt: torch.Tensor, sigma_bits: int = 2):
U_q, U_scale = ternary_quantize(U)
Vt_q, Vt_scale = ternary_quantize(Vt)
S_q, S_scale = sigma_quantize(S, sigma_bits)
return {
'U': U_q, 'U_scale': U_scale,
'Vt': Vt_q, 'Vt_scale': Vt_scale,
'S': S_q, 'S_scale': S_scale,
}
def pack_factor(data: dict):
return {
'U_packed': ternary_pack(data['U']),
'U_scale': data['U_scale'],
'Vt_packed': ternary_pack(data['Vt']),
'Vt_scale': data['Vt_scale'],
'S': data['S'],
'S_scale': data['S_scale'],
'U_shape': data['U'].shape,
'Vt_shape': data['Vt'].shape,
}
def unpack_factor(data: dict):
if 'U_packed' in data:
U = ternary_unpack(data['U_packed'], data['U_shape'])
Vt = ternary_unpack(data['Vt_packed'], data['Vt_shape'])
else:
U = data['U']
Vt = data['Vt']
return {
'U': U,
'U_scale': data['U_scale'],
'Vt': Vt,
'Vt_scale': data['Vt_scale'],
'S': data['S'],
'S_scale': data['S_scale'],
}
def dequantize_factor(data: dict) -> torch.Tensor:
if 'U_packed' in data:
U = ternary_unpack(data['U_packed'], data['U_shape']).float()
Vt = ternary_unpack(data['Vt_packed'], data['Vt_shape']).float()
else:
U = data['U'].float()
Vt = data['Vt'].float()
S = sigma_dequantize(data['S'], data['S_scale'])
return torch.matmul(U * S.unsqueeze(0), Vt)