chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
import logging
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from typing import Iterable, List, Optional, Set, Tuple, TypedDict, Union
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
from transformers import (
|
||||
Gemma3nAudioConfig,
|
||||
Gemma3nConfig,
|
||||
Gemma3nTextConfig,
|
||||
Gemma3nVisionConfig,
|
||||
PreTrainedModel,
|
||||
)
|
||||
from transformers.models.auto.modeling_auto import AutoModel
|
||||
|
||||
from sglang.srt.layers.linear import ReplicatedLinear
|
||||
from sglang.srt.layers.logits_processor import LogitsProcessor
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
from sglang.srt.layers.vocab_parallel_embedding import VocabParallelEmbedding
|
||||
from sglang.srt.managers.mm_utils import (
|
||||
MultiModalityDataPaddingPatternMultimodalTokens,
|
||||
general_mm_embed_routine,
|
||||
)
|
||||
from sglang.srt.managers.schedule_batch import (
|
||||
Modality,
|
||||
MultimodalDataItem,
|
||||
MultimodalInputs,
|
||||
flatten_nested_list,
|
||||
)
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
||||
from sglang.srt.model_loader.weight_utils import (
|
||||
default_weight_loader,
|
||||
maybe_remap_kv_scale_name,
|
||||
)
|
||||
from sglang.srt.models.gemma3n_audio import Gemma3nAudioEncoder
|
||||
from sglang.srt.models.gemma3n_causal import Gemma3nRMSNorm, Gemma3nTextModel
|
||||
from sglang.srt.utils import add_prefix
|
||||
from sglang.srt.utils.hf_transformers_utils import get_processor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
cached_get_processor = lru_cache(get_processor)
|
||||
|
||||
|
||||
class Gemma3nImagePixelInputs(TypedDict):
|
||||
pixel_values: torch.Tensor
|
||||
"""Shape: `(batch_size * num_images, num_channels, height, width)`"""
|
||||
|
||||
|
||||
class Gemma3nAudioInputs(TypedDict):
|
||||
input_features: torch.Tensor
|
||||
"""Shape: `(batch_size * num_audio, seq_length, num_features)`"""
|
||||
input_features_mask: torch.Tensor
|
||||
"""Shape: `(batch_size * num_audio, seq_length)`"""
|
||||
|
||||
|
||||
class Gemma3nMultimodalEmbedder(nn.Module):
|
||||
"""Embeds token ids or soft tokens for multimodal content into language model space."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
multimodal_config: Union[Gemma3nAudioConfig, Gemma3nVisionConfig],
|
||||
text_config: Gemma3nTextConfig,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.multimodal_hidden_size = multimodal_config.hidden_size
|
||||
self.eps = multimodal_config.rms_norm_eps
|
||||
self.vocab_offset = multimodal_config.vocab_offset
|
||||
self.vocab_size = multimodal_config.vocab_size
|
||||
self.text_hidden_size = text_config.hidden_size
|
||||
|
||||
self.embedding = VocabParallelEmbedding(
|
||||
self.vocab_size,
|
||||
self.multimodal_hidden_size,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("embedding", prefix),
|
||||
)
|
||||
|
||||
self.hard_embedding_norm = Gemma3nRMSNorm(
|
||||
self.multimodal_hidden_size,
|
||||
eps=self.eps,
|
||||
)
|
||||
|
||||
self.soft_embedding_norm = Gemma3nRMSNorm(
|
||||
self.multimodal_hidden_size,
|
||||
eps=self.eps,
|
||||
)
|
||||
|
||||
self.embedding_projection = ReplicatedLinear(
|
||||
self.multimodal_hidden_size,
|
||||
self.text_hidden_size,
|
||||
bias=False,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("embedding_projection", prefix),
|
||||
)
|
||||
|
||||
self.embedding_post_projection_norm = Gemma3nRMSNorm(
|
||||
self.text_hidden_size,
|
||||
eps=self.eps,
|
||||
with_scale=False,
|
||||
)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
input_ids: Optional[torch.LongTensor] = None,
|
||||
inputs_embeds: Optional[torch.Tensor] = None,
|
||||
) -> torch.Tensor:
|
||||
"""Embeds token ids or soft tokens for multimodal content into language model space.
|
||||
|
||||
Args:
|
||||
input_ids: A torch.LongTensor containing the token ids to embed. Values should be in the range
|
||||
`[vocab_offset, vocab_offset + vocab_size)`.
|
||||
inputs_embeds: A torch.Tensor containing the soft tokens to embed.
|
||||
|
||||
Returns:
|
||||
A torch.Tensor of embeddings with shape `[batch_size, seq_len, self.config.text_config.hidden_size]`.
|
||||
"""
|
||||
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 not None:
|
||||
emb_norm = self.soft_embedding_norm(inputs_embeds)
|
||||
else:
|
||||
# Handle out of vocab ids to prevent CUDA assertion failures
|
||||
out_of_vocab_id = self.vocab_size - 1
|
||||
adjusted_ids = input_ids - self.vocab_offset
|
||||
adjusted_ids = torch.where(adjusted_ids < 0, out_of_vocab_id, adjusted_ids)
|
||||
adjusted_ids = torch.where(
|
||||
adjusted_ids >= self.vocab_size, out_of_vocab_id, adjusted_ids
|
||||
)
|
||||
hard_emb = self.embedding(adjusted_ids)
|
||||
emb_norm = self.hard_embedding_norm(hard_emb)
|
||||
|
||||
emb_norm_proj, _ = self.embedding_projection(emb_norm)
|
||||
return self.embedding_post_projection_norm(emb_norm_proj)
|
||||
|
||||
|
||||
class Gemma3nForConditionalGeneration(PreTrainedModel):
|
||||
config_class = Gemma3nConfig
|
||||
"""Gemma3n multimodal model for conditional generation."""
|
||||
|
||||
# BitandBytes specific attributes
|
||||
default_bitsandbytes_target_modules = [
|
||||
".gate_proj.",
|
||||
".down_proj.",
|
||||
".up_proj.",
|
||||
".q_proj.",
|
||||
".k_proj.",
|
||||
".v_proj.",
|
||||
".o_proj.",
|
||||
".out_proj.",
|
||||
]
|
||||
bitsandbytes_stacked_params_mapping = {
|
||||
"q_proj": ("qkv_proj", 0),
|
||||
"k_proj": ("qkv_proj", 1),
|
||||
"v_proj": ("qkv_proj", 2),
|
||||
"gate_proj": ("gate_up_proj", 0),
|
||||
"up_proj": ("gate_up_proj", 1),
|
||||
"out_proj": ("proj", 0),
|
||||
}
|
||||
|
||||
packed_modules_mapping = {
|
||||
"qkv_proj": [
|
||||
"q_proj",
|
||||
"k_proj",
|
||||
"v_proj",
|
||||
],
|
||||
"gate_up_proj": [
|
||||
"gate_proj",
|
||||
"up_proj",
|
||||
],
|
||||
}
|
||||
|
||||
# LoRA specific attributes
|
||||
supported_lora_modules = [
|
||||
"qkv_proj",
|
||||
"o_proj",
|
||||
"gate_up_proj",
|
||||
"down_proj",
|
||||
]
|
||||
# Gemma does not apply LoRA to the embedding layer
|
||||
embedding_modules = {}
|
||||
embedding_padding_modules = []
|
||||
supports_lora = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Gemma3nConfig,
|
||||
quant_config: Optional[QuantizationConfig] = None,
|
||||
prefix: str = "",
|
||||
) -> None:
|
||||
super().__init__(config=config)
|
||||
self.config = config
|
||||
self.quant_config = quant_config
|
||||
|
||||
prefix = add_prefix("model", prefix)
|
||||
|
||||
# Vision components
|
||||
# TODO: Use sglang's vision model
|
||||
self.vision_tower = AutoModel.from_config(config=config.vision_config)
|
||||
|
||||
self.embed_vision = Gemma3nMultimodalEmbedder(
|
||||
config.vision_config,
|
||||
config.text_config,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("embed_vision", prefix),
|
||||
)
|
||||
|
||||
# Audio components
|
||||
self.embed_audio = Gemma3nMultimodalEmbedder(
|
||||
config.audio_config,
|
||||
config.text_config,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("embed_audio", prefix),
|
||||
)
|
||||
|
||||
self.audio_tower = Gemma3nAudioEncoder(
|
||||
config.audio_config,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix("audio_tower", prefix),
|
||||
)
|
||||
|
||||
self.vocab_size = config.text_config.vocab_size
|
||||
self.vocab_size_per_layer_input = config.text_config.vocab_size_per_layer_input
|
||||
|
||||
# Text model
|
||||
self.language_model = Gemma3nTextModel(
|
||||
config.text_config,
|
||||
quant_config,
|
||||
prefix=add_prefix("language_model", prefix),
|
||||
)
|
||||
|
||||
# Create logits processor for the multimodal model
|
||||
self.logits_processor = LogitsProcessor(config.text_config)
|
||||
|
||||
self.post_init()
|
||||
|
||||
def pad_input_ids(
|
||||
self,
|
||||
input_ids: List[int],
|
||||
mm_inputs: MultimodalInputs,
|
||||
) -> List[int]:
|
||||
"""Pad input IDs with image and audio tokens."""
|
||||
pattern = MultiModalityDataPaddingPatternMultimodalTokens()
|
||||
return pattern.pad_input_tokens(input_ids, mm_inputs)
|
||||
|
||||
def get_input_embeddings(self) -> nn.Embedding:
|
||||
return self.language_model.get_input_embeddings()
|
||||
|
||||
def get_attention_sliding_window_size(self):
|
||||
return self.config.text_config.sliding_window - 1
|
||||
|
||||
def get_image_feature(self, items: List[MultimodalDataItem]):
|
||||
"""
|
||||
Projects the last hidden state from the vision model into language model space.
|
||||
|
||||
Returns:
|
||||
image_features (`torch.Tensor`): Image feature tensor of shape `(num_images, image_length, embed_dim)`).
|
||||
"""
|
||||
# Process images one by one to handle flatten_batch=True constraint in vision_tower
|
||||
all_pixel_values = flatten_nested_list([item.feature for item in items])
|
||||
vision_outputs_list = []
|
||||
|
||||
for pixel_values_batch in all_pixel_values:
|
||||
# Normalize input shape to [batch_size, channels, height, width]
|
||||
if pixel_values_batch.dim() == 5:
|
||||
pixel_values_batch = pixel_values_batch.squeeze(0)
|
||||
elif pixel_values_batch.dim() == 3:
|
||||
pixel_values_batch = pixel_values_batch.unsqueeze(0)
|
||||
elif pixel_values_batch.dim() != 4:
|
||||
raise ValueError(
|
||||
f"Unexpected pixel_values shape: {pixel_values_batch.shape}"
|
||||
)
|
||||
|
||||
# Process each image in the batch
|
||||
batch_size = pixel_values_batch.shape[0]
|
||||
for i in range(batch_size):
|
||||
pixel_value = pixel_values_batch[i : i + 1] # Keep batch dimension as 1
|
||||
pixel_value = pixel_value.to(
|
||||
device=self.vision_tower.device, dtype=self.language_model.dtype()
|
||||
)
|
||||
vision_outputs = self.vision_tower(
|
||||
pixel_values=pixel_value, do_pooling=False, return_dict=True
|
||||
).last_hidden_state
|
||||
vision_outputs_list.append(vision_outputs)
|
||||
|
||||
# Concatenate all vision outputs
|
||||
vision_outputs = torch.cat(vision_outputs_list, dim=0)
|
||||
|
||||
# Convert from (batch, channels, height, width) to (batch, height * width, channels)
|
||||
vision_outputs = vision_outputs.reshape(
|
||||
vision_outputs.shape[0],
|
||||
self.config.vision_config.hidden_size,
|
||||
self.config.vision_soft_tokens_per_image,
|
||||
).permute(0, 2, 1)
|
||||
|
||||
# Normalize and embed the soft tokens into language model space
|
||||
vision_outputs *= self.config.vision_config.hidden_size**0.5
|
||||
return self.embed_vision(inputs_embeds=vision_outputs)
|
||||
|
||||
def get_audio_feature(self, items: List[MultimodalDataItem]) -> torch.Tensor:
|
||||
"""
|
||||
Projects the last hidden state from the audio encoder into language model space.
|
||||
|
||||
Args:
|
||||
items: List of multimodal data items containing audio data.
|
||||
|
||||
Returns:
|
||||
audio_features (`torch.Tensor`): Audio feature tensor of shape `(num_audios, audio_length, embed_dim)`).
|
||||
"""
|
||||
# Extract audio features and masks from items
|
||||
all_input_features = flatten_nested_list([item.feature for item in items])
|
||||
all_input_features_mask = flatten_nested_list(
|
||||
[~item.input_features_mask for item in items]
|
||||
) # Note(Xinyuan): reverse the mask according to the HF implementation
|
||||
|
||||
# Process audio features one by one
|
||||
audio_features_list = []
|
||||
|
||||
for input_features, input_features_mask in zip(
|
||||
all_input_features, all_input_features_mask
|
||||
):
|
||||
# Ensure proper tensor format
|
||||
if input_features.dim() == 2:
|
||||
input_features = input_features.unsqueeze(0)
|
||||
if input_features_mask.dim() == 1:
|
||||
input_features_mask = input_features_mask.unsqueeze(0)
|
||||
|
||||
# Move to device and dtype
|
||||
input_features = input_features.to(
|
||||
device=next(self.audio_tower.parameters()).device,
|
||||
dtype=self.language_model.dtype(),
|
||||
)
|
||||
input_features_mask = input_features_mask.to(device=input_features.device)
|
||||
|
||||
# Process through audio tower
|
||||
audio_outputs, audio_mask = self.audio_tower(
|
||||
input_features, input_features_mask
|
||||
)
|
||||
|
||||
# Embed the audio outputs
|
||||
audio_embeds = self.embed_audio(inputs_embeds=audio_outputs)
|
||||
audio_features_list.append(audio_embeds)
|
||||
|
||||
# Concatenate all audio features
|
||||
if audio_features_list:
|
||||
audio_features = torch.cat(audio_features_list, dim=0)
|
||||
|
||||
# The Gemma3nProcessor expects all audio will be 30s in length and inserts 188 audio soft tokens into the
|
||||
# text to account for this. However, the audio preprocessing and encoder do not gurarantee they will
|
||||
# produce 188 soft tokens; they will produce at most that many tokens, but they may produce fewer tokens
|
||||
# depending on the length of the longest audio input in the batch. When we encounter this situation, we pad
|
||||
# the audio feature out to 188 soft tokens with the emebedding of the last token in the embed_audio vocab.
|
||||
audio_padding_toks = torch.tensor(
|
||||
[[self.vocab_size - 1]], dtype=torch.long, device=audio_features.device
|
||||
)
|
||||
audio_padding_embs = self.embed_audio(input_ids=audio_padding_toks)
|
||||
audio_features = torch.where(
|
||||
audio_mask.unsqueeze(-1), audio_padding_embs, audio_features
|
||||
)
|
||||
|
||||
audio_batch_size, audio_seq_len, audio_embed_dim = audio_features.shape
|
||||
extra_padding_tokens = (
|
||||
self.config.audio_soft_tokens_per_image - audio_seq_len
|
||||
)
|
||||
extra_padding_features = audio_padding_embs.expand(
|
||||
audio_batch_size, extra_padding_tokens, audio_embed_dim
|
||||
)
|
||||
|
||||
audio_features = torch.cat((audio_features, extra_padding_features), dim=1)
|
||||
return audio_features
|
||||
else:
|
||||
return torch.empty(
|
||||
0,
|
||||
0,
|
||||
self.language_model.config.hidden_size,
|
||||
device=next(self.parameters()).device,
|
||||
dtype=self.language_model.dtype(),
|
||||
)
|
||||
|
||||
def get_per_layer_inputs(
|
||||
self, input_ids: torch.LongTensor
|
||||
) -> Optional[torch.Tensor]:
|
||||
return self.language_model.get_per_layer_inputs(input_ids)
|
||||
|
||||
def project_per_layer_inputs(
|
||||
self,
|
||||
inputs_embeds: torch.Tensor,
|
||||
per_layer_inputs: Optional[torch.Tensor] = None,
|
||||
) -> torch.Tensor:
|
||||
return self.language_model.project_per_layer_inputs(
|
||||
inputs_embeds, per_layer_inputs
|
||||
)
|
||||
|
||||
@torch.no_grad()
|
||||
def forward(
|
||||
self,
|
||||
input_ids: torch.LongTensor,
|
||||
positions: torch.Tensor,
|
||||
forward_batch: ForwardBatch,
|
||||
input_embeds: torch.Tensor = None,
|
||||
**kwargs: object,
|
||||
) -> LogitsProcessor:
|
||||
"""Forward pass for multimodal Gemma3n."""
|
||||
if (input_ids is None) ^ (input_embeds is not None):
|
||||
raise ValueError(
|
||||
"You must specify exactly one of input_ids or inputs_embeds"
|
||||
)
|
||||
|
||||
positions += 1
|
||||
if input_ids is not None:
|
||||
# Prepare per-layer inputs from inputs_ids
|
||||
per_layer_inputs_mask = torch.logical_and(
|
||||
input_ids >= 0, input_ids < self.vocab_size_per_layer_input
|
||||
)
|
||||
per_layer_inputs_tokens = torch.where(
|
||||
per_layer_inputs_mask, input_ids, torch.zeros_like(input_ids)
|
||||
)
|
||||
per_layer_inputs = self.language_model.get_per_layer_inputs(
|
||||
per_layer_inputs_tokens
|
||||
)
|
||||
|
||||
# Use general_mm_embed_routine for handling multimodal data
|
||||
# This will automatically handle text, image, and audio embeddings
|
||||
hidden_states = general_mm_embed_routine(
|
||||
input_ids=input_ids,
|
||||
forward_batch=forward_batch,
|
||||
language_model=self.language_model,
|
||||
data_embedding_funcs={
|
||||
Modality.IMAGE: self.get_image_feature,
|
||||
Modality.AUDIO: self.get_audio_feature,
|
||||
},
|
||||
positions=positions,
|
||||
per_layer_inputs=per_layer_inputs,
|
||||
)
|
||||
|
||||
# Process hidden states through logits processor
|
||||
return self.logits_processor(
|
||||
input_ids, hidden_states, self.language_model.embed_tokens, forward_batch
|
||||
)
|
||||
|
||||
def tie_weights(self, **kwargs):
|
||||
return self.language_model.tie_weights(**kwargs)
|
||||
|
||||
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]):
|
||||
stacked_params_mapping = [
|
||||
# (param_name, shard_name, shard_id)
|
||||
(".qkv_proj", ".q_proj", "q"),
|
||||
(".qkv_proj", ".k_proj", "k"),
|
||||
(".qkv_proj", ".v_proj", "v"),
|
||||
(".gate_up_proj", ".up_proj", 1),
|
||||
(".gate_up_proj", ".gate_proj", 0),
|
||||
]
|
||||
"""Load weights for the model."""
|
||||
params_dict = dict(self.named_parameters())
|
||||
loaded_params: Set[str] = set()
|
||||
|
||||
for name, loaded_weight in weights:
|
||||
name = re.sub(r"^model\.", "", name)
|
||||
for param_name, weight_name, shard_id in stacked_params_mapping:
|
||||
if weight_name not in name:
|
||||
continue
|
||||
name = name.replace(weight_name, param_name)
|
||||
# Skip loading extra bias for GPTQ models
|
||||
if name.endswith(".bias") and name not in params_dict:
|
||||
continue
|
||||
param = params_dict[name]
|
||||
weight_loader = param.weight_loader
|
||||
weight_loader(param, loaded_weight, shard_id)
|
||||
break
|
||||
else:
|
||||
if "vision_model" in name:
|
||||
# adapt to VisionAttention
|
||||
name = name.replace(".self_attn.out_proj", ".self_attn.proj")
|
||||
# Skip loading extra bias for GPTQ models
|
||||
if name.endswith(".bias") and name not in params_dict:
|
||||
continue
|
||||
# Remapping the name of FP8 kv-scale
|
||||
name = maybe_remap_kv_scale_name(name, params_dict)
|
||||
if name is None:
|
||||
continue
|
||||
param = params_dict[name]
|
||||
weight_loader = getattr(param, "weight_loader", default_weight_loader)
|
||||
weight_loader(param, loaded_weight)
|
||||
loaded_params.add(name)
|
||||
return loaded_params
|
||||
|
||||
lora_pattern = re.compile(
|
||||
r"^language_model\.layers\.(\d+)\.(?:self_attn|mlp)\.(?:qkv_proj|o_proj|down_proj|gate_up_proj)"
|
||||
)
|
||||
|
||||
def should_apply_lora(self, module_name: str) -> bool:
|
||||
return bool(self.lora_pattern.match(module_name))
|
||||
|
||||
def get_hidden_dim(self, module_name, layer_idx):
|
||||
# return input_dim, output_dim
|
||||
if module_name == "qkv_proj":
|
||||
return (
|
||||
self.config.hidden_size,
|
||||
self.config.head_dim
|
||||
* (
|
||||
self.config.num_attention_heads
|
||||
+ self.config.num_key_value_heads * 2
|
||||
),
|
||||
)
|
||||
elif module_name == "o_proj":
|
||||
return (
|
||||
self.config.head_dim * self.config.num_attention_heads,
|
||||
self.config.hidden_size,
|
||||
)
|
||||
elif module_name == "gate_up_proj":
|
||||
assert len(set(self.config.intermediate_size)) == 1, (
|
||||
"Currently SGLang requires uniform intermediate size for all layers. "
|
||||
"Please file an issue if you need support for non-uniform intermediate sizes."
|
||||
)
|
||||
return self.config.hidden_size, self.config.intermediate_size[0] * 2
|
||||
elif module_name == "down_proj":
|
||||
assert len(set(self.config.intermediate_size)) == 1, (
|
||||
"Currently SGLang requires uniform intermediate size for all layers. "
|
||||
"Please file an issue if you need support for non-uniform intermediate sizes."
|
||||
)
|
||||
return self.config.intermediate_size[0], self.config.hidden_size
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
EntryClass = Gemma3nForConditionalGeneration
|
||||
Reference in New Issue
Block a user