brulee-1 commited on
Commit
a75dbb6
Β·
verified Β·
1 Parent(s): 011504b

Upload inference.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. inference.py +377 -0
inference.py ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SSMoELM Packed Inference β€” 12MB パヒγƒͺζŽ¨θ«–
3
+ packed uint8 weights をパヒγƒͺγ«δΏζŒγ—γ€forward時にγ‚ͺγƒ³γƒ‡γƒžγƒ³γƒ‰γ§ unpack する。
4
+
5
+ 使い方:
6
+ python inference_packed.py --prompt "Hello"
7
+ """
8
+ import argparse
9
+ import math
10
+ from pathlib import Path
11
+
12
+ import numpy as np
13
+ import torch
14
+ import torch.nn as nn
15
+ import torch.nn.functional as F
16
+ from safetensors.numpy import load_file
17
+ from tokenizers import Tokenizer
18
+
19
+ D_MODEL = 768
20
+ N_LAYERS = 6
21
+ N_HEADS = 12
22
+ KV_HEADS = 3
23
+ HEAD_DIM = 64
24
+ N_EXPERTS = 8
25
+ N_ACTIVE = 2
26
+ D_FF = 256
27
+ VOCAB_SIZE = 8192
28
+ CTX_LEN = 2048
29
+
30
+ BOS_ID, EOS_ID, EOT_ID = 0, 1, 6
31
+
32
+
33
+ # ── Packed Linear Modules ────────────────────────────────────────────────────
34
+
35
+ class Linear1bit(nn.Module):
36
+ """1-bit packed linear layer: scale(fp16) + packed bits(uint8) β†’ fp32 matmul on-the-fly"""
37
+ def __init__(self, out_f: int, in_f: int):
38
+ super().__init__()
39
+ self.in_features = in_f
40
+ self.register_buffer("scale", torch.zeros(out_f, dtype=torch.float16))
41
+ self.register_buffer("packed", torch.zeros(out_f, (in_f + 7) // 8, dtype=torch.uint8))
42
+
43
+ def _unpack(self) -> torch.Tensor:
44
+ # packed: [out, ceil(in/8)] β†’ [out, in] values Β±1
45
+ bits = ((self.packed.unsqueeze(-1)
46
+ >> torch.arange(7, -1, -1, device=self.packed.device, dtype=torch.uint8))
47
+ & 1) # [out, ceil(in/8), 8]
48
+ bits = bits.reshape(self.packed.shape[0], -1)[:, :self.in_features] # [out, in]
49
+ w = bits.float() * 2.0 - 1.0 # {0,1} β†’ {-1,1}
50
+ w = w * self.scale.float().unsqueeze(-1) # row-wise scale
51
+ return w # [out, in]
52
+
53
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
54
+ w = self._unpack()
55
+ out = F.linear(x, w)
56
+ del w
57
+ return out
58
+
59
+
60
+ class Linear4bit(nn.Module):
61
+ """4-bit nibble-packed linear layer"""
62
+ def __init__(self, out_f: int, in_f: int):
63
+ super().__init__()
64
+ self.in_features = in_f
65
+ self.register_buffer("scale", torch.zeros(out_f, dtype=torch.float16))
66
+ self.register_buffer("packed", torch.zeros(out_f, (in_f + 1) // 2, dtype=torch.uint8))
67
+
68
+ def _unpack(self) -> torch.Tensor:
69
+ lo = (self.packed & 0x0F).to(torch.int8) - 8 # [out, in//2]
70
+ hi = ((self.packed >> 4) & 0x0F).to(torch.int8) - 8
71
+ w = torch.stack([lo, hi], dim=-1).reshape(self.packed.shape[0], -1)
72
+ w = w[:, :self.in_features].float()
73
+ w = w * self.scale.float().unsqueeze(-1)
74
+ return w
75
+
76
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
77
+ w = self._unpack()
78
+ out = F.linear(x, w)
79
+ del w
80
+ return out
81
+
82
+
83
+ class EmbeddingPacked(nn.Module):
84
+ """4-bit packed embedding table"""
85
+ def __init__(self, vocab: int, d: int):
86
+ super().__init__()
87
+ self.vocab = vocab
88
+ self.d = d
89
+ self.register_buffer("scale", torch.zeros(vocab, dtype=torch.float16))
90
+ self.register_buffer("packed", torch.zeros(vocab, (d + 1) // 2, dtype=torch.uint8))
91
+
92
+ def get_weight(self) -> torch.Tensor:
93
+ lo = (self.packed & 0x0F).to(torch.int8) - 8
94
+ hi = ((self.packed >> 4) & 0x0F).to(torch.int8) - 8
95
+ w = torch.stack([lo, hi], dim=-1).reshape(self.vocab, -1)[:, :self.d].float()
96
+ return w * self.scale.float().unsqueeze(-1)
97
+
98
+ def forward(self, idx: torch.Tensor) -> torch.Tensor:
99
+ return self.get_weight()[idx]
100
+
101
+
102
+ # ── Model ─────────────────────────────────────────────────────────────────────
103
+
104
+ class RMSNorm(nn.Module):
105
+ def __init__(self, d: int, eps: float = 1e-6):
106
+ super().__init__()
107
+ self.weight = nn.Parameter(torch.ones(d))
108
+ self.eps = eps
109
+
110
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
111
+ rms = (x.float().pow(2).mean(-1, keepdim=True) + self.eps).rsqrt()
112
+ return (self.weight * (x.float() * rms)).to(x.dtype)
113
+
114
+
115
+ def precompute_rope(head_dim: int, max_len: int, base: float = 10000.0) -> torch.Tensor:
116
+ inv_freq = 1.0 / (base ** (torch.arange(0, head_dim, 2).float() / head_dim))
117
+ freqs = torch.outer(torch.arange(max_len).float(), inv_freq)
118
+ return torch.cat([freqs, freqs], dim=-1)
119
+
120
+
121
+ def rotate_half(x: torch.Tensor) -> torch.Tensor:
122
+ h = x.shape[-1] // 2
123
+ return torch.cat([-x[..., h:], x[..., :h]], dim=-1)
124
+
125
+
126
+ def apply_rope(x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
127
+ cos = freqs.cos()[None, :, None, :].to(x.dtype)
128
+ sin = freqs.sin()[None, :, None, :].to(x.dtype)
129
+ return x * cos + rotate_half(x) * sin
130
+
131
+
132
+ class Attention(nn.Module):
133
+ def __init__(self, layer_idx: int):
134
+ super().__init__()
135
+ boundary = {0, 5}
136
+ vo_4bit = layer_idx in boundary
137
+ d = D_MODEL
138
+ self.n_heads = N_HEADS
139
+ self.kv_heads = KV_HEADS
140
+ self.head_dim = HEAD_DIM
141
+ self.n_rep = N_HEADS // KV_HEADS
142
+ self.q_proj = Linear4bit(N_HEADS * HEAD_DIM, d)
143
+ self.k_proj = Linear4bit(KV_HEADS * HEAD_DIM, d)
144
+ self.v_proj = Linear4bit(KV_HEADS * HEAD_DIM, d) if vo_4bit else Linear1bit(KV_HEADS * HEAD_DIM, d)
145
+ self.o_proj = Linear4bit(d, N_HEADS * HEAD_DIM) if vo_4bit else Linear1bit(d, N_HEADS * HEAD_DIM)
146
+
147
+ def forward(self, x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
148
+ B, T, _ = x.shape
149
+ q = self.q_proj(x).reshape(B, T, self.n_heads, self.head_dim)
150
+ k = self.k_proj(x).reshape(B, T, self.kv_heads, self.head_dim)
151
+ v = self.v_proj(x).reshape(B, T, self.kv_heads, self.head_dim)
152
+ q, k = apply_rope(q, freqs), apply_rope(k, freqs)
153
+ k = k.repeat_interleave(self.n_rep, dim=2)
154
+ v = v.repeat_interleave(self.n_rep, dim=2)
155
+ out = F.scaled_dot_product_attention(
156
+ q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=True)
157
+ return self.o_proj(out.transpose(1, 2).reshape(B, T, -1))
158
+
159
+
160
+ class SwiGLU(nn.Module):
161
+ def __init__(self, d_model: int, d_ff: int, bits: int):
162
+ super().__init__()
163
+ L = Linear4bit if bits == 4 else Linear1bit
164
+ self.gate = L(d_ff, d_model)
165
+ self.up = L(d_ff, d_model)
166
+ self.down = L(d_model, d_ff)
167
+
168
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
169
+ return self.down(F.silu(self.gate(x)) * self.up(x))
170
+
171
+
172
+ class MoELayer(nn.Module):
173
+ def __init__(self):
174
+ super().__init__()
175
+ self.shared = SwiGLU(D_MODEL, D_FF, bits=4)
176
+ # stacked routed expert weights (1-bit)
177
+ self.gate_scale = nn.ParameterList([nn.Parameter(torch.zeros(D_FF), requires_grad=False) for _ in range(N_EXPERTS)])
178
+ self.gate_packed = nn.ParameterList([nn.Parameter(torch.zeros(D_FF, (D_MODEL+7)//8, dtype=torch.uint8), requires_grad=False) for _ in range(N_EXPERTS)])
179
+ self.up_scale = nn.ParameterList([nn.Parameter(torch.zeros(D_FF), requires_grad=False) for _ in range(N_EXPERTS)])
180
+ self.up_packed = nn.ParameterList([nn.Parameter(torch.zeros(D_FF, (D_MODEL+7)//8, dtype=torch.uint8), requires_grad=False) for _ in range(N_EXPERTS)])
181
+ self.down_scale = nn.ParameterList([nn.Parameter(torch.zeros(D_MODEL), requires_grad=False) for _ in range(N_EXPERTS)])
182
+ self.down_packed = nn.ParameterList([nn.Parameter(torch.zeros(D_MODEL, (D_FF+7)//8, dtype=torch.uint8), requires_grad=False) for _ in range(N_EXPERTS)])
183
+ self.router = nn.Parameter(torch.zeros(N_EXPERTS, D_MODEL))
184
+
185
+ def _unpack1bit(self, scale: torch.Tensor, packed: torch.Tensor, in_f: int) -> torch.Tensor:
186
+ bits = ((packed.unsqueeze(-1)
187
+ >> torch.arange(7, -1, -1, device=packed.device, dtype=torch.uint8)) & 1)
188
+ bits = bits.reshape(packed.shape[0], -1)[:, :in_f].float() * 2.0 - 1.0
189
+ return bits * scale.float().unsqueeze(-1)
190
+
191
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
192
+ B, T, d = x.shape
193
+ shared_out = self.shared(x)
194
+
195
+ logits = x @ self.router.T
196
+ top_idx = logits.topk(N_ACTIVE, dim=-1).indices
197
+ top_w = F.softmax(logits.gather(-1, top_idx).float(), dim=-1).to(x.dtype)
198
+
199
+ # process only active experts (memory-efficient)
200
+ x_flat = x.reshape(B * T, d)
201
+ out = torch.zeros_like(x_flat)
202
+
203
+ for k in range(N_ACTIVE):
204
+ e_idx = top_idx[..., k].reshape(-1) # [B*T]
205
+ w_k = top_w[..., k].reshape(-1, 1) # [B*T, 1]
206
+
207
+ for e in range(N_EXPERTS):
208
+ mask = (e_idx == e)
209
+ if not mask.any():
210
+ continue
211
+ x_e = x_flat[mask]
212
+ # unpack this expert's weights on-the-fly
213
+ wg = self._unpack1bit(self.gate_scale[e], self.gate_packed[e], D_MODEL)
214
+ wu = self._unpack1bit(self.up_scale[e], self.up_packed[e], D_MODEL)
215
+ wd = self._unpack1bit(self.down_scale[e], self.down_packed[e], D_FF)
216
+ h = F.silu(F.linear(x_e, wg)) * F.linear(x_e, wu)
217
+ out[mask] += F.linear(h, wd) * w_k[mask]
218
+ del wg, wu, wd, h
219
+
220
+ return shared_out + out.reshape(B, T, d)
221
+
222
+
223
+ class TransformerLayer(nn.Module):
224
+ def __init__(self, layer_idx: int):
225
+ super().__init__()
226
+ self.attn_norm = RMSNorm(D_MODEL)
227
+ self.ffn_norm = RMSNorm(D_MODEL)
228
+ self.attn = Attention(layer_idx)
229
+ self.moe = MoELayer()
230
+
231
+ def forward(self, x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
232
+ x = x + self.attn(self.attn_norm(x), freqs)
233
+ x = x + self.moe(self.ffn_norm(x))
234
+ return x
235
+
236
+
237
+ class SSMoELMPacked(nn.Module):
238
+ def __init__(self):
239
+ super().__init__()
240
+ self.embed = EmbeddingPacked(VOCAB_SIZE, D_MODEL)
241
+ self.layers = nn.ModuleList([TransformerLayer(i) for i in range(N_LAYERS)])
242
+ self.norm = RMSNorm(D_MODEL)
243
+ self.register_buffer("freqs", precompute_rope(HEAD_DIM, CTX_LEN))
244
+
245
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
246
+ T = x.shape[1]
247
+ h = self.embed(x).float()
248
+ freqs = self.freqs[:T]
249
+ for layer in self.layers:
250
+ h = layer(h, freqs)
251
+ h = self.norm(h)
252
+ w = self.embed.get_weight()
253
+ return h @ w.T
254
+
255
+ @torch.inference_mode()
256
+ def generate(self, input_ids: list[int], max_new_tokens: int = 200,
257
+ temperature: float = 0.8, top_p: float = 0.9,
258
+ eos_ids: tuple[int, ...] = (EOS_ID, EOT_ID)) -> list[int]:
259
+ ids = list(input_ids)
260
+ generated = []
261
+ for _ in range(max_new_tokens):
262
+ x = torch.tensor([ids[-CTX_LEN:]], dtype=torch.long)
263
+ logits = self(x)[0, -1]
264
+ if temperature > 0:
265
+ logits_np = logits.numpy().astype(np.float64)
266
+ logits_np = (logits_np - logits_np.max()) / temperature
267
+ probs = np.exp(logits_np); probs /= probs.sum()
268
+ idx = np.argsort(-probs); cumsum = np.cumsum(probs[idx])
269
+ cutoff = np.searchsorted(cumsum, top_p) + 1
270
+ probs[idx[cutoff:]] = 0.0; probs /= probs.sum()
271
+ next_id = int(np.random.choice(idx, p=probs))
272
+ else:
273
+ next_id = int(logits.argmax().item())
274
+ if next_id in eos_ids:
275
+ break
276
+ generated.append(next_id)
277
+ ids.append(next_id)
278
+ return generated
279
+
280
+
281
+ # ── Load ──────────────────────────────────────────────────────────────────────
282
+
283
+ def load_packed_model(path: str) -> SSMoELMPacked:
284
+ data = load_file(path)
285
+ data = {k.replace("/", "."): v for k, v in data.items()}
286
+
287
+ model = SSMoELMPacked()
288
+
289
+ def _set(module, scale_name, packed_name, key_base):
290
+ s_key = f"{key_base}__scale"
291
+ p_key_bin = f"{key_base}__bin"
292
+ p_key_int4 = f"{key_base}__int4"
293
+ if s_key in data:
294
+ getattr(module, scale_name).data.copy_(torch.from_numpy(data[s_key].astype(np.float16)))
295
+ if p_key_bin in data:
296
+ getattr(module, packed_name).data.copy_(torch.from_numpy(data[p_key_bin]))
297
+ elif p_key_int4 in data:
298
+ getattr(module, packed_name).data.copy_(torch.from_numpy(data[p_key_int4]))
299
+
300
+ # embed
301
+ _set(model.embed, "scale", "packed", "embed_weight")
302
+
303
+ for i, layer in enumerate(model.layers):
304
+ pfx = f"layers.{i}"
305
+ # attention
306
+ for proj_name, key in [("q_proj","q_weight"),("k_proj","k_weight"),
307
+ ("v_proj","v_weight"),("o_proj","o_weight")]:
308
+ proj = getattr(layer.attn, proj_name)
309
+ _set(proj, "scale", "packed", f"{pfx}.attn.{key}")
310
+ # norms
311
+ for norm_name, key in [("attn_norm","attn_norm"),("ffn_norm","ffn_norm")]:
312
+ norm = getattr(layer, norm_name)
313
+ w = data.get(f"{pfx}.{key}.weight")
314
+ if w is not None:
315
+ norm.weight.data.copy_(torch.from_numpy(w.astype(np.float32)))
316
+ # shared expert
317
+ se = layer.moe.shared
318
+ for attr, wname in [("gate","gate_weight"),("up","up_weight"),("down","down_weight")]:
319
+ _set(getattr(se, attr), "scale", "packed", f"{pfx}.moe.shared_expert.{wname}")
320
+ # router
321
+ rw = data.get(f"{pfx}.moe.router_weight")
322
+ if rw is not None:
323
+ layer.moe.router.data.copy_(torch.from_numpy(rw.astype(np.float32)))
324
+ # routed experts: stacked weights [E, out, in] β†’ scale [E*out], packed [E*out, ...]
325
+ for attr, wname, out_f, in_f in [
326
+ ("gate", "gate", D_FF, D_MODEL),
327
+ ("up", "up", D_FF, D_MODEL),
328
+ ("down", "down", D_MODEL, D_FF),
329
+ ]:
330
+ s_key = f"{pfx}.moe.{wname}_weight__scale"
331
+ p_key = (f"{pfx}.moe.{wname}_weight__bin"
332
+ if f"{pfx}.moe.{wname}_weight__bin" in data
333
+ else f"{pfx}.moe.{wname}_weight__int4")
334
+ if s_key not in data or p_key not in data:
335
+ continue
336
+ s_arr = data[s_key] # [E*out_f]
337
+ p_arr = data[p_key] # [E*out_f, packed_cols]
338
+ for e in range(N_EXPERTS):
339
+ sl = slice(e * out_f, (e + 1) * out_f)
340
+ getattr(layer.moe, f"{attr}_scale")[e].data.copy_(
341
+ torch.from_numpy(s_arr[sl].astype(np.float16)))
342
+ getattr(layer.moe, f"{attr}_packed")[e].data.copy_(
343
+ torch.from_numpy(p_arr[sl]))
344
+
345
+ return model.eval()
346
+
347
+
348
+ # ── CLI ───────────────────────────────────────────────────────────────────────
349
+
350
+ def main():
351
+ parser = argparse.ArgumentParser()
352
+ parser.add_argument("--ckpt", default="checkpoints/ssmoelm_base_packed.safetensors")
353
+ parser.add_argument("--tokenizer", default="tokenizer/tokenizer.json")
354
+ parser.add_argument("--prompt", default="The quick brown fox")
355
+ parser.add_argument("--max-tokens", type=int, default=200)
356
+ parser.add_argument("--temperature", type=float, default=0.8)
357
+ args = parser.parse_args()
358
+
359
+ print(f"Loading packed model from {args.ckpt} ...")
360
+ model = load_packed_model(args.ckpt)
361
+
362
+ packed_bytes = sum(
363
+ p.numel() * p.element_size() for p in model.buffers()
364
+ ) + sum(p.numel() * p.element_size() for p in model.parameters())
365
+ print(f"Memory footprint: {packed_bytes/1024/1024:.1f} MB (packed, no dequantization)")
366
+
367
+ tok = Tokenizer.from_file(args.tokenizer)
368
+ ids = [BOS_ID] + tok.encode(args.prompt).ids
369
+
370
+ print(f"\nPrompt: {args.prompt}")
371
+ print("Output: ", end="", flush=True)
372
+ out = model.generate(ids, args.max_tokens, args.temperature)
373
+ print(tok.decode(out))
374
+
375
+
376
+ if __name__ == "__main__":
377
+ main()