Spaces:
Running on Zero
Running on Zero
| import os | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| from typing import Callable, List, Optional | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS | |
| from rosetta.utils import DataClassMixin, PRECISION_TO_TYPE | |
| MODEL_ZOO: dict[str, "QwenViTConfig"] = {} | |
| def register_model_config(name, base=None, **kwargs): | |
| if base is not None: | |
| if base not in MODEL_ZOO: | |
| raise ValueError(f"Base model {base} not found in MODEL_ZOO. Valid models: {list(MODEL_ZOO.keys())}") | |
| base_config = MODEL_ZOO[base].to_dict() | |
| base_config.update({**kwargs, "name": name}) | |
| MODEL_ZOO[name] = QwenViTConfig(**base_config) | |
| else: | |
| MODEL_ZOO[name] = QwenViTConfig(name=name, **kwargs) | |
| class QwenViTConfig(DataClassMixin): | |
| name: str = "" | |
| depth: int = 27 | |
| hidden_size: int = 1152 | |
| intermediate_size: int = 4304 | |
| num_heads: int = 16 | |
| in_channels: int = 3 | |
| patch_size: int = 16 | |
| spatial_merge_size: int = 2 | |
| temporal_patch_size: int = 2 | |
| out_hidden_size: int = 3584 | |
| num_position_embeddings: int = 2304 | |
| deepstack_visual_indexes: List[int] = field(default_factory=lambda: [8, 16, 24]) | |
| initializer_range: float = 0.02 | |
| def from_name(cls, model_name: str, **kwargs) -> "QwenViTConfig": | |
| if model_name not in MODEL_ZOO: | |
| raise ValueError(f"Model {model_name} not found in MODEL_ZOO. Valid models: {list(MODEL_ZOO.keys())}") | |
| model_config = MODEL_ZOO[model_name].to_dict() | |
| model_config.update(kwargs) | |
| return cls(**model_config) | |
| register_model_config( | |
| name="qwen3vl-vit-for-30b-a3b", | |
| depth=27, | |
| hidden_size=1152, | |
| intermediate_size=4304, | |
| num_heads=16, | |
| in_channels=3, | |
| patch_size=16, | |
| spatial_merge_size=2, | |
| temporal_patch_size=2, | |
| out_hidden_size=2048, | |
| num_position_embeddings=2304, | |
| deepstack_visual_indexes=[8, 16, 24], | |
| ) | |
| register_model_config( | |
| name="qwen3vl-vit-for-0.6b", | |
| base="qwen3vl-vit-for-30b-a3b", | |
| out_hidden_size=1024, | |
| ) | |
| def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: | |
| 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: Optional[torch.Tensor], | |
| scaling: float, | |
| dropout: float = 0.0, | |
| ): | |
| key_states = repeat_kv(key, module.num_key_value_groups) | |
| value_states = repeat_kv(value, module.num_key_value_groups) | |
| attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling | |
| if attention_mask is not None: | |
| causal_mask = attention_mask[:, :, :, : key_states.shape[-2]] | |
| attn_weights = attn_weights + causal_mask | |
| attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype) | |
| attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training) | |
| attn_output = torch.matmul(attn_weights, value_states) | |
| attn_output = attn_output.transpose(1, 2).contiguous() | |
| return attn_output, attn_weights | |
| class Qwen3VLVisionPatchMerger(nn.Module): | |
| def __init__(self, hidden_size, spatial_merge_size, out_hidden_size, use_postshuffle_norm=False) -> None: | |
| super().__init__() | |
| self.hidden_size = hidden_size * (spatial_merge_size**2) | |
| self.use_postshuffle_norm = use_postshuffle_norm | |
| self.norm = nn.LayerNorm(self.hidden_size if use_postshuffle_norm else 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, 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 | |
| class Qwen3VLVisionRotaryEmbedding(nn.Module): | |
| inv_freq: torch.Tensor # fix linting for `register_buffer` | |
| 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=True) | |
| def reset_parameters(self): | |
| inv_freq = 1.0 / (self.theta ** (torch.arange(0, self.dim, 2, dtype=torch.float) / self.dim)) | |
| self.register_buffer("inv_freq", inv_freq, persistent=True) | |
| 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 Qwen3VLVisionPatchEmbed(nn.Module): | |
| def __init__(self, patch_size, temporal_patch_size, in_channels, hidden_size) -> None: | |
| super().__init__() | |
| self.patch_size = patch_size | |
| self.temporal_patch_size = temporal_patch_size | |
| self.in_channels = in_channels | |
| self.embed_dim = 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 | |
| def rotate_half(x): | |
| x1 = x[..., : x.shape[-1] // 2] | |
| x2 = x[..., x.shape[-1] // 2 :] | |
| return torch.cat((-x2, x1), dim=-1) | |
| 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 Qwen3VLVisionAttention(nn.Module): | |
| def __init__(self, hidden_size, num_heads, attn_implementation="sdpa") -> None: | |
| super().__init__() | |
| self.dim = hidden_size | |
| self.num_heads = num_heads | |
| self.head_dim = self.dim // self.num_heads | |
| self.num_key_value_groups = 1 # needed for eager attention | |
| 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._attn_implementation = attn_implementation | |
| self.attention_dropout = 0.0 | |
| self.is_causal = False | |
| # Create a minimal config object for transformers flash attention compatibility | |
| self.config = type('Config', (), {'_attn_implementation': attn_implementation})() | |
| def forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: Optional[torch.Tensor] = None, | |
| position_embeddings: Optional[tuple[torch.Tensor, torch.Tensor]] = 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 = eager_attention_forward | |
| if self._attn_implementation != "eager": | |
| attention_interface = ALL_ATTENTION_FUNCTIONS[self._attn_implementation] | |
| if self._attn_implementation == "flash_attention_2": | |
| # Flash Attention 2: Use cu_seqlens for variable length attention | |
| 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: | |
| # Other implementations: Process each chunk separately | |
| 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 GELUTanh(nn.Module): | |
| def __init__(self) -> None: | |
| super().__init__() | |
| self.act_fn = nn.GELU(approximate="tanh") | |
| def forward(self, input: torch.Tensor) -> torch.Tensor: | |
| return self.act_fn(input) | |
| class Qwen3VLVisionMLP(nn.Module): | |
| def __init__(self, hidden_size, intermediate_size): | |
| super().__init__() | |
| self.hidden_size = hidden_size | |
| self.intermediate_size = 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 = GELUTanh() | |
| def forward(self, hidden_state): | |
| return self.linear_fc2(self.act_fn(self.linear_fc1(hidden_state))) | |
| class Qwen3VLVisionBlock(nn.Module): | |
| def __init__(self, hidden_size, num_heads, intermediate_size, attn_implementation: str = "sdpa") -> None: | |
| super().__init__() | |
| self.norm1 = nn.LayerNorm(hidden_size, eps=1e-6) | |
| self.norm2 = nn.LayerNorm(hidden_size, eps=1e-6) | |
| self.attn = Qwen3VLVisionAttention(hidden_size, num_heads, attn_implementation) | |
| self.mlp = Qwen3VLVisionMLP(hidden_size, intermediate_size) | |
| def forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: Optional[torch.Tensor] = None, | |
| position_embeddings: Optional[tuple[torch.Tensor, torch.Tensor]] = None, | |
| **kwargs, | |
| ) -> torch.Tensor: | |
| attn = 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 + attn | |
| mlp = self.mlp(self.norm2(hidden_states)) | |
| hidden_states = hidden_states + mlp | |
| return hidden_states | |
| class Qwen3VLVisionModel(nn.Module): | |
| def __init__(self, config: QwenViTConfig, attn_implementation="sdpa", **kwargs): | |
| super().__init__() | |
| self.config = config | |
| 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 = Qwen3VLVisionPatchEmbed( | |
| config.patch_size, | |
| config.temporal_patch_size, | |
| config.in_channels, | |
| config.hidden_size, | |
| ) | |
| 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 = Qwen3VLVisionRotaryEmbedding(head_dim // 2) | |
| self.blocks = nn.ModuleList( | |
| [ | |
| Qwen3VLVisionBlock(config.hidden_size, config.num_heads, config.intermediate_size, attn_implementation) for _ in range(config.depth) | |
| ] | |
| ) | |
| self.merger = Qwen3VLVisionPatchMerger( | |
| config.hidden_size, | |
| config.spatial_merge_size, | |
| config.out_hidden_size, | |
| use_postshuffle_norm=False, | |
| ) | |
| self.deepstack_visual_indexes = config.deepstack_visual_indexes | |
| self.deepstack_merger_list = nn.ModuleList( | |
| [ | |
| Qwen3VLVisionPatchMerger( | |
| config.hidden_size, | |
| config.spatial_merge_size, | |
| config.out_hidden_size, | |
| use_postshuffle_norm=True, | |
| ) | |
| for _ in range(len(config.deepstack_visual_indexes)) | |
| ] | |
| ) | |
| self.gradient_checkpointing = False | |
| def rot_pos_emb(self, grid_thw: torch.Tensor) -> torch.Tensor: | |
| merge_size = self.spatial_merge_size | |
| max_hw = int(grid_thw[:, 1:].max().item()) | |
| freq_table = self.rotary_pos_emb(max_hw) # (max_hw, dim // 2) | |
| device = freq_table.device | |
| total_tokens = int(torch.prod(grid_thw, dim=1).sum().item()) | |
| pos_ids = torch.empty((total_tokens, 2), dtype=torch.long, device=device) | |
| offset = 0 | |
| for num_frames, height, width in grid_thw: | |
| merged_h, merged_w = height // merge_size, width // merge_size | |
| block_rows = torch.arange(merged_h, device=device) # block row indices | |
| block_cols = torch.arange(merged_w, device=device) # block col indices | |
| intra_row = torch.arange(merge_size, device=device) # intra-block row offsets | |
| intra_col = torch.arange(merge_size, device=device) # intra-block col offsets | |
| # Compute full-resolution positions | |
| 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] # lookup rotary embeddings | |
| embeddings = embeddings.flatten(1) | |
| return embeddings | |
| def fast_pos_embed_interpolate(self, grid_thw): | |
| grid_ts, grid_hs, grid_ws = grid_thw[:, 0], grid_thw[:, 1], grid_thw[:, 2] | |
| idx_list = [[] for _ in range(4)] | |
| weight_list = [[] for _ in range(4)] | |
| for t, h, w in zip(grid_ts, grid_hs, grid_ws): | |
| # Convert tensor to Python int for torch.linspace | |
| h_int = h.item() if isinstance(h, torch.Tensor) else int(h) | |
| w_int = w.item() if isinstance(w, torch.Tensor) else int(w) | |
| h_idxs = torch.linspace(0, self.num_grid_per_side - 1, h_int) | |
| w_idxs = torch.linspace(0, self.num_grid_per_side - 1, w_int) | |
| 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=self.pos_embed.weight.device) | |
| weight_tensor = torch.tensor( | |
| weight_list, dtype=self.pos_embed.weight.dtype, device=self.pos_embed.weight.device | |
| ) | |
| pos_embeds = self.pos_embed(idx_tensor) * 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 | |
| def process_single_hidden_states(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. | |
| """ | |
| if hidden_states.ndim == 3: | |
| hidden_states = hidden_states.squeeze(0) | |
| 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, | |
| # Select dtype based on the following factors: | |
| # - FA2 requires that cu_seqlens_q must have dtype int32 | |
| # - torch.onnx.export requires that cu_seqlens_q must have same dtype as grid_thw | |
| # See https://github.com/huggingface/transformers/pull/34852 for more information | |
| dtype=grid_thw.dtype if torch.jit.is_tracing() else torch.int32, | |
| ) | |
| cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0) | |
| deepstack_feature_lists = [] | |
| for layer_num, blk in enumerate(self.blocks): | |
| hidden_states = blk( | |
| hidden_states, | |
| cu_seqlens=cu_seqlens, | |
| position_embeddings=position_embeddings, | |
| **kwargs, | |
| ) | |
| if layer_num in self.deepstack_visual_indexes: | |
| deepstack_feature = self.deepstack_merger_list[self.deepstack_visual_indexes.index(layer_num)]( | |
| hidden_states | |
| ) | |
| deepstack_feature_lists.append(deepstack_feature) | |
| hidden_states = self.merger(hidden_states) | |
| hidden_states = hidden_states.unsqueeze(0) | |
| return hidden_states, deepstack_feature_lists | |
| def forward(self, hidden_states: torch.Tensor, grid_thw: Optional[torch.Tensor] = None, **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. | |
| **kwargs: additional keyword arguments. | |
| Returns: | |
| `torch.Tensor`: hidden_states, (bsz, seqlen, n_embd). | |
| `list[torch.Tensor]`: deepstack_feature_lists, [[], [], ...] | |
| """ | |
| if hidden_states.ndim == 3 and hidden_states.shape[0] > 1: | |
| # 如果输入的hidden_states是(batch, seq_len, hidden_size) | |
| batch_size, seq_len, hidden_size = hidden_states.shape | |
| # 处理**kwargs将其也拆分成和hidden_states_i = hidden_states[i] | |
| all_kwargs = [] | |
| for i in range(batch_size): | |
| kwargs_i = {k: v[i:i+1] for k, v in kwargs.items()} | |
| all_kwargs.append(kwargs_i) | |
| all_hidden_states = [] | |
| all_deepstack_feature_lists = [] | |
| for i in range(batch_size): | |
| hidden_states_i = hidden_states[i] | |
| grid_thw_i = grid_thw[i:i+1] | |
| kwargs_i = all_kwargs[i] | |
| hidden_states_i, deepstack_feature_lists_i = self.process_single_hidden_states(hidden_states_i, grid_thw_i, **kwargs_i) | |
| all_hidden_states.append(hidden_states_i) | |
| all_deepstack_feature_lists.append(deepstack_feature_lists_i) | |
| # 合并所有hidden_states和deepstack_feature_lists | |
| hidden_states = torch.cat(all_hidden_states, dim=0) | |
| deepstack_feature_lists = all_deepstack_feature_lists | |
| else: | |
| if isinstance(grid_thw, list) and len(grid_thw) == 1: | |
| grid_thw = grid_thw[0] | |
| elif isinstance(grid_thw, list) and len(grid_thw) > 1: | |
| assert False, "process_single_hidden_states should be called with a single grid_thw" | |
| hidden_states, deepstack_feature_lists = self.process_single_hidden_states(hidden_states, grid_thw, **kwargs) | |
| if not len(self.deepstack_visual_indexes) > 0: | |
| deepstack_feature_lists = None | |
| return hidden_states, deepstack_feature_lists | |
| ASSETS_BASE = os.getenv("ASSETS_BASE", "./public_assets").rstrip("/") | |
| VISION_ENCODER_BASE = os.getenv("VISION_ENCODER_BASE", f"{ASSETS_BASE}/vision_encoder").rstrip("/") | |
| VISION_ENCODER_META_INFO = { | |
| "qwen3vl-vit-for-0.6b": { | |
| "path": f"{VISION_ENCODER_BASE}/Qwen3-VL-30B-A3B-Instruct", | |
| "downsample_factor": [32, 32], | |
| "spatial_merge_size": 2, | |
| "patch_dim": 1536, | |
| "dummy_number": 1, | |
| }, | |
| } | |
| def load_vision_model( | |
| vision_model_type=None, | |
| vision_model_precision=None, | |
| device=None, | |
| logger=None, | |
| require_grad=False, | |
| eval_mode=True, | |
| no_load_pretrained=False, | |
| vision_model_params=None, | |
| config=None, | |
| ): | |
| if logger is None: | |
| from loguru import logger | |
| if config is not None: | |
| vision_model_type = config["vision_model_type"] | |
| vision_model_precision = config.get("vision_model_precision", vision_model_precision) | |
| require_grad = not config.get("vision_model_freeze", not require_grad) | |
| eval_mode = config.get("vision_model_freeze", eval_mode) | |
| no_load_pretrained = config.get("no_load_pretrained_vision_model", False) | |
| vision_model_params = config.get("vision_model_params", vision_model_params) | |
| if vision_model_params is None: | |
| vision_model_params = {} | |
| if vision_model_type.startswith("qwen3vl-vit"): | |
| model_config = QwenViTConfig.from_name(vision_model_type) | |
| vision_model = Qwen3VLVisionModel(model_config, **vision_model_params) | |
| if not no_load_pretrained and vision_model_type in VISION_ENCODER_META_INFO: | |
| meta = VISION_ENCODER_META_INFO[vision_model_type] | |
| if "path" in meta: | |
| from loguru import logger as _logger | |
| from safetensors.torch import load_file as st_load_file | |
| import json | |
| hf_path = Path(meta["path"]) | |
| index_file = hf_path / "model.safetensors.index.json" | |
| if index_file.exists(): | |
| with open(index_file) as _f: | |
| shard_map = json.load(_f)["weight_map"] | |
| # Only load keys that belong to visual encoder. HF checkpoints may | |
| # store them as either "visual.*" or "model.visual.*". | |
| vit_shards = set( | |
| v for k, v in shard_map.items() | |
| if k.startswith("visual.") or k.startswith("model.visual.") | |
| ) | |
| vit_state = {} | |
| for shard in sorted(vit_shards): | |
| sd = st_load_file(hf_path / shard) | |
| for k, v in sd.items(): | |
| if k.startswith("visual."): | |
| vit_state[k[len("visual."):]] = v | |
| elif k.startswith("model.visual."): | |
| vit_state[k[len("model.visual."):]] = v | |
| model_state = vision_model.state_dict() | |
| vit_state = { | |
| k: v for k, v in vit_state.items() | |
| if k in model_state and model_state[k].shape == v.shape | |
| } | |
| has_meta_params = any(param.is_meta for param in vision_model.parameters()) | |
| missing, unexpected = vision_model.load_state_dict( | |
| vit_state, strict=False, assign=has_meta_params | |
| ) | |
| meta_params = [name for name, param in vision_model.named_parameters() if param.is_meta] | |
| if meta_params: | |
| raise RuntimeError( | |
| "Vision encoder still has meta parameters after loading weights: " | |
| f"{meta_params[:8]}" | |
| ) | |
| _logger.info( | |
| f"[qwen3vl-vit] Loaded backbone from {hf_path.name}: " | |
| f"{len(vit_state)-len(missing)} keys loaded, " | |
| f"{len(missing)} missing (e.g. shape-mismatch merger.linear_fc2 → random init), " | |
| f"{len(unexpected)} unexpected." | |
| ) | |
| else: | |
| raise NotImplementedError(f"vision_model_type {vision_model_type} not implemented") | |
| if vision_model_precision is not None: | |
| logger.warning(f"You are transforming the Vision Encoder to {vision_model_precision}. Please make sure this is what you want.") | |
| if isinstance(vision_model_precision, str): | |
| vision_model_precision = PRECISION_TO_TYPE[vision_model_precision] | |
| vision_model = vision_model.to(dtype=vision_model_precision) | |
| if device is not None: | |
| vision_model = vision_model.to(device=device) | |
| if not require_grad: | |
| vision_model.requires_grad_(False) | |
| if eval_mode: | |
| vision_model.eval() | |
| return vision_model | |
| def load_vision_model_processor(vision_model_type, **kwargs): | |
| if vision_model_type.startswith("qwen3vl-vit"): | |
| from transformers import AutoImageProcessor | |
| vision_model_meta_info = VISION_ENCODER_META_INFO[vision_model_type] | |
| vision_model_path = Path(vision_model_meta_info["path"]) | |
| processor = AutoImageProcessor.from_pretrained(vision_model_path, **kwargs) | |
| else: | |
| raise NotImplementedError(f"vision_model_type {vision_model_type} not implemented") | |
| return processor | |
| load_vit = load_vision_model | |
| load_vit_processor = load_vision_model_processor | |