semantic-lite-2 / quant_loader.py
ukung's picture
Upload quant_loader.py with huggingface_hub
00f5885 verified
Raw History Blame Contribute Delete
3.11 kB
import json
import torch
from safetensors.torch import load_file
from transformers import AutoModel, AutoTokenizer
def _dequantize(sd, cfg):
"""Dekuantisasi tensor int8/int4 (per-group) kembali ke float16."""
bits = cfg["bits"]
group_size = cfg["group_size"]
shapes = cfg["shapes"]
out = {}
for name, shape in shapes.items():
n_orig = int(torch.tensor(shape).prod())
n_pad = ((n_orig + group_size - 1) // group_size) * group_size
scale = sd[name + ".scale"].float()
zero = sd[name + ".zero"].float()
n_groups = n_pad // group_size
scale_e = scale.reshape(n_groups, 1).expand(n_groups, group_size).reshape(-1)
zero_e = zero.reshape(n_groups, 1).expand(n_groups, group_size).reshape(-1)
if bits == 8:
q = sd[name].float()
w = (q - zero_e) * scale_e
else:
q = sd[name].to(torch.uint8).cpu()
lo = (q & 0x0F).to(torch.int8)
hi = (q >> 4).to(torch.int8)
vals = torch.stack([hi, lo], dim=1).reshape(-1)
vals = vals - 8
w = (vals.float() - zero_e) * scale_e
w = w[:n_orig].reshape(shape)
out[name] = w.half()
return out
def load_quantized_model(repo_id, bits, device=None):
"""Muat model semantic-lite-2 versi terkuantisasi (8 atau 4 bit).
Args:
repo_id: id repo Hugging Face (misal "ukung/semantic-lite-2")
bits: 8 atau 4
device: "cuda" atau "cpu" (default otomatis)
"""
assert bits in (8, 4), "bits harus 8 atau 4"
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
# 1. Muat model fp16 dasar (struktur + bias/norm kecil)
model = AutoModel.from_pretrained(repo_id, trust_remote_code=True)
model = model.to(device)
# 2. Muat tensor terkuantisasi
from huggingface_hub import hf_hub_download
q_path = hf_hub_download(repo_id, f"model_q{bits}.safetensors")
c_path = hf_hub_download(repo_id, f"model_q{bits}_config.json")
sd = load_file(q_path)
cfg = json.load(open(c_path))
deq = _dequantize(sd, cfg)
# 3. Ganti weight 2D dengan versi dekuantisasi
with torch.no_grad():
for name, p in model.named_parameters():
if name in deq:
p.data = deq[name].to(device)
return model
def encode(model, tokenizer, texts, device=None):
"""Encode teks jadi vektor embedding (sudah ternormalisasi L2)."""
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
tok = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
tok = {k: v.to(device) for k, v in tok.items()}
with torch.no_grad():
out = model(**tok)
return out
if __name__ == "__main__":
import sys
bits = int(sys.argv[1]) if len(sys.argv) > 1 else 8
repo = "ukung/semantic-lite-2"
model = load_quantized_model(repo, bits)
tok = AutoTokenizer.from_pretrained(repo)
v = encode(model, tok, ["contoh kalimat bahasa Indonesia"])
print(f"Q{bits} loaded, output shape: {tuple(v.shape)}")