Upload folder using huggingface_hub
Browse files
decider/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (174 Bytes)
|
|
|
decider/__pycache__/infer.cpython-312.pyc
DELETED
|
Binary file (13.5 kB)
|
|
|
decider/__pycache__/model.cpython-312.pyc
DELETED
|
Binary file (4.86 kB)
|
|
|
decider/__pycache__/prompt.cpython-312.pyc
DELETED
|
Binary file (3.66 kB)
|
|
|
decider/engine.py
CHANGED
|
@@ -19,25 +19,67 @@ def _bucket(x, buckets):
|
|
| 19 |
return None
|
| 20 |
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
class Engine:
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
self.m = DecisionModel(path, dtype=dtype, grad_ckpt=False).to(device).eval()
|
| 25 |
self.tok = self.m.tok; self.dev = device; self.use_graphs = use_graphs; self.max_ctx = max_ctx_tokens
|
| 26 |
self.core, self.W = self.m.lm.model, self.m.lm.lm_head.weight[self.m.letters].detach().clone()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
self.graphs = {} # (B, T) -> (static_ids, static_out, graph)
|
| 28 |
self.pool = torch.cuda.graph_pool_handle() if use_graphs else None
|
| 29 |
self.stats = dict(graph_captures=0, forwards=0)
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
@torch.no_grad()
|
| 32 |
def _fwd(self, ids):
|
| 33 |
-
|
| 34 |
-
return F.linear(h, self.W).float() # [B, T, K]
|
| 35 |
|
| 36 |
def _capture(self, B, T):
|
| 37 |
s_ids = torch.full((B, T), self.tok.pad_token_id, dtype=torch.long, device=self.dev)
|
| 38 |
st = torch.cuda.Stream(); st.wait_stream(torch.cuda.current_stream())
|
| 39 |
with torch.cuda.stream(st):
|
| 40 |
-
for _ in range(
|
| 41 |
torch.cuda.current_stream().wait_stream(st)
|
| 42 |
g = torch.cuda.CUDAGraph()
|
| 43 |
with torch.cuda.graph(g, pool=self.pool):
|
|
@@ -89,8 +131,9 @@ if __name__ == "__main__":
|
|
| 89 |
from . import data as D
|
| 90 |
from .infer import Decider
|
| 91 |
path = sys.argv[1] if len(sys.argv) > 1 else "runs/r3_v2/model"
|
|
|
|
| 92 |
_, evals = D.load_cache("data/tasks.pkl")
|
| 93 |
-
eng = Engine(path)
|
| 94 |
rng = random.Random(0)
|
| 95 |
exs = evals["support_tickets"][:64] + evals["clinc_oos"][:64] + evals["race"][:32]
|
| 96 |
items = [build(e, eng.tok, rng, max_ctx_tokens=1536) for e in exs]
|
|
|
|
| 19 |
return None
|
| 20 |
|
| 21 |
|
| 22 |
+
def fused_causal_conv1d_fn(hidden_states, weight, bias=None, activation=None, **kwargs):
|
| 23 |
+
"""Depthwise causal conv (kernel k) as k shifted multiply-adds: fuses under torch.compile,
|
| 24 |
+
unlike the cuDNN grouped conv fallback (which was ~11% of batched GPU time)."""
|
| 25 |
+
B, C, T = hidden_states.shape; k = weight.shape[-1]
|
| 26 |
+
x = F.pad(hidden_states.to(weight.dtype), (k - 1, 0))
|
| 27 |
+
out = x[:, :, k - 1:k - 1 + T] * weight[:, k - 1][None, :, None]
|
| 28 |
+
for j in range(k - 1):
|
| 29 |
+
out = out + x[:, :, j:j + T] * weight[:, j][None, :, None]
|
| 30 |
+
if bias is not None:
|
| 31 |
+
out = out + bias[None, :, None]
|
| 32 |
+
if activation == "silu":
|
| 33 |
+
out = F.silu(out)
|
| 34 |
+
elif activation is not None:
|
| 35 |
+
from transformers.activations import ACT2FN
|
| 36 |
+
out = ACT2FN[activation](out)
|
| 37 |
+
return out.to(hidden_states.dtype)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def patch_conv():
|
| 41 |
+
from transformers.models.qwen3_5 import modeling_qwen3_5 as mq
|
| 42 |
+
mq.causal_conv1d_fn = fused_causal_conv1d_fn
|
| 43 |
+
|
| 44 |
+
|
| 45 |
class Engine:
|
| 46 |
+
"""compile: torch.compile the forward (needs use_cache=False; ~1.4x batched, fuses elementwise work).
|
| 47 |
+
fp8: e4m3 weights + per-token activation scaling on the big linears (Hopper tensor cores).
|
| 48 |
+
conv_patch: fusable depthwise causal conv instead of the cuDNN fallback."""
|
| 49 |
+
def __init__(self, path, device="cuda", dtype=torch.bfloat16, use_graphs=True, max_ctx_tokens=1536,
|
| 50 |
+
compile=True, fp8=False, conv_patch=True):
|
| 51 |
+
if conv_patch:
|
| 52 |
+
patch_conv()
|
| 53 |
self.m = DecisionModel(path, dtype=dtype, grad_ckpt=False).to(device).eval()
|
| 54 |
self.tok = self.m.tok; self.dev = device; self.use_graphs = use_graphs; self.max_ctx = max_ctx_tokens
|
| 55 |
self.core, self.W = self.m.lm.model, self.m.lm.lm_head.weight[self.m.letters].detach().clone()
|
| 56 |
+
self.cfg = dict(compile=compile, fp8=fp8, conv_patch=conv_patch, graphs=use_graphs)
|
| 57 |
+
if fp8:
|
| 58 |
+
from .fp8 import convert_to_fp8
|
| 59 |
+
self.cfg["fp8_layers"] = convert_to_fp8(self.core)
|
| 60 |
+
if compile:
|
| 61 |
+
import torch._dynamo
|
| 62 |
+
torch._dynamo.config.cache_size_limit = 128
|
| 63 |
+
self._fwd_impl = torch.compile(self._fwd_eager, dynamic=False)
|
| 64 |
+
else:
|
| 65 |
+
self._fwd_impl = self._fwd_eager
|
| 66 |
self.graphs = {} # (B, T) -> (static_ids, static_out, graph)
|
| 67 |
self.pool = torch.cuda.graph_pool_handle() if use_graphs else None
|
| 68 |
self.stats = dict(graph_captures=0, forwards=0)
|
| 69 |
|
| 70 |
+
def _fwd_eager(self, ids):
|
| 71 |
+
h = self.core(input_ids=ids, use_cache=False).last_hidden_state
|
| 72 |
+
return F.linear(h, self.W).float() # [B, T, K]
|
| 73 |
+
|
| 74 |
@torch.no_grad()
|
| 75 |
def _fwd(self, ids):
|
| 76 |
+
return self._fwd_impl(ids)
|
|
|
|
| 77 |
|
| 78 |
def _capture(self, B, T):
|
| 79 |
s_ids = torch.full((B, T), self.tok.pad_token_id, dtype=torch.long, device=self.dev)
|
| 80 |
st = torch.cuda.Stream(); st.wait_stream(torch.cuda.current_stream())
|
| 81 |
with torch.cuda.stream(st):
|
| 82 |
+
for _ in range(3): self._fwd(s_ids) # warm-up: compile / triton autotune
|
| 83 |
torch.cuda.current_stream().wait_stream(st)
|
| 84 |
g = torch.cuda.CUDAGraph()
|
| 85 |
with torch.cuda.graph(g, pool=self.pool):
|
|
|
|
| 131 |
from . import data as D
|
| 132 |
from .infer import Decider
|
| 133 |
path = sys.argv[1] if len(sys.argv) > 1 else "runs/r3_v2/model"
|
| 134 |
+
cfg = dict(compile="nocompile" not in sys.argv[2:], fp8="fp8" in sys.argv[2:], conv_patch="noconv" not in sys.argv[2:])
|
| 135 |
_, evals = D.load_cache("data/tasks.pkl")
|
| 136 |
+
eng = Engine(path, **cfg); print("engine cfg", eng.cfg)
|
| 137 |
rng = random.Random(0)
|
| 138 |
exs = evals["support_tickets"][:64] + evals["clinc_oos"][:64] + evals["race"][:32]
|
| 139 |
items = [build(e, eng.tok, rng, max_ctx_tokens=1536) for e in exs]
|
decider/fp8.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FP8 (e4m3) linear layers for Hopper via torch._scaled_mm.
|
| 2 |
+
Weights: per-output-channel scales, quantised once. Activations: per-token dynamic scales.
|
| 3 |
+
Under torch.compile the quantisation ops fuse into the surrounding elementwise work."""
|
| 4 |
+
import torch, torch.nn as nn
|
| 5 |
+
|
| 6 |
+
E4M3_MAX = 448.0
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def _quant_rowwise(x):
|
| 10 |
+
s = x.abs().amax(dim=-1, keepdim=True).float().clamp(min=1e-12) / E4M3_MAX
|
| 11 |
+
return (x.float() / s).clamp(-E4M3_MAX, E4M3_MAX).to(torch.float8_e4m3fn), s
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class FP8Linear(nn.Module):
|
| 15 |
+
def __init__(self, lin: nn.Linear):
|
| 16 |
+
super().__init__()
|
| 17 |
+
wq, sw = _quant_rowwise(lin.weight.detach()) # [N,K] fp8, [N,1]
|
| 18 |
+
self.register_buffer("wq", wq.contiguous()) # [N,K]; passed as wq.t() -> [K,N] column-major, as _scaled_mm wants
|
| 19 |
+
self.register_buffer("sw_t", sw.t().contiguous()) # [1,N]
|
| 20 |
+
self.bias = None if lin.bias is None else nn.Parameter(lin.bias.detach().clone(), requires_grad=False)
|
| 21 |
+
self.in_features, self.out_features = lin.in_features, lin.out_features
|
| 22 |
+
self.out_dtype = lin.weight.dtype
|
| 23 |
+
|
| 24 |
+
def forward(self, x):
|
| 25 |
+
shp = x.shape[:-1]
|
| 26 |
+
x2 = x.reshape(-1, self.in_features)
|
| 27 |
+
xq, sx = _quant_rowwise(x2)
|
| 28 |
+
y = torch._scaled_mm(xq, self.wq.t(), scale_a=sx, scale_b=self.sw_t, bias=self.bias, out_dtype=self.out_dtype)
|
| 29 |
+
return y.reshape(*shp, self.out_features)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def convert_to_fp8(model, skip=("lm_head",), min_dim=1024):
|
| 33 |
+
"""Replace nn.Linear (with in/out >= min_dim) by FP8Linear in place. Returns count."""
|
| 34 |
+
n = 0
|
| 35 |
+
for name, mod in list(model.named_modules()):
|
| 36 |
+
for cname, child in list(mod.named_children()):
|
| 37 |
+
full = f"{name}.{cname}" if name else cname
|
| 38 |
+
if isinstance(child, nn.Linear) and not any(s in full for s in skip) and min(child.in_features, child.out_features) >= min_dim:
|
| 39 |
+
setattr(mod, cname, FP8Linear(child)); n += 1
|
| 40 |
+
return n
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
import time
|
| 45 |
+
lin = nn.Linear(2048, 6144, bias=False).cuda().to(torch.bfloat16)
|
| 46 |
+
f8 = FP8Linear(lin)
|
| 47 |
+
x = torch.randn(8192, 2048, device="cuda", dtype=torch.bfloat16)
|
| 48 |
+
ref = lin(x); got = f8(x)
|
| 49 |
+
print("rel err", ((ref.float() - got.float()).abs().mean() / ref.float().abs().mean()).item())
|
| 50 |
+
for f, name in [(lin, "bf16 linear"), (f8, "fp8 linear (eager)"), (torch.compile(f8), "fp8 linear (compiled)")]:
|
| 51 |
+
for _ in range(3): f(x)
|
| 52 |
+
torch.cuda.synchronize(); t = time.time()
|
| 53 |
+
for _ in range(20): f(x)
|
| 54 |
+
torch.cuda.synchronize(); print(f"{name:24s} {(time.time()-t)/20*1000:.3f} ms")
|
decider/infer.py
CHANGED
|
@@ -24,8 +24,16 @@ class Example:
|
|
| 24 |
|
| 25 |
|
| 26 |
class Decider:
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
self.dev = device; self.T = temperature; self.abstain_below = abstain_below
|
| 30 |
|
| 31 |
@torch.no_grad()
|
|
@@ -40,10 +48,13 @@ class Decider:
|
|
| 40 |
def shuffle(self, x): pass
|
| 41 |
def sample(self, xs, k): return xs[:k]
|
| 42 |
items = [build(e, self.m.tok, _NoShuffle(), max_ctx_tokens=max_ctx_tokens) for e in exs]
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
| 47 |
out, k = [], 0
|
| 48 |
for context, qs in requests:
|
| 49 |
res = []
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
class Decider:
|
| 27 |
+
"""use_graphs=True (default on CUDA) routes scoring through decider.engine.Engine: shape-bucketed
|
| 28 |
+
CUDA graphs, ~7x lower single-request latency than eager. Set False for CPU or debugging."""
|
| 29 |
+
def __init__(self, path, device="cuda", dtype=torch.bfloat16, temperature=1.0, abstain_below=0.0, use_graphs=None):
|
| 30 |
+
if use_graphs is None:
|
| 31 |
+
use_graphs = str(device).startswith("cuda")
|
| 32 |
+
if use_graphs:
|
| 33 |
+
from .engine import Engine
|
| 34 |
+
self.eng = Engine(path, device=device, dtype=dtype); self.m = self.eng.m
|
| 35 |
+
else:
|
| 36 |
+
self.eng = None; self.m = DecisionModel(path, dtype=dtype, grad_ckpt=False).to(device).eval()
|
| 37 |
self.dev = device; self.T = temperature; self.abstain_below = abstain_below
|
| 38 |
|
| 39 |
@torch.no_grad()
|
|
|
|
| 48 |
def shuffle(self, x): pass
|
| 49 |
def sample(self, xs, k): return xs[:k]
|
| 50 |
items = [build(e, self.m.tok, _NoShuffle(), max_ctx_tokens=max_ctx_tokens) for e in exs]
|
| 51 |
+
if self.eng is not None:
|
| 52 |
+
probs = torch.cat(self.eng.score_items(items, temperature=self.T))
|
| 53 |
+
else:
|
| 54 |
+
b = collate(items, self.m.tok.pad_token_id)
|
| 55 |
+
logits = self.m.slot_logits(b["input_ids"].to(self.dev), b["attention_mask"].to(self.dev), b["slot_idx"].to(self.dev),
|
| 56 |
+
b["slot_batch"].to(self.dev), b["nopts"].to(self.dev))
|
| 57 |
+
probs = torch.softmax(logits / self.T, -1).cpu()
|
| 58 |
out, k = [], 0
|
| 59 |
for context, qs in requests:
|
| 60 |
res = []
|