| |
| """Collect per-layer expert routing histograms from the hybrid GLM-5.2. |
| |
| Uses vLLM's enable_return_routed_experts to get [seq_len, n_moe_layers, |
| topk] expert ids per request, over a mixed local calibration corpus |
| (code + docs + synthetic instructions). Prefill-only (max_tokens=1). |
| |
| Output: /data/glm52-expert-stats.npz with counts [n_layers, 256]. |
| """ |
| import glob |
| import json |
| import os |
| import random |
|
|
| import numpy as np |
|
|
| MODEL = "/data/glm52" |
| OUT = "/data/glm52-expert-stats.npz" |
| N_EXPERTS = 256 |
| PROMPT_TOKENS = 6000 |
| N_PROMPTS = 192 |
|
|
|
|
| def build_corpus() -> list[str]: |
| random.seed(7) |
| texts = [] |
| |
| py = sorted(glob.glob("/home/coder/git/glm52/vllm/vllm/**/*.py", recursive=True)) |
| random.shuffle(py) |
| |
| md = sorted(glob.glob("/home/coder/git/glm52/vllm/docs/**/*.md", recursive=True)) |
| random.shuffle(md) |
|
|
| def chunks(paths, n): |
| out, buf = [], "" |
| for p in paths: |
| try: |
| buf += open(p, errors="ignore").read() + "\n\n" |
| except OSError: |
| continue |
| while len(buf) >= PROMPT_TOKENS * 4: |
| out.append(buf[: PROMPT_TOKENS * 4]) |
| buf = buf[PROMPT_TOKENS * 4:] |
| if len(out) >= n: |
| return out |
| if buf: |
| out.append(buf) |
| return out[:n] |
|
|
| texts += chunks(py, N_PROMPTS // 2) |
| texts += chunks(md, N_PROMPTS // 4) |
| |
| topics = ["quantum computing", "the French Revolution", "sourdough bread", |
| "distributed databases", "protein folding", "jazz harmony", |
| "supply chains", "volcanoes", "Rust lifetimes", "photosynthesis", |
| "medieval trade routes", "black holes"] |
| templates = [ |
| "Explain {t} to a beginner, covering the key concepts step by step, " |
| "common misconceptions, practical examples, and finally an advanced " |
| "summary with open research questions. Be very detailed.\n\n", |
| "Write a detailed technical design document about building a system " |
| "related to {t}: requirements, architecture, tradeoffs, testing plan, " |
| "rollout strategy, and failure modes.\n\n", |
| "You are a helpful assistant. The user asks a long multi-part " |
| "question about {t}. Answer each part with careful reasoning:\n" |
| "1) history 2) fundamentals 3) state of the art 4) critiques " |
| "5) future directions.\n\n", |
| ] |
| synth = [] |
| while len(synth) < N_PROMPTS // 4: |
| t = random.choice(topics) |
| body = random.choice(templates).format(t=t) |
| synth.append((body * 40)[: PROMPT_TOKENS * 4]) |
| texts += synth |
| random.shuffle(texts) |
| return texts |
|
|
|
|
| def main(): |
| os.environ.setdefault("VLLM_PP_LAYER_PARTITION", "19,20,21,18") |
| stats_dir = "/data/glm52-expert-stats" |
| os.environ["VLLM_HYBRID_EXPERT_STATS"] = stats_dir |
| from vllm import LLM, SamplingParams |
|
|
| llm = LLM( |
| model=MODEL, |
| pipeline_parallel_size=4, |
| gpu_memory_utilization=0.51, |
| kv_cache_dtype="fp8_ds_mla", |
| max_model_len=8192, |
| max_num_seqs=2, |
| max_num_batched_tokens=4096, |
| enforce_eager=True, |
| ) |
| from vllm.inputs import TokensPrompt |
|
|
| texts = build_corpus() |
| tok = llm.get_tokenizer() |
| prompts = [ |
| TokensPrompt(prompt_token_ids=tok.encode(t)[:7900]) for t in texts |
| ] |
| print(f"{len(prompts)} calibration prompts") |
| sp = SamplingParams(max_tokens=1, temperature=0.0) |
| outs = llm.generate(prompts, sp) |
| total_tokens = sum(len(o.prompt_token_ids) for o in outs) |
| del llm |
|
|
| |
| files = sorted(glob.glob(os.path.join(stats_dir, "layer_*.npy"))) |
| assert files, f"no stats dumped in {stats_dir}" |
| layer_ids = sorted(int(os.path.basename(f)[6:-4]) for f in files) |
| counts = np.stack( |
| [np.load(os.path.join(stats_dir, f"layer_{li}.npy")) for li in layer_ids] |
| ) |
| np.savez(OUT, counts=counts, layer_ids=np.array(layer_ids), |
| total_tokens=total_tokens) |
| |
| frac = counts / counts.sum(axis=1, keepdims=True).clip(min=1) |
| sorted_frac = np.sort(frac, axis=1)[:, ::-1] |
| top32 = sorted_frac[:, :32].sum(axis=1) |
| top64 = sorted_frac[:, :64].sum(axis=1) |
| print(f"tokens: {total_tokens}, layers: {counts.shape[0]}") |
| print(f"routing mass in top-32 experts: mean {top32.mean():.3f} " |
| f"min {top32.min():.3f} max {top32.max():.3f}") |
| print(f"routing mass in top-64 experts: mean {top64.mean():.3f} " |
| f"min {top64.min():.3f} max {top64.max():.3f}") |
| print("saved", OUT) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|