"""Apply REAP's expert selection to an original checkpoint (any expert dtype: NVFP4, FP8, BF16). REAP's prune.py picks, per layer, the n experts with the lowest saliency (torch.topk(saliency, n, largest=False) on the observer data) and keeps the rest in index order. This recomputes that exact selection from the saved observations, checks it against the router rows of REAP's own pruned checkpoint (--reference), and writes the source checkpoint with only the kept experts: per-expert tensors (`...layers..mlp.experts..*`; DeepSeek-V4.1 `layers..ffn.experts..*`) renumbered 0..K-1 in kept order, the router (`...mlp.gate.weight`; DeepSeek `ffn.gate.weight/bias/bias_vl`) sliced to the kept rows, the MTP draft layers (`mtp.*`) dropped, every other tensor and file unchanged, and num_experts (DeepSeek: n_routed_experts) = K in the (text) config. The kept indices go to /reap_selection.json. --pad-to N appends N - K experts no token can reach (zero weights, zero router rows, router bias -1e4; DeepSeek selects by score + bias with biases 9.6-14), for kernels that take only some expert counts; the model computes exactly what the K-expert model does. external/reap/.venv/bin/python external/reap/experiments/apex/apply_selection.py \ --observations /all/observations_1024_cosine-seed_42.pt --ratio 0.5 \ --source --reference --out """ import argparse import json import re import shutil from pathlib import Path import torch from safetensors import safe_open from safetensors.torch import save_file # Qwen: ...layers..mlp.experts..* and ...mlp.gate.weight; DeepSeek-V4.1: layers..ffn.experts..* # and layers..ffn.gate.{weight, bias, bias_vl} (all indexed by expert) EXPERT = re.compile(r"^((?:.*\.)?layers\.(\d+)\.(?:mlp|ffn)\.experts\.)(\d+)(\..*)$") ROUTER = re.compile(r"^(?:.*\.)?layers\.(\d+)\.(?:mlp|ffn)\.gate\.(?:weight|bias|bias_vl)$") SHARD_BYTES = 5 * 2**30 def selection(observations: Path, ratio: float, metric: str) -> dict[int, list[int]]: """REAP prune.py's choice: per layer, prune the n lowest-saliency experts, keep the rest.""" data = torch.load(observations, weights_only=False) kept = {} for layer, state in data.items(): num_experts = state["expert_frequency"].shape[0] n = int(num_experts * ratio) _, pruned = torch.topk(state[metric], n, largest=False) pruned = set(pruned.tolist()) kept[int(layer)] = [i for i in range(num_experts) if i not in pruned] return kept def reference_routers(reference: Path) -> dict[int, torch.Tensor]: out = {} for f in sorted(reference.glob("*.safetensors")): with safe_open(f, "pt") as fh: for k in fh.keys(): if (m := ROUTER.match(k)) and k.endswith(".weight"): out[int(m.group(1))] = fh.get_tensor(k) return out def main() -> None: parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) parser.add_argument("--observations", required=True) parser.add_argument("--ratio", type=float, required=True, help="share of experts pruned per layer") parser.add_argument("--metric", default="reap") parser.add_argument("--source", required=True, help="original checkpoint directory") parser.add_argument("--reference", default=None, help="REAP's pruned checkpoint (router check)") parser.add_argument("--out", required=True) parser.add_argument( "--pad-to", type=int, default=None, help="append never-routed experts up to this count (DeepSeek: vLLM's sqrtsoftplus top-k kernel " "takes 64/128/192/256/384 experts): zero weights, zero router rows, router bias -1e4", ) args = parser.parse_args() source, out = Path(args.source), Path(args.out) kept = selection(Path(args.observations), args.ratio, args.metric) k = {len(v) for v in kept.values()} assert len(k) == 1, k k = k.pop() index = json.loads((source / "model.safetensors.index.json").read_text())["weight_map"] routers = {} for name, f in index.items(): if (m := ROUTER.match(name)) and not name.startswith("mtp.") and name.endswith(".weight"): with safe_open(source / f, "pt") as fh: routers[int(m.group(1))] = fh.get_tensor(name) assert sorted(routers) == sorted(kept), (sorted(routers)[:5], sorted(kept)[:5]) if args.reference: ref = reference_routers(Path(args.reference)) assert sorted(ref) == sorted(kept), "reference layers differ" for layer, rows in kept.items(): same = torch.equal(routers[layer][rows].to(ref[layer].dtype), ref[layer]) assert same, f"layer {layer}: kept set differs" print(f"selection matches REAP's pruned checkpoint in all {len(kept)} layers") out.mkdir(parents=True, exist_ok=False) new_index, shard, size, n_shard = {}, {}, 0, 0 def flush() -> None: nonlocal shard, size, n_shard if shard: n_shard += 1 name = f"model-{n_shard:05d}.safetensors" save_file(shard, out / name, metadata={"format": "pt"}) new_index.update({key: name for key in shard}) shard, size = {}, 0 position = {layer: {e: j for j, e in enumerate(rows)} for layer, rows in kept.items()} pad = 0 if args.pad_to is None else args.pad_to - k assert pad >= 0, (args.pad_to, k) templates: dict[int, dict[str, tuple[str, torch.Tensor]]] = {} # layer -> suffix -> (prefix, tensor) by_file: dict[str, list[str]] = {} for name, f in index.items(): by_file.setdefault(f, []).append(name) dropped = 0 for f, names in sorted(by_file.items()): with safe_open(source / f, "pt") as fh: for name in sorted(names): if name.startswith("mtp."): dropped += 1 continue if m := EXPERT.match(name): layer, expert = int(m.group(2)), int(m.group(3)) if expert not in position[layer]: dropped += 1 continue key, tensor = f"{m.group(1)}{position[layer][expert]}{m.group(4)}", fh.get_tensor(name) if pad and m.group(4) not in templates.setdefault(layer, {}): templates[layer][m.group(4)] = (m.group(1), torch.zeros_like(tensor)) elif (m := ROUTER.match(name)) and int(m.group(1)) in kept: key, tensor = name, fh.get_tensor(name)[kept[int(m.group(1))]].contiguous() if pad: fill = -1e4 if not name.endswith(".weight") else 0.0 extra = torch.full((pad, *tensor.shape[1:]), fill, dtype=tensor.dtype) tensor = torch.cat([tensor, extra]) else: key, tensor = name, fh.get_tensor(name) shard[key] = tensor size += tensor.numel() * tensor.element_size() if size >= SHARD_BYTES: flush() for layer, suffixes in sorted(templates.items()): for suffix, (prefix, zero) in suffixes.items(): for j in range(k, k + pad): shard[f"{prefix}{j}{suffix}"] = zero.clone() # safetensors refuses shared storage size += zero.numel() * zero.element_size() if size >= SHARD_BYTES: flush() flush() total = sum((out / f).stat().st_size for f in set(new_index.values())) (out / "model.safetensors.index.json").write_text( json.dumps({"metadata": {"total_size": total}, "weight_map": dict(sorted(new_index.items()))}, indent=2) ) config = json.loads((source / "config.json").read_text()) text = config.get("text_config", config) text["n_routed_experts" if "n_routed_experts" in text else "num_experts"] = k + pad (out / "config.json").write_text(json.dumps(config, indent=2) + "\n") for f in source.iterdir(): if f.is_file() and not f.name.endswith(".safetensors") and f.name not in ( "model.safetensors.index.json", "config.json", ): shutil.copy(f, out / f.name) (out / "reap_selection.json").write_text( json.dumps( { "observations": str(Path(args.observations).resolve()), "ratio_pruned": args.ratio, "metric": args.metric, "source": str(source.resolve()), "kept_per_layer": k, "padding_experts": pad, # ids k..k+pad-1: zero weights, router bias -1e4, never routed "kept": {str(layer): rows for layer, rows in sorted(kept.items())}, } ) + "\n" ) print(f"{out}: {k} experts per layer kept, {dropped} tensors dropped, {total / 1e9:.1f} GB") if __name__ == "__main__": main()