nadiva1243 commited on
Commit
d5a6177
Β·
verified Β·
1 Parent(s): 1f3b205

Upload reproduction/merge_lora.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. reproduction/merge_lora.py +125 -0
reproduction/merge_lora.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LoRA adapter merger for base model consolidation.
3
+
4
+ Loads a trained LoRA adapter and merges it with the original base model
5
+ to produce a single consolidated model ready for GGUF export. The merged
6
+ model and its tokenizer are saved together to preserve compatibility
7
+ for downstream conversion and quantization steps.
8
+
9
+ Usage:
10
+ python scripts/conversion/merge_lora.py --model qwen-3
11
+ Dependencies:
12
+ - torch
13
+ - peft (PeftModel, PeftConfig)
14
+ - transformers (AutoModelForCausalLM, AutoTokenizer)
15
+ """
16
+
17
+
18
+ # ─────────────────────────────────────────────
19
+ # MODULE MAP -- Section index
20
+ # ─────────────────────────────────────────────
21
+ #
22
+ # CONFIGURATION
23
+ # +-- 1. Imports and CLI args
24
+ # +-- 2. Paths and artifact validation
25
+ #
26
+ # PIPELINE
27
+ # +-- 3. Adapter configuration loading
28
+ # +-- 4. Base model and tokenizer loading
29
+ # +-- 5. LoRA adapter merge
30
+ # +-- 6. Merged model export
31
+ #
32
+ # ─────────────────────────────────────────────
33
+
34
+ import argparse
35
+ import os
36
+ import torch
37
+ from peft import PeftModel, PeftConfig
38
+ from transformers import AutoModelForCausalLM, AutoTokenizer
39
+
40
+ # ─────────────────────────────────────────────
41
+ # SECTION 1: IMPORTS AND CLI ARGS
42
+ # ─────────────────────────────────────────────
43
+
44
+ # Accepts both HF_TOKEN and HUGGINGFACE_HUB_TOKEN for compatibility.
45
+ HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_HUB_TOKEN") or None
46
+
47
+ # ─────────────────────────────────────────────
48
+ # SECTION 2: PATHS AND ARTIFACT VALIDATION
49
+ # ─────────────────────────────────────────────
50
+
51
+ VALID_MODELS = ["qwen-3", "gemma-3", "phi-4"]
52
+
53
+ parser = argparse.ArgumentParser(description="Merge LoRA adapter with base model.")
54
+ parser.add_argument(
55
+ "--model", choices=VALID_MODELS, default="qwen-3",
56
+ help="Model to merge (default: qwen-3).",
57
+ )
58
+ args = parser.parse_args()
59
+
60
+ PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
61
+ LORA_PATH = os.path.join(PROJECT_ROOT, "training-output", args.model)
62
+ MERGED_PATH = os.path.join(PROJECT_ROOT, "models", "merged-model", args.model)
63
+
64
+ if not os.path.exists(os.path.join(LORA_PATH, "adapter_config.json")):
65
+ raise FileNotFoundError(
66
+ f"LoRA adapter not found at {LORA_PATH}. "
67
+ "Run training first (scripts/training/train-{args.model}.py)."
68
+ )
69
+
70
+ os.makedirs(MERGED_PATH, exist_ok=True)
71
+
72
+ # ─────────────────────────────────────────────
73
+ # SECTION 3: ADAPTER CONFIGURATION LOADING
74
+ # ─────────────────────────────────────────────
75
+
76
+ print("=" * 60)
77
+ print("[1/4] Loading LoRA adapter configuration...")
78
+ print("=" * 60)
79
+ peft_config = PeftConfig.from_pretrained(LORA_PATH)
80
+ base_model_name = peft_config.base_model_name_or_path
81
+ print(f" Base model: {base_model_name}")
82
+ print(f" LoRA path: {LORA_PATH}")
83
+
84
+ # ─────────────────────────────────────────────
85
+ # SECTION 4: BASE MODEL AND TOKENIZER LOADING
86
+ # ─────────────────────────────────────────────
87
+
88
+ print("\n" + "=" * 60)
89
+ print("[2/4] Downloading/loading base model (may take a while)...")
90
+ print("=" * 60)
91
+ base_model = AutoModelForCausalLM.from_pretrained(
92
+ base_model_name,
93
+ torch_dtype=torch.float16,
94
+ device_map="cpu",
95
+ trust_remote_code=True,
96
+ token=HF_TOKEN,
97
+ )
98
+ tokenizer = AutoTokenizer.from_pretrained(base_model_name, token=HF_TOKEN)
99
+ print(" Base model loaded.")
100
+
101
+ # ─────────────────────────────────────────────
102
+ # SECTION 5: LORA ADAPTER MERGE
103
+ # ─────────────────────────────────────────────
104
+
105
+ print("\n" + "=" * 60)
106
+ print("[3/4] Merging LoRA adapter with base model...")
107
+ print("=" * 60)
108
+ model = PeftModel.from_pretrained(base_model, LORA_PATH, token=HF_TOKEN)
109
+ merged_model = model.merge_and_unload()
110
+ print(" Merge completed.")
111
+
112
+ # ─────────────────────────────────��───────────
113
+ # SECTION 6: MERGED MODEL EXPORT
114
+ # ─────────────────────────────────────────────
115
+
116
+ print("\n" + "=" * 60)
117
+ print(f"[4/4] Saving merged model to: {MERGED_PATH}")
118
+ print("=" * 60)
119
+ merged_model.save_pretrained(MERGED_PATH, safe_serialization=True)
120
+ tokenizer.save_pretrained(MERGED_PATH)
121
+
122
+ print("\n" + "=" * 60)
123
+ print("COMPLETED!")
124
+ print(f"Merged model saved to: {MERGED_PATH}")
125
+ print("=" * 60)