Taimwe commited on
Commit
6de7d94
·
verified ·
1 Parent(s): 37be743

Declare torch (and transformers for GGUF) so uv run works

Browse files
Files changed (1) hide show
  1. merge_securecoder.py +109 -108
merge_securecoder.py CHANGED
@@ -1,108 +1,109 @@
1
- # /// script
2
- # requires-python = ">=3.10"
3
- # dependencies = [
4
- # "unsloth",
5
- # "transformers>=4.57",
6
- # "huggingface_hub",
7
- # ]
8
-
9
-
10
-
11
- # ///
12
- """Merge the SecureCoder LoRA into the base Qwen3-Coder-30B-A3B-Instruct
13
- checkpoint and upload a 16-bit safetensors repo. GGUF is done in a separate
14
- job (the 30B-A3B MoE does not fit on one 80 GB card when merge and quantise
15
- both run in the same process).
16
-
17
- Default base: unsloth/Qwen3-Coder-30B-A3B-Instruct
18
- Default adapter: Taimwe/securecoder-30b-pro
19
-
20
- Run on HF Jobs:
21
- hf jobs run -d --flavor a100-large --timeout 90m --secrets HF_TOKEN \\
22
- ghcr.io/astral-sh/uv:python3.12-bookworm \\
23
- uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/merge_securecoder.py \\
24
- -- --adapter Taimwe/securecoder-30b-pro \\
25
- --output-repo Taimwe/securecoder-30b-pro-merged
26
- """
27
-
28
- from __future__ import annotations
29
-
30
- import argparse
31
- import logging
32
- import os
33
- import shutil
34
- import sys
35
- import time
36
-
37
- logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
38
- log = logging.getLogger("merge")
39
- def parse_args() -> argparse.Namespace:
40
- p = argparse.ArgumentParser(description="Merge + push the SecureCoder LoRA")
41
- p.add_argument("--base", default="unsloth/Qwen3-Coder-30B-A3B-Instruct")
42
- p.add_argument("--adapter", default="Taimwe/securecoder-30b-pro")
43
- p.add_argument("--output-repo", default="Taimwe/securecoder-30b-pro-merged")
44
- p.add_argument("--private", action="store_true")
45
- p.add_argument("--work-dir", default="/data/securecoder-merge")
46
- p.add_argument("--max-shard-size", default="5GB")
47
- return p.parse_args()
48
-
49
-
50
- def main() -> int:
51
- args = parse_args()
52
- token = os.environ.get("HF_TOKEN")
53
- if not token:
54
- log.error("HF_TOKEN not set")
55
- return 1
56
-
57
- import torch
58
- from huggingface_hub import HfApi
59
- from unsloth import FastLanguageModel
60
-
61
- if not torch.cuda.is_available():
62
- log.error("no CUDA - merge needs a GPU")
63
- return 1
64
- log.info("GPU: %s", torch.cuda.get_device_name(0))
65
-
66
- work = args.work_dir
67
- if os.path.exists(work):
68
- shutil.rmtree(work)
69
- os.makedirs(work, exist_ok=True)
70
-
71
- log.info("loading base %s in 16-bit ...", args.base)
72
- started = time.time()
73
- model, tokenizer = FastLanguageModel.from_pretrained(
74
- model_name=args.base,
75
- max_seq_length=2048,
76
- dtype=torch.bfloat16,
77
- load_in_4bit=False,
78
- )
79
-
80
- log.info("loading adapter %s ...", args.adapter)
81
- from peft import PeftModel
82
- model = PeftModel.from_pretrained(model, args.adapter, token=token)
83
- log.info("merging ...")
84
- model = model.merge_and_unload()
85
- log.info("merge done in %.1f min", (time.time() - started) / 60)
86
-
87
- out_dir = os.path.join(work, "merged")
88
- model.save_pretrained(out_dir, safe_serialization=True, max_shard_size=args.max_shard_size)
89
- tokenizer.save_pretrained(out_dir)
90
- log.info("saved merged model to %s", out_dir)
91
-
92
- api = HfApi(token=token)
93
- api.create_repo(args.output_repo, repo_type="model", exist_ok=True, private=args.private)
94
- log.info("uploading to %s ...", args.output_repo)
95
- api.upload_folder(folder_path=out_dir, repo_id=args.output_repo, repo_type="model",
96
- commit_message="Merge SecureCoder LoRA into base (16-bit)")
97
- log.info("merged model live: https://huggingface.co/%s", args.output_repo)
98
-
99
- print("=" * 78)
100
- print("MERGE COMPLETE")
101
- print(f" merged: https://huggingface.co/{args.output_repo}")
102
- print(" next : run quantise_securecoder.py separately for GGUF")
103
- print("=" * 78)
104
- return 0
105
-
106
-
107
- if __name__ == "__main__":
108
- raise SystemExit(main())
 
 
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = [
4
+ # "unsloth",
5
+ # "transformers>=4.57",
6
+ # "huggingface_hub",
7
+ # "torch",
8
+ # ]
9
+
10
+
11
+
12
+ # ///
13
+ """Merge the SecureCoder LoRA into the base Qwen3-Coder-30B-A3B-Instruct
14
+ checkpoint and upload a 16-bit safetensors repo. GGUF is done in a separate
15
+ job (the 30B-A3B MoE does not fit on one 80 GB card when merge and quantise
16
+ both run in the same process).
17
+
18
+ Default base: unsloth/Qwen3-Coder-30B-A3B-Instruct
19
+ Default adapter: Taimwe/securecoder-30b-pro
20
+
21
+ Run on HF Jobs:
22
+ hf jobs run -d --flavor a100-large --timeout 90m --secrets HF_TOKEN \\
23
+ ghcr.io/astral-sh/uv:python3.12-bookworm \\
24
+ uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/merge_securecoder.py \\
25
+ -- --adapter Taimwe/securecoder-30b-pro \\
26
+ --output-repo Taimwe/securecoder-30b-pro-merged
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import argparse
32
+ import logging
33
+ import os
34
+ import shutil
35
+ import sys
36
+ import time
37
+
38
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
39
+ log = logging.getLogger("merge")
40
+ def parse_args() -> argparse.Namespace:
41
+ p = argparse.ArgumentParser(description="Merge + push the SecureCoder LoRA")
42
+ p.add_argument("--base", default="unsloth/Qwen3-Coder-30B-A3B-Instruct")
43
+ p.add_argument("--adapter", default="Taimwe/securecoder-30b-pro")
44
+ p.add_argument("--output-repo", default="Taimwe/securecoder-30b-pro-merged")
45
+ p.add_argument("--private", action="store_true")
46
+ p.add_argument("--work-dir", default="/data/securecoder-merge")
47
+ p.add_argument("--max-shard-size", default="5GB")
48
+ return p.parse_args()
49
+
50
+
51
+ def main() -> int:
52
+ args = parse_args()
53
+ token = os.environ.get("HF_TOKEN")
54
+ if not token:
55
+ log.error("HF_TOKEN not set")
56
+ return 1
57
+
58
+ import torch
59
+ from huggingface_hub import HfApi
60
+ from unsloth import FastLanguageModel
61
+
62
+ if not torch.cuda.is_available():
63
+ log.error("no CUDA - merge needs a GPU")
64
+ return 1
65
+ log.info("GPU: %s", torch.cuda.get_device_name(0))
66
+
67
+ work = args.work_dir
68
+ if os.path.exists(work):
69
+ shutil.rmtree(work)
70
+ os.makedirs(work, exist_ok=True)
71
+
72
+ log.info("loading base %s in 16-bit ...", args.base)
73
+ started = time.time()
74
+ model, tokenizer = FastLanguageModel.from_pretrained(
75
+ model_name=args.base,
76
+ max_seq_length=2048,
77
+ dtype=torch.bfloat16,
78
+ load_in_4bit=False,
79
+ )
80
+
81
+ log.info("loading adapter %s ...", args.adapter)
82
+ from peft import PeftModel
83
+ model = PeftModel.from_pretrained(model, args.adapter, token=token)
84
+ log.info("merging ...")
85
+ model = model.merge_and_unload()
86
+ log.info("merge done in %.1f min", (time.time() - started) / 60)
87
+
88
+ out_dir = os.path.join(work, "merged")
89
+ model.save_pretrained(out_dir, safe_serialization=True, max_shard_size=args.max_shard_size)
90
+ tokenizer.save_pretrained(out_dir)
91
+ log.info("saved merged model to %s", out_dir)
92
+
93
+ api = HfApi(token=token)
94
+ api.create_repo(args.output_repo, repo_type="model", exist_ok=True, private=args.private)
95
+ log.info("uploading to %s ...", args.output_repo)
96
+ api.upload_folder(folder_path=out_dir, repo_id=args.output_repo, repo_type="model",
97
+ commit_message="Merge SecureCoder LoRA into base (16-bit)")
98
+ log.info("merged model live: https://huggingface.co/%s", args.output_repo)
99
+
100
+ print("=" * 78)
101
+ print("MERGE COMPLETE")
102
+ print(f" merged: https://huggingface.co/{args.output_repo}")
103
+ print(" next : run quantise_securecoder.py separately for GGUF")
104
+ print("=" * 78)
105
+ return 0
106
+
107
+
108
+ if __name__ == "__main__":
109
+ raise SystemExit(main())