| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import argparse |
| import glob |
| import math |
| import os |
| import torch |
| from safetensors.torch import load_file, save_file |
|
|
| INIT_STD = 0.02 |
| torch.manual_seed(0) |
|
|
| |
| |
| NEW_LAYER_INIT = { |
| "q_proj.weight": "rand", |
| "k_proj.weight": "rand", |
| "v_proj.weight": "rand", |
| "out_proj.weight": "zero", |
| "ffn_w1.weight": "rand", |
| "ffn_w3.weight": "rand", |
| "ffn_w2.weight": "zero", |
| "norm1.weight": "one", |
| "norm2.weight": "one", |
| } |
|
|
|
|
| def load_state(path): |
| if os.path.isdir(path): |
| sd = {} |
| for f in sorted(glob.glob(os.path.join(path, "*.safetensors"))): |
| sd.update(load_file(f)) |
| if not sd: |
| raise FileNotFoundError(f"No .safetensors in {path}") |
| return sd |
| return load_file(path) |
|
|
|
|
| def detect_layers(sd): |
| idx = set() |
| for k in sd: |
| if k.startswith("blocks."): |
| idx.add(int(k.split(".")[1])) |
| return sorted(idx) |
|
|
|
|
| def widen_ffn(block, target_I): |
| """In-place widen one block's FFN tensors to target intermediate size.""" |
| w1 = block["ffn_w1.weight"] |
| cur_I, H = w1.shape |
| if target_I <= cur_I: |
| return block |
| add = target_I - cur_I |
| dt, dev = w1.dtype, w1.device |
| for name in ("ffn_w1.weight", "ffn_w3.weight"): |
| w = block[name] |
| new_rows = torch.randn(add, H, dtype=dt, device=dev) * INIT_STD |
| block[name] = torch.cat([w, new_rows], dim=0) |
| w2 = block["ffn_w2.weight"] |
| new_cols = torch.zeros(w2.shape[0], add, dtype=dt, device=dev) |
| block["ffn_w2.weight"] = torch.cat([w2, new_cols], dim=1) |
| return block |
|
|
|
|
| def make_identity_block(template): |
| """Build a fresh identity-initialized block from {suffix: tensor} template.""" |
| out = {} |
| for suffix, ref in template.items(): |
| rule = NEW_LAYER_INIT[suffix] |
| if rule == "zero": |
| out[suffix] = torch.zeros_like(ref) |
| elif rule == "one": |
| out[suffix] = torch.ones_like(ref) |
| else: |
| out[suffix] = torch.randn_like(ref) * INIT_STD |
| return out |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--in", dest="inp", required=True) |
| ap.add_argument("--out", dest="out", required=True) |
| ap.add_argument("--layers", type=int, required=True, help="target NUM_LAYERS") |
| ap.add_argument("--intermediate", type=int, default=0, |
| help="target INTERMEDIATE_SIZE (0 = keep current)") |
| args = ap.parse_args() |
|
|
| print(f"Loading {args.inp} ...") |
| sd = load_state(args.inp) |
| old_idx = detect_layers(sd) |
| old_L = len(old_idx) |
| if args.layers < old_L: |
| raise ValueError(f"--layers ({args.layers}) < existing layers ({old_L})") |
|
|
| |
| def block_of(j): |
| pfx = f"blocks.{j}." |
| return {k[len(pfx):]: v for k, v in sd.items() if k.startswith(pfx)} |
|
|
| orig_blocks = [block_of(j) for j in old_idx] |
|
|
| |
| tgt_I = args.intermediate or orig_blocks[0]["ffn_w1.weight"].shape[0] |
| for b in orig_blocks: |
| widen_ffn(b, tgt_I) |
| template = orig_blocks[0] |
|
|
| |
| L_new = args.layers |
| step = L_new / old_L |
| positions = [int(math.floor(i * step)) for i in range(old_L)] |
| pos_to_orig = {p: i for i, p in enumerate(positions)} |
|
|
| new_sd = {} |
| |
| for k, v in sd.items(): |
| if not k.startswith("blocks."): |
| new_sd[k] = v |
|
|
| n_identity = 0 |
| for n in range(L_new): |
| if n in pos_to_orig: |
| block = orig_blocks[pos_to_orig[n]] |
| else: |
| block = make_identity_block(template) |
| n_identity += 1 |
| for suffix, tensor in block.items(): |
| new_sd[f"blocks.{n}.{suffix}"] = tensor |
|
|
| |
| total = sum(t.numel() for t in new_sd.values()) |
| print(f"Existing layers : {old_L} -> new layers: {L_new} " |
| f"({n_identity} identity-initialized)") |
| print(f"Intermediate : {tgt_I}") |
| print(f"Trained layers placed at indices: {positions}") |
| print(f"Total parameters: {total/1e9:.3f} B") |
|
|
| print(f"Saving {args.out} ...") |
| save_file(new_sd, args.out) |
| print("Done. New layers start as identity (no loss spike) and learn from ~step 2.") |
| print("Remember to update NUM_LAYERS and INTERMEDIATE_SIZE in the model file.") |
|
|
|
|
| if __name__ == "__main__": |
| main() |