|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| import os
|
| import torch
|
| import json
|
| from pathlib import Path
|
| from transformers import AutoModelForCausalLM, AutoTokenizer
|
| from ctransformers import AutoModelForCausalLM as GGUFModel
|
| from models.sapnous import SapnousT1Config
|
|
|
| def load_safetensors_state_dict(model_path, weight_map):
|
| """Load state dict from safetensors shards with custom metadata handling."""
|
| import safetensors
|
| from safetensors.torch import load_file
|
|
|
| state_dict = {}
|
| metadata = {}
|
|
|
|
|
| for param_name, shard_file in weight_map['weight_map'].items():
|
| shard_path = os.path.join(model_path, shard_file)
|
| if not os.path.exists(shard_path):
|
| raise OSError(f"Missing weight shard: {shard_path}")
|
|
|
| try:
|
|
|
| shard_dict = load_file(shard_path)
|
| shard_metadata = safetensors.safe_open(shard_path, framework="pt").metadata()
|
|
|
| if shard_metadata:
|
| metadata.update(shard_metadata)
|
|
|
|
|
| for key, tensor in shard_dict.items():
|
| if key in state_dict:
|
| raise ValueError(f"Duplicate parameter {key} found in multiple shards")
|
| state_dict[key] = tensor
|
|
|
| except Exception as e:
|
| raise OSError(f"Error loading shard {shard_file}: {str(e)}")
|
|
|
|
|
| if metadata:
|
| state_dict['_metadata'] = metadata
|
|
|
| return state_dict
|
| return state_dict
|
|
|
| def convert_to_gguf(model_path, output_path):
|
|
|
| config_path = os.path.join(model_path, 'config.json')
|
| weight_map_path = os.path.join(model_path, 'model.safetensors.index.json')
|
|
|
| if not os.path.exists(config_path):
|
| raise OSError(f"Missing config file: {config_path}")
|
| if not os.path.exists(weight_map_path):
|
| raise OSError(f"Missing weight map file: {weight_map_path}")
|
|
|
| with open(config_path, 'r') as f:
|
| config = json.load(f)
|
| with open(weight_map_path, 'r') as f:
|
| weight_map = json.load(f)
|
|
|
|
|
| if 'weight_map' not in weight_map:
|
| raise ValueError("Invalid weight map format: missing 'weight_map' key")
|
| if 'metadata' not in weight_map:
|
| raise ValueError("Invalid weight map format: missing 'metadata' key")
|
|
|
|
|
| model = AutoModelForCausalLM.from_pretrained(
|
| model_path,
|
| trust_remote_code=True,
|
| device_map=None,
|
| torch_dtype=torch.float16,
|
| low_cpu_mem_usage=True,
|
| local_files_only=True,
|
| ignore_mismatched_sizes=True,
|
| use_safetensors=True,
|
| use_auth_token=False
|
| )
|
| tokenizer = AutoTokenizer.from_pretrained(
|
| model_path,
|
| trust_remote_code=True
|
| )
|
|
|
|
|
| config = model.config
|
| if not isinstance(config, SapnousT1Config):
|
| raise ValueError("Model must be a SapnousT1 model")
|
|
|
|
|
| model.save_pretrained(output_path, safe_serialization=True)
|
| tokenizer.save_pretrained(output_path)
|
|
|
|
|
| gguf_model = GGUFModel.from_pretrained(
|
| output_path,
|
| model_type='sapnous_t1',
|
| gpu_layers=0,
|
| config={
|
| 'context_length': config.sliding_window,
|
| 'attention_type': 'multihead',
|
| 'num_attention_heads': config.num_attention_heads,
|
| 'num_key_value_heads': config.num_key_value_heads,
|
| 'hidden_size': config.hidden_size,
|
| 'intermediate_size': config.intermediate_size,
|
| 'max_position_embeddings': config.max_position_embeddings,
|
| 'vocab_size': config.vocab_size,
|
| 'num_hidden_layers': config.num_hidden_layers,
|
| 'rms_norm_eps': config.rms_norm_eps,
|
| 'rope_theta': config.rope_theta,
|
|
|
| 'vision_config': {
|
| 'hidden_size': config.vision_hidden_size,
|
| 'num_hidden_layers': config.vision_layers,
|
| 'num_attention_heads': config.vision_heads,
|
| 'intermediate_size': config.vision_intermediate_size,
|
| 'patch_size': config.patch_size,
|
| 'image_size': config.image_size
|
| }
|
| }
|
| )
|
|
|
| print(f"Model converted and saved to {output_path}")
|
| return gguf_model
|
|
|
| def convert_to_hf(gguf_path, output_path):
|
| """Convert GGUF model back to Hugging Face format"""
|
|
|
| config_path = Path(gguf_path) / "config.json"
|
| with open(config_path, 'r') as f:
|
| gguf_config = json.load(f)
|
|
|
|
|
| config = SapnousT1Config(
|
| vocab_size=gguf_config['vocab_size'],
|
| hidden_size=gguf_config['hidden_size'],
|
| num_hidden_layers=gguf_config['num_hidden_layers'],
|
| num_attention_heads=gguf_config['num_attention_heads'],
|
| num_key_value_heads=gguf_config['num_key_value_heads'],
|
| intermediate_size=gguf_config['intermediate_size'],
|
| max_position_embeddings=gguf_config['max_position_embeddings'],
|
| rms_norm_eps=gguf_config['rms_norm_eps'],
|
| rope_theta=gguf_config['rope_theta'],
|
|
|
| vision_hidden_size=gguf_config['vision_config']['hidden_size'],
|
| vision_layers=gguf_config['vision_config']['num_hidden_layers'],
|
| vision_heads=gguf_config['vision_config']['num_attention_heads'],
|
| vision_intermediate_size=gguf_config['vision_config']['intermediate_size'],
|
| patch_size=gguf_config['vision_config']['patch_size'],
|
| image_size=gguf_config['vision_config']['image_size']
|
| )
|
|
|
|
|
| gguf_model = GGUFModel.from_pretrained(gguf_path)
|
|
|
|
|
| model = AutoModelForCausalLM.from_config(config)
|
| model.load_state_dict(gguf_model.state_dict())
|
|
|
|
|
| model.save_pretrained(output_path)
|
| print(f"Model converted back to Hugging Face format at {output_path}")
|
| return model
|
|
|
| if __name__ == '__main__':
|
| model_path = os.path.dirname(os.path.abspath(__file__))
|
| output_path = os.path.join(model_path, 'gguf_model')
|
|
|
| if not os.path.exists(output_path):
|
| os.makedirs(output_path)
|
|
|
| convert_to_gguf(model_path, output_path) |