Views
No views yet
model.language_model.language_model.language_model.X renamed to model.language_model.Xmodel.language_model.visual.X moved to model.visual.X, visual weights restored from base model| Parameter | Value |
|---|---|
| Training Mode | ORPO |
| Base Model | huihui-ai/Huihui-Qwen3.5-27B-abliterated |
| Learning Rate | 9e-05 |
| Epochs | 1 |
| Batch Size | 1 |
| Gradient Accumulation | 32 |
| Effective Batch Size | 32 |
| Max Sequence Length | 2048 |
| Optimizer | paged_adamw_8bit |
| LR Scheduler | cosine |
| Warmup Ratio | 0.05 |
| Weight Decay | 0.01 |
| Max Grad Norm | 0.25 |
| Seed | 42 |
| Beta | 0.1 |
| Max Prompt Length | 1024 |
| LoRA Rank (r) | 128 |
| LoRA Alpha | 64 |
| LoRA Dropout | 0.05 |
| Target Modules | up_proj, down_proj, gate_proj, k_proj, q_proj, v_proj, o_proj |
| Quantization | 4-bit (NF4) |
| GPU | NVIDIA A100-SXM4-80GB |
1#!/usr/bin/env python3
2"""
3Graft multimodal (visual + MTP) weights from the base Qwen3.5-27B model
4back onto a fine-tuned variant that has broken key naming and missing components.
5
6Problems in nbeerbower/Huihui-Qwen3.5-27B-abliterated-Athanorlite-ORPO:
71. LM keys are triple-nested: model.language_model.language_model.language_model.X
8 -> should be model.language_model.X
92. Visual keys are under: model.language_model.visual.X
10 -> should be model.visual.X
113. MTP (multi-token prediction) weights are completely missing (15 keys)
12
13Fix: rename LM keys, replace visual weights from base model, graft MTP weights from base.
14"""
15
16import json
17import os
18import shutil
19from pathlib import Path
20from collections import OrderedDict
21
22from huggingface_hub import snapshot_download, hf_hub_download
23from safetensors.torch import load_file, save_file
24
25FINETUNED_REPO = "nbeerbower/Huihui-Qwen3.5-27B-abliterated-Athanorlite-ORPO"
26BASE_REPO = "huihui-ai/Huihui-Qwen3.5-27B-abliterated"
27WORK_DIR = Path(".")
28FINETUNED_DIR = WORK_DIR / "finetuned"
29OUTPUT_DIR = WORK_DIR / "output"
30BASE_CACHE_DIR = WORK_DIR / "base_shard"
31MAX_SHARD_SIZE = 5 * 1024 * 1024 * 1024 # 5 GB
32
33
34def download_models():
35 """Download the fine-tuned model and base model files."""
36 print("Step 1: Downloading models")
37
38 snapshot_download(FINETUNED_REPO, local_dir=str(FINETUNED_DIR))
39
40 BASE_CACHE_DIR.mkdir(parents=True, exist_ok=True)
41 # Download base model index to find which shards have visual/MTP keys
42 hf_hub_download(BASE_REPO, filename="model.safetensors.index.json", local_dir=str(BASE_CACHE_DIR))
43 hf_hub_download(BASE_REPO, filename="config.json", local_dir=str(BASE_CACHE_DIR))
44
45 # Parse the index to find all shards containing visual/MTP keys
46 with open(BASE_CACHE_DIR / "model.safetensors.index.json") as f:
47 base_index = json.load(f)
48
49 needed_shards = set()
50 for key, shard in base_index["weight_map"].items():
51 if key.startswith("model.visual.") or key.startswith("mtp."):
52 needed_shards.add(shard)
53
54 for shard_name in sorted(needed_shards):
55 print(f" Downloading base shard: {shard_name}")
56 hf_hub_download(BASE_REPO, filename=shard_name, local_dir=str(BASE_CACHE_DIR))
57
58 for fname in ["preprocessor_config.json", "video_preprocessor_config.json"]:
59 try:
60 hf_hub_download(BASE_REPO, filename=fname, local_dir=str(BASE_CACHE_DIR))
61 except Exception:
62 pass
63
64
65def fix_key(key: str) -> str:
66 """Fix a single weight key name."""
67 if key.startswith("model.language_model.language_model.language_model."):
68 return key.replace(
69 "model.language_model.language_model.language_model.",
70 "model.language_model.", 1,
71 )
72 if key.startswith("model.language_model.visual."):
73 return key.replace("model.language_model.visual.", "model.visual.", 1)
74 return key
75
76
77def process_weights():
78 """Rename keys in fine-tuned model, graft visual + MTP from base."""
79 print("Step 2: Processing weights")
80 OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
81
82 # Collect visual + MTP tensors from all needed base shards
83 base_visual = {}
84 base_mtp = {}
85 for shard_path in sorted(BASE_CACHE_DIR.glob("model.safetensors-*.safetensors")):
86 tensors = load_file(str(shard_path))
87 for k, v in tensors.items():
88 if k.startswith("model.visual."):
89 base_visual[k] = v
90 elif k.startswith("mtp."):
91 base_mtp[k] = v
92 del tensors
93 print(f" Base visual keys: {len(base_visual)}, MTP keys: {len(base_mtp)}")
94
95 # Load fine-tuned shards and fix keys
96 all_tensors = OrderedDict()
97 for shard_path in sorted(FINETUNED_DIR.glob("model-*.safetensors")):
98 shard = load_file(str(shard_path))
99 for key, tensor in shard.items():
100 fixed_key = fix_key(key)
101 if fixed_key.startswith("model.visual."):
102 continue # Replace with base model's visual weights
103 all_tensors[fixed_key] = tensor
104 del shard
105
106 # Graft visual + MTP from base
107 all_tensors.update(base_visual)
108 all_tensors.update(base_mtp)
109 print(f" Total keys: {len(all_tensors)}")
110
111 # Save as sharded safetensors
112 weight_map = {}
113 shard_idx = 1
114 current_shard = OrderedDict()
115 current_size = 0
116 shard_files = []
117
118 for key in sorted(all_tensors.keys()):
119 tensor = all_tensors[key]
120 tensor_size = tensor.nelement() * tensor.element_size()
121
122 if current_size + tensor_size > MAX_SHARD_SIZE and current_shard:
123 shard_name = f"model-{shard_idx:05d}-of-PLACEHOLDER.safetensors"
124 save_file(current_shard, str(OUTPUT_DIR / shard_name))
125 shard_files.append(shard_name)
126 for k in current_shard:
127 weight_map[k] = shard_name
128 current_shard = OrderedDict()
129 current_size = 0
130 shard_idx += 1
131
132 current_shard[key] = tensor
133 current_size += tensor_size
134
135 if current_shard:
136 shard_name = f"model-{shard_idx:05d}-of-PLACEHOLDER.safetensors"
137 save_file(current_shard, str(OUTPUT_DIR / shard_name))
138 shard_files.append(shard_name)
139 for k in current_shard:
140 weight_map[k] = shard_name
141
142 total_shards = shard_idx
143 final_weight_map = {}
144 for i, old_name in enumerate(shard_files, 1):
145 new_name = f"model-{i:05d}-of-{total_shards:05d}.safetensors"
146 (OUTPUT_DIR / old_name).rename(OUTPUT_DIR / new_name)
147 for k, v in weight_map.items():
148 if v == old_name:
149 final_weight_map[k] = new_name
150
151 total_size = sum(t.nelement() * t.element_size() for t in all_tensors.values())
152 index = {"metadata": {"total_size": total_size}, "weight_map": final_weight_map}
153 with open(OUTPUT_DIR / "model.safetensors.index.json", "w") as f:
154 json.dump(index, f, indent=2, sort_keys=True)
155
156 # Copy config files
157 for fname in ["config.json", "generation_config.json", "tokenizer.json",
158 "tokenizer_config.json", "chat_template.jinja"]:
159 src = FINETUNED_DIR / fname
160 if src.exists():
161 shutil.copy2(src, OUTPUT_DIR / fname)
162 for fname in ["preprocessor_config.json", "video_preprocessor_config.json"]:
163 src = BASE_CACHE_DIR / fname
164 if src.exists():
165 shutil.copy2(src, OUTPUT_DIR / fname)
166
167
168if __name__ == "__main__":
169 download_models()
170 process_weights()
171 print("Done! Output in:", OUTPUT_DIR)