| import sys |
| import os |
| import shutil |
| import argparse |
| import torch |
| from transformers import AutoConfig, AutoTokenizer |
|
|
| try: |
| current_dir = os.path.dirname(os.path.abspath(__file__)) |
| except NameError: |
| current_dir = os.getcwd() |
|
|
| sys.path.append(current_dir) |
|
|
| from .modeling_fused_lladamoe import FusedLLaDAMoEModelLM |
| from transformers import AutoTokenizer, AutoModel |
|
|
| def convert_and_save( |
| input_path: str, |
| output_path: str, |
| modeling_file_name: str, |
| device: str = "cpu" |
| ): |
| """ |
| Converts a standard OlmoeForCausalLM model to a FusedOlmoeForCausalLM model |
| by fusing the MoE expert weights. |
| """ |
| print(f"Loading original model from {input_path}...") |
| config = AutoConfig.from_pretrained(input_path, trust_remote_code=True) |
| model = AutoModel.from_pretrained(input_path, trust_remote_code=True, torch_dtype=torch.bfloat16).to(device).eval() |
| state_dict = model.state_dict() |
|
|
| num_layers = config.num_hidden_layers |
| num_experts = config.num_experts |
| print(f"Model config found: {num_layers} layers, {num_experts} experts per layer.") |
|
|
|
|
| print("Building fused model...") |
| fused_model = FusedLLaDAMoEModelLM(config).to(device) |
| fused_state_dict = fused_model.state_dict() |
|
|
| print("Mapping and fusing expert weights...") |
| for i in range(num_layers): |
| layer_prefix = f"model.layers.{i}.mlp." |
|
|
| gate_weights = [state_dict[f"{layer_prefix}experts.{j}.gate_proj.weight"].to(device) for j in range(num_experts)] |
| up_weights = [state_dict[f"{layer_prefix}experts.{j}.up_proj.weight"].to(device) for j in range(num_experts)] |
| down_weights = [state_dict[f"{layer_prefix}experts.{j}.down_proj.weight"].to(device) for j in range(num_experts)] |
|
|
| combined_w1 = torch.stack([torch.cat([g, u], dim=0) for g, u in zip(gate_weights, up_weights)]) |
| combined_w2 = torch.stack(down_weights) |
|
|
| fused_state_dict[f"{layer_prefix}w1"] = combined_w1 |
| fused_state_dict[f"{layer_prefix}w2"] = combined_w2 |
| |
| print("Copying non-expert parameters...") |
| for key in state_dict: |
| if 'experts' not in key: |
| fused_state_dict[key] = state_dict[key] |
|
|
| fused_model.load_state_dict(fused_state_dict) |
|
|
| print("Updating model configuration for fused model...") |
| if not hasattr(fused_model.config, "auto_map"): |
| fused_model.config.auto_map = {} |
|
|
| fused_model_class_name = FusedLLaDAMoEModelLM.__name__ |
|
|
| full_module_class_path = f"{modeling_file_name}.{fused_model_class_name}" |
|
|
| fused_model.config.auto_map["AutoModelForCausalLM"] = full_module_class_path |
| fused_model.config.auto_map.pop("AutoModel") |
| fused_model.config.architectures = [fused_model_class_name] |
| fused_model.config.architectures = [fused_model_class_name] |
|
|
| print(f"Saving fused model to {output_path}") |
| fused_model.save_pretrained(output_path) |
| tokenizer = AutoTokenizer.from_pretrained(input_path, trust_remote_code=True) |
| tokenizer.save_pretrained(output_path) |
|
|
| print("Copying custom modeling files to output directory...") |
| source_files = [ |
| os.path.join(current_dir, f"transfer/{modeling_file_name}.py"), |
| os.path.join(current_dir, "transfer/fuse_moe.py") |
| ] |
|
|
| os.makedirs(output_path, exist_ok=True) |
|
|
| for src_file in source_files: |
| if os.path.exists(src_file): |
| dest_file = os.path.join(output_path, os.path.basename(src_file)) |
| try: |
| shutil.copy2(src_file, dest_file) |
| print(f"Copied {os.path.basename(src_file)} to {output_path}") |
| except Exception as e: |
| print(f"Error copying {os.path.basename(src_file)}: {e}") |
| else: |
| print(f"Warning: Source file not found, skipping copy: {src_file}") |
|
|
|
|
| print("✅ Conversion completed!") |
|
|
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--input', type=str, required=True) |
| parser.add_argument('--output', type=str, required=True) |
| parser.add_argument('--modeling', type=str, default='modeling_fused_olmoe') |
| args = parser.parse_args() |
|
|
| input_path = args.input.rstrip('/') |
| output_path = args.output |
|
|
| print(f"\n----- Starting CPU conversion for {input_path} -----") |
| convert_and_save( |
| input_path=input_path, |
| output_path=output_path, |
| modeling_file_name=args.modeling, |
| ) |
| print(f"----- Finished conversion for {input_path} -> {output_path} -----\n") |