| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from collections.abc import Callable |
| from dataclasses import dataclass |
| from typing import Any, Optional |
|
|
| import torch |
| import torch.nn.functional as F |
| from torch import nn |
|
|
| import transformers.initialization as init |
| from transformers.activations import ACT2FN |
| from transformers.modeling_utils import PreTrainedModel |
| from transformers.utils import logging |
| from transformers.modeling_outputs import ( |
| BaseModelOutputWithPast, |
| BaseModelOutputWithPooling, |
| CausalLMOutputWithPast, |
| ModelOutput, |
| ) |
| from configuration_qwen3_5 import QuasarConfig, QuasarTextConfig, QuasarVisionConfig |
|
|
| |
| try: |
| from transformers.cache_utils import Cache |
| except ImportError: |
| from transformers.cache_utils import DynamicCache as Cache |
| Caches = Cache |
|
|
| |
| try: |
| from transformers.generation import GenerationMixin |
| except ImportError: |
| from transformers.generation.utils import GenerationMixin |
|
|
| |
| try: |
| from transformers.integrations import use_kernelized_func |
| except ImportError: |
| def use_kernelized_func(fn): |
| def decorator(cls): return cls |
| return decorator |
|
|
| |
| try: |
| from transformers.masking_utils import create_causal_mask |
| except ImportError: |
| def create_causal_mask(position_ids, attention_mask, cache_position, config, past_key_values=None, **kwargs): |
| import torch |
| bsz, seq_len = position_ids.shape |
| device = position_ids.device |
| causal = torch.tril(torch.ones(seq_len, seq_len, device=device)).view(1, 1, seq_len, seq_len) |
| return (1.0 - causal) * torch.finfo(torch.float32).min |
|
|
| |
| try: |
| from transformers.modeling_flash_attention_utils import FlashAttentionKwargs |
| except ImportError: |
| class FlashAttentionKwargs(dict): pass |
|
|
| |
| import torch.nn as nn |
| class GradientCheckpointingLayer(nn.Module): pass |
|
|
| |
| try: |
| from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update |
| except ImportError: |
| ROPE_INIT_FUNCTIONS = {} |
| def dynamic_rope_update(fn): return fn |
|
|
| |
| try: |
| from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS |
| except ImportError: |
| class _AttentionFunctionRegistry(dict): |
| def get_interface(self, impl, default=None): return self.get(impl, default) |
| ALL_ATTENTION_FUNCTIONS = _AttentionFunctionRegistry() |
|
|
| |
| try: |
| from transformers.processing_utils import Unpack |
| except ImportError: |
| try: |
| from typing_extensions import Unpack |
| except ImportError: |
| class _UnpackMeta(type): |
| def __getitem__(cls, item): return cls |
| class Unpack(metaclass=_UnpackMeta): pass |
|
|
| try: |
| from transformers.utils import TransformersKwargs |
| except ImportError: |
| class TransformersKwargs(dict): pass |
|
|
| |
| try: |
| from transformers.utils import auto_docstring |
| except ImportError: |
| def auto_docstring(*args, **kwargs): |
| if len(args) == 1 and callable(args[0]): return args[0] |
| def decorator(fn): return fn |
| return decorator |
|
|
| try: |
| from transformers.utils import can_return_tuple |
| except ImportError: |
| def can_return_tuple(fn): return fn |
|
|
| try: |
| from transformers.utils import torch_compilable_check |
| except ImportError: |
| def torch_compilable_check(*args, **kwargs): pass |
|
|
| |
| try: |
| from transformers.utils.generic import is_flash_attention_requested |
| except ImportError: |
| def is_flash_attention_requested(config): return getattr(config, "_attn_implementation", "eager") == "flash_attention_2" |
|
|
| try: |
| from transformers.utils.generic import maybe_autocast |
| except ImportError: |
| from contextlib import contextmanager |
| @contextmanager |
| def maybe_autocast(*args, **kwargs): yield |
|
|
| try: |
| from transformers.utils.generic import merge_with_config_defaults |
| except ImportError: |
| def merge_with_config_defaults(fn): return fn |
|
|
| |
| try: |
| from transformers.utils.import_utils import is_causal_conv1d_available, is_flash_linear_attention_available |
| except ImportError: |
| def is_causal_conv1d_available(): return True |
| def is_flash_linear_attention_available(): return True |
|
|
| |
| try: |
| from transformers.utils.output_capturing import capture_outputs |
| except ImportError: |
| def capture_outputs(fn): return fn |
|
|
|
|
| from causal_conv1d import causal_conv1d_fn, causal_conv1d_update |
| from fla.modules import FusedRMSNormGated |
| from fla.ops.gated_delta_rule import chunk_gated_delta_rule, fused_recurrent_gated_delta_rule |
|
|
| logger = logging.get_logger(__name__) |
|
|
|
|
|
|
| class QuasarDynamicCache: |
| """ |
| A dynamic cache that can handle both the attention cache (which has a seq_len dimension) and the linear attention |
| cache (which has a constant shape regardless of seq_len). |
| |
| This cache has two sets of lists of tensors: `key_cache` and `value_cache` for attention cache and `conv_states` |
| and `ssm_states` for gated deltanet cache. Each of these lists has `num_layers` tensors. The expected shape for each tensor |
| For attention layers, `key_cache` and `value_cache` have a shape of `(batch_size, num_heads, seq_len, head_dim)`, |
| while `conv_states` and `ssm_states` have a shape of `(batch_size, 0)` (empty tensors). |
| For linear attention layers, `key_cache` and `value_cache` have a shape of `(batch_size, 0)` (empty tensors), |
| while `conv_states` represents the convolution state and has a shape of `(batch_size, d_inner, d_conv)`, |
| and `recurrent_states` represents the recurrent state and has a shape of `(batch_size, d_inner, d_state)`. |
| """ |
|
|
| is_compileable = False |
|
|
| def __init__(self, config: QuasarConfig): |
| super().__init__() |
| self.layer_types = config.layer_types |
| self.transformer_layers = [ |
| i for i in range(config.num_hidden_layers) if self.layer_types[i] == "full_attention" |
| ] |
| self.last_linear_layer = len(self.layer_types) - 1 - self.layer_types[::-1].index("linear_attention") |
|
|
| |
| self.conv_states = [None for _ in range(config.num_hidden_layers)] |
| self.recurrent_states = [None for _ in range(config.num_hidden_layers)] |
| self.key_cache = [None for _ in range(config.num_hidden_layers)] |
| self.value_cache = [None for _ in range(config.num_hidden_layers)] |
| |
| |
| self.original_conv_states = [None for _ in range(config.num_hidden_layers)] |
| self.original_recurrent_states = [None for _ in range(config.num_hidden_layers)] |
| self.is_recomputing = False |
|
|
| def __len__(self): |
| return len(self.layer_types) |
|
|
| def __getitem__(self, layer_idx: int) -> dict[str, Any]: |
| if self.is_recomputing: |
| return { |
| "conv_state": self.original_conv_states[layer_idx], |
| "recurrent_state": self.original_recurrent_states[layer_idx], |
| } |
| else: |
| return { |
| "conv_state": self.conv_states[layer_idx], |
| "recurrent_state": self.recurrent_states[layer_idx], |
| } |
|
|
| def update( |
| self, |
| key_states: torch.Tensor | None = None, |
| value_states: torch.Tensor | None = None, |
| layer_idx: int | None = None, |
| cache_kwargs: dict[str, Any] | None = None, |
| recurrent_state: torch.Tensor | None = None, |
| conv_state: Any | None = None, |
| offset: int | None = None, |
| ) -> tuple[torch.Tensor, torch.Tensor] | None: |
| if key_states is not None: |
| if self.key_cache[layer_idx] is None: |
| self.key_cache[layer_idx] = key_states |
| self.value_cache[layer_idx] = value_states |
| else: |
| self.key_cache[layer_idx] = torch.cat([self.key_cache[layer_idx], key_states], dim=2) |
| self.value_cache[layer_idx] = torch.cat([self.value_cache[layer_idx], value_states], dim=2) |
| return self.key_cache[layer_idx], self.value_cache[layer_idx] |
|
|
| if recurrent_state is not None or conv_state is not None: |
| if not self.is_recomputing: |
| |
| self.original_recurrent_states[layer_idx] = self.recurrent_states[layer_idx] |
| self.original_conv_states[layer_idx] = self.conv_states[layer_idx] |
| |
| |
| if recurrent_state is not None: |
| self.recurrent_states[layer_idx] = recurrent_state |
| if conv_state is not None: |
| self.conv_states[layer_idx] = conv_state |
| return None |
|
|
| def reorder_cache(self, beam_idx: torch.LongTensor): |
| """Reorders the cache for beam search, given the selected beam indices.""" |
| for layer_idx in range(len(self.key_cache)): |
| if self.key_cache[layer_idx] is not None: |
| device = self.key_cache[layer_idx].device |
| beam_idx = beam_idx.to(device) |
| self.key_cache[layer_idx] = self.key_cache[layer_idx].index_select(0, beam_idx) |
| self.value_cache[layer_idx] = self.value_cache[layer_idx].index_select(0, beam_idx) |
|
|
| if self.conv_states[layer_idx] is not None: |
| device = self.conv_states[layer_idx].device |
| beam_idx = beam_idx.to(device) |
| self.conv_states[layer_idx] = self.conv_states[layer_idx].index_select(0, beam_idx) |
| self.recurrent_states[layer_idx] = self.recurrent_states[layer_idx].index_select(0, beam_idx) |
|
|
| def get_seq_length(self, layer_idx: int | None = 0) -> int: |
| """Returns the sequence length of the cached states. A layer index can be optionally passed.""" |
| |
| layer_idx = self.transformer_layers[0] if layer_idx not in self.transformer_layers else layer_idx |
| if len(self.key_cache) <= layer_idx or self.key_cache[layer_idx] is None: |
| return 0 |
| return self.key_cache[layer_idx].shape[-2] |
|
|
| def get_mask_sizes(self, cache_position: torch.Tensor, layer_idx: int) -> tuple[int, int]: |
| """ |
| Return a tuple (kv_length, kv_offset) corresponding to the length and offset that will be returned for |
| the given layer at `layer_idx`. |
| The masks are then prepared according to the given lengths (kv_length, kv_offset) and patterns for each layer. |
| """ |
| kv_offset = 0 |
| query_length = cache_position.shape[0] |
| past_seen_tokens = self.get_seq_length(layer_idx) |
| kv_length = query_length + past_seen_tokens |
| return kv_length, kv_offset |
|
|
| @property |
| def has_previous_state(self): |
| """We have a previous state if the last linear (conv) layer was already updated.""" |
| return self.conv_states[self.last_linear_layer] is not None |
|
|
|
|
| class QuasarVisionRotaryEmbedding(nn.Module): |
| inv_freq: torch.Tensor |
|
|
| def __init__(self, dim: int, theta: float = 10000.0) -> None: |
| super().__init__() |
| self.dim = dim |
| self.theta = theta |
| inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float) / dim)) |
| self.register_buffer("inv_freq", inv_freq, persistent=False) |
|
|
| def forward(self, seqlen: int) -> torch.Tensor: |
| seq = torch.arange(seqlen, device=self.inv_freq.device, dtype=self.inv_freq.dtype) |
| freqs = torch.outer(seq, self.inv_freq) |
| return freqs |
|
|
|
|
| class QuasarTextRotaryEmbedding(nn.Module): |
| inv_freq: torch.Tensor |
|
|
| def __init__(self, config: QuasarTextConfig, device=None): |
| super().__init__() |
| self.max_seq_len_cached = config.max_position_embeddings |
| self.original_max_seq_len = config.max_position_embeddings |
|
|
| self.config = config |
|
|
| self.rope_type = self.config.rope_parameters["rope_type"] |
| rope_init_fn: Callable = self.compute_default_rope_parameters |
| if self.rope_type != "default": |
| rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type] |
| inv_freq, self.attention_scaling = rope_init_fn(self.config, device) |
|
|
| self.register_buffer("inv_freq", inv_freq, persistent=False) |
| self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False) |
| self.mrope_section = config.rope_parameters.get("mrope_section", [11, 11, 10]) |
|
|
| @staticmethod |
| def compute_default_rope_parameters( |
| config: QuasarTextConfig | None = None, |
| device: Optional["torch.device"] = None, |
| seq_len: int | None = None, |
| ) -> tuple["torch.Tensor", float]: |
| """ |
| Computes the inverse frequencies according to the original RoPE implementation |
| Args: |
| config ([`~transformers.PreTrainedConfig`]): |
| The model configuration. |
| device (`torch.device`): |
| The device to use for initialization of the inverse frequencies. |
| seq_len (`int`, *optional*): |
| The current sequence length. Unused for this type of RoPE. |
| Returns: |
| Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the |
| post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE). |
| """ |
| base = config.rope_parameters["rope_theta"] |
| partial_rotary_factor = config.rope_parameters.get("partial_rotary_factor", 1.0) |
| head_dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads |
| dim = int(head_dim * partial_rotary_factor) |
|
|
| attention_factor = 1.0 |
|
|
| |
| inv_freq = 1.0 / ( |
| base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim) |
| ) |
| return inv_freq, attention_factor |
|
|
| @torch.no_grad() |
| @dynamic_rope_update |
| def forward(self, x, position_ids): |
| |
| |
| if position_ids.ndim == 2: |
| position_ids = position_ids[None, ...].expand(3, position_ids.shape[0], -1) |
| inv_freq_expanded = self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1) |
| position_ids_expanded = position_ids[:, :, None, :].float() |
|
|
| device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu" |
| with maybe_autocast(device_type=device_type, enabled=False): |
| freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(2, 3) |
| freqs = self.apply_interleaved_mrope(freqs, self.mrope_section) |
| emb = torch.cat((freqs, freqs), dim=-1) |
| cos = emb.cos() * self.attention_scaling |
| sin = emb.sin() * self.attention_scaling |
|
|
| return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype) |
|
|
| def apply_interleaved_mrope(self, freqs, mrope_section): |
| """Apply interleaved MRoPE to 3D rotary embeddings. |
| Reorganizes frequency layout from chunked [TTT...HHH...WWW] to |
| interleaved [THWTHWTHW...TT], preserving frequency continuity. |
| args: |
| x: (3, bs, seq_len, head_dim // 2) |
| mrope_section: (3,) |
| returns: |
| x_t: (bs, seq_len, head_dim // 2) |
| """ |
| freqs_t = freqs[0] |
| for dim, offset in enumerate((1, 2), start=1): |
| length = mrope_section[dim] * 3 |
| idx = slice(offset, length, 3) |
| freqs_t[..., idx] = freqs[dim, ..., idx] |
| return freqs_t |
|
|
|
|
| class QuasarRMSNormGated(nn.Module): |
| def __init__(self, hidden_size, eps=1e-6, **kwargs): |
| super().__init__() |
| self.weight = nn.Parameter(torch.ones(hidden_size)) |
| self.variance_epsilon = eps |
|
|
| def forward(self, hidden_states, gate=None): |
| input_dtype = hidden_states.dtype |
| hidden_states = hidden_states.to(torch.float32) |
| variance = hidden_states.pow(2).mean(-1, keepdim=True) |
| |
| hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon) |
| hidden_states = self.weight * hidden_states.to(input_dtype) |
| hidden_states = hidden_states * F.silu(gate.to(torch.float32)) |
|
|
| return hidden_states.to(input_dtype) |
|
|
|
|
| def apply_mask_to_padding_states(hidden_states, attention_mask): |
| """ |
| Tunes out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66 |
| """ |
| |
| if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1: |
| dtype = hidden_states.dtype |
| hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype) |
|
|
| return hidden_states |
|
|
| def l2norm(x: torch.FloatTensor, dim: int = -1, eps: float = 1e-6): |
| """This function is intended to align with the l2norm implementation in the FLA library.""" |
| inv_norm = torch.rsqrt((x * x).sum(dim=dim, keepdim=True) + eps) |
| return x * inv_norm |
|
|
| class QuasarGatedDeltaNet(nn.Module): |
| def __init__(self, config: QuasarConfig, layer_idx: int): |
| super().__init__() |
| self.hidden_size = config.hidden_size |
| self.num_v_heads = config.linear_num_value_heads |
| self.num_k_heads = config.linear_num_key_heads |
| self.head_k_dim = config.linear_key_head_dim |
| self.head_v_dim = config.linear_value_head_dim |
| self.key_dim = self.head_k_dim * self.num_k_heads |
| self.value_dim = self.head_v_dim * self.num_v_heads |
|
|
| self.conv_kernel_size = config.linear_conv_kernel_dim |
| self.layer_idx = layer_idx |
| self.activation = config.hidden_act |
| self.act = ACT2FN[config.hidden_act] |
| self.layer_norm_epsilon = config.rms_norm_eps |
|
|
| |
| self.conv_dim = self.key_dim * 2 + self.value_dim |
| self.conv1d = nn.Conv1d( |
| in_channels=self.conv_dim, |
| out_channels=self.conv_dim, |
| bias=False, |
| kernel_size=self.conv_kernel_size, |
| groups=self.conv_dim, |
| padding=self.conv_kernel_size - 1, |
| ) |
|
|
| |
| |
| self.dt_bias = nn.Parameter(torch.ones(self.num_v_heads)) |
|
|
| A = torch.empty(self.num_v_heads).uniform_(0, 16) |
| self.A_log = nn.Parameter(torch.log(A)) |
|
|
| self.norm = FusedRMSNormGated( |
| self.head_v_dim, |
| eps=self.layer_norm_epsilon, |
| activation=self.activation, |
| |
| dtype=config.torch_dtype if hasattr(config, "torch_dtype") and config.torch_dtype is not None else torch.get_default_dtype(), |
| ) |
|
|
| self.out_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False) |
|
|
| self.causal_conv1d_fn = causal_conv1d_fn |
| self.causal_conv1d_update = causal_conv1d_update |
| self.chunk_gated_delta_rule = chunk_gated_delta_rule |
| self.recurrent_gated_delta_rule = fused_recurrent_gated_delta_rule |
|
|
| self.in_proj_qkv = nn.Linear(self.hidden_size, self.key_dim * 2 + self.value_dim, bias=False) |
| self.in_proj_z = nn.Linear(self.hidden_size, self.value_dim, bias=False) |
| self.in_proj_b = nn.Linear(self.hidden_size, self.num_v_heads, bias=False) |
| self.in_proj_a = nn.Linear(self.hidden_size, self.num_v_heads, bias=False) |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| cache_params: QuasarDynamicCache | None = None, |
| cache_position: torch.LongTensor | None = None, |
| attention_mask: torch.Tensor | None = None, |
| ): |
| hidden_states = apply_mask_to_padding_states(hidden_states, attention_mask) |
|
|
| |
| batch_size, seq_len, _ = hidden_states.shape |
|
|
| use_precomputed_states = ( |
| cache_params is not None |
| and cache_params.has_previous_state |
| and seq_len == 1 |
| and cache_position is not None |
| ) |
|
|
| |
| if cache_params is not None: |
| conv_state = cache_params.conv_states[self.layer_idx] |
| recurrent_state = cache_params.recurrent_states[self.layer_idx] |
|
|
| mixed_qkv = self.in_proj_qkv(hidden_states) |
| mixed_qkv = mixed_qkv.transpose(1, 2) |
|
|
| z = self.in_proj_z(hidden_states) |
| z = z.reshape(batch_size, seq_len, -1, self.head_v_dim) |
|
|
| b = self.in_proj_b(hidden_states) |
| a = self.in_proj_a(hidden_states) |
|
|
| if use_precomputed_states: |
| |
| |
| mixed_qkv = self.causal_conv1d_update( |
| mixed_qkv, |
| conv_state, |
| self.conv1d.weight.squeeze(1), |
| self.conv1d.bias, |
| self.activation, |
| ) |
| else: |
| if cache_params is not None: |
| conv_state = F.pad(mixed_qkv, (self.conv_kernel_size - mixed_qkv.shape[-1], 0)) |
| cache_params.conv_states[self.layer_idx] = conv_state |
| if self.causal_conv1d_fn is not None: |
| mixed_qkv = self.causal_conv1d_fn( |
| x=mixed_qkv, |
| weight=self.conv1d.weight.squeeze(1), |
| bias=self.conv1d.bias, |
| activation=self.activation, |
| seq_idx=None, |
| ) |
| else: |
| mixed_qkv = F.silu(self.conv1d(mixed_qkv)[:, :, :seq_len]) |
|
|
| mixed_qkv = mixed_qkv.transpose(1, 2) |
| query, key, value = torch.split( |
| mixed_qkv, |
| [ |
| self.key_dim, |
| self.key_dim, |
| self.value_dim, |
| ], |
| dim=-1, |
| ) |
|
|
| query = query.reshape(batch_size, seq_len, -1, self.head_k_dim) |
| key = key.reshape(batch_size, seq_len, -1, self.head_k_dim) |
| value = value.reshape(batch_size, seq_len, -1, self.head_v_dim) |
|
|
| beta = b.sigmoid() |
| |
| g = -self.A_log.float().exp() * F.softplus(a.float() + self.dt_bias) |
| if self.num_v_heads // self.num_k_heads > 1: |
| query = query.repeat_interleave(self.num_v_heads // self.num_k_heads, dim=2) |
| key = key.repeat_interleave(self.num_v_heads // self.num_k_heads, dim=2) |
|
|
| if not use_precomputed_states: |
| core_attn_out, last_recurrent_state = self.chunk_gated_delta_rule( |
| query, |
| key, |
| value, |
| g=g, |
| beta=beta, |
| initial_state=None, |
| output_final_state=cache_params is not None, |
| use_qk_l2norm_in_kernel=True, |
| ) |
|
|
| else: |
| core_attn_out, last_recurrent_state = self.recurrent_gated_delta_rule( |
| query, |
| key, |
| value, |
| g=g, |
| beta=beta, |
| initial_state=recurrent_state, |
| output_final_state=cache_params is not None, |
| use_qk_l2norm_in_kernel=True, |
| ) |
|
|
| |
| if cache_params is not None: |
| cache_params.recurrent_states[self.layer_idx] = last_recurrent_state |
|
|
| |
| core_attn_out = core_attn_out.reshape(-1, self.head_v_dim) |
| z = z.reshape(-1, self.head_v_dim) |
| core_attn_out = self.norm(core_attn_out, z) |
| core_attn_out = core_attn_out.reshape(batch_size, seq_len, -1) |
|
|
| output = self.out_proj(core_attn_out) |
| return output |
|
|
|
|
| def rotate_half(x): |
| """Rotates half the hidden dims of the input.""" |
| x1 = x[..., : x.shape[-1] // 2] |
| x2 = x[..., x.shape[-1] // 2 :] |
| return torch.cat((-x2, x1), dim=-1) |
|
|
|
|
| |
| def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1): |
| """Applies Rotary Position Embedding to the query and key tensors. |
| |
| Removes the interleaving of cos and sin from GLM |
| |
| Args: |
| q (`torch.Tensor`): The query tensor. |
| k (`torch.Tensor`): The key tensor. |
| cos (`torch.Tensor`): The cosine part of the rotary embedding. |
| sin (`torch.Tensor`): The sine part of the rotary embedding. |
| unsqueeze_dim (`int`, *optional*, defaults to 1): |
| The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and |
| sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note |
| that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and |
| k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes |
| cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have |
| the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2. |
| Returns: |
| `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding. |
| """ |
| cos = cos.unsqueeze(unsqueeze_dim) |
| sin = sin.unsqueeze(unsqueeze_dim) |
|
|
| |
| rotary_dim = cos.shape[-1] |
| q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:] |
| k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:] |
|
|
| |
| q_embed = (q_rot * cos) + (rotate_half(q_rot) * sin) |
| k_embed = (k_rot * cos) + (rotate_half(k_rot) * sin) |
|
|
| |
| q_embed = torch.cat([q_embed, q_pass], dim=-1) |
| k_embed = torch.cat([k_embed, k_pass], dim=-1) |
| return q_embed, k_embed |
|
|
|
|
| def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: |
| """ |
| This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch, |
| num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim) |
| """ |
| batch, num_key_value_heads, slen, head_dim = hidden_states.shape |
| if n_rep == 1: |
| return hidden_states |
| hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim) |
| return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim) |
|
|
|
|
| def eager_attention_forward( |
| module: nn.Module, |
| query: torch.Tensor, |
| key: torch.Tensor, |
| value: torch.Tensor, |
| attention_mask: torch.Tensor | None, |
| scaling: float, |
| dropout: float = 0.0, |
| **kwargs: Unpack[TransformersKwargs], |
| ): |
| """Memory-efficient attention using SDPA kernel. Math fallback disabled to prevent OOM on long sequences.""" |
| key_states = repeat_kv(key, module.num_key_value_groups) |
| value_states = repeat_kv(value, module.num_key_value_groups) |
|
|
| |
| if attention_mask is not None: |
| |
| attn_mask = attention_mask < -1 |
| else: |
| attn_mask = None |
|
|
| |
| with torch.backends.cuda.sdp_kernel(enable_flash=True, enable_mem_efficient=True, enable_math=False): |
| attn_output = F.scaled_dot_product_attention( |
| query, key_states, value_states, |
| attn_mask=attn_mask, |
| dropout_p=dropout if module.training else 0.0, |
| scale=scaling, |
| is_causal=(attn_mask is None), |
| ) |
|
|
| attn_output = attn_output.transpose(1, 2).contiguous() |
| return attn_output, None |
|
|
|
|
|
|
| @use_kernelized_func(apply_rotary_pos_emb) |
| class QuasarAttention(nn.Module): |
| """Multi-headed attention from 'Attention Is All You Need' paper""" |
|
|
| def __init__(self, config: QuasarConfig, layer_idx: int): |
| super().__init__() |
| self.config = config |
| self.layer_idx = layer_idx |
| self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads) |
| self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads |
| self.scaling = self.head_dim**-0.5 |
| self.attention_dropout = config.attention_dropout |
| self.is_causal = True |
| self.q_proj = nn.Linear( |
| config.hidden_size, config.num_attention_heads * self.head_dim * 2, bias=config.attention_bias |
| ) |
| self.k_proj = nn.Linear( |
| config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias |
| ) |
| self.v_proj = nn.Linear( |
| config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias |
| ) |
| self.o_proj = nn.Linear( |
| config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias |
| ) |
| self.q_norm = QuasarRMSNorm(self.head_dim, eps=config.rms_norm_eps) |
| self.k_norm = QuasarRMSNorm(self.head_dim, eps=config.rms_norm_eps) |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| position_embeddings: tuple[torch.Tensor, torch.Tensor], |
| attention_mask: torch.Tensor | None, |
| past_key_values: Cache | None = None, |
| cache_position: torch.LongTensor | None = None, |
| **kwargs: Unpack[FlashAttentionKwargs], |
| ) -> tuple[torch.Tensor, torch.Tensor | None]: |
| input_shape = hidden_states.shape[:-1] |
| hidden_shape = (*input_shape, -1, self.head_dim) |
|
|
| query_states, gate = torch.chunk( |
| self.q_proj(hidden_states).view(*input_shape, -1, self.head_dim * 2), 2, dim=-1 |
| ) |
| gate = gate.reshape(*input_shape, -1) |
|
|
| query_states = self.q_norm(query_states.view(hidden_shape)).transpose(1, 2) |
| key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2) |
| value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2) |
|
|
| cos, sin = position_embeddings |
| if not getattr(self.config, "use_nope", False): |
| query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) |
|
|
| if past_key_values is not None: |
| |
| cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} |
| key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_kwargs) |
|
|
| attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface( |
| self.config._attn_implementation, eager_attention_forward |
| ) |
|
|
| attn_output, attn_weights = attention_interface( |
| self, |
| query_states, |
| key_states, |
| value_states, |
| attention_mask, |
| dropout=0.0 if not self.training else self.attention_dropout, |
| scaling=self.scaling, |
| **kwargs, |
| ) |
|
|
| attn_output = attn_output.reshape(*input_shape, -1).contiguous() |
| attn_output = attn_output * torch.sigmoid(gate) |
|
|
| attn_output = self.o_proj(attn_output) |
| return attn_output, attn_weights |
|
|
|
|
| class QuasarMLP(nn.Module): |
| def __init__(self, config: QuasarConfig, intermediate_size: int): |
| super().__init__() |
| self.config = config |
| self.hidden_size = config.hidden_size |
| self.intermediate_size = intermediate_size |
| self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) |
| self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) |
| self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False) |
| self.act_fn = ACT2FN[config.hidden_act] |
|
|
| def forward(self, x): |
| down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) |
| return down_proj |
|
|
|
|
| class QuasarRMSNorm(nn.Module): |
| def __init__(self, dim: int, eps: float = 1e-6): |
| super().__init__() |
| self.eps = eps |
| self.weight = nn.Parameter(torch.zeros(dim)) |
|
|
| def _norm(self, x): |
| return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) |
|
|
| def forward(self, x): |
| output = self._norm(x.float()) |
| |
| |
| output = output * (1.0 + self.weight.float()) |
| return output.type_as(x) |
|
|
| def extra_repr(self): |
| return f"{tuple(self.weight.shape)}, eps={self.eps}" |
|
|
|
|
| class QuasarGatedLinearAttention(nn.Module): |
| def __init__(self, config: QuasarConfig, layer_idx: int): |
| super().__init__() |
| |
| self.config = getattr(config, "text_config", config) |
| self.layer_idx = layer_idx |
| |
| hidden_size = self.config.hidden_size |
| num_heads = self.config.num_attention_heads |
| num_kv_heads = self.config.num_key_value_heads |
| head_dim = getattr(self.config, "head_dim", hidden_size // num_heads) |
| |
| expand_k = (num_kv_heads * head_dim) / hidden_size |
| expand_v = (num_kv_heads * head_dim) / hidden_size |
|
|
| from fla.layers.gla import GatedLinearAttention |
| |
| self.gla = GatedLinearAttention( |
| mode='chunk', |
| hidden_size=hidden_size, |
| expand_k=expand_k, |
| expand_v=expand_v, |
| num_heads=num_heads, |
| num_kv_heads=num_kv_heads, |
| use_short_conv=False, |
| layer_idx=layer_idx |
| ) |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| position_embeddings: tuple[torch.Tensor, torch.Tensor], |
| attention_mask: torch.Tensor | None, |
| past_key_values: Cache | None = None, |
| cache_position: torch.LongTensor | None = None, |
| **kwargs: Unpack[FlashAttentionKwargs], |
| ) -> tuple[torch.Tensor, torch.Tensor | None]: |
| |
| outputs = self.gla( |
| hidden_states=hidden_states, |
| attention_mask=attention_mask, |
| past_key_values=past_key_values, |
| use_cache=past_key_values is not None, |
| output_attentions=False, |
| ) |
| |
| attn_output = outputs[0] |
| |
| return attn_output, None |
|
|
|
|
| class QuasarDecoderLayer(GradientCheckpointingLayer): |
| def __init__(self, config: QuasarTextConfig, layer_idx: int): |
| super().__init__() |
| self.hidden_size = config.hidden_size |
| self.config = config |
| self.layer_type = config.layer_types[layer_idx] |
| if self.layer_type == "linear_attention": |
| self.linear_attn = QuasarGatedDeltaNet(config, layer_idx) |
| elif self.layer_type == "full_attention": |
| |
| |
| |
| text_config = getattr(config, "text_config", config) |
| use_gla = getattr(text_config, "use_gla", False) |
| |
| |
| |
| if not use_gla and "linear_attention" in getattr(text_config, "layer_types", []): |
| use_gla = True |
|
|
| if use_gla: |
| print(f" [L{layer_idx}] Initializing Quasar Gated-Linear Attention...") |
| self.self_attn = QuasarGatedLinearAttention(config, layer_idx) |
| else: |
| self.self_attn = QuasarAttention(config, layer_idx) |
| self.mlp = QuasarMLP(config, config.intermediate_size) |
| self.input_layernorm = QuasarRMSNorm(config.hidden_size, eps=config.rms_norm_eps) |
| self.post_attention_layernorm = QuasarRMSNorm(config.hidden_size, eps=config.rms_norm_eps) |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| position_embeddings: tuple[torch.Tensor, torch.Tensor], |
| attention_mask: torch.Tensor | None = None, |
| position_ids: torch.LongTensor | None = None, |
| past_key_values: Cache | None = None, |
| cache_position: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> torch.FloatTensor: |
| residual = hidden_states |
|
|
| hidden_states = self.input_layernorm(hidden_states) |
|
|
| |
| if self.layer_type == "linear_attention": |
| hidden_states = self.linear_attn( |
| hidden_states=hidden_states, |
| cache_params=past_key_values, |
| cache_position=cache_position, |
| attention_mask=attention_mask, |
| ) |
| elif self.layer_type == "full_attention": |
| |
| |
| fa_position_ids = position_ids |
| if position_ids is not None and position_ids.ndim == 3: |
| fa_position_ids = position_ids[0] |
| |
| hidden_states, _ = self.self_attn( |
| hidden_states=hidden_states, |
| attention_mask=attention_mask, |
| position_ids=fa_position_ids, |
| past_key_values=past_key_values, |
| cache_position=cache_position, |
| position_embeddings=position_embeddings, |
| **kwargs, |
| ) |
|
|
| hidden_states = residual + hidden_states |
|
|
| |
| residual = hidden_states |
| hidden_states = self.post_attention_layernorm(hidden_states) |
| hidden_states = self.mlp(hidden_states) |
| hidden_states = residual + hidden_states |
|
|
| return hidden_states |
|
|
|
|
| class QuasarPreTrainedModel(PreTrainedModel): |
| def _set_state_dict_hook(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): |
| """Ultra-robust hook to remap model.language_model -> model prefix mismatch.""" |
| keys = list(state_dict.keys()) |
| remapped_count = 0 |
| for key in keys: |
| if "language_model." in key: |
| new_key = key.replace("language_model.", "").replace("..", ".") |
| if new_key != key and new_key not in state_dict: |
| state_dict[new_key] = state_dict.pop(key) |
| remapped_count += 1 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| config: QuasarConfig |
| base_model_prefix = "model" |
| supports_gradient_checkpointing = True |
| _no_split_modules = ["QuasarDecoderLayer", "QuasarVisionBlock"] |
| _skip_keys_device_placement = "past_key_values" |
| _supports_flash_attn = True |
| _supports_sdpa = True |
| _keys_to_ignore_on_load_unexpected = [r"^mtp.*"] |
| _can_record_outputs = { |
| "hidden_states": QuasarDecoderLayer, |
| "attentions": QuasarAttention, |
| } |
| _is_stateful = True |
|
|
| @torch.no_grad() |
| def _init_weights(self, module): |
| super()._init_weights(module) |
| if isinstance(module, QuasarGatedDeltaNet): |
| init.ones_(module.dt_bias) |
| init.copy_(module.A_log, torch.empty_like(module.A_log).uniform_(0, 16).log_()) |
| |
| elif isinstance(module, QuasarRMSNorm): |
| init.zeros_(module.weight) |
| elif isinstance(module, QuasarVisionRotaryEmbedding): |
| inv_freq = 1.0 / (module.theta ** (torch.arange(0, module.dim, 2, dtype=torch.float) / module.dim)) |
| init.copy_(module.inv_freq, inv_freq) |
|
|
|
|
| class QuasarVisionMLP(nn.Module): |
| def __init__(self, config): |
| super().__init__() |
| self.hidden_size = config.hidden_size |
| self.intermediate_size = config.intermediate_size |
| self.linear_fc1 = nn.Linear(self.hidden_size, self.intermediate_size, bias=True) |
| self.linear_fc2 = nn.Linear(self.intermediate_size, self.hidden_size, bias=True) |
| self.act_fn = ACT2FN[config.hidden_act] |
|
|
| def forward(self, hidden_state): |
| return self.linear_fc2(self.act_fn(self.linear_fc1(hidden_state))) |
|
|
|
|
| class QuasarVisionPatchEmbed(nn.Module): |
| def __init__(self, config) -> None: |
| super().__init__() |
| self.patch_size = config.patch_size |
| self.temporal_patch_size = config.temporal_patch_size |
| self.in_channels = config.in_channels |
| self.embed_dim = config.hidden_size |
|
|
| kernel_size = [self.temporal_patch_size, self.patch_size, self.patch_size] |
| self.proj = nn.Conv3d(self.in_channels, self.embed_dim, kernel_size=kernel_size, stride=kernel_size, bias=True) |
|
|
| def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| target_dtype = self.proj.weight.dtype |
| hidden_states = hidden_states.view( |
| -1, self.in_channels, self.temporal_patch_size, self.patch_size, self.patch_size |
| ) |
| hidden_states = self.proj(hidden_states.to(dtype=target_dtype)).view(-1, self.embed_dim) |
| return hidden_states |
|
|
|
|
| class QuasarVisionPatchMerger(nn.Module): |
| def __init__(self, config: QuasarVisionConfig, use_postshuffle_norm=False) -> None: |
| super().__init__() |
| self.hidden_size = config.hidden_size * (config.spatial_merge_size**2) |
| self.use_postshuffle_norm = use_postshuffle_norm |
| self.norm = nn.LayerNorm(self.hidden_size if use_postshuffle_norm else config.hidden_size, eps=1e-6) |
| self.linear_fc1 = nn.Linear(self.hidden_size, self.hidden_size) |
| self.act_fn = nn.GELU() |
| self.linear_fc2 = nn.Linear(self.hidden_size, config.out_hidden_size) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x = self.norm(x.view(-1, self.hidden_size) if self.use_postshuffle_norm else x).view(-1, self.hidden_size) |
| x = self.linear_fc2(self.act_fn(self.linear_fc1(x))) |
| return x |
|
|
|
|
| def apply_rotary_pos_emb_vision( |
| q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| orig_q_dtype = q.dtype |
| orig_k_dtype = k.dtype |
| q, k = q.float(), k.float() |
| cos, sin = cos.unsqueeze(-2).float(), sin.unsqueeze(-2).float() |
| q_embed = (q * cos) + (rotate_half(q) * sin) |
| k_embed = (k * cos) + (rotate_half(k) * sin) |
| q_embed = q_embed.to(orig_q_dtype) |
| k_embed = k_embed.to(orig_k_dtype) |
| return q_embed, k_embed |
|
|
|
|
| class QuasarVisionAttention(nn.Module): |
| def __init__(self, config: QuasarVisionConfig) -> None: |
| super().__init__() |
| self.dim = config.hidden_size |
| self.num_heads = config.num_heads |
| self.head_dim = self.dim // self.num_heads |
| self.num_key_value_groups = 1 |
| self.qkv = nn.Linear(self.dim, self.dim * 3, bias=True) |
| self.proj = nn.Linear(self.dim, self.dim) |
| self.scaling = self.head_dim**-0.5 |
| self.config = config |
| self.attention_dropout = 0.0 |
| self.is_causal = False |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| cu_seqlens: torch.Tensor, |
| rotary_pos_emb: torch.Tensor | None = None, |
| position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None, |
| **kwargs, |
| ) -> torch.Tensor: |
| seq_length = hidden_states.shape[0] |
| query_states, key_states, value_states = ( |
| self.qkv(hidden_states).reshape(seq_length, 3, self.num_heads, -1).permute(1, 0, 2, 3).unbind(0) |
| ) |
| cos, sin = position_embeddings |
| query_states, key_states = apply_rotary_pos_emb_vision(query_states, key_states, cos, sin) |
|
|
| query_states = query_states.transpose(0, 1).unsqueeze(0) |
| key_states = key_states.transpose(0, 1).unsqueeze(0) |
| value_states = value_states.transpose(0, 1).unsqueeze(0) |
|
|
| attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface( |
| self.config._attn_implementation, eager_attention_forward |
| ) |
|
|
| if is_flash_attention_requested(self.config): |
| |
| max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max() |
| attn_output, _ = attention_interface( |
| self, |
| query_states, |
| key_states, |
| value_states, |
| attention_mask=None, |
| scaling=self.scaling, |
| dropout=0.0 if not self.training else self.attention_dropout, |
| cu_seq_lens_q=cu_seqlens, |
| cu_seq_lens_k=cu_seqlens, |
| max_length_q=max_seqlen, |
| max_length_k=max_seqlen, |
| is_causal=False, |
| **kwargs, |
| ) |
| else: |
| |
| lengths = cu_seqlens[1:] - cu_seqlens[:-1] |
| splits = [ |
| torch.split(tensor, lengths.tolist(), dim=2) for tensor in (query_states, key_states, value_states) |
| ] |
|
|
| attn_outputs = [ |
| attention_interface( |
| self, |
| q, |
| k, |
| v, |
| attention_mask=None, |
| scaling=self.scaling, |
| dropout=0.0 if not self.training else self.attention_dropout, |
| is_causal=False, |
| **kwargs, |
| )[0] |
| for q, k, v in zip(*splits) |
| ] |
| attn_output = torch.cat(attn_outputs, dim=1) |
|
|
| attn_output = attn_output.reshape(seq_length, -1).contiguous() |
| attn_output = self.proj(attn_output) |
| return attn_output |
|
|
|
|
| class QuasarVisionBlock(GradientCheckpointingLayer): |
| def __init__(self, config, attn_implementation: str = "sdpa") -> None: |
| super().__init__() |
| self.norm1 = nn.LayerNorm(config.hidden_size, eps=1e-6) |
| self.norm2 = nn.LayerNorm(config.hidden_size, eps=1e-6) |
| self.attn = QuasarVisionAttention(config=config) |
| self.mlp = QuasarVisionMLP(config=config) |
|
|
| def forward( |
| self, |
| hidden_states: torch.Tensor, |
| cu_seqlens: torch.Tensor, |
| rotary_pos_emb: torch.Tensor | None = None, |
| position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None, |
| **kwargs, |
| ) -> torch.Tensor: |
| hidden_states = hidden_states + self.attn( |
| self.norm1(hidden_states), |
| cu_seqlens=cu_seqlens, |
| rotary_pos_emb=rotary_pos_emb, |
| position_embeddings=position_embeddings, |
| **kwargs, |
| ) |
| hidden_states = hidden_states + self.mlp(self.norm2(hidden_states)) |
| return hidden_states |
|
|
|
|
| class QuasarVisionModel(QuasarPreTrainedModel): |
| config: QuasarVisionConfig |
| input_modalities = ("image", "video") |
| _no_split_modules = ["QuasarVisionBlock"] |
| _can_record_outputs = { |
| "hidden_states": QuasarVisionBlock, |
| "attentions": QuasarVisionAttention, |
| } |
|
|
| def __init__(self, config, *inputs, **kwargs) -> None: |
| super().__init__(config, *inputs, **kwargs) |
| self.spatial_merge_size = config.spatial_merge_size |
| self.patch_size = config.patch_size |
| self.spatial_merge_unit = self.spatial_merge_size * self.spatial_merge_size |
|
|
| self.patch_embed = QuasarVisionPatchEmbed( |
| config=config, |
| ) |
|
|
| self.pos_embed = nn.Embedding(config.num_position_embeddings, config.hidden_size) |
| self.num_grid_per_side = int(config.num_position_embeddings**0.5) |
|
|
| head_dim = config.hidden_size // config.num_heads |
| self.rotary_pos_emb = QuasarVisionRotaryEmbedding(head_dim // 2) |
|
|
| self.blocks = nn.ModuleList([QuasarVisionBlock(config) for _ in range(config.depth)]) |
| self.merger = QuasarVisionPatchMerger( |
| config=config, |
| use_postshuffle_norm=False, |
| ) |
|
|
| self.gradient_checkpointing = False |
|
|
| self.post_init() |
|
|
| def rot_pos_emb(self, grid_thw: torch.Tensor) -> torch.Tensor: |
| merge_size = self.spatial_merge_size |
| grid_thw_list = grid_thw.tolist() |
|
|
| max_hw = max(max(h, w) for _, h, w in grid_thw_list) |
| freq_table = self.rotary_pos_emb(max_hw) |
| device = freq_table.device |
|
|
| total_tokens = sum(t * h * w for t, h, w in grid_thw_list) |
| pos_ids = torch.empty((total_tokens, 2), dtype=torch.long, device=device) |
|
|
| offset = 0 |
| for num_frames, height, width in grid_thw_list: |
| merged_h, merged_w = height // merge_size, width // merge_size |
|
|
| block_rows = torch.arange(merged_h, device=device) |
| block_cols = torch.arange(merged_w, device=device) |
| intra_row = torch.arange(merge_size, device=device) |
| intra_col = torch.arange(merge_size, device=device) |
|
|
| |
| row_idx = block_rows[:, None, None, None] * merge_size + intra_row[None, None, :, None] |
| col_idx = block_cols[None, :, None, None] * merge_size + intra_col[None, None, None, :] |
|
|
| row_idx = row_idx.expand(merged_h, merged_w, merge_size, merge_size).reshape(-1) |
| col_idx = col_idx.expand(merged_h, merged_w, merge_size, merge_size).reshape(-1) |
|
|
| coords = torch.stack((row_idx, col_idx), dim=-1) |
|
|
| if num_frames > 1: |
| coords = coords.repeat(num_frames, 1) |
|
|
| num_tokens = coords.shape[0] |
| pos_ids[offset : offset + num_tokens] = coords |
| offset += num_tokens |
|
|
| embeddings = freq_table[pos_ids] |
| embeddings = embeddings.flatten(1) |
| return embeddings |
|
|
| def fast_pos_embed_interpolate(self, grid_thw): |
| grid_thw_list = grid_thw.tolist() |
| grid_ts = [row[0] for row in grid_thw_list] |
| grid_hs = [row[1] for row in grid_thw_list] |
| grid_ws = [row[2] for row in grid_thw_list] |
| device = self.pos_embed.weight.device |
|
|
| idx_list = [[] for _ in range(4)] |
| weight_list = [[] for _ in range(4)] |
|
|
| for t, h, w in grid_thw_list: |
| h_idxs = torch.linspace(0, self.num_grid_per_side - 1, h) |
| w_idxs = torch.linspace(0, self.num_grid_per_side - 1, w) |
|
|
| h_idxs_floor = h_idxs.int() |
| w_idxs_floor = w_idxs.int() |
| h_idxs_ceil = (h_idxs.int() + 1).clip(max=self.num_grid_per_side - 1) |
| w_idxs_ceil = (w_idxs.int() + 1).clip(max=self.num_grid_per_side - 1) |
|
|
| dh = h_idxs - h_idxs_floor |
| dw = w_idxs - w_idxs_floor |
|
|
| base_h = h_idxs_floor * self.num_grid_per_side |
| base_h_ceil = h_idxs_ceil * self.num_grid_per_side |
|
|
| indices = [ |
| (base_h[None].T + w_idxs_floor[None]).flatten(), |
| (base_h[None].T + w_idxs_ceil[None]).flatten(), |
| (base_h_ceil[None].T + w_idxs_floor[None]).flatten(), |
| (base_h_ceil[None].T + w_idxs_ceil[None]).flatten(), |
| ] |
|
|
| weights = [ |
| ((1 - dh)[None].T * (1 - dw)[None]).flatten(), |
| ((1 - dh)[None].T * dw[None]).flatten(), |
| (dh[None].T * (1 - dw)[None]).flatten(), |
| (dh[None].T * dw[None]).flatten(), |
| ] |
|
|
| for i in range(4): |
| idx_list[i].extend(indices[i].tolist()) |
| weight_list[i].extend(weights[i].tolist()) |
|
|
| idx_tensor = torch.tensor(idx_list, dtype=torch.long, device=device) |
| weight_tensor = torch.tensor(weight_list, dtype=self.pos_embed.weight.dtype, device=device) |
| pos_embeds = self.pos_embed(idx_tensor).to(device) * weight_tensor[:, :, None] |
| patch_pos_embeds = pos_embeds[0] + pos_embeds[1] + pos_embeds[2] + pos_embeds[3] |
|
|
| patch_pos_embeds = patch_pos_embeds.split([h * w for h, w in zip(grid_hs, grid_ws)]) |
|
|
| patch_pos_embeds_permute = [] |
| merge_size = self.config.spatial_merge_size |
| for pos_embed, t, h, w in zip(patch_pos_embeds, grid_ts, grid_hs, grid_ws): |
| pos_embed = pos_embed.repeat(t, 1) |
| pos_embed = ( |
| pos_embed.view(t, h // merge_size, merge_size, w // merge_size, merge_size, -1) |
| .permute(0, 1, 3, 2, 4, 5) |
| .flatten(0, 4) |
| ) |
| patch_pos_embeds_permute.append(pos_embed) |
| patch_pos_embeds = torch.cat(patch_pos_embeds_permute) |
| return patch_pos_embeds |
|
|
| @merge_with_config_defaults |
| @capture_outputs |
| def forward(self, hidden_states: torch.Tensor, grid_thw: torch.Tensor, **kwargs) -> torch.Tensor: |
| """ |
| Args: |
| hidden_states (`torch.Tensor` of shape `(seq_len, hidden_size)`): |
| The final hidden states of the model. |
| grid_thw (`torch.Tensor` of shape `(num_images_or_videos, 3)`): |
| The temporal, height and width of feature shape of each image in LLM. |
| |
| Returns: |
| `torch.Tensor`: hidden_states. |
| """ |
| hidden_states = self.patch_embed(hidden_states) |
|
|
| pos_embeds = self.fast_pos_embed_interpolate(grid_thw) |
| hidden_states = hidden_states + pos_embeds |
|
|
| rotary_pos_emb = self.rot_pos_emb(grid_thw) |
|
|
| seq_len, _ = hidden_states.size() |
| hidden_states = hidden_states.reshape(seq_len, -1) |
| rotary_pos_emb = rotary_pos_emb.reshape(seq_len, -1) |
| emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1) |
| position_embeddings = (emb.cos(), emb.sin()) |
|
|
| cu_seqlens = torch.repeat_interleave(grid_thw[:, 1] * grid_thw[:, 2], grid_thw[:, 0]).cumsum( |
| dim=0, |
| |
| |
| |
| |
| dtype=grid_thw.dtype if torch.jit.is_tracing() else torch.int32, |
| ) |
| cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0) |
|
|
| for blk in self.blocks: |
| hidden_states = blk( |
| hidden_states, |
| cu_seqlens=cu_seqlens, |
| position_embeddings=position_embeddings, |
| **kwargs, |
| ) |
|
|
| merged_hidden_states = self.merger(hidden_states) |
|
|
| return BaseModelOutputWithPooling( |
| last_hidden_state=hidden_states, |
| pooler_output=merged_hidden_states, |
| ) |
|
|
|
|
| @dataclass |
| @auto_docstring( |
| custom_intro=""" |
| Base class for Llava outputs, with hidden states and attentions. |
| """ |
| ) |
| class QuasarModelOutputWithPast(ModelOutput): |
| r""" |
| past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`): |
| It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache). |
| |
| Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see |
| `past_key_values` input) to speed up sequential decoding. |
| rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*): |
| The rope index difference between sequence length and multimodal rope. |
| """ |
|
|
| last_hidden_state: torch.FloatTensor | None = None |
| past_key_values: Cache | None = None |
| hidden_states: tuple[torch.FloatTensor] | None = None |
| attentions: tuple[torch.FloatTensor] | None = None |
| rope_deltas: torch.LongTensor | None = None |
|
|
|
|
| class QuasarTextModel(QuasarPreTrainedModel): |
| def __init__(self, config: QuasarTextConfig): |
| if hasattr(config, 'text_config'): config = config.text_config |
| if hasattr(config, 'text_config'): config = config.text_config |
| if hasattr(config, 'text_config'): config = config.text_config |
| |
| if hasattr(config, "text_config"): |
| config = config.text_config |
| super().__init__(config) |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, config.pad_token_id) |
| self.layers = nn.ModuleList( |
| [QuasarDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)] |
| ) |
| self.norm = QuasarRMSNorm(config.hidden_size, eps=config.rms_norm_eps) |
| self.rotary_emb = QuasarTextRotaryEmbedding(config=config) |
| self.gradient_checkpointing = False |
| |
| self.post_init() |
|
|
| @merge_with_config_defaults |
| @capture_outputs |
| @auto_docstring |
| def forward( |
| self, |
| input_ids: torch.LongTensor | None = None, |
| attention_mask: torch.Tensor | None = None, |
| position_ids: torch.LongTensor | None = None, |
| past_key_values: Cache | None = None, |
| inputs_embeds: torch.FloatTensor | None = None, |
| use_cache: bool | None = None, |
| cache_position: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> BaseModelOutputWithPast: |
| if (input_ids is None) ^ (inputs_embeds is not None): |
| raise ValueError("You must specify exactly one of input_ids or inputs_embeds") |
|
|
| if inputs_embeds is None: |
| inputs_embeds = self.embed_tokens(input_ids) |
|
|
| if use_cache and past_key_values is None: |
| past_key_values = QuasarDynamicCache(config=self.config) |
|
|
| if cache_position is None: |
| past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0 |
| cache_position = torch.arange( |
| past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device |
| ) |
|
|
| |
| if position_ids is None: |
| position_ids = cache_position.view(1, 1, -1).expand(4, inputs_embeds.shape[0], -1) |
| elif position_ids.ndim == 2: |
| position_ids = position_ids[None, ...].expand(4, position_ids.shape[0], -1) |
|
|
| if position_ids.ndim == 3 and position_ids.shape[0] == 4: |
| text_position_ids = position_ids[0] |
| position_ids = position_ids[1:] |
| else: |
| text_position_ids = None |
|
|
| causal_mask = create_causal_mask( |
| config=self.config, |
| inputs_embeds=inputs_embeds, |
| attention_mask=attention_mask, |
| cache_position=cache_position, |
| past_key_values=past_key_values, |
| position_ids=text_position_ids, |
| ) |
| linear_attn_mask = self._update_linear_attn_mask(attention_mask, cache_position) |
|
|
| all_hidden_states = () if kwargs.get("output_hidden_states", False) else None |
| hidden_states = inputs_embeds |
| position_embeddings = self.rotary_emb(hidden_states, position_ids) |
|
|
| for layer_idx, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]): |
| if all_hidden_states is not None: |
| all_hidden_states += (hidden_states,) |
|
|
| layer_mask = linear_attn_mask if decoder_layer.layer_type == "linear_attention" else causal_mask |
|
|
| hidden_states = decoder_layer( |
| hidden_states, |
| position_embeddings=position_embeddings, |
| attention_mask=layer_mask, |
| position_ids=position_ids, |
| past_key_values=past_key_values, |
| use_cache=use_cache, |
| cache_position=cache_position, |
| **kwargs, |
| ) |
|
|
| hidden_states = self.norm(hidden_states) |
| if all_hidden_states is not None: |
| all_hidden_states += (hidden_states,) |
|
|
| return QuasarModelOutputWithPast( |
| last_hidden_state=hidden_states, |
| past_key_values=past_key_values, |
| hidden_states=all_hidden_states, |
| ) |
|
|
| def _update_linear_attn_mask(self, attention_mask, cache_position): |
| """ |
| NOTE: Left-padding is used for linear attention mask. |
| No need for zeroing states when |
| 1. Cached forward |
| 2. Attending to all inputs |
| """ |
| linear_attn_mask = attention_mask |
| if cache_position[0] > 0 or (attention_mask is not None and torch.all(attention_mask == 1)): |
| linear_attn_mask = None |
| return linear_attn_mask |
|
|
|
|
| @auto_docstring |
| class QuasarModel(QuasarPreTrainedModel): |
| base_model_prefix = "model" |
| _checkpoint_conversion_mapping = {} |
| |
| accepts_loss_kwargs = False |
| config: QuasarConfig |
| _no_split_modules = ["QuasarDecoderLayer", "QuasarVisionBlock"] |
|
|
| def __init__(self, config): |
| super().__init__(config) |
| self.visual = QuasarVisionModel._from_config(config.vision_config) |
| self.language_model = QuasarTextModel._from_config(config.text_config) |
| self.rope_deltas = None |
|
|
| |
| self.post_init() |
|
|
| def get_input_embeddings(self): |
| return self.language_model.get_input_embeddings() |
|
|
| def set_input_embeddings(self, value): |
| self.language_model.set_input_embeddings(value) |
|
|
| def get_vision_position_ids( |
| self, |
| start_position: int, |
| grid_thw: list[int, int, int] | torch.Tensor, |
| temp_merge_size: int = 1, |
| spatial_merge_size: int = 1, |
| time_interval: int = 1, |
| device: str | torch.device | None = None, |
| ): |
| """ |
| Compute 3D positional indices for vision tokens derived from a single image or video input. |
| |
| The positions are generated from the input grid defined by temporal (T), height (H), and |
| width (W) dimensions. Temporal and spatial dimensions can be downscaled according to the |
| merge sizes used in the vision backbone. The resulting positions are offset by `start_position`. |
| |
| Args: |
| start_position (`int`): |
| Offset added to all computed positional indices. |
| grid_thw (`Sequence[int]` or `torch.Tensor` of shape `(3,)`): |
| The (T, H, W) grid representing the feature layout of the current image or video after patch embedding. |
| temp_merge_size (`int`, *optional*): |
| Factor by which the temporal dimension is reduced in the backbone. The temporal grid size is divided |
| by this value. Defaults to 1. |
| spatial_merge_size (`int`, *optional*): |
| Factor by which the spatial dimensions (H and W) are reduced in the backbone. Both H and W are divided |
| by this value. Defaults to 1. |
| time_interval (`int`, *optional*): |
| Spacing factor applied between consecutive temporal position indices.Defaults to 1. |
| device (`str` or `torch.device`, *optional*): |
| Device on which the resulting tensor is allocated. If `None`, uses the current default device. |
| |
| Returns: |
| torch.LongTensor of shape (3, sequence_length): |
| Positional indices for temporal, height, and width dimensions, |
| flattened into sequence form and offset by `start_position`. |
| """ |
| llm_grid_t, llm_grid_h, llm_grid_w = ( |
| grid_thw[0].item() // temp_merge_size, |
| grid_thw[1].item() // spatial_merge_size, |
| grid_thw[2].item() // spatial_merge_size, |
| ) |
|
|
| image_seq_length = llm_grid_h * llm_grid_w * llm_grid_t |
| position_width = torch.arange(start_position, start_position + llm_grid_w, device=device).repeat( |
| llm_grid_h * llm_grid_t |
| ) |
| position_height = torch.arange(start_position, start_position + llm_grid_h, device=device).repeat_interleave( |
| llm_grid_w * llm_grid_t |
| ) |
| position_temporal = torch.full((image_seq_length,), start_position, device=device, dtype=torch.long) |
| position_temporal = position_temporal * time_interval |
| vision_position_ids = torch.stack([position_temporal, position_height, position_width], dim=0) |
|
|
| return vision_position_ids |
|
|
| def get_rope_index( |
| self, |
| input_ids: torch.LongTensor, |
| mm_token_type_ids: torch.IntTensor, |
| image_grid_thw: torch.LongTensor | None = None, |
| video_grid_thw: torch.LongTensor | None = None, |
| attention_mask: torch.Tensor | None = None, |
| **kwargs, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| """ |
| Calculate the 3D rope index based on image and video's sizes. The utility expects a `vision + text` |
| sequence and will error out otherwise. For pure text sequence, please rely on model's auto-inferred |
| position ids. In a mixed vision + text sequence, vision tokens use 3D RoPE (temporal, height, width) |
| while text tokens use standard 1D RoPE. |
| |
| Example: |
| Temporal patches: 3; Height patches: 2; Width patches: 2 |
| Each vision input results in (temporal x height × width) positions. Here: 3 x 2 × 2 = 12 positions total. |
| |
| Temporal position IDs are spaced by: |
| `interval = tokens_per_second * temporal_patch_size / fps` |
| |
| If fps = 1; tokens_per_second = 25; temporal_patch_size = 2, temporal IDs increase by 50 for each temporal patch: |
| `[0, 0, 0, 0, 50, 50, 50, 50, 100, 100, 100, 100]` |
| |
| Height IDs repeat per row: `[0, 0, 1, 1, ...]` |
| Width IDs alternate per column: `[0, 1, 0, 1, ...]` |
| Text tokens follow standard 1D RoPE and the position IDs grow consequently with a step of `1` |
| |
| Args: |
| input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`): |
| Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide |
| it. |
| mm_token_type_ids (`torch.IntTensor` of shape `(batch_size, sequence_length)`): |
| Token type ids matching each modality to a different value in the input sequence, i.e. text (0), image (1), video (2). |
| image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*): |
| The temporal, height and width of feature shape of each image in LLM. |
| video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*): |
| The temporal, height and width of feature shape of each video in LLM. |
| attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*): |
| Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`: |
| |
| - 1 for tokens that are **not masked**, |
| - 0 for tokens that are **masked**. |
| |
| Returns: |
| position_ids (`torch.LongTensor` of shape `(3, batch_size, sequence_length)`) |
| mrope_position_deltas (`torch.Tensor` of shape `(batch_size)`) |
| """ |
| spatial_merge_size = self.config.vision_config.spatial_merge_size |
|
|
| mrope_position_deltas = [] |
| position_ids = torch.zeros( |
| 3, |
| input_ids.shape[0], |
| input_ids.shape[1], |
| dtype=input_ids.dtype, |
| device=input_ids.device, |
| ) |
| grid_iters = { |
| 1: iter(image_grid_thw) if image_grid_thw is not None else None, |
| 2: iter(video_grid_thw) if video_grid_thw is not None else None, |
| } |
|
|
| for batch_idx, current_input_ids in enumerate(input_ids): |
| input_token_type = mm_token_type_ids[batch_idx] |
| if attention_mask is not None: |
| current_input_ids = current_input_ids[attention_mask[batch_idx].bool()] |
| input_token_type = input_token_type[attention_mask[batch_idx].bool()] |
|
|
| input_type_group = [] |
| for key, group in itertools.groupby(enumerate(input_token_type.tolist()), lambda x: x[1]): |
| group = list(group) |
| start_index = group[0][0] |
| end_index = group[-1][0] + 1 |
| input_type_group.append((key, start_index, end_index)) |
|
|
| current_pos = 0 |
| llm_pos_ids_list = [] |
| for modality_type, start_idx, end_idx in input_type_group: |
| |
| if modality_type == 0: |
| text_len = end_idx - start_idx |
| llm_pos_ids_list.append( |
| torch.arange(text_len, device=input_ids.device).view(1, -1).expand(3, -1) + current_pos |
| ) |
| current_pos += text_len |
| |
| else: |
| grid_thw = next(grid_iters[modality_type]) |
| vision_position_ids = self.get_vision_position_ids( |
| current_pos, grid_thw, 1, spatial_merge_size, device=input_ids.device |
| ) |
| llm_pos_ids_list.append(vision_position_ids) |
| current_pos += max(grid_thw[1], grid_thw[2]) // spatial_merge_size |
| llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1) |
| if attention_mask is not None: |
| position_ids[:, batch_idx, attention_mask[batch_idx].bool()] = llm_positions.to(position_ids.device) |
| else: |
| position_ids[:, batch_idx] = llm_positions.to(position_ids.device) |
| mrope_position_deltas.append(llm_positions.max() + 1 - len(current_input_ids)) |
| mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1) |
| return position_ids, mrope_position_deltas |
|
|
| @can_return_tuple |
| @auto_docstring |
| def get_video_features( |
| self, |
| pixel_values_videos: torch.FloatTensor, |
| video_grid_thw: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | BaseModelOutputWithPooling: |
| r""" |
| pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`): |
| The tensors corresponding to the input videos. |
| video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*): |
| The temporal, height and width of feature shape of each video in LLM. |
| """ |
| |
| return self.get_image_features(pixel_values_videos, video_grid_thw, **kwargs) |
|
|
| @can_return_tuple |
| @auto_docstring |
| def get_image_features( |
| self, |
| pixel_values: torch.FloatTensor, |
| image_grid_thw: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | BaseModelOutputWithPooling: |
| r""" |
| pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`): |
| The tensors corresponding to the input images. |
| image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*): |
| The temporal, height and width of feature shape of each image in LLM. |
| """ |
| pixel_values = pixel_values.type(self.visual.dtype) |
| vision_output: BaseModelOutputWithPooling = self.visual( |
| pixel_values, grid_thw=image_grid_thw, return_dict=True, **kwargs |
| ) |
| image_embeds = vision_output.pooler_output |
| split_sizes = (image_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist() |
| image_embeds = torch.split(image_embeds, split_sizes) |
| vision_output.pooler_output = image_embeds |
|
|
| return vision_output |
|
|
| def get_placeholder_mask( |
| self, |
| input_ids: torch.LongTensor, |
| inputs_embeds: torch.FloatTensor, |
| image_features: torch.FloatTensor | None = None, |
| video_features: torch.FloatTensor | None = None, |
| ): |
| """ |
| Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is |
| equal to the length of multimodal features. If the lengths are different, an error is raised. |
| """ |
| if input_ids is None: |
| special_image_mask = inputs_embeds == self.get_input_embeddings()( |
| torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device) |
| ) |
| special_image_mask = special_image_mask.all(-1) |
| special_video_mask = inputs_embeds == self.get_input_embeddings()( |
| torch.tensor(self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device) |
| ) |
| special_video_mask = special_video_mask.all(-1) |
| else: |
| special_image_mask = input_ids == self.config.image_token_id |
| special_video_mask = input_ids == self.config.video_token_id |
|
|
| n_image_tokens = special_image_mask.sum() |
| special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device) |
| if image_features is not None: |
| torch_compilable_check( |
| inputs_embeds[special_image_mask].numel() == image_features.numel(), |
| f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {image_features.shape[0]}", |
| ) |
|
|
| n_video_tokens = special_video_mask.sum() |
| special_video_mask = special_video_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device) |
| if video_features is not None: |
| torch_compilable_check( |
| inputs_embeds[special_video_mask].numel() == video_features.numel(), |
| f"Video features and video tokens do not match, tokens: {n_video_tokens}, features: {video_features.shape[0]}", |
| ) |
| return special_image_mask, special_video_mask |
|
|
| def compute_3d_position_ids( |
| self, |
| input_ids: torch.Tensor | None, |
| inputs_embeds: torch.Tensor | None, |
| image_grid_thw: torch.Tensor | None = None, |
| video_grid_thw: torch.Tensor | None = None, |
| attention_mask: torch.Tensor | None = None, |
| past_key_values: torch.Tensor | None = None, |
| mm_token_type_ids: torch.IntTensor | None = None, |
| ) -> torch.Tensor | None: |
| past_key_values_length = 0 if past_key_values is None else past_key_values.get_seq_length() |
| can_compute_mrope = ( |
| input_ids is not None |
| and mm_token_type_ids is not None |
| and (image_grid_thw is not None or video_grid_thw is not None) |
| ) |
|
|
| if can_compute_mrope and (self.rope_deltas is None or past_key_values_length == 0): |
| position_ids, rope_deltas = self.get_rope_index( |
| input_ids, |
| image_grid_thw=image_grid_thw, |
| video_grid_thw=video_grid_thw, |
| attention_mask=attention_mask, |
| mm_token_type_ids=mm_token_type_ids, |
| ) |
| self.rope_deltas = rope_deltas |
| |
| elif self.rope_deltas is not None: |
| batch_size, seq_length, _ = inputs_embeds.shape |
| if attention_mask is not None: |
| position_ids = attention_mask.long().cumsum(-1) - 1 |
| position_ids = position_ids.masked_fill(attention_mask == 0, 0) |
| position_ids = position_ids.view(1, batch_size, -1).repeat(3, 1, 1).to(inputs_embeds.device) |
| else: |
| position_ids = torch.arange(past_key_values_length, past_key_values_length + seq_length) |
| position_ids = position_ids.view(1, 1, -1).expand(3, batch_size, -1).to(inputs_embeds.device) |
| delta = self.rope_deltas.repeat_interleave(batch_size // self.rope_deltas.shape[0], dim=0) |
| position_ids = position_ids + delta.to(device=inputs_embeds.device) |
| else: |
| |
| position_ids = None |
| return position_ids |
|
|
| @auto_docstring |
| @can_return_tuple |
| def forward( |
| self, |
| input_ids: torch.LongTensor = None, |
| attention_mask: torch.Tensor | None = None, |
| position_ids: torch.LongTensor | None = None, |
| past_key_values: Cache | None = None, |
| inputs_embeds: torch.FloatTensor | None = None, |
| pixel_values: torch.Tensor | None = None, |
| pixel_values_videos: torch.FloatTensor | None = None, |
| image_grid_thw: torch.LongTensor | None = None, |
| video_grid_thw: torch.LongTensor | None = None, |
| mm_token_type_ids: torch.IntTensor | None = None, |
| cache_position: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | QuasarModelOutputWithPast: |
| r""" |
| image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*): |
| The temporal, height and width of feature shape of each image in LLM. |
| video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*): |
| The temporal, height and width of feature shape of each video in LLM. |
| """ |
| if (input_ids is None) ^ (inputs_embeds is not None): |
| raise ValueError("You must specify exactly one of input_ids or inputs_embeds") |
|
|
| if inputs_embeds is None: |
| inputs_embeds = self.get_input_embeddings()(input_ids) |
|
|
| if pixel_values is not None: |
| image_outputs: BaseModelOutputWithPooling = self.get_image_features( |
| pixel_values, image_grid_thw, return_dict=True |
| ) |
| image_embeds = image_outputs.pooler_output |
| image_embeds = torch.cat(image_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype) |
| image_mask, _ = self.get_placeholder_mask( |
| input_ids, inputs_embeds=inputs_embeds, image_features=image_embeds |
| ) |
| inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds) |
|
|
| if pixel_values_videos is not None: |
| video_outputs: BaseModelOutputWithPooling = self.get_video_features( |
| pixel_values_videos, video_grid_thw, return_dict=True |
| ) |
| video_embeds = video_outputs.pooler_output |
| video_embeds = torch.cat(video_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype) |
| _, video_mask = self.get_placeholder_mask( |
| input_ids, inputs_embeds=inputs_embeds, video_features=video_embeds |
| ) |
| inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds) |
|
|
| if position_ids is None: |
| position_ids = self.compute_3d_position_ids( |
| input_ids=input_ids, |
| image_grid_thw=image_grid_thw, |
| video_grid_thw=video_grid_thw, |
| inputs_embeds=inputs_embeds, |
| attention_mask=attention_mask, |
| past_key_values=past_key_values, |
| mm_token_type_ids=mm_token_type_ids, |
| ) |
|
|
| outputs = self.language_model( |
| input_ids=None, |
| position_ids=position_ids, |
| attention_mask=attention_mask, |
| past_key_values=past_key_values, |
| inputs_embeds=inputs_embeds, |
| cache_position=cache_position, |
| **kwargs, |
| ) |
|
|
| return QuasarModelOutputWithPast( |
| **outputs, |
| rope_deltas=self.rope_deltas, |
| ) |
|
|
|
|
| @auto_docstring |
| class QuasarForCausalLM(QuasarPreTrainedModel, GenerationMixin): |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| _checkpoint_conversion_mapping = { |
| "model.language_model.layers": "model.layers", |
| "model.language_model.embed_tokens": "model.embed_tokens", |
| "model.language_model.norm": "model.norm", |
| } |
| _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"} |
| _tp_plan = {"lm_head": "colwise_gather_output"} |
| _pp_plan = {"lm_head": (["hidden_states"], ["logits"])} |
| config: QuasarTextConfig |
| _keys_to_ignore_on_load_unexpected = [r"^mtp.*", r"^model.visual.*"] |
|
|
| def __init__(self, config): |
| |
| if hasattr(config, "text_config"): |
| config = config.text_config |
| super().__init__(config) |
| self.model = QuasarTextModel(config) |
| |
| |
| self._register_load_state_dict_pre_hook( |
| lambda state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs: |
| self._set_state_dict_hook(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs) |
| ) |
|
|
| self.vocab_size = config.vocab_size |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) |
|
|
| |
| self.post_init() |
|
|
| @can_return_tuple |
| @auto_docstring |
| def forward( |
| self, |
| input_ids: torch.LongTensor | None = None, |
| attention_mask: torch.Tensor | None = None, |
| position_ids: torch.LongTensor | None = None, |
| past_key_values: Cache | None = None, |
| inputs_embeds: torch.FloatTensor | None = None, |
| labels: torch.LongTensor | None = None, |
| use_cache: bool | None = None, |
| cache_position: torch.LongTensor | None = None, |
| logits_to_keep: int | torch.Tensor = 0, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> CausalLMOutputWithPast: |
| r""" |
| labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*): |
| Labels for computing the masked language modeling loss. Indices should either be in `[0, ..., |
| config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored |
| (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`. |
| |
| Example: |
| |
| ```python |
| >>> from transformers import AutoTokenizer, QuasarForCausalLM |
| |
| >>> model = QuasarForCausalLM.from_pretrained("Qwen/Quasar-8B") |
| >>> tokenizer = AutoTokenizer.from_pretrained("Qwen/Quasar-8B") |
| |
| >>> prompt = "Hey, are you conscious? Can you talk to me?" |
| >>> inputs = tokenizer(prompt, return_tensors="pt") |
| |
| >>> # Generate |
| >>> generate_ids = model.generate(inputs.input_ids, max_length=30) |
| >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] |
| "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you." |
| ```""" |
| outputs: BaseModelOutputWithPast = self.model( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| position_ids=position_ids, |
| past_key_values=past_key_values, |
| inputs_embeds=inputs_embeds, |
| use_cache=use_cache, |
| cache_position=cache_position, |
| **kwargs, |
| ) |
|
|
| hidden_states = outputs.last_hidden_state |
| |
| if isinstance(logits_to_keep, int) and logits_to_keep < 0: |
| logits = torch.empty((hidden_states.size(0), 0, self.config.vocab_size), device=hidden_states.device, dtype=hidden_states.dtype) |
| elif isinstance(logits_to_keep, int) and logits_to_keep == 0: |
| |
| logits = self.lm_head(hidden_states) |
| else: |
| slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep |
| logits = self.lm_head(hidden_states[:, slice_indices, :]) |
|
|
| loss = None |
| if labels is not None: |
| loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs) |
|
|
| return CausalLMOutputWithPast( |
| loss=loss, |
| logits=logits, |
| past_key_values=outputs.past_key_values, |
| hidden_states=outputs.hidden_states, |
| attentions=outputs.attentions, |
| ) |
|
|
|
|
| @dataclass |
| @auto_docstring( |
| custom_intro=""" |
| Base class for Quasar causal language model (or autoregressive) outputs. |
| """ |
| ) |
| class QuasarCausalLMOutputWithPast(ModelOutput): |
| r""" |
| loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided): |
| Language modeling loss (for next-token prediction). |
| logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`): |
| Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax). |
| past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`): |
| It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache). |
| |
| Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see |
| `past_key_values` input) to speed up sequential decoding. |
| rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*): |
| The rope index difference between sequence length and multimodal rope. |
| """ |
|
|
| loss: torch.FloatTensor | None = None |
| logits: torch.FloatTensor | None = None |
| past_key_values: Cache | None = None |
| hidden_states: tuple[torch.FloatTensor] | None = None |
| attentions: tuple[torch.FloatTensor] | None = None |
| rope_deltas: torch.LongTensor | None = None |
|
|
|
|
| class QuasarForConditionalGeneration(QuasarPreTrainedModel, GenerationMixin): |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| config_class = QuasarConfig |
| _checkpoint_conversion_mapping = { |
| "model.language_model.layers": "model.model.language_model.layers", |
| "model.language_model.embed_tokens": "model.model.language_model.embed_tokens", |
| "model.language_model.norm": "model.model.language_model.norm", |
| "model.visual": "model.visual", |
| } |
| _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"} |
| |
| accepts_loss_kwargs = False |
| config: QuasarConfig |
|
|
| def __init__(self, config): |
| super().__init__(config) |
| self.model = QuasarModel(config) |
| self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False) |
|
|
| self.post_init() |
|
|
| def get_input_embeddings(self): |
| return self.model.get_input_embeddings() |
|
|
| def set_input_embeddings(self, value): |
| self.model.set_input_embeddings(value) |
|
|
| @auto_docstring |
| def get_video_features( |
| self, |
| pixel_values_videos: torch.FloatTensor, |
| video_grid_thw: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | BaseModelOutputWithPooling: |
| r""" |
| pixel_values_videos (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`): |
| The tensors corresponding to the input videos. |
| video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*): |
| The temporal, height and width of feature shape of each video in LLM. |
| """ |
| return self.model.get_video_features( |
| pixel_values_videos=pixel_values_videos, video_grid_thw=video_grid_thw, **kwargs |
| ) |
|
|
| @auto_docstring |
| def get_image_features( |
| self, |
| pixel_values: torch.FloatTensor, |
| image_grid_thw: torch.LongTensor | None = None, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | BaseModelOutputWithPooling: |
| r""" |
| pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, image_size, image_size)`): |
| The tensors corresponding to the input images. |
| image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*): |
| The temporal, height and width of feature shape of each image in LLM. |
| """ |
| return self.model.get_image_features(pixel_values=pixel_values, image_grid_thw=image_grid_thw, **kwargs) |
|
|
| @can_return_tuple |
| def forward( |
| self, |
| input_ids: torch.LongTensor = None, |
| attention_mask: torch.Tensor | None = None, |
| position_ids: torch.LongTensor | None = None, |
| past_key_values: Cache | None = None, |
| inputs_embeds: torch.FloatTensor | None = None, |
| labels: torch.LongTensor | None = None, |
| pixel_values: torch.Tensor | None = None, |
| pixel_values_videos: torch.FloatTensor | None = None, |
| image_grid_thw: torch.LongTensor | None = None, |
| video_grid_thw: torch.LongTensor | None = None, |
| mm_token_type_ids: torch.IntTensor | None = None, |
| cache_position: torch.LongTensor | None = None, |
| logits_to_keep: int | torch.Tensor = 0, |
| **kwargs: Unpack[TransformersKwargs], |
| ) -> tuple | QuasarCausalLMOutputWithPast: |
| r""" |
| labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*): |
| Labels for computing the masked language modeling loss. Indices should either be in `[0, ..., |
| config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored |
| (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`. |
| image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*): |
| The temporal, height and width of feature shape of each image in LLM. |
| video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*): |
| The temporal, height and width of feature shape of each video in LLM. |
| |
| Example: |
| |
| ```python |
| >>> from transformers import AutoProcessor, QuasarForConditionalGeneration |
| |
| >>> model = QuasarForConditionalGeneration.from_pretrained("Qwen/Qwen3-VL-8B-Instruct") |
| >>> processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-8B-Instruct") |
| |
| >>> messages = [ |
| { |
| "role": "user", |
| "content": [ |
| { |
| "type": "image", |
| "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg", |
| }, |
| {"type": "text", "text": "Describe the image."}, |
| ], |
| } |
| ] |
| |
| >>> inputs = processor.apply_chat_template( |
| messages, |
| tokenize=True, |
| add_generation_prompt=True, |
| return_dict=True, |
| return_tensors="pt" |
| ) |
| |
| >>> # Generate |
| >>> generated_ids = model.generate(**inputs, max_new_tokens=1024) |
| >>> generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)] |
| >>> output_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] |
| >>> print(output_text) |
| ``` |
| """ |
|
|
| outputs = self.model( |
| input_ids=input_ids, |
| pixel_values=pixel_values, |
| pixel_values_videos=pixel_values_videos, |
| image_grid_thw=image_grid_thw, |
| video_grid_thw=video_grid_thw, |
| position_ids=position_ids, |
| attention_mask=attention_mask, |
| past_key_values=past_key_values, |
| inputs_embeds=inputs_embeds, |
| cache_position=cache_position, |
| mm_token_type_ids=mm_token_type_ids, |
| **kwargs, |
| ) |
|
|
| hidden_states = outputs[0] |
|
|
| |
| if isinstance(logits_to_keep, int) and logits_to_keep <= 0: |
| logits = None |
| else: |
| slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep |
| logits = self.lm_head(hidden_states[:, slice_indices, :]) |
|
|
| loss = None |
| if labels is not None: |
| loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size) |
|
|
| return QuasarCausalLMOutputWithPast( |
| loss=loss, |
| logits=logits, |
| past_key_values=outputs.past_key_values, |
| hidden_states=outputs.hidden_states, |
| attentions=outputs.attentions, |
| rope_deltas=outputs.rope_deltas, |
| ) |
|
|
| def prepare_inputs_for_generation( |
| self, |
| input_ids, |
| past_key_values=None, |
| attention_mask=None, |
| inputs_embeds=None, |
| cache_position=None, |
| position_ids=None, |
| use_cache=True, |
| pixel_values=None, |
| pixel_values_videos=None, |
| image_grid_thw=None, |
| video_grid_thw=None, |
| is_first_iteration=False, |
| **kwargs, |
| ): |
| |
|
|
| model_inputs = super().prepare_inputs_for_generation( |
| input_ids, |
| past_key_values=past_key_values, |
| attention_mask=attention_mask, |
| inputs_embeds=inputs_embeds, |
| cache_position=cache_position, |
| position_ids=position_ids, |
| pixel_values=pixel_values, |
| pixel_values_videos=pixel_values_videos, |
| image_grid_thw=image_grid_thw, |
| video_grid_thw=video_grid_thw, |
| use_cache=use_cache, |
| is_first_iteration=is_first_iteration, |
| **kwargs, |
| ) |
|
|
| if not is_first_iteration and use_cache: |
| model_inputs["pixel_values"] = None |
| model_inputs["pixel_values_videos"] = None |
|
|
| return model_inputs |
|
|
| def _prepare_position_ids_for_generation(self, inputs_tensor, model_kwargs): |
| |
|
|
| text_positions = super()._prepare_position_ids_for_generation(inputs_tensor, model_kwargs) |
|
|
| |
| past_length = 0 |
| if (cache := model_kwargs.get("past_key_values")) is not None: |
| past_length = cache.get_seq_length() |
| if past_length != 0 and self.model.rope_deltas is not None: |
| position_ids = text_positions[None, ...] + self.model.rope_deltas |
| return position_ids |
|
|
| |
| if "input_ids" in model_kwargs and model_kwargs["input_ids"].shape[1] > 0: |
| inputs_tensor = model_kwargs["input_ids"] |
|
|
| is_input_ids = len(inputs_tensor.shape) == 2 and inputs_tensor.dtype in [torch.int, torch.long] |
| if ( |
| is_input_ids |
| and model_kwargs.get("mm_token_type_ids") is not None |
| and (model_kwargs.get("image_grid_thw") is not None or model_kwargs.get("video_grid_thw") is not None) |
| ): |
| model_kwargs = {k: v for k, v in model_kwargs.items() if k != "input_ids"} |
| vision_positions, rope_deltas = self.model.get_rope_index(inputs_tensor, **model_kwargs) |
| self.model.rope_deltas = rope_deltas |
| else: |
| vision_positions = text_positions.unsqueeze(0).expand(3, -1, -1) |
| self.model.rope_deltas = torch.zeros( |
| inputs_tensor.shape[0], 1, dtype=torch.long, device=inputs_tensor.device |
| ) |
|
|
| |
| text_positions = text_positions[None, ...] |
| position_ids = torch.cat([text_positions, vision_positions], dim=0) |
|
|
| return position_ids |
|
|
| def _get_image_nums_and_video_nums( |
| self, |
| input_ids: torch.LongTensor | None, |
| inputs_embeds: torch.Tensor | None = None, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| """ |
| Get the number of images and videos for each sample to calculate the separation length of the sample tensor. |
| These parameters are not passed through the processor to avoid unpredictable impacts from interface modifications. |
| |
| Args: |
| input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`): |
| Indices of input sequence tokens in the vocabulary. |
| |
| Returns: |
| image_nums (`torch.LongTensor` of shape `(batch_size, num_images_sample)`) |
| video_nums (`torch.LongTensor` of shape `(batch_size, num_videos_sample)`) |
| """ |
| image_token_id = self.config.image_token_id |
| video_token_id = self.config.video_token_id |
| vision_start_token_id = self.config.vision_start_token_id |
|
|
| if inputs_embeds is not None: |
| vision_start_mask = ( |
| inputs_embeds |
| == self.get_input_embeddings()( |
| torch.tensor(vision_start_token_id, dtype=torch.long, device=inputs_embeds.device) |
| ) |
| )[..., 0] |
| image_mask = ( |
| inputs_embeds |
| == self.get_input_embeddings()( |
| torch.tensor(image_token_id, dtype=torch.long, device=inputs_embeds.device) |
| ) |
| )[..., 0] |
| video_mask = ( |
| inputs_embeds |
| == self.get_input_embeddings()( |
| torch.tensor(video_token_id, dtype=torch.long, device=inputs_embeds.device) |
| ) |
| )[..., 0] |
| else: |
| vision_start_mask = input_ids == vision_start_token_id |
| image_mask = input_ids == image_token_id |
| video_mask = input_ids == video_token_id |
|
|
| vision_first_mask = torch.roll(vision_start_mask, shifts=1, dims=1) |
| image_nums = torch.sum(vision_first_mask & image_mask, dim=1) |
| video_nums = torch.sum(vision_first_mask & video_mask, dim=1) |
|
|
| return image_nums, video_nums |
|
|
| def _expand_inputs_for_generation( |
| self, |
| expand_size: int = 1, |
| is_encoder_decoder: bool = False, |
| input_ids: torch.LongTensor | None = None, |
| **model_kwargs, |
| ) -> tuple[torch.LongTensor, dict[str, Any]]: |
| |
| |
| |
| |
| |
|
|
| if expand_size == 1: |
| return input_ids, model_kwargs |
|
|
| visual_keys = ["pixel_values", "image_grid_thw", "pixel_values_videos", "video_grid_thw"] |
|
|
| def _expand_dict_for_generation_visual(dict_to_expand): |
| image_grid_thw = model_kwargs.get("image_grid_thw", None) |
| video_grid_thw = model_kwargs.get("video_grid_thw", None) |
| image_nums, video_nums = self._get_image_nums_and_video_nums( |
| input_ids, inputs_embeds=model_kwargs.get("inputs_embeds", None) |
| ) |
|
|
| |
| |
| |
| if video_grid_thw is not None: |
| cumulative_frame_counts = torch.cumsum(video_grid_thw[:, 0], dim=0) |
| cumulative_token_video_counts = torch.cumsum(video_nums, dim=0) |
| |
| video_boundary_indices = torch.searchsorted(cumulative_frame_counts, cumulative_token_video_counts) |
| |
| video_nums = torch.diff(torch.cat([-video_boundary_indices.new_ones(1), video_boundary_indices])) |
|
|
| def _repeat_interleave_samples(x, lengths, repeat_times): |
| samples = torch.split(x, lengths) |
| repeat_args = [repeat_times] + [1] * (x.dim() - 1) |
| result = torch.cat([sample.repeat(*repeat_args) for sample in samples], dim=0) |
| return result |
|
|
| for key in dict_to_expand: |
| if key == "pixel_values": |
| |
| samples = torch.split(image_grid_thw, list(image_nums)) |
| |
| lengths = [torch.prod(sample, dim=1).sum() for sample in samples] |
| dict_to_expand[key] = _repeat_interleave_samples( |
| dict_to_expand[key], lengths=lengths, repeat_times=expand_size |
| ) |
| elif key == "image_grid_thw": |
| |
| lengths = list(image_nums) |
| dict_to_expand[key] = _repeat_interleave_samples( |
| dict_to_expand[key], lengths=lengths, repeat_times=expand_size |
| ) |
| elif key == "pixel_values_videos": |
| samples = torch.split(video_grid_thw, list(video_nums)) |
| lengths = [torch.prod(sample, dim=1).sum() for sample in samples] |
| dict_to_expand[key] = _repeat_interleave_samples( |
| dict_to_expand[key], lengths=lengths, repeat_times=expand_size |
| ) |
| elif key == "video_grid_thw": |
| lengths = list(video_nums) |
| dict_to_expand[key] = _repeat_interleave_samples( |
| dict_to_expand[key], lengths=lengths, repeat_times=expand_size |
| ) |
| return dict_to_expand |
|
|
| def _expand_dict_for_generation(dict_to_expand): |
| for key in dict_to_expand: |
| if key == "position_ids" and dict_to_expand[key].ndim == 3: |
| dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=1) |
| elif ( |
| key != "cache_position" |
| and dict_to_expand[key] is not None |
| and isinstance(dict_to_expand[key], torch.Tensor) |
| and key not in visual_keys |
| ): |
| dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=0) |
| return dict_to_expand |
|
|
| model_kwargs = _expand_dict_for_generation_visual(model_kwargs) |
|
|
| if input_ids is not None: |
| input_ids = input_ids.repeat_interleave(expand_size, dim=0) |
|
|
| model_kwargs = _expand_dict_for_generation(model_kwargs) |
|
|
| if is_encoder_decoder: |
| if model_kwargs.get("encoder_outputs") is None: |
| raise ValueError("If `is_encoder_decoder` is True, make sure that `encoder_outputs` is defined.") |
| model_kwargs["encoder_outputs"] = _expand_dict_for_generation(model_kwargs["encoder_outputs"]) |
|
|
| return input_ids, model_kwargs |
|
|
|
|
| __all__ = [ |
| "QuasarVisionModel", |
| "QuasarTextModel", |
| "QuasarModel", |
| "QuasarForCausalLM", |
| "QuasarForConditionalGeneration", |
| "QuasarPreTrainedModel", |
| ] |
|
|