neural-riscv / quant_units.py
Quazim0t0's picture
Bonus tab: int8 datapath units (3.6x smaller, still bit-exact); selectable fp32/int8 in Audit + Bonus tabs
e83ee32 verified
Raw
History Blame Contribute Delete
2.93 kB
"""
quant_units.py -- load the int8-quantized datapath units shipped in
`models_int8/`, and (re)verify any unit variant bit-exact over its full domain.
The int8 files are produced by riscv/export_int8.py: the dominant 256x256 hidden
weight is per-channel symmetric int8, everything else is fp16. Dequantizing
reconstructs a normal BitMLP state_dict, so the rest of the stack (build_all /
NeuralUnits / the rv32 core) is untouched -- this is purely an alternate weight
source. All 13 units stay bit-exact, at ~28% of the fp32 size.
"""
import os
import torch
from x86_units import (BitMLP, u_ADC8, u_SBB8, u_logic, u_SHL1, u_SHR1,
u_MASK8, u_NOT8, u_FLAGS8, u_prefix, u_modrm, u_sib)
HERE = os.path.dirname(os.path.abspath(__file__))
INT8_DIR = os.path.join(HERE, "models_int8")
# name -> truth-table generator, for exhaustive re-verification of either variant
DOMAINS = {
"ADC8": u_ADC8, "SBB8": u_SBB8,
"AND8": lambda: u_logic(lambda a, b: a & b),
"OR8": lambda: u_logic(lambda a, b: a | b),
"XOR8": lambda: u_logic(lambda a, b: a ^ b),
"SHL1": u_SHL1, "SHR1": u_SHR1, "MASK8": u_MASK8, "NOT8": u_NOT8,
"FLAGS8": u_FLAGS8, "PREFIX": u_prefix, "MODRM": u_modrm, "SIB": u_sib,
}
def _dequant(blob):
"""int8 blob -> fp32 BitMLP state_dict."""
sd = {}
for k, v in blob.items():
if k.endswith(".q"):
sd[k[:-2]] = v.float() * blob[k[:-2] + ".s"].float()
elif k.endswith(".s"):
continue
else:
sd[k] = v.float()
return sd
def build_int8(int8_dir=INT8_DIR):
"""Like build_all(), but loads the int8 units (dequantized) -- no training."""
nets = {}
for name in DOMAINS:
blob = torch.load(os.path.join(int8_dir, f"{name}.pt"), weights_only=True)
sd = _dequant(blob)
nin = sd["net.0.weight"].shape[1]
nout = sd["net.4.weight"].shape[0]
net = BitMLP(nin, nout)
net.load_state_dict(sd)
net.eval()
nets[name] = net
return nets
def verify(nets):
"""Exhaustively re-check every unit over its FULL domain. Returns
(n_exact, n_total, list_of_failures)."""
ok = 0; fails = []
for name, gen in DOMAINS.items():
X, Y = gen()
Xt, Yt = torch.tensor(X), torch.tensor(Y)
good = True
with torch.no_grad():
for i in range(0, Xt.shape[0], 65536):
if not bool(((nets[name](Xt[i:i+65536]) > 0).float()
== Yt[i:i+65536]).all()):
good = False; break
ok += int(good)
if not good:
fails.append(name)
return ok, len(DOMAINS), fails
def dir_size(model_dir):
"""Total bytes of the 13 unit files in a model directory."""
tot = 0
for name in DOMAINS:
p = os.path.join(model_dir, f"{name}.pt")
if os.path.exists(p):
tot += os.path.getsize(p)
return tot