Image-Text-to-Text
Transformers
Safetensors
English
visionpsynano
feature-extraction
vision-language-model
multimodal
edge
on-device
nanovlm
vqa
conversational
custom_code
Instructions to use qvac/VisionPsy-Nano-460M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use qvac/VisionPsy-Nano-460M with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="qvac/VisionPsy-Nano-460M", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("qvac/VisionPsy-Nano-460M", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use qvac/VisionPsy-Nano-460M with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "qvac/VisionPsy-Nano-460M" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "qvac/VisionPsy-Nano-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/qvac/VisionPsy-Nano-460M
- SGLang
How to use qvac/VisionPsy-Nano-460M with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "qvac/VisionPsy-Nano-460M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "qvac/VisionPsy-Nano-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "qvac/VisionPsy-Nano-460M" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "qvac/VisionPsy-Nano-460M", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use qvac/VisionPsy-Nano-460M with Docker Model Runner:
docker model run hf.co/qvac/VisionPsy-Nano-460M
| import inspect | |
| import math | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| def _sdpa_supports_native_gqa() -> bool: | |
| try: | |
| params = inspect.signature(F.scaled_dot_product_attention).parameters | |
| return "enable_gqa" in params | |
| except (TypeError, ValueError): | |
| try: | |
| q = torch.zeros(1, 2, 1, 4) | |
| k = torch.zeros(1, 1, 1, 4) | |
| v = torch.zeros(1, 1, 1, 4) | |
| F.scaled_dot_product_attention(q, k, v, enable_gqa=True) | |
| return True | |
| except TypeError: | |
| return False | |
| except Exception: | |
| return True | |
| _SDPA_HAS_GQA = _sdpa_supports_native_gqa() | |
| class RMSNorm(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(cfg.lm_hidden_dim)) | |
| self.eps = cfg.lm_rms_eps | |
| self._normalized_shape = (cfg.lm_hidden_dim,) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return F.rms_norm(x, self._normalized_shape, self.weight, self.eps) | |
| class RotaryEmbedding(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| assert cfg.lm_hidden_dim % cfg.lm_n_heads == 0, "Hidden dimension must be divisible by number of heads" | |
| self.dim = cfg.lm_hidden_dim // cfg.lm_n_heads | |
| self.base = cfg.lm_re_base | |
| self.max_seq_len = cfg.lm_max_position_embeddings | |
| inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2).float() / self.dim)) | |
| self.register_buffer("inv_freq", inv_freq) | |
| self.original_max_seq_len = cfg.lm_max_position_embeddings | |
| self.attention_scaling = cfg.lm_attn_scaling | |
| positions = torch.arange(self.max_seq_len, dtype=torch.float) | |
| freqs = positions.unsqueeze(-1) * inv_freq.unsqueeze(0) | |
| emb = torch.cat([freqs, freqs], dim=-1) | |
| cos_cached = emb.cos() * self.attention_scaling | |
| sin_cached = emb.sin() * self.attention_scaling | |
| self.register_buffer("cos_cached", cos_cached, persistent=False) | |
| self.register_buffer("sin_cached", sin_cached, persistent=False) | |
| def forward(self, position_ids: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: | |
| if position_ids.dtype != torch.long: | |
| position_ids = position_ids.long() | |
| cos = self.cos_cached[position_ids] | |
| sin = self.sin_cached[position_ids] | |
| return cos, sin | |
| def extend_cache(self, new_max_seq_len: int) -> None: | |
| positions = torch.arange( | |
| new_max_seq_len, dtype=torch.float, device=self.cos_cached.device, | |
| ) | |
| freqs = positions.unsqueeze(-1) * self.inv_freq.unsqueeze(0) | |
| emb = torch.cat([freqs, freqs], dim=-1) | |
| self.cos_cached = emb.cos() * self.attention_scaling | |
| self.sin_cached = emb.sin() * self.attention_scaling | |
| self.original_max_seq_len = new_max_seq_len | |
| def apply_rotary_pos_embd( | |
| q: torch.Tensor, | |
| k: torch.Tensor, | |
| cos: torch.Tensor, | |
| sin: torch.Tensor, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| half = q.shape[-1] // 2 | |
| cos_h = cos[..., :half] | |
| sin_h = sin[..., :half] | |
| q1 = q[..., :half] | |
| q2 = q[..., half:] | |
| k1 = k[..., :half] | |
| k2 = k[..., half:] | |
| q_embed = torch.cat( | |
| (q1 * cos_h - q2 * sin_h, q2 * cos_h + q1 * sin_h), | |
| dim=-1, | |
| ) | |
| k_embed = torch.cat( | |
| (k1 * cos_h - k2 * sin_h, k2 * cos_h + k1 * sin_h), | |
| dim=-1, | |
| ) | |
| return q_embed, k_embed | |
| class LanguageModelGroupedQueryAttention(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| self.n_heads = cfg.lm_n_heads | |
| self.n_kv_heads = cfg.lm_n_kv_heads | |
| self.embd_dim = cfg.lm_hidden_dim | |
| self.dropout = cfg.lm_dropout | |
| assert self.n_heads % self.n_kv_heads == 0, "n_heads must be divisible by n_kv_heads" | |
| assert self.embd_dim % self.n_heads == 0, "embd_dim must be divisible by num_heads" | |
| self.n_kv_groups = self.n_heads // self.n_kv_heads | |
| self.head_dim = self.embd_dim // self.n_heads | |
| self.q_proj = nn.Linear(self.embd_dim, self.embd_dim, bias=False) | |
| self.k_proj = nn.Linear(self.embd_dim, self.head_dim * self.n_kv_heads, bias=False) | |
| self.v_proj = nn.Linear(self.embd_dim, self.head_dim * self.n_kv_heads, bias=False) | |
| self.out_proj = nn.Linear(self.embd_dim, self.embd_dim, bias=False) | |
| self.attn_dropout = nn.Dropout(self.dropout) | |
| self.resid_dropout = nn.Dropout(self.dropout) | |
| self.sdpa = hasattr(torch.nn.functional, 'scaled_dot_product_attention') | |
| if not self.sdpa: | |
| print("Warning: scaled dot product attention not available, using standard attention in LM.") | |
| def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, attention_mask=None, block_kv_cache=None, start_pos: int = 0) -> tuple[torch.Tensor, dict]: | |
| B, T_curr, C = x.size() | |
| q_curr = self.q_proj(x).view(B, T_curr, self.n_heads, self.head_dim).transpose(1, 2) | |
| k_curr = self.k_proj(x).view(B, T_curr, self.n_kv_heads, self.head_dim).transpose(1, 2) | |
| v_curr = self.v_proj(x).view(B, T_curr, self.n_kv_heads, self.head_dim).transpose(1, 2) | |
| q, k_rotated = apply_rotary_pos_embd(q_curr, k_curr, cos, sin) | |
| use_static_cache = ( | |
| block_kv_cache is not None | |
| and block_kv_cache.get('cache_max_length') is not None | |
| ) | |
| static_compile_decode = ( | |
| use_static_cache | |
| and T_curr == 1 | |
| and isinstance(start_pos, torch.Tensor) | |
| ) | |
| sdpa_attn_mask_static: torch.Tensor | None = None | |
| if static_compile_decode: | |
| cache_max_length = block_kv_cache['cache_max_length'] | |
| idx = start_pos.unsqueeze(0) | |
| block_kv_cache['key_cache'].index_copy_(2, idx, k_rotated) | |
| block_kv_cache['value_cache'].index_copy_(2, idx, v_curr) | |
| k = block_kv_cache['key_cache'] | |
| v = block_kv_cache['value_cache'] | |
| T_kv = cache_max_length | |
| arange = torch.arange( | |
| cache_max_length, device=q.device, dtype=torch.long, | |
| ) | |
| sdpa_attn_mask_static = (arange <= start_pos).view(1, 1, 1, -1) | |
| elif use_static_cache: | |
| cache_max_length = block_kv_cache['cache_max_length'] | |
| if block_kv_cache.get('key_cache') is None: | |
| cache_shape = (B, self.n_kv_heads, cache_max_length, self.head_dim) | |
| block_kv_cache['key_cache'] = torch.zeros( | |
| cache_shape, dtype=k_rotated.dtype, device=k_rotated.device, | |
| ) | |
| block_kv_cache['value_cache'] = torch.zeros( | |
| cache_shape, dtype=v_curr.dtype, device=v_curr.device, | |
| ) | |
| new_pos = start_pos + T_curr | |
| block_kv_cache['key_cache'][:, :, start_pos:new_pos] = k_rotated | |
| block_kv_cache['value_cache'][:, :, start_pos:new_pos] = v_curr | |
| k = block_kv_cache['key_cache'][:, :, :new_pos] | |
| v = block_kv_cache['value_cache'][:, :, :new_pos] | |
| T_kv = new_pos | |
| else: | |
| is_prefill = block_kv_cache is None | |
| if not is_prefill and block_kv_cache.get('key') is not None: | |
| k = block_kv_cache['key'] | |
| v = block_kv_cache['value'] | |
| k = torch.cat([k, k_rotated], dim=2) | |
| v = torch.cat([v, v_curr], dim=2) | |
| block_kv_cache['key'] = k | |
| block_kv_cache['value'] = v | |
| else: | |
| k = k_rotated | |
| v = v_curr | |
| block_kv_cache = {'key': k, 'value': v} | |
| if _SDPA_HAS_GQA and self.sdpa and x.device.type != 'mps': | |
| k_exp = k | |
| v_exp = v | |
| else: | |
| k_exp = k.repeat_interleave(self.n_kv_groups, dim=1) | |
| v_exp = v.repeat_interleave(self.n_kv_groups, dim=1) | |
| T_kv = k_exp.size(2) | |
| if self.sdpa and x.device.type != 'mps': | |
| if sdpa_attn_mask_static is not None: | |
| sdpa_attn_mask = sdpa_attn_mask_static | |
| sdpa_is_causal = False | |
| elif attention_mask is None: | |
| sdpa_attn_mask = None | |
| sdpa_is_causal = (T_curr == T_kv and T_curr > 1) | |
| else: | |
| if attention_mask.size(-1) < T_kv: | |
| pad = attention_mask.new_ones( | |
| attention_mask.size(0), T_kv - attention_mask.size(-1) | |
| ) | |
| key_padding = torch.cat([attention_mask, pad], dim=-1).bool() | |
| else: | |
| key_padding = attention_mask[:, :T_kv].bool() | |
| if T_curr == T_kv and T_curr > 1: | |
| causal = torch.ones( | |
| T_curr, T_kv, dtype=torch.bool, device=q.device, | |
| ).tril() | |
| else: | |
| causal = torch.ones( | |
| T_curr, T_kv, dtype=torch.bool, device=q.device, | |
| ) | |
| sdpa_attn_mask = ( | |
| causal.unsqueeze(0).unsqueeze(0) | |
| & key_padding.unsqueeze(1).unsqueeze(2) | |
| ) | |
| sdpa_is_causal = False | |
| sdpa_kwargs = { | |
| "attn_mask": sdpa_attn_mask, | |
| "dropout_p": self.dropout if self.training else 0.0, | |
| "is_causal": sdpa_is_causal, | |
| } | |
| if _SDPA_HAS_GQA and k_exp.size(1) != q.size(1): | |
| sdpa_kwargs["enable_gqa"] = True | |
| y = torch.nn.functional.scaled_dot_product_attention( | |
| q, k_exp, v_exp, | |
| **sdpa_kwargs, | |
| ) | |
| else: | |
| attn = torch.matmul(q, k_exp.transpose(2, 3)) / math.sqrt(self.head_dim) | |
| if T_curr == T_kv and T_curr > 1: | |
| causal_mask_val = torch.tril( | |
| torch.ones(T_curr, T_curr, device=x.device, dtype=torch.bool), | |
| ).view(1, 1, T_curr, T_curr) | |
| attn = attn.masked_fill(~causal_mask_val, float('-inf')) | |
| if attention_mask is not None: | |
| additive = ( | |
| 1.0 - attention_mask[:, :T_kv].unsqueeze(1).unsqueeze(2).float() | |
| ) * torch.finfo(q.dtype).min | |
| attn = attn + additive | |
| attn = F.softmax(attn, dim=-1) | |
| attn = self.attn_dropout(attn) | |
| y = attn @ v_exp | |
| y = y.transpose(1, 2).contiguous().view(B, T_curr, C) | |
| y = self.out_proj(y) | |
| y = self.resid_dropout(y) | |
| return y, block_kv_cache | |
| class LanguageModelMLP(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| self.embd_dim = cfg.lm_hidden_dim | |
| self.inter_dim = cfg.lm_inter_dim | |
| self.activation_fn = F.silu | |
| self.gate_up_proj = nn.Linear(self.embd_dim, 2 * self.inter_dim, bias=False) | |
| self.down_proj = nn.Linear(self.inter_dim, self.embd_dim, bias=False) | |
| self._register_load_state_dict_pre_hook(self._fuse_legacy_gate_up_hook) | |
| def _fuse_legacy_gate_up_hook( | |
| state_dict, prefix, local_metadata, strict, | |
| missing_keys, unexpected_keys, error_msgs, | |
| ): | |
| gate_key = f"{prefix}gate_proj.weight" | |
| up_key = f"{prefix}up_proj.weight" | |
| fused_key = f"{prefix}gate_up_proj.weight" | |
| if ( | |
| gate_key in state_dict | |
| and up_key in state_dict | |
| and fused_key not in state_dict | |
| ): | |
| gate_w = state_dict.pop(gate_key) | |
| up_w = state_dict.pop(up_key) | |
| state_dict[fused_key] = torch.cat([gate_w, up_w], dim=0) | |
| def forward(self, x): | |
| gate, up = self.gate_up_proj(x).chunk(2, dim=-1) | |
| return self.down_proj(self.activation_fn(gate) * up) | |
| class LanguageModelBlock(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| self.mlp = LanguageModelMLP(cfg) | |
| self.attn = LanguageModelGroupedQueryAttention(cfg) | |
| self.norm1 = RMSNorm(cfg) | |
| self.norm2 = RMSNorm(cfg) | |
| def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, attention_mask: torch.Tensor=None, block_kv_cache: dict=None, start_pos: int = 0): | |
| res = x | |
| x = self.norm1(x) | |
| x, block_kv_cache = self.attn(x, cos, sin, attention_mask, block_kv_cache, start_pos=start_pos) | |
| x = res + x | |
| res = x | |
| x = self.norm2(x) | |
| x = self.mlp(x) | |
| x = res + x | |
| return x, block_kv_cache | |
| class LanguageModel(nn.Module): | |
| def __init__(self, cfg): | |
| super().__init__() | |
| self.cfg = cfg | |
| self.lm_use_tokens = cfg.lm_use_tokens | |
| self.lm_tie_weights = cfg.lm_tie_weights | |
| self.token_embedding = nn.Embedding(cfg.lm_vocab_size, cfg.lm_hidden_dim) | |
| self.rotary_embd = RotaryEmbedding(cfg) | |
| self.blocks = nn.ModuleList([ | |
| LanguageModelBlock(cfg) for _ in range(cfg.lm_n_blocks) | |
| ]) | |
| self.norm = RMSNorm(cfg) | |
| self.head = nn.Linear(cfg.lm_hidden_dim, cfg.lm_vocab_size, bias=False) | |
| if self.lm_tie_weights: | |
| self.head.weight = self.token_embedding.weight | |
| self.apply(self._init_weights) | |
| def _init_weights(self, module): | |
| if isinstance(module, nn.Linear): | |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| if module.bias is not None: | |
| torch.nn.init.zeros_(module.bias) | |
| elif isinstance(module, nn.Embedding): | |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| elif isinstance(module, RMSNorm): | |
| module.weight.data.fill_(1.0) | |
| def forward(self, x: torch.Tensor, attention_mask: torch.Tensor=None, kv_cache: list[dict]=None, start_pos: int=0): | |
| if self.lm_use_tokens: | |
| x = self.token_embedding(x) | |
| B, T_curr, _ = x.size() | |
| if isinstance(start_pos, torch.Tensor): | |
| offsets = torch.arange(0, T_curr, device=x.device, dtype=torch.long) | |
| current_position_ids = (offsets + start_pos).unsqueeze(0).expand(B, -1) | |
| else: | |
| current_position_ids = torch.arange(start_pos, start_pos + T_curr, device=x.device).unsqueeze(0).expand(B, -1) | |
| cos, sin = self.rotary_embd(current_position_ids) | |
| cos = cos.unsqueeze(1) | |
| sin = sin.unsqueeze(1) | |
| if kv_cache is None: | |
| kv_cache = [None] * len(self.blocks) | |
| for i, block in enumerate(self.blocks): | |
| x, kv_cache[i] = block(x, cos, sin, attention_mask, kv_cache[i], start_pos=start_pos) | |
| x = self.norm(x) | |
| if self.lm_use_tokens: | |
| x = self.head(x) | |
| return x, kv_cache | |
| def generate(self, inputs: torch.Tensor, max_new_tokens: int=20): | |
| if inputs.dim() == 1: | |
| inputs = inputs.unsqueeze(0) | |
| generated_outputs = inputs.clone() | |
| prompt_output, kv_cache_list = self.forward( | |
| generated_outputs, | |
| attention_mask=None, | |
| kv_cache=None, | |
| start_pos=0 | |
| ) | |
| last_output = prompt_output[:, -1, :] | |
| for i in range(max_new_tokens): | |
| if self.lm_use_tokens: | |
| next_output = torch.argmax(last_output, dim=-1, keepdim=True) | |
| else: | |
| next_output = last_output.unsqueeze(1) | |
| generated_outputs = torch.cat((generated_outputs, next_output), dim=1) | |
| current_token_start_pos = generated_outputs.size(1) - 1 | |
| if i == max_new_tokens - 1: | |
| break | |
| decode_step_output, kv_cache_list = self.forward( | |
| next_output, | |
| attention_mask=None, | |
| kv_cache=kv_cache_list, | |
| start_pos=current_token_start_pos | |
| ) | |
| last_output = decode_step_output[:, -1, :] | |
| return generated_outputs | |
| def from_pretrained(cls, cfg): | |
| from transformers import AutoConfig | |
| from huggingface_hub import hf_hub_download | |
| import safetensors | |
| import torch.nn.init as init | |
| import json | |
| from huggingface_hub.utils import EntryNotFoundError | |
| hf_config = AutoConfig.from_pretrained(cfg.lm_model_type) | |
| original_vocab_size = hf_config.vocab_size | |
| cfg.lm_hidden_dim = hf_config.hidden_size | |
| cfg.lm_inter_dim = hf_config.intermediate_size | |
| cfg.lm_rms_eps = hf_config.rms_norm_eps | |
| cfg.lm_re_base = hf_config.rope_theta | |
| cfg.lm_max_position_embeddings = hf_config.max_position_embeddings | |
| if hasattr(cfg, 'lm_vocab_size'): | |
| if cfg.lm_vocab_size < original_vocab_size: | |
| raise ValueError(f"Config vocab size ({cfg.lm_vocab_size}) is smaller than pretrained model vocab size ({original_vocab_size})") | |
| else: | |
| cfg.lm_vocab_size = original_vocab_size | |
| cfg.lm_n_heads = hf_config.num_attention_heads | |
| cfg.lm_n_kv_heads = hf_config.num_key_value_heads | |
| cfg.lm_dropout = hf_config.attention_dropout | |
| cfg.lm_n_blocks = hf_config.num_hidden_layers | |
| model = cls(cfg) | |
| try: | |
| index_path = hf_hub_download(repo_id=cfg.lm_model_type, filename="model.safetensors.index.json") | |
| with open(index_path, 'r') as f: | |
| index = json.load(f) | |
| safetensors_filenames = sorted(list(set(index['weight_map'].values()))) | |
| safetensors_files = [hf_hub_download(repo_id=cfg.lm_model_type, filename=fn) for fn in safetensors_filenames] | |
| except EntryNotFoundError: | |
| safetensors_files = [hf_hub_download(repo_id=cfg.lm_model_type, filename="model.safetensors")] | |
| sd = model.state_dict() | |
| mapping = { | |
| 'model.embed_tokens.weight': 'token_embedding.weight', | |
| 'model.norm.weight': 'norm.weight' | |
| } | |
| for i in range(cfg.lm_n_blocks): | |
| layer_prefix = f'model.layers.{i}.' | |
| block_prefix = f'blocks.{i}.' | |
| mapping.update({ | |
| f"{layer_prefix}self_attn.q_proj.weight": f"{block_prefix}attn.q_proj.weight", | |
| f"{layer_prefix}self_attn.k_proj.weight": f"{block_prefix}attn.k_proj.weight", | |
| f"{layer_prefix}self_attn.v_proj.weight": f"{block_prefix}attn.v_proj.weight", | |
| f"{layer_prefix}self_attn.o_proj.weight": f"{block_prefix}attn.out_proj.weight", | |
| f"{layer_prefix}mlp.down_proj.weight": f"{block_prefix}mlp.down_proj.weight", | |
| f"{layer_prefix}input_layernorm.weight": f"{block_prefix}norm1.weight", | |
| f"{layer_prefix}post_attention_layernorm.weight": f"{block_prefix}norm2.weight" | |
| }) | |
| has_extended_embeddings = False | |
| loaded_keys = set() | |
| for safetensors_file in safetensors_files: | |
| with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f: | |
| for hf_key, our_key in mapping.items(): | |
| if our_key in loaded_keys: | |
| continue | |
| if hf_key in f.keys() and our_key in sd: | |
| tensor = f.get_tensor(hf_key) | |
| if hf_key == 'model.embed_tokens.weight' and tensor.shape[0] != sd[our_key].shape[0]: | |
| has_extended_embeddings = True | |
| print(f"Extending token embeddings from {tensor.shape} to {sd[our_key].shape}") | |
| sd[our_key][:tensor.shape[0]].copy_(tensor) | |
| std = 0.02 | |
| init.normal_(sd[our_key][tensor.shape[0]:], mean=0.0, std=std) | |
| print(f"Initialized {sd[our_key].shape[0] - tensor.shape[0]} new token embeddings") | |
| sd['head.weight'].copy_(sd[our_key]) | |
| elif tensor.shape == sd[our_key].shape: | |
| sd[our_key].copy_(tensor) | |
| else: | |
| print(f"Shape mismatch for {hf_key} -> {our_key}: {tensor.shape} vs {sd[our_key].shape}") | |
| loaded_keys.add(our_key) | |
| for hf_key, our_key in mapping.items(): | |
| if our_key not in loaded_keys: | |
| if our_key in sd: | |
| print(f"Warning: Key {our_key} not found in any safetensors file (HF key: {hf_key})") | |
| for i in range(cfg.lm_n_blocks): | |
| layer_prefix = f"model.layers.{i}." | |
| block_prefix = f"blocks.{i}." | |
| fused_param_key = f"{block_prefix}mlp.gate_up_proj.weight" | |
| if fused_param_key not in sd or fused_param_key in loaded_keys: | |
| continue | |
| gate_hf_key = f"{layer_prefix}mlp.gate_proj.weight" | |
| up_hf_key = f"{layer_prefix}mlp.up_proj.weight" | |
| gate_w = None | |
| up_w = None | |
| for safetensors_file in safetensors_files: | |
| with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f: | |
| keys = f.keys() | |
| if gate_w is None and gate_hf_key in keys: | |
| gate_w = f.get_tensor(gate_hf_key) | |
| if up_w is None and up_hf_key in keys: | |
| up_w = f.get_tensor(up_hf_key) | |
| if gate_w is not None and up_w is not None: | |
| break | |
| if gate_w is None or up_w is None: | |
| print( | |
| f"Warning: gate_proj or up_proj missing for layer {i} " | |
| f"(gate={gate_w is not None}, up={up_w is not None}); " | |
| f"{fused_param_key} left at init values." | |
| ) | |
| continue | |
| fused = torch.cat([gate_w, up_w], dim=0) | |
| if fused.shape != sd[fused_param_key].shape: | |
| print( | |
| f"Shape mismatch for fused gate_up at layer {i}: " | |
| f"{fused.shape} vs {sd[fused_param_key].shape}" | |
| ) | |
| continue | |
| sd[fused_param_key].copy_(fused) | |
| loaded_keys.add(fused_param_key) | |
| model.load_state_dict(sd) | |
| if has_extended_embeddings and hasattr(model, 'head') and 'head.weight' in sd: | |
| lm_head_loaded = False | |
| for safetensors_file in safetensors_files: | |
| with safetensors.safe_open(filename=safetensors_file, framework="pt", device="cpu") as f: | |
| if 'lm_head.weight' in f.keys(): | |
| lm_head = f.get_tensor('lm_head.weight') | |
| if lm_head.shape[0] != sd['head.weight'].shape[0]: | |
| print(f"Extending LM head from {lm_head.shape} to {sd['head.weight'].shape}") | |
| sd['head.weight'][:lm_head.shape[0]].copy_(lm_head) | |
| std = 0.02 | |
| init.normal_(sd['head.weight'][lm_head.shape[0]:], mean=0.0, std=std) | |
| model.load_state_dict(sd) | |
| lm_head_loaded = True | |
| break | |
| if cfg.lm_tie_weights and hasattr(model, 'head') and hasattr(model, 'token_embedding'): | |
| model.head.weight = model.token_embedding.weight | |
| print(f"Successfully loaded {cfg.lm_model_type} weights from safetensors. Model has {sum(p.numel() for p in model.parameters()):,} parameters.") | |
| return model | |