Spaces:
Running on Zero
Running on Zero
| import warnings | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| LLM_TOKEN_INDICATOR = 3 | |
| QWEN3_VL_ACTIVATION_LAYERS = (0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 35) | |
| FP8_E4M3_MAX = 448.0 | |
| FP8_WEIGHT_DTYPE = torch.float8_e4m3fn | |
| FP8_SCALE_SUFFIX = ".weight_scale" | |
| _BNB_SIBLING_SUFFIXES = ( | |
| ".absmax", | |
| ".quant_map", | |
| ".nested_absmax", | |
| ".nested_quant_map", | |
| ) | |
| class Fp8Linear(nn.Module): | |
| """Linear layer holding an e4m3 float8 weight + per-row float32 scale.""" | |
| weight: torch.Tensor | |
| weight_scale: torch.Tensor | |
| bias: torch.Tensor | None | |
| def __init__( | |
| self, | |
| in_features: int, | |
| out_features: int, | |
| bias: bool, | |
| compute_dtype: torch.dtype, | |
| ) -> None: | |
| super().__init__() | |
| self.in_features = in_features | |
| self.out_features = out_features | |
| self.compute_dtype = compute_dtype | |
| self.register_buffer( | |
| "weight", | |
| torch.empty(out_features, in_features, dtype=FP8_WEIGHT_DTYPE), | |
| ) | |
| self.register_buffer("weight_scale", torch.empty(out_features, dtype=torch.float32)) | |
| if bias: | |
| self.register_buffer("bias", torch.empty(out_features, dtype=compute_dtype)) | |
| else: | |
| self.bias = None | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| w = self.weight.to(x.dtype) * self.weight_scale.to(x.dtype).unsqueeze(1) | |
| bias = self.bias.to(x.dtype) if self.bias is not None else None | |
| return F.linear(x, w, bias) | |
| def is_fp8_state_dict(state_dict: dict[str, torch.Tensor]) -> bool: | |
| return any(k.endswith(FP8_SCALE_SUFFIX) for k in state_dict) or any( | |
| v.dtype == FP8_WEIGHT_DTYPE for v in state_dict.values() | |
| ) | |
| def is_bnb4bit_state_dict(state_dict: dict[str, torch.Tensor]) -> bool: | |
| return any(".quant_state.bitsandbytes__" in k for k in state_dict) | |
| def swap_linears_to_fp8( | |
| module: nn.Module, | |
| state_dict: dict[str, torch.Tensor], | |
| compute_dtype: torch.dtype, | |
| *, | |
| prefix: str = "", | |
| ) -> None: | |
| for name, child in list(module.named_children()): | |
| child_prefix = f"{prefix}{name}" | |
| if ( | |
| isinstance(child, nn.Linear) and f"{child_prefix}{FP8_SCALE_SUFFIX}" in state_dict | |
| ): | |
| setattr( | |
| module, | |
| name, | |
| Fp8Linear( | |
| child.in_features, | |
| child.out_features, | |
| bias=child.bias is not None, | |
| compute_dtype=compute_dtype, | |
| ), | |
| ) | |
| else: | |
| swap_linears_to_fp8(child, state_dict, compute_dtype, prefix=f"{child_prefix}.") | |
| def load_fp8_state_dict( | |
| model: nn.Module, | |
| state_dict: dict[str, torch.Tensor], | |
| device: torch.device, | |
| dtype: torch.dtype, | |
| *, | |
| assign: bool = False, | |
| strict: bool = True, | |
| ) -> None: | |
| prepared: dict[str, torch.Tensor] = {} | |
| for k, v in state_dict.items(): | |
| if v.dtype == FP8_WEIGHT_DTYPE: | |
| prepared[k] = v.to(device=device) | |
| elif k.endswith(FP8_SCALE_SUFFIX): | |
| prepared[k] = v.to(device=device, dtype=torch.float32) | |
| elif v.is_floating_point(): | |
| prepared[k] = v.to(device=device, dtype=dtype) | |
| else: | |
| prepared[k] = v.to(device=device) | |
| missing, unexpected = model.load_state_dict(prepared, strict=False, assign=assign) | |
| if unexpected: | |
| raise RuntimeError(f"unexpected keys after fp8 load: {unexpected[:10]}") | |
| if missing: | |
| if strict: | |
| raise RuntimeError(f"missing keys after fp8 load: {missing[:10]}") | |
| warnings.warn(f"missing keys after fp8 load: {missing[:10]}", stacklevel=2) | |
| model.to(device) | |
| def swap_linears_to_bnb4bit( | |
| module: nn.Module, | |
| compute_dtype: torch.dtype, | |
| *, | |
| quant_type: str = "nf4", | |
| compress_statistics: bool = False, | |
| ) -> None: | |
| import bitsandbytes as bnb | |
| for name, child in list(module.named_children()): | |
| if isinstance(child, nn.Linear): | |
| new_linear = bnb.nn.Linear4bit( | |
| child.in_features, | |
| child.out_features, | |
| bias=child.bias is not None, | |
| compute_dtype=compute_dtype, | |
| compress_statistics=compress_statistics, | |
| quant_type=quant_type, | |
| ) | |
| setattr(module, name, new_linear) | |
| else: | |
| swap_linears_to_bnb4bit( | |
| child, | |
| compute_dtype, | |
| quant_type=quant_type, | |
| compress_statistics=compress_statistics, | |
| ) | |
| def load_bnb4bit_state_dict( | |
| model: nn.Module, | |
| state_dict: dict[str, torch.Tensor], | |
| device: torch.device, | |
| dtype: torch.dtype, | |
| ) -> None: | |
| import bitsandbytes as bnb | |
| consumed: set[str] = set() | |
| for full_name, tensor in state_dict.items(): | |
| if ".quant_state." in full_name or full_name.endswith(_BNB_SIBLING_SUFFIXES): | |
| continue | |
| parent_path, _, param_name = full_name.rpartition(".") | |
| parent = model.get_submodule(parent_path) if parent_path else model | |
| current = parent._parameters.get(param_name) | |
| if not isinstance(current, bnb.nn.Params4bit): | |
| continue | |
| prefix = full_name + "." | |
| quantized_stats = {k: v for k, v in state_dict.items() if k.startswith(prefix)} | |
| consumed.add(full_name) | |
| consumed.update(quantized_stats.keys()) | |
| parent._parameters[param_name] = bnb.nn.Params4bit.from_prequantized( | |
| data=tensor, | |
| quantized_stats=quantized_stats, | |
| requires_grad=False, | |
| device=device, | |
| ) | |
| remaining = {k: v for k, v in state_dict.items() if k not in consumed} | |
| for k in list(remaining): | |
| if remaining[k].is_floating_point(): | |
| remaining[k] = remaining[k].to(device=device, dtype=dtype) | |
| else: | |
| remaining[k] = remaining[k].to(device=device) | |
| missing, unexpected = model.load_state_dict(remaining, strict=False) | |
| real_missing = [m for m in missing if m not in consumed] | |
| if real_missing: | |
| raise RuntimeError(f"missing keys after quantized load: {real_missing[:10]}") | |
| if unexpected: | |
| raise RuntimeError(f"unexpected keys after quantized load: {unexpected[:10]}") | |
| for p in model.parameters(): | |
| if isinstance(p, bnb.nn.Params4bit): | |
| continue | |
| if p.is_floating_point() and p.dtype != dtype: | |
| p.data = p.data.to(dtype=dtype) | |
| if p.device != device: | |
| p.data = p.data.to(device=device) | |
| for name, b in list(model.named_buffers()): | |
| if b.is_floating_point() and b.dtype != dtype: | |
| parent_path, _, leaf = name.rpartition(".") | |
| parent = model.get_submodule(parent_path) if parent_path else model | |
| parent.register_buffer( | |
| leaf, | |
| b.to(device=device, dtype=dtype), | |
| persistent=leaf not in parent._non_persistent_buffers_set, | |
| ) | |
| elif b.device != device: | |
| parent_path, _, leaf = name.rpartition(".") | |
| parent = model.get_submodule(parent_path) if parent_path else model | |
| parent.register_buffer( | |
| leaf, | |
| b.to(device=device), | |
| persistent=leaf not in parent._non_persistent_buffers_set, | |
| ) | |
| _DEFAULT_TEXT_ENCODER_CONFIG = { | |
| "architectures": ["Qwen3VLModel"], | |
| "dtype": "bfloat16", | |
| "image_token_id": 151655, | |
| "model_type": "qwen3_vl", | |
| "text_config": { | |
| "attention_bias": False, | |
| "attention_dropout": 0.0, | |
| "bos_token_id": 151643, | |
| "dtype": "bfloat16", | |
| "eos_token_id": 151645, | |
| "head_dim": 128, | |
| "hidden_act": "silu", | |
| "hidden_size": 4096, | |
| "initializer_range": 0.02, | |
| "intermediate_size": 12288, | |
| "max_position_embeddings": 262144, | |
| "model_type": "qwen3_vl_text", | |
| "num_attention_heads": 32, | |
| "num_hidden_layers": 36, | |
| "num_key_value_heads": 8, | |
| "pad_token_id": None, | |
| "rms_norm_eps": 1e-06, | |
| "rope_parameters": { | |
| "mrope_interleaved": True, | |
| "mrope_section": [24, 20, 20], | |
| "rope_theta": 5000000, | |
| "rope_type": "default", | |
| }, | |
| "use_cache": True, | |
| "vocab_size": 151936, | |
| }, | |
| "tie_word_embeddings": False, | |
| "transformers_version": "5.8.0", | |
| "video_token_id": 151656, | |
| "vision_config": { | |
| "deepstack_visual_indexes": [8, 16, 24], | |
| "depth": 27, | |
| "dtype": "bfloat16", | |
| "hidden_act": "gelu_pytorch_tanh", | |
| "hidden_size": 1152, | |
| "in_channels": 3, | |
| "initializer_range": 0.02, | |
| "intermediate_size": 4304, | |
| "model_type": "qwen3_vl_vision", | |
| "num_heads": 16, | |
| "num_position_embeddings": 2304, | |
| "out_hidden_size": 4096, | |
| "patch_size": 16, | |
| "spatial_merge_size": 2, | |
| "temporal_patch_size": 2, | |
| }, | |
| "vision_end_token_id": 151653, | |
| "vision_start_token_id": 151652, | |
| "ideogram_fp8_weight_only": True, | |
| } | |
| class Ideogram4TextEncoder(nn.Module): | |
| """Qwen3-VL-8B-Instruct wrapper that extracts hidden states from specific layers.""" | |
| def __init__(self, config_path: str = None, **kwargs) -> None: | |
| super().__init__() | |
| from transformers import AutoConfig, AutoModel | |
| if config_path is None: | |
| config_kwargs = {k: v for k, v in _DEFAULT_TEXT_ENCODER_CONFIG.items() if k != "model_type"} | |
| config = AutoConfig.for_model("qwen3_vl", **config_kwargs) | |
| else: | |
| config = AutoConfig.from_pretrained(config_path, trust_remote_code=True) | |
| self.model = AutoModel.from_config(config, trust_remote_code=True) | |
| self.config = config | |
| def load_state_dict(self, state_dict, strict=True, assign=False): | |
| if is_fp8_state_dict(state_dict): | |
| swap_linears_to_fp8(self.model, state_dict, torch.bfloat16) | |
| return self.model.load_state_dict(state_dict, strict=False, assign=assign) | |
| return self.model.load_state_dict(state_dict, strict=strict, assign=assign) | |
| def forward( | |
| self, | |
| token_ids: torch.Tensor, | |
| attention_mask: torch.Tensor, | |
| text_position_ids: torch.Tensor, | |
| ) -> torch.Tensor: | |
| """Extract hidden states from specific layers of Qwen3-VL. | |
| Args: | |
| token_ids: (B, L) token ids | |
| attention_mask: (B, L) attention mask | |
| text_position_ids: (B, L) position ids for text tokens | |
| Returns: | |
| (B, L, hidden_size * num_activation_layers) concatenated hidden states | |
| """ | |
| from transformers.masking_utils import create_causal_mask | |
| language_model = self.model.language_model | |
| inputs_embeds = language_model.embed_tokens(token_ids) | |
| position_ids_4d = text_position_ids[None, ...].expand(4, text_position_ids.shape[0], -1) | |
| text_position_ids_4d = position_ids_4d[0] | |
| mrope_position_ids = position_ids_4d[1:] | |
| causal_mask = create_causal_mask( | |
| config=language_model.config, | |
| inputs_embeds=inputs_embeds, | |
| attention_mask=attention_mask, | |
| past_key_values=None, | |
| position_ids=text_position_ids_4d, | |
| ) | |
| position_embeddings = language_model.rotary_emb(inputs_embeds, mrope_position_ids) | |
| tap_set = set(QWEN3_VL_ACTIVATION_LAYERS) | |
| captured: dict[int, torch.Tensor] = {} | |
| hidden_states = inputs_embeds | |
| for layer_idx, decoder_layer in enumerate(language_model.layers): | |
| hidden_states = decoder_layer( | |
| hidden_states, | |
| attention_mask=causal_mask, | |
| position_ids=text_position_ids_4d, | |
| past_key_values=None, | |
| position_embeddings=position_embeddings, | |
| ) | |
| if layer_idx in tap_set: | |
| captured[layer_idx] = hidden_states | |
| selected = [captured[i] for i in QWEN3_VL_ACTIVATION_LAYERS] | |
| stacked = torch.stack(selected, dim=0) | |
| stacked = torch.permute(stacked, (1, 2, 3, 0)) | |
| batch_size, seq_len, hidden_size = stacked.shape[:3] | |
| stacked = stacked.reshape(batch_size, seq_len, -1) | |
| text_mask = attention_mask.to(stacked.dtype).unsqueeze(-1) | |
| stacked = stacked * text_mask | |
| return stacked.to(torch.float32) | |