diff --git a/src/liger_kernel/transformers/model/qwen2_5_vl.py b/src/liger_kernel/transformers/model/qwen2_5_vl.py index a3271f53..41214d8c 100644 --- a/src/liger_kernel/transformers/model/qwen2_5_vl.py +++ b/src/liger_kernel/transformers/model/qwen2_5_vl.py @@ -5,12 +5,13 @@ from typing import Union import torch -from torch.nn import CrossEntropyLoss from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLCausalLMOutputWithPast +from transformers.utils import can_return_tuple from liger_kernel.transformers.model.loss_utils import LigerForCausalLMLoss +@can_return_tuple def lce_forward( self, input_ids: torch.LongTensor = None, @@ -34,14 +35,22 @@ def lce_forward( **kwargs, ) -> Union[Tuple, Qwen2_5_VLCausalLMOutputWithPast]: r""" - Copy paste Qwen2_5_VL's forward but replace torch cross entropy with liger fused linear cross entropy - Args: - 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]`. - - Returns: + 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]`. + pixel_values_videos (`torch.FloatTensor` of shape `(seq_length, num_channels * temporal_size * image_size * image_size)): + The tensors corresponding to the input videos. Pixel values can be obtained using + [`AutoImageProcessor`]. See [`Qwen2_5_VLImageProcessor.__call__`] for details. [`Qwen2_5_VLProcessor`] uses + [`Qwen2_5_VLImageProcessor`] for processing videos. + 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. + rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*): + The rope index difference between sequence length and multimodal rope. + second_per_grid_ts (`torch.Tensor` of shape `(num_videos)`, *optional*): + The time interval (in seconds) for each grid along the temporal dimension in the 3D position IDs. Example: @@ -73,78 +82,20 @@ def lce_forward( >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] "The image shows a street scene with a red stop sign in the foreground. In the background, there is a large red gate with Chinese characters ..." ```""" + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions output_hidden_states = ( output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states ) return_dict = return_dict if return_dict is not None else self.config.use_return_dict - if inputs_embeds is None: - inputs_embeds = self.model.embed_tokens(input_ids) - if pixel_values is not None: - pixel_values = pixel_values.type(self.visual.dtype) - image_embeds = self.visual(pixel_values, grid_thw=image_grid_thw) - n_image_tokens = (input_ids == self.config.image_token_id).sum().item() - n_image_features = image_embeds.shape[0] - if n_image_tokens != n_image_features: - raise ValueError( - f"Image features and image tokens do not match: tokens: {n_image_tokens}, features {n_image_features}" - ) - - mask = input_ids == self.config.image_token_id - mask_unsqueezed = mask.unsqueeze(-1) - mask_expanded = mask_unsqueezed.expand_as(inputs_embeds) - image_mask = mask_expanded.to(inputs_embeds.device) - - image_embeds = image_embeds.to(inputs_embeds.device, inputs_embeds.dtype) - inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds) - - if pixel_values_videos is not None: - pixel_values_videos = pixel_values_videos.type(self.visual.dtype) - video_embeds = self.visual(pixel_values_videos, grid_thw=video_grid_thw) - n_video_tokens = (input_ids == self.config.video_token_id).sum().item() - n_video_features = video_embeds.shape[0] - if n_video_tokens != n_video_features: - raise ValueError( - f"Video features and video tokens do not match: tokens: {n_video_tokens}, features {n_video_features}" - ) - - mask = input_ids == self.config.video_token_id - mask_unsqueezed = mask.unsqueeze(-1) - mask_expanded = mask_unsqueezed.expand_as(inputs_embeds) - video_mask = mask_expanded.to(inputs_embeds.device) - - video_embeds = video_embeds.to(inputs_embeds.device, inputs_embeds.dtype) - inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds) - - if attention_mask is not None: - attention_mask = attention_mask.to(inputs_embeds.device) - - # if we get 4D attention mask we cannot calculate rope deltas anymore. TODO @raushan fixme - if position_ids is None and (attention_mask is None or attention_mask.ndim == 2): - # calculate RoPE index once per generation in the pre-fill stage only - if (cache_position is not None and cache_position[0] == 0) or self.rope_deltas is None: - position_ids, rope_deltas = self.get_rope_index( - input_ids, - image_grid_thw, - video_grid_thw, - second_per_grid_ts, - attention_mask, - ) - self.rope_deltas = rope_deltas - # then use the prev pre-calculated rope-deltas to get the correct position ids - else: - batch_size, seq_length, _ = inputs_embeds.shape - delta = (cache_position[0] + self.rope_deltas).to(inputs_embeds.device) if cache_position is not None else 0 - position_ids = torch.arange(seq_length, device=inputs_embeds.device) - position_ids = position_ids.view(1, -1).expand(batch_size, -1) - if cache_position is not None: # otherwise `deltas` is an int `0` - delta = delta.repeat_interleave(batch_size // delta.shape[0], dim=0) - position_ids = position_ids.add(delta) - position_ids = position_ids.unsqueeze(0).expand(3, -1, -1) - outputs = self.model( - input_ids=None, + 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, + second_per_grid_ts=second_per_grid_ts, position_ids=position_ids, attention_mask=attention_mask, past_key_values=past_key_values, @@ -180,19 +131,10 @@ def lce_forward( ) else: logits = self.lm_head(hidden_states) + + loss = None if labels is not None: - # Upcast to float if we need to compute the loss to avoid potential precision issues - logits = logits.float() - # Shift so that tokens < n predict n - shift_logits = logits[..., :-1, :].contiguous() - shift_labels = labels[..., 1:].contiguous() - # Flatten the tokens - loss_fct = CrossEntropyLoss() - shift_logits = shift_logits.view(-1, self.config.vocab_size) - shift_labels = shift_labels.view(-1) - # Enable model parallelism - shift_labels = shift_labels.to(shift_logits.device) - loss = loss_fct(shift_logits, shift_labels) + loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size) if not return_dict: output = (logits,) + outputs[1:] @@ -204,5 +146,5 @@ def lce_forward( past_key_values=outputs.past_key_values, hidden_states=outputs.hidden_states, attentions=outputs.attentions, - rope_deltas=rope_deltas, + rope_deltas=outputs.rope_deltas, ) diff --git a/src/liger_kernel/transformers/model/qwen2_vl.py b/src/liger_kernel/transformers/model/qwen2_vl.py index fb30df11..342ace1a 100644 --- a/src/liger_kernel/transformers/model/qwen2_vl.py +++ b/src/liger_kernel/transformers/model/qwen2_vl.py @@ -5,14 +5,13 @@ from typing import Union import torch -from packaging import version -from torch.nn import CrossEntropyLoss -from transformers import __version__ as transformers_version from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLCausalLMOutputWithPast +from transformers.utils import can_return_tuple from liger_kernel.transformers.model.loss_utils import LigerForCausalLMLoss +@can_return_tuple def lce_forward( self, input_ids: torch.LongTensor = None, @@ -35,15 +34,20 @@ def lce_forward( **kwargs, ) -> Union[Tuple, Qwen2VLCausalLMOutputWithPast]: r""" - Copy paste Qwen2VL's forward but replace torch cross entropy with liger fused linear cross entropy - - Args: - 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]`. - - Returns: + 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]`. + pixel_values_videos (`torch.FloatTensor` of shape `(seq_length, num_channels * temporal_size * image_size * image_size)): + The tensors corresponding to the input videos. Pixel values can be obtained using + [`AutoImageProcessor`]. See [`Qwen2VLImageProcessor.__call__`] for details. [`Qwen2VLProcessor`] uses + [`Qwen2VLImageProcessor`] for processing videos. + 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. + rope_deltas (`torch.LongTensor` of shape `(batch_size, )`, *optional*): + The rope index difference between sequence length and multimodal rope. Example: @@ -75,80 +79,19 @@ def lce_forward( >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] "The image shows a street scene with a red stop sign in the foreground. In the background, there is a large red gate with Chinese characters ..." ```""" + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions output_hidden_states = ( output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states ) return_dict = return_dict if return_dict is not None else self.config.use_return_dict - if inputs_embeds is None: - inputs_embeds = self.model.embed_tokens(input_ids) - if pixel_values is not None: - pixel_values = pixel_values.type(self.visual.get_dtype()) - image_embeds = self.visual(pixel_values, grid_thw=image_grid_thw) - n_image_tokens = (input_ids == self.config.image_token_id).sum().item() - n_image_features = image_embeds.shape[0] - if n_image_tokens != n_image_features: - raise ValueError( - f"Image features and image tokens do not match: tokens: {n_image_tokens}, features {n_image_features}" - ) - image_mask = ( - (input_ids == self.config.image_token_id) - .unsqueeze(-1) - .expand_as(inputs_embeds) - .to(inputs_embeds.device) - ) - image_embeds = image_embeds.to(inputs_embeds.device, inputs_embeds.dtype) - inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds) - - if pixel_values_videos is not None: - pixel_values_videos = pixel_values_videos.type(self.visual.get_dtype()) - video_embeds = self.visual(pixel_values_videos, grid_thw=video_grid_thw) - n_video_tokens = (input_ids == self.config.video_token_id).sum().item() - n_video_features = video_embeds.shape[0] - if n_video_tokens != n_video_features: - raise ValueError( - f"Video features and video tokens do not match: tokens: {n_video_tokens}, features {n_video_features}" - ) - video_mask = ( - (input_ids == self.config.video_token_id) - .unsqueeze(-1) - .expand_as(inputs_embeds) - .to(inputs_embeds.device) - ) - video_embeds = video_embeds.to(inputs_embeds.device, inputs_embeds.dtype) - inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds) - - if attention_mask is not None: - attention_mask = attention_mask.to(inputs_embeds.device) - - if version.parse(transformers_version) > version.parse("4.46.3"): - # NOTE: this bug fix for qwen2-vl is not applied until transformers 4.47.0 - # https://github.com/huggingface/transformers/issues/33401 - # While correct, this breaks equivalence with past versions of Qwen2-VL from - # transformers and leads to failed tests or users noticing differences in results. - # TODO: remove above conditional when liger drops support for transformers<4.47.0 - # if we get 4D attention mask we cannot calculate rope deltas anymore. TODO @raushan fixme - if position_ids is None and (attention_mask is None or attention_mask.ndim == 2): - # calculate RoPE index once per generation in the pre-fill stage only - if (cache_position is not None and cache_position[0] == 0) or self.rope_deltas is None: - position_ids, rope_deltas = self.get_rope_index( - input_ids, image_grid_thw, video_grid_thw, attention_mask - ) - self.rope_deltas = rope_deltas - # then use the prev pre-calculated rope-deltas to get the correct position ids - else: - batch_size, seq_length, _ = inputs_embeds.shape - delta = cache_position[0] + self.rope_deltas if cache_position is not None else 0 - position_ids = torch.arange(seq_length, device=inputs_embeds.device) - position_ids = position_ids.view(1, -1).expand(batch_size, -1) - if cache_position is not None: # otherwise `deltas` is an int `0` - delta = delta.repeat_interleave(batch_size // delta.shape[0], dim=0) - position_ids = position_ids.add(delta) - position_ids = position_ids.unsqueeze(0).expand(3, -1, -1) - outputs = self.model( - input_ids=None, + 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, @@ -184,23 +127,10 @@ def lce_forward( ) else: logits = self.lm_head(hidden_states) - if labels is not None: - # Upcast to float if we need to compute the loss to avoid potential precision issues - logits = logits.float() - # Shift so that tokens < n predict n - shift_logits = logits[..., :-1, :].contiguous() - shift_labels = labels[..., 1:].contiguous() - # Flatten the tokens - loss_fct = CrossEntropyLoss() - shift_logits = shift_logits.view(-1, self.config.vocab_size) - shift_labels = shift_labels.view(-1) - # Enable model parallelism - shift_labels = shift_labels.to(shift_logits.device) - loss = loss_fct(shift_logits, shift_labels) - if not return_dict: - output = (logits,) + outputs[1:] - return (loss,) + output if loss is not None else output + loss = None + if labels is not None: + loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size) return Qwen2VLCausalLMOutputWithPast( loss=loss, @@ -208,5 +138,5 @@ def lce_forward( past_key_values=outputs.past_key_values, hidden_states=outputs.hidden_states, attentions=outputs.attentions, - rope_deltas=rope_deltas, + rope_deltas=outputs.rope_deltas, ) diff --git a/src/liger_kernel/transformers/monkey_patch.py b/src/liger_kernel/transformers/monkey_patch.py index 3356ddac..3e93f5dd 100644 --- a/src/liger_kernel/transformers/monkey_patch.py +++ b/src/liger_kernel/transformers/monkey_patch.py @@ -1225,7 +1225,7 @@ def apply_liger_kernel_to_qwen2_vl( ) -> None: """ Apply Liger kernels to replace original implementation in HuggingFace Qwen2-VL models. - NOTE: Qwen2-VL is not available in transformers<4.45.0 + NOTE: Qwen2-VL is not supported in transformers<4.52.4 Args: cross_entropy (bool): Whether to apply Liger's cross entropy loss. Default is False. @@ -1239,12 +1239,19 @@ def apply_liger_kernel_to_qwen2_vl( model (PreTrainedModel): The model instance to apply Liger kernels to, if the model has already been loaded. Default is None. """ + if transformer_version < version.parse("4.52.4"): + logger.warning("Qwen2-VL support is only compatible with transformers >= 4.52.4") + return + assert not (cross_entropy and fused_linear_cross_entropy), ( "cross_entropy and fused_linear_cross_entropy cannot both be True." ) from transformers.models.qwen2_vl import modeling_qwen2_vl + from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VisionTransformerPretrainedModel + from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLModel + from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLTextModel from liger_kernel.transformers.model.qwen2_vl import lce_forward as qwen2_vl_lce_forward @@ -1266,24 +1273,38 @@ def apply_liger_kernel_to_qwen2_vl( # The model instance already exists, so we need to additionally patch the # instance variables that reference already-instantiated modules - # get the base model from the model instance - base_model: Qwen2VLModel = getattr(model, model.base_model_prefix, model) + if isinstance(model, (Qwen2VLForConditionalGeneration, Qwen2VLModel)): + # Note: language_model and visual properties can be accessed throught conditional class for BC. + # Not sure if it is subject to changes in the future. + # Reference: https://github.com/huggingface/transformers/blob/v4.52.4/src/transformers/models/qwen2_vl/modeling_qwen2_vl.py#L1698 + text_model: Qwen2VLTextModel = model.language_model + vision_model: Qwen2VisionTransformerPretrainedModel = model.visual + elif isinstance(model, Qwen2VLTextModel): + text_model: Qwen2VLTextModel = model + vision_model = None + else: + # Note: Currently there's no support for patching vision model only. Feel free to raise an issue if needed. + raise TypeError( + f"Unsupported Qwen2VL model type. `model` must be `Qwen2VLForConditionalGeneration`, `Qwen2VLModel` or `Qwen2VLTextModel`. Got: {type(model)}" + ) - if hasattr(model, "visual"): - # Patch Qwen2VisionTransformerPretrainedModel - for vision_block in model.visual.blocks: + # Patch Qwen2VisionTransformerPretrainedModel + if vision_model is not None: + for vision_block in vision_model.blocks: if layer_norm: _patch_layer_norm_module(vision_block.norm1) _patch_layer_norm_module(vision_block.norm2) - if rms_norm: - _patch_rms_norm_module(base_model.norm) - for decoder_layer in base_model.layers: - if swiglu: - _patch_swiglu_module(decoder_layer.mlp, LigerSwiGLUMLP) + # Patch Qwen2VisionTextModel + if text_model is not None: if rms_norm: - _patch_rms_norm_module(decoder_layer.input_layernorm) - _patch_rms_norm_module(decoder_layer.post_attention_layernorm) + _patch_rms_norm_module(text_model.norm) + for decoder_layer in text_model.layers: + if swiglu: + _patch_swiglu_module(decoder_layer.mlp, LigerSwiGLUMLP) + if rms_norm: + _patch_rms_norm_module(decoder_layer.input_layernorm) + _patch_rms_norm_module(decoder_layer.post_attention_layernorm) def apply_liger_kernel_to_qwen2_5_vl( @@ -1309,12 +1330,19 @@ def apply_liger_kernel_to_qwen2_5_vl( model (PreTrainedModel): The model instance to apply Liger kernels to, if the model has already been loaded. Default is None. """ + if transformer_version < version.parse("4.52.4"): + logger.warning("Qwen2.5-VL support is only compatible with transformers >= 4.52.4") + return + assert not (cross_entropy and fused_linear_cross_entropy), ( "cross_entropy and fused_linear_cross_entropy cannot both be True." ) from transformers.models.qwen2_5_vl import modeling_qwen2_5_vl + from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VisionTransformerPretrainedModel + from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLModel + from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLTextModel from liger_kernel.transformers.model.qwen2_5_vl import lce_forward as qwen2_5_vl_lce_forward @@ -1333,24 +1361,37 @@ def apply_liger_kernel_to_qwen2_5_vl( # The model instance already exists, so we need to additionally patch the # instance variables that reference already-instantiated modules - # get the base model from the model instance - base_model: Qwen2_5_VLModel = getattr(model, model.base_model_prefix, model) + if isinstance(model, (Qwen2_5_VLForConditionalGeneration, Qwen2_5_VLModel)): + # Note: language_model and visual properties can be accessed throught conditional class for BC. + # Not sure if it is subject to changes in the future. + # Reference: https://github.com/huggingface/transformers/blob/v4.52.4/src/transformers/models/qwen2_5_vl/modeling_qwen2_5_vl.py#L1823 + text_model: Qwen2_5_VLTextModel = model.language_model + vision_model: Qwen2_5_VisionTransformerPretrainedModel = model.visual + elif isinstance(model, Qwen2_5_VLTextModel): + text_model: Qwen2_5_VLTextModel = model + vision_model = None + else: + # Note: Currently there's no support for patching vision model only. Feel free to raise an issue if needed. + raise TypeError( + f"Unsupported Qwen2VL model type. `model` must be `Qwen2VLForConditionalGeneration`, `Qwen2VLModel` or `Qwen2VLTextModel`. Got: {type(model)}" + ) - if hasattr(model, "visual"): + if vision_model is not None: # Patch Qwen2_5_VisionTransformerPretrainedModel for vision_block in model.visual.blocks: if rms_norm: _patch_rms_norm_module(vision_block.norm1) _patch_rms_norm_module(vision_block.norm2) - if rms_norm: - _patch_rms_norm_module(base_model.norm) - for decoder_layer in base_model.layers: - if swiglu: - _patch_swiglu_module(decoder_layer.mlp, LigerSwiGLUMLP) + if text_model is not None: if rms_norm: - _patch_rms_norm_module(decoder_layer.input_layernorm) - _patch_rms_norm_module(decoder_layer.post_attention_layernorm) + _patch_rms_norm_module(text_model.norm) + for decoder_layer in text_model.layers: + if swiglu: + _patch_swiglu_module(decoder_layer.mlp, LigerSwiGLUMLP) + if rms_norm: + _patch_rms_norm_module(decoder_layer.input_layernorm) + _patch_rms_norm_module(decoder_layer.post_attention_layernorm) def apply_liger_kernel_to_phi3( @@ -1571,7 +1612,9 @@ MODEL_TYPE_TO_APPLY_LIGER_FN = { "qwen3": apply_liger_kernel_to_qwen3, "qwen3_moe": apply_liger_kernel_to_qwen3_moe, "qwen2_vl": apply_liger_kernel_to_qwen2_vl, + "qwen2_vl_text": apply_liger_kernel_to_qwen2_vl, "qwen2_5_vl": apply_liger_kernel_to_qwen2_5_vl, + "qwen2_5_vl_text": apply_liger_kernel_to_qwen2_5_vl, "phi3": apply_liger_kernel_to_phi3, "paligemma": apply_liger_kernel_to_paligemma, } diff --git a/test/convergence/bf16/test_mini_models.py b/test/convergence/bf16/test_mini_models.py index a4395822..17a2a621 100644 --- a/test/convergence/bf16/test_mini_models.py +++ b/test/convergence/bf16/test_mini_models.py @@ -69,20 +69,26 @@ except ImportError: MLLAMA_AVAILABLE = False try: - # Qwen2-VL is only available in transformers>4.44.2 + # Qwen2-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: - # Qwen2.5-VL is only available in transformers>4.48.2 + # Qwen2.5-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False diff --git a/test/convergence/bf16/test_mini_models_multimodal.py b/test/convergence/bf16/test_mini_models_multimodal.py index a0499687..1cf4d4db 100644 --- a/test/convergence/bf16/test_mini_models_multimodal.py +++ b/test/convergence/bf16/test_mini_models_multimodal.py @@ -35,26 +35,34 @@ from test.utils import supports_bfloat16 from test.utils import train_bpe_tokenizer try: - # Qwen2-VL is only available in transformers>=4.45.0 + # Qwen2-VL is only available in transformers>=4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2.tokenization_qwen2_fast import Qwen2TokenizerFast from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.image_processing_qwen2_vl import Qwen2VLImageProcessor from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration from transformers.models.qwen2_vl.processing_qwen2_vl import Qwen2VLProcessor + from transformers.models.qwen2_vl.video_processing_qwen2_vl import Qwen2VLVideoProcessor - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: - # Qwen2.5-VL is only available in transformers>4.48.2 + # Qwen2.5-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2.tokenization_qwen2_fast import Qwen2TokenizerFast from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration from transformers.models.qwen2_5_vl.processing_qwen2_5_vl import Qwen2_5_VLProcessor from transformers.models.qwen2_vl.image_processing_qwen2_vl import Qwen2VLImageProcessor + from transformers.models.qwen2_vl.video_processing_qwen2_vl import Qwen2VLVideoProcessor - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False @@ -504,7 +512,12 @@ def create_processor(model_name: str): ) qwen_tokenizer = Qwen2TokenizerFast(tokenizer_object=tokenizer_base, **tokenizer_config) image_processor = Qwen2VLImageProcessor() - return Qwen2VLProcessor(image_processor=image_processor, tokenizer=qwen_tokenizer) + video_processor = Qwen2VLVideoProcessor() + return Qwen2VLProcessor( + image_processor=image_processor, + video_processor=video_processor, + tokenizer=qwen_tokenizer, + ) elif model_name == "mini_qwen2_5_vl": tokenizer_config = load_tokenizer_config( @@ -521,7 +534,12 @@ def create_processor(model_name: str): ) qwen_tokenizer = Qwen2TokenizerFast(tokenizer_object=tokenizer_base, **tokenizer_config) image_processor = Qwen2VLImageProcessor() - return Qwen2_5_VLProcessor(image_processor=image_processor, tokenizer=qwen_tokenizer) + video_processor = Qwen2VLVideoProcessor() + return Qwen2_5_VLProcessor( + image_processor=image_processor, + video_processor=video_processor, + tokenizer=qwen_tokenizer, + ) elif model_name == "mini_llava": tokenizer_config = load_tokenizer_config( diff --git a/test/convergence/bf16/test_mini_models_with_logits.py b/test/convergence/bf16/test_mini_models_with_logits.py index 4781195a..0a6f61eb 100644 --- a/test/convergence/bf16/test_mini_models_with_logits.py +++ b/test/convergence/bf16/test_mini_models_with_logits.py @@ -69,20 +69,26 @@ except ImportError: MLLAMA_AVAILABLE = False try: - # Qwen2-VL is only available in transformers>4.44.2 + # Qwen2-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: - # Qwen2.5-VL is only available in transformers>4.48.2 + # Qwen2.5-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False diff --git a/test/convergence/fp32/test_mini_models.py b/test/convergence/fp32/test_mini_models.py index e5250cf5..3a9c9e57 100644 --- a/test/convergence/fp32/test_mini_models.py +++ b/test/convergence/fp32/test_mini_models.py @@ -68,20 +68,26 @@ except ImportError: MLLAMA_AVAILABLE = False try: - # Qwen2-VL is only available in transformers>4.44.2 + # Qwen2-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: - # Qwen2.5-VL is only available in transformers>4.48.2 + # Qwen2.5-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False diff --git a/test/convergence/fp32/test_mini_models_multimodal.py b/test/convergence/fp32/test_mini_models_multimodal.py index 54f6067f..fe543136 100644 --- a/test/convergence/fp32/test_mini_models_multimodal.py +++ b/test/convergence/fp32/test_mini_models_multimodal.py @@ -34,26 +34,34 @@ from test.utils import set_seed from test.utils import train_bpe_tokenizer try: - # Qwen2-VL is only available in transformers>=4.45.0 + # Qwen2-VL is only available in transformers>=4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2.tokenization_qwen2_fast import Qwen2TokenizerFast from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.image_processing_qwen2_vl import Qwen2VLImageProcessor from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration from transformers.models.qwen2_vl.processing_qwen2_vl import Qwen2VLProcessor + from transformers.models.qwen2_vl.video_processing_qwen2_vl import Qwen2VLVideoProcessor - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: # Qwen2.5-VL is only available in transformers>4.48.2 + import transformers + + from packaging import version from transformers.models.qwen2.tokenization_qwen2_fast import Qwen2TokenizerFast from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration from transformers.models.qwen2_5_vl.processing_qwen2_5_vl import Qwen2_5_VLProcessor from transformers.models.qwen2_vl.image_processing_qwen2_vl import Qwen2VLImageProcessor + from transformers.models.qwen2_vl.video_processing_qwen2_vl import Qwen2VLVideoProcessor - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False @@ -501,7 +509,12 @@ def create_processor(model_name: str): ) qwen_tokenizer = Qwen2TokenizerFast(tokenizer_object=tokenizer_base, **tokenizer_config) image_processor = Qwen2VLImageProcessor() - return Qwen2VLProcessor(image_processor=image_processor, tokenizer=qwen_tokenizer) + video_processor = Qwen2VLVideoProcessor() + return Qwen2VLProcessor( + image_processor=image_processor, + video_processor=video_processor, + tokenizer=qwen_tokenizer, + ) elif model_name == "mini_qwen2_5_vl": tokenizer_config = load_tokenizer_config( @@ -518,7 +531,12 @@ def create_processor(model_name: str): ) qwen_tokenizer = Qwen2TokenizerFast(tokenizer_object=tokenizer_base, **tokenizer_config) image_processor = Qwen2VLImageProcessor() - return Qwen2_5_VLProcessor(image_processor=image_processor, tokenizer=qwen_tokenizer) + video_processor = Qwen2VLVideoProcessor() + return Qwen2_5_VLProcessor( + image_processor=image_processor, + video_processor=video_processor, + tokenizer=qwen_tokenizer, + ) elif model_name == "mini_llava": tokenizer_config = load_tokenizer_config( diff --git a/test/convergence/fp32/test_mini_models_with_logits.py b/test/convergence/fp32/test_mini_models_with_logits.py index 83eb542f..457df66d 100644 --- a/test/convergence/fp32/test_mini_models_with_logits.py +++ b/test/convergence/fp32/test_mini_models_with_logits.py @@ -68,20 +68,26 @@ except ImportError: MLLAMA_AVAILABLE = False try: - # Qwen2-VL is only available in transformers>4.44.2 + # Qwen2-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_vl.configuration_qwen2_vl import Qwen2VLConfig from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration - QWEN2_VL_AVAILABLE = True + QWEN2_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_VL_AVAILABLE = False try: - # Qwen2.5-VL is only available in transformers>4.48.2 + # Qwen2.5-VL is only available in transformers>4.52.4 + import transformers + + from packaging import version from transformers.models.qwen2_5_vl.configuration_qwen2_5_vl import Qwen2_5_VLConfig from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration - QWEN2_5_VL_AVAILABLE = True + QWEN2_5_VL_AVAILABLE = version.parse(transformers.__version__) >= version.parse("4.52.4") except ImportError: QWEN2_5_VL_AVAILABLE = False diff --git a/test/transformers/test_monkey_patch.py b/test/transformers/test_monkey_patch.py index de806368..bbd988d8 100644 --- a/test/transformers/test_monkey_patch.py +++ b/test/transformers/test_monkey_patch.py @@ -870,7 +870,7 @@ def test_apply_liger_kernel_to_instance_for_qwen3_moe(): @pytest.mark.skipif(not is_qwen2_vl_available(), reason="qwen2_vl module not available") -def test_apply_liger_kernel_to_instance_for_qwen2_vl(): +def test_apply_liger_kernel_to_instance_for_qwen2_vl_for_conditional_generation(): # Ensure any monkey patching is cleaned up for subsequent tests with patch("transformers.models.qwen2_vl.modeling_qwen2_vl"): from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLForConditionalGeneration @@ -899,8 +899,10 @@ def test_apply_liger_kernel_to_instance_for_qwen2_vl(): assert isinstance(dummy_model_instance, Qwen2VLForConditionalGeneration) # Check that model instance variables are not yet patched with Liger modules - assert inspect.getsource(dummy_model_instance.model.norm.forward) != inspect.getsource(LigerRMSNorm.forward) - for layer in dummy_model_instance.model.layers: + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) != inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) @@ -912,8 +914,10 @@ def test_apply_liger_kernel_to_instance_for_qwen2_vl(): _apply_liger_kernel_to_instance(model=dummy_model_instance) # Check that the model's instance variables were correctly patched with Liger modules - assert inspect.getsource(dummy_model_instance.model.norm.forward) == inspect.getsource(LigerRMSNorm.forward) - for layer in dummy_model_instance.model.layers: + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) == inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) @@ -927,8 +931,178 @@ def test_apply_liger_kernel_to_instance_for_qwen2_vl(): pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") +@pytest.mark.skipif(not is_qwen2_vl_available(), reason="qwen2_vl module not available") +def test_apply_liger_kernel_to_instance_for_qwen2_vl(): + # Ensure any monkey patching is cleaned up for subsequent tests + with patch("transformers.models.qwen2_vl.modeling_qwen2_vl"): + from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLModel + + # Instantiate a dummy model + config = transformers.models.qwen2_vl.configuration_qwen2_vl.Qwen2VLConfig( + torch_dtype=torch.bfloat16, + rms_norm_eps=1e-5, + hidden_size=32, + intermediate_size=48, + embed_dim=16, + hidden_act="silu", + num_hidden_layers=2, + num_attention_heads=2, + max_position_embeddings=128, + vocab_size=1000, + vision_config={ + "depth": 4, + "embed_dim": 128, + "num_heads": 8, + "hidden_size": 1024, + }, + ) + dummy_model_instance = Qwen2VLModel._from_config(config) + + assert isinstance(dummy_model_instance, Qwen2VLModel) + + # Check that model instance variables are not yet patched with Liger modules + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) != inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: + assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + for vision_block in dummy_model_instance.visual.blocks: + assert inspect.getsource(vision_block.norm1.forward) != inspect.getsource(LigerLayerNorm.forward) + assert inspect.getsource(vision_block.norm2.forward) != inspect.getsource(LigerLayerNorm.forward) + + # Test applying kernels to the model instance + _apply_liger_kernel_to_instance(model=dummy_model_instance) + + # Check that the model's instance variables were correctly patched with Liger modules + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) == inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: + assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + for vision_block in dummy_model_instance.visual.blocks: + assert inspect.getsource(vision_block.norm1.forward) == inspect.getsource(LigerLayerNorm.forward) + assert inspect.getsource(vision_block.norm2.forward) == inspect.getsource(LigerLayerNorm.forward) + + try: + print(dummy_model_instance) + except Exception as e: + pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") + + +@pytest.mark.skipif(not is_qwen2_vl_available(), reason="qwen2_vl module not available") +def test_apply_liger_kernel_to_instance_for_qwen2_vl_text(): + # Ensure any monkey patching is cleaned up for subsequent tests + with patch("transformers.models.qwen2_vl.modeling_qwen2_vl"): + from transformers.models.qwen2_vl.modeling_qwen2_vl import Qwen2VLTextModel + + # Instantiate a dummy model + config = transformers.models.qwen2_vl.configuration_qwen2_vl.Qwen2VLTextConfig( + torch_dtype=torch.bfloat16, + rms_norm_eps=1e-5, + hidden_size=32, + intermediate_size=48, + embed_dim=16, + hidden_act="silu", + num_hidden_layers=2, + num_attention_heads=2, + max_position_embeddings=128, + vocab_size=1000, + ) + dummy_model_instance = Qwen2VLTextModel._from_config(config) + + assert isinstance(dummy_model_instance, Qwen2VLTextModel) + + # Check that model instance variables are not yet patched with Liger modules + assert inspect.getsource(dummy_model_instance.norm.forward) != inspect.getsource(LigerRMSNorm.forward) + for layer in dummy_model_instance.layers: + assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + + # Test applying kernels to the model instance + _apply_liger_kernel_to_instance(model=dummy_model_instance) + + # Check that the model's instance variables were correctly patched with Liger modules + assert inspect.getsource(dummy_model_instance.norm.forward) == inspect.getsource(LigerRMSNorm.forward) + for layer in dummy_model_instance.layers: + assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + + try: + print(dummy_model_instance) + except Exception as e: + pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") + + @pytest.mark.skipif(not is_qwen2_5_vl_available(), reason="qwen2_5_vl module not available") def test_apply_liger_kernel_to_instance_for_qwen2_5_vl(): + # Ensure any monkey patching is cleaned up for subsequent tests + with patch("transformers.models.qwen2_5_vl.modeling_qwen2_5_vl"): + from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLModel + + # Instantiate a dummy model + config = transformers.models.qwen2_5_vl.configuration_qwen2_5_vl.Qwen2_5_VLConfig( + torch_dtype=torch.bfloat16, + rms_norm_eps=1e-5, + hidden_size=32, + intermediate_size=48, + embed_dim=16, + hidden_act="silu", + num_hidden_layers=2, + num_attention_heads=2, + max_position_embeddings=128, + vocab_size=1000, + vision_config={ + "depth": 4, + "embed_dim": 128, + "num_heads": 8, + "hidden_size": 1024, + }, + ) + dummy_model_instance = Qwen2_5_VLModel._from_config(config) + + assert isinstance(dummy_model_instance, Qwen2_5_VLModel) + + # Check that model instance variables are not yet patched with Liger modules + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) != inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: + assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + for vision_block in dummy_model_instance.visual.blocks: + assert inspect.getsource(vision_block.norm1.forward) != inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(vision_block.norm2.forward) != inspect.getsource(LigerRMSNorm.forward) + + # Test applying kernels to the model instance + _apply_liger_kernel_to_instance(model=dummy_model_instance) + + # Check that the model's instance variables were correctly patched with Liger modules + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) == inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: + assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + for vision_block in dummy_model_instance.visual.blocks: + assert inspect.getsource(vision_block.norm1.forward) == inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(vision_block.norm2.forward) == inspect.getsource(LigerRMSNorm.forward) + + try: + print(dummy_model_instance) + except Exception as e: + pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") + + +@pytest.mark.skipif(not is_qwen2_5_vl_available(), reason="qwen2_5_vl module not available") +def test_apply_liger_kernel_to_instance_for_qwen2_5_vl_for_conditional_generation(): # Ensure any monkey patching is cleaned up for subsequent tests with patch("transformers.models.qwen2_5_vl.modeling_qwen2_5_vl"): from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLForConditionalGeneration @@ -957,8 +1131,10 @@ def test_apply_liger_kernel_to_instance_for_qwen2_5_vl(): assert isinstance(dummy_model_instance, Qwen2_5_VLForConditionalGeneration) # Check that model instance variables are not yet patched with Liger modules - assert inspect.getsource(dummy_model_instance.model.norm.forward) != inspect.getsource(LigerRMSNorm.forward) - for layer in dummy_model_instance.model.layers: + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) != inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) @@ -970,8 +1146,10 @@ def test_apply_liger_kernel_to_instance_for_qwen2_5_vl(): _apply_liger_kernel_to_instance(model=dummy_model_instance) # Check that the model's instance variables were correctly patched with Liger modules - assert inspect.getsource(dummy_model_instance.model.norm.forward) == inspect.getsource(LigerRMSNorm.forward) - for layer in dummy_model_instance.model.layers: + assert inspect.getsource(dummy_model_instance.language_model.norm.forward) == inspect.getsource( + LigerRMSNorm.forward + ) + for layer in dummy_model_instance.language_model.layers: assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) @@ -985,6 +1163,52 @@ def test_apply_liger_kernel_to_instance_for_qwen2_5_vl(): pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") +@pytest.mark.skipif(not is_qwen2_5_vl_available(), reason="qwen2_5_vl module not available") +def test_apply_liger_kernel_to_instance_for_qwen2_5_vl_text(): + # Ensure any monkey patching is cleaned up for subsequent tests + with patch("transformers.models.qwen2_5_vl.modeling_qwen2_5_vl"): + from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLTextModel + + # Instantiate a dummy model + config = transformers.models.qwen2_5_vl.configuration_qwen2_5_vl.Qwen2_5_VLTextConfig( + torch_dtype=torch.bfloat16, + rms_norm_eps=1e-5, + hidden_size=32, + intermediate_size=48, + embed_dim=16, + hidden_act="silu", + num_hidden_layers=2, + num_attention_heads=2, + max_position_embeddings=128, + vocab_size=1000, + ) + dummy_model_instance = Qwen2_5_VLTextModel._from_config(config) + + assert isinstance(dummy_model_instance, Qwen2_5_VLTextModel) + + # Check that model instance variables are not yet patched with Liger modules + assert inspect.getsource(dummy_model_instance.norm.forward) != inspect.getsource(LigerRMSNorm.forward) + for layer in dummy_model_instance.layers: + assert inspect.getsource(layer.mlp.forward) != inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) != inspect.getsource(LigerRMSNorm.forward) + + # Test applying kernels to the model instance + _apply_liger_kernel_to_instance(model=dummy_model_instance) + + # Check that the model's instance variables were correctly patched with Liger modules + assert inspect.getsource(dummy_model_instance.norm.forward) == inspect.getsource(LigerRMSNorm.forward) + for layer in dummy_model_instance.layers: + assert inspect.getsource(layer.mlp.forward) == inspect.getsource(LigerSwiGLUMLP.forward) + assert inspect.getsource(layer.input_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + assert inspect.getsource(layer.post_attention_layernorm.forward) == inspect.getsource(LigerRMSNorm.forward) + + try: + print(dummy_model_instance) + except Exception as e: + pytest.fail(f"An exception occured in extra_expr: {type(e).__name__} - {e}") + + def test_apply_liger_kernel_to_instance_for_phi3(): # Ensure any monkey patching is cleaned up for subsequent tests with patch("transformers.models.phi3.modeling_phi3"):