|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |