Files
SoundGame/tools/stable-audio-3/optimized/tensorRT/quantize/gptq_samel.py
T
SoundGame Dev f0f0a9b50e 初始提交:3 款纯音频游戏 + 共享引擎 + 完整文档与资产管线
- 共享引擎(app/src/main):GestureEngine 6 手势识别、AudioEngine 三层音频
  (ExoPlayer BGM 交叉淡入 / 对白 ducking / SoundPool 低延迟)、Game/GameEngine
  状态机接口、MainActivity 全屏封面 + TalkBack
- 《声命线》storyline:末世互动广播剧,3 幕 8 抉择点 4 结局
- 《声东击西》reflex:听声辨位反应,12 波战斗 + BGM 三档 BPM 变速
- 《暗室》escape:3x3 网格密室逃脱,钥匙→铁柜→保险丝→电箱→大门谜题链
- 文档:setup(从零搭建)/ architecture(架构)/ engine-api(引擎 API)/
  asset-pipeline(资产流水线)/ games/*(三款游戏设计文档)
- 资产管线:Qwen3-TTS 对白 + 程序合成音效/BGM + Mureka BGM 的 gen 脚本与台账,
  全部 ogg 资产入库可复现
- env.d 环境脚本:android/tts 入库;sfx/mureka 含密钥不入库,仅提交 .example.sh 模板
2026-08-29 13:00:34 +08:00

131 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""GPTQ int8 for the SAME-L decoder FFN linears. ORT can't run the SWA plugin, so capture the
per-linear input activations from the EAGER SAME-L autoencoder (hooks), build Hessians, GPTQ, and
graft int8 (fp16 scale) onto the SAME-L onnx (attention proj kept fp32). Writes samel_w8_gptq.onnx."""
import sys, os, re
from pathlib import Path
import numpy as np, torch, onnx
from onnx import TensorProto, helper, numpy_helper
sys.path.insert(0, "/weka2/cj/clod/sa3s/stable-audio-3/optimized/tensorRT/scripts")
sys.path.insert(0, "/weka2/cj/clod/fp8_calib/build")
from stable_audio_3.factory import create_autoencoder_from_config
from stable_audio_3.loading_utils import copy_state_dict
import json
torch.set_grad_enabled(False)
ML = "/weka2/cj/clod/sa3s/models/SAME-L"; AB = "/weka2/cj/clod/sames_fp8/decoder_ab"
cfg = json.load(open(f"{ML}/SAME-L.json"))
ae = create_autoencoder_from_config(cfg["model"], cfg["sample_rate"])
ck = torch.load(f"{ML}/SAME-L.ckpt", map_location="cpu", weights_only=False)
copy_state_dict(ae, ck.get("state_dict", ck) if isinstance(ck, dict) else ck)
ae = ae.to("cuda").eval()
# hook the decoder FFN linears (exclude attention proj). Normalize eager+onnx names to (block, role).
def ekey(nm):
m = re.search(r"transformers\.(\d+)\.ff\.ff\.0\.proj$", nm)
if m: return f"b{m.group(1)}.proj"
m = re.search(r"transformers\.(\d+)\.ff\.ff\.2$", nm)
if m: return f"b{m.group(1)}.out"
if re.search(r"latent_proj$", nm): return "latent_proj"
return None
def okey(nm):
m = re.search(r"blocks\.(\d+)/ff/proj", nm)
if m: return f"b{m.group(1)}.proj"
m = re.search(r"blocks\.(\d+)/ff/out", nm)
if m: return f"b{m.group(1)}.out"
if re.search(r"latent_proj", nm): return "latent_proj"
return None
mods = {}
for nm, mod in ae.named_modules():
if hasattr(mod, "weight") and getattr(mod.weight, "ndim", 0) == 2:
k = ekey(nm)
if k: mods[k] = mod
print(f"hooked {len(mods)} eager FFN linears: {sorted(mods)[:3]} ...", flush=True)
H = {k: None for k in mods}
def mk_hook(k):
def hook(m, inp):
A = inp[0].reshape(-1, inp[0].shape[-1]).double()
h = (A.T @ A).cpu().numpy()
H[k] = h if H[k] is None else H[k] + h
return hook
handles = [m.register_forward_pre_hook(mk_hook(k)) for k, m in mods.items()]
# calibration latents: medium 2-min + 380s + the 10 real-song SAME-L latents
lats = [np.load("/weka2/cj/clod/fp8_listening/latents_2min.npz")["bf16"]] # 1292 samples ...
lats += [np.load(f) for f in sorted(Path(AB).glob("samel_lat_*.npy"))] # + 10×376 = ample for the Hessian
for i, L in enumerate(lats):
ae.decode(torch.tensor(L, device="cuda", dtype=torch.float32)); print(f" captured latent {i} ({L.shape[-1]})", flush=True)
for h in handles: h.remove()
def gptq(Wnp, Hnp, scale_np, pd=0.05, bs=128):
dev = "cuda"
W = torch.tensor(np.asarray(Wnp, np.float32), device=dev) # [out, in]
H = torch.tensor(np.asarray(Hnp, np.float32), device=dev)
sc = torch.tensor(scale_np.astype(np.float32), device=dev) # [out]
cin = W.shape[1]; idx = torch.arange(cin, device=dev)
H[~torch.isfinite(H)] = 0.0
d = torch.diag(H).clone(); dead = d <= 0; d[dead] = 1.0; H[idx, idx] = d; W[:, dead] = 0.0
H = (H + H.T) / 2; damp = pd * d.mean(); Hinv = None
for k in range(6): # damped Cholesky of H⁻¹ (upper)
try:
L = torch.linalg.cholesky(H + torch.eye(cin, device=dev) * (damp * 4.0 ** k))
Hinv = torch.linalg.cholesky(torch.cholesky_inverse(L), upper=True); break
except Exception:
continue
if Hinv is None: Hinv = torch.diag(1.0 / torch.sqrt(d + damp))
Q = torch.zeros_like(W)
for i1 in range(0, cin, bs): # BLOCKED: small updates in-block, 1 matmul for the rest
i2 = min(i1 + bs, cin); W1 = W[:, i1:i2].clone(); Q1 = torch.zeros_like(W1); Err = torch.zeros_like(W1)
Hi = Hinv[i1:i2, i1:i2]
for j in range(i2 - i1):
w = W1[:, j]; dd = Hi[j, j].clamp_min(1e-12)
qi = torch.clamp(torch.round(w / sc), -127, 127); err = (w - qi * sc) / dd
Q1[:, j] = qi; Err[:, j] = err; W1[:, j:] -= err[:, None] * Hi[j, j:][None, :]
Q[:, i1:i2] = Q1
if i2 < cin: W[:, i2:] -= Err @ Hinv[i1:i2, i2:]
return torch.clamp(torch.round(Q), -127, 127).to(torch.int8).cpu().numpy()
# graft onto the onnx
from huggingface_hub import hf_hub_download
DEC = hf_hub_download("stabilityai/stable-audio-3-optimized", "onnx/same-l/dec_dynamic_triton_swa.onnx")
m = onnx.load(DEC, load_external_data=True); g = m.graph
inits = {i.name: i for i in g.initializer}; prod = {o: n for n in g.node for o in n.output}
def warr(n):
w = n.input[1]
if w in inits: return w, numpy_helper.to_array(inits[w]), False, None
p = prod.get(w)
if p and p.op_type == "Transpose" and p.input and p.input[0] in inits: return p.input[0], numpy_helper.to_array(inits[p.input[0]]), True, p
return None, None, None, None
new_nodes, new_inits, done = [], [], 0
for n in [x for x in g.node if x.op_type == "MatMul"]:
key = okey(n.name)
if key not in mods: continue # skip attn proj / non-FFN
ws, W, vt, tn = warr(n)
if ws is None: continue
Wg = mods[key].weight.detach().double().cpu().numpy() # eager [out,in]
scale = np.maximum(np.abs(Wg).max(axis=1) / 127.0, 1e-4).astype(np.float32)
Qi = gptq(Wg, H[key], scale) # [out,in]
qax = 0 if vt else 1
inits[ws].CopyFrom(numpy_helper.from_array(Qi if vt else Qi.T, ws))
pfx = n.name.strip("/").replace("/", "_")
new_inits += [numpy_helper.from_array(scale.astype(np.float16), f"{pfx}_wsc"),
helper.make_tensor(f"{pfx}_wzp", TensorProto.INT8, [scale.size], [0] * scale.size)]
dq = f"{pfx}_wdq"
new_nodes.append(helper.make_node("DequantizeLinear", [ws, f"{pfx}_wsc", f"{pfx}_wzp"], [dq], name=f"{pfx}_DQw", axis=qax))
if vt:
for i, inp in enumerate(tn.input):
if inp == ws: tn.input[i] = dq
else: n.input[1] = dq
done += 1
g.initializer.extend(new_inits); g.node.extend(new_nodes)
avail = {i.name for i in g.initializer} | {i.name for i in g.input} | {""}
order, rem = [], list(g.node)
while rem:
nx, prog = [], False
for nd in rem:
if all(i in avail for i in nd.input): order.append(nd); [avail.add(o) for o in nd.output]; prog = True
else: nx.append(nd)
rem = nx
if not prog: raise RuntimeError("topo stuck")
del g.node[:]; g.node.extend(order)
OUT = "/weka2/cj/clod/sames_fp8/samel_w8_gptq.onnx"
for f in (OUT, OUT + ".data"):
if os.path.exists(f): os.remove(f)
onnx.save(m, OUT, save_as_external_data=True, all_tensors_to_one_file=True, location=os.path.basename(OUT) + ".data", size_threshold=1024)
print(f"GPTQ int8 grafted on {done} SAME-L FFN linears -> {OUT}", flush=True)