make_padded.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #!/usr/bin/env python3
  2. # SPDX-License-Identifier: Apache-2.0
  3. """Build the TP=3 serving dirs on one node (run inside the spark-glm53 image).
  4. target: hardlinks of every checkpoint file + a config.json with padded sizes
  5. and tp_pad_orig (overlay/tp3pad.py pads the tensors at load time).
  6. drafter: DFlash2 (Qwen3, 32 q / 8 kv heads) padded offline to 36 q / 9 kv so
  7. each rank holds 12 q / 3 kv heads with the same 4:1 grouping. Zero
  8. q/k/v rows and zero o_proj cols are no-ops.
  9. usage: make_padded.py SRC_MODEL DST_MODEL SRC_DRAFT DST_DRAFT --heads 66 --kda-heads 66 --moe-i 2304
  10. """
  11. import argparse
  12. import json
  13. import os
  14. import shutil
  15. p = argparse.ArgumentParser()
  16. p.add_argument("src_model"); p.add_argument("dst_model")
  17. p.add_argument("src_draft"); p.add_argument("dst_draft")
  18. p.add_argument("--heads", type=int, default=66)
  19. p.add_argument("--kda-heads", type=int, default=66)
  20. p.add_argument("--moe-i", type=int, default=2304)
  21. p.add_argument("--draft-heads", type=int, default=36)
  22. p.add_argument("--draft-kv-heads", type=int, default=9)
  23. p.add_argument("--tp", type=int, default=3)
  24. a = p.parse_args()
  25. # ---- target ---------------------------------------------------------------
  26. os.makedirs(a.dst_model, exist_ok=True)
  27. for f in os.listdir(a.src_model):
  28. s, d = os.path.join(a.src_model, f), os.path.join(a.dst_model, f)
  29. if f == "config.json" or os.path.isdir(s):
  30. continue
  31. if not os.path.exists(d):
  32. os.link(s, d)
  33. cfg = json.load(open(os.path.join(a.src_model, "config.json")))
  34. tc = cfg["text_config"]
  35. orig = {"num_attention_heads": tc["num_attention_heads"],
  36. "linear_num_heads": tc["linear_num_heads"],
  37. "moe_intermediate_size": tc["moe_intermediate_size"]}
  38. tc["tp_pad_orig"] = orig
  39. tc["num_attention_heads"] = a.heads
  40. tc["num_key_value_heads"] = a.heads
  41. tc["linear_num_heads"] = a.kda_heads
  42. tc["linear_attn_config"]["num_heads"] = a.kda_heads
  43. tc["moe_intermediate_size"] = a.moe_i
  44. tmp = os.path.join(a.dst_model, "config.json.tmp")
  45. json.dump(cfg, open(tmp, "w"), indent=2)
  46. os.replace(tmp, os.path.join(a.dst_model, "config.json"))
  47. print("target", a.dst_model, orig, "->", a.heads, a.kda_heads, a.moe_i)
  48. # ---- drafter --------------------------------------------------------------
  49. import torch # noqa: E402
  50. from safetensors.torch import load_file, save_file # noqa: E402
  51. os.makedirs(a.dst_draft, exist_ok=True)
  52. dc = json.load(open(os.path.join(a.src_draft, "config.json")))
  53. H0, KV0, hd = dc["num_attention_heads"], dc["num_key_value_heads"], dc["head_dim"]
  54. H1, KV1 = a.draft_heads, a.draft_kv_heads
  55. assert H0 // KV0 == H1 // KV1 and H1 % a.tp == 0 and KV1 % a.tp == 0
  56. sd = load_file(os.path.join(a.src_draft, "model.safetensors"))
  57. def pad(t, dim, size):
  58. shape = list(t.shape); shape[dim] = size
  59. out = t.new_zeros(shape)
  60. out.narrow(dim, 0, t.shape[dim]).copy_(t)
  61. return out
  62. n = 0
  63. for k in list(sd):
  64. t = sd[k]
  65. if k.endswith("self_attn.q_proj.weight"):
  66. sd[k] = pad(t, 0, H1 * hd); n += 1
  67. elif k.endswith(("self_attn.k_proj.weight", "self_attn.v_proj.weight")):
  68. sd[k] = pad(t, 0, KV1 * hd); n += 1
  69. elif k.endswith("self_attn.o_proj.weight"):
  70. sd[k] = pad(t, 1, H1 * hd); n += 1
  71. save_file(sd, os.path.join(a.dst_draft, "model.safetensors"), metadata={"format": "pt"})
  72. dc["num_attention_heads"], dc["num_key_value_heads"] = H1, KV1
  73. json.dump(dc, open(os.path.join(a.dst_draft, "config.json"), "w"), indent=2)
  74. for f in os.listdir(a.src_draft):
  75. if f not in ("config.json", "model.safetensors") and os.path.isfile(os.path.join(a.src_draft, f)):
  76. shutil.copy(os.path.join(a.src_draft, f), a.dst_draft)
  77. print("drafter", a.dst_draft, f"padded {n} tensors: {H0}/{KV0} -> {H1}/{KV1} heads")