Repository navigation
[MM] Move Nemotron VL image normalization to device #57924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
525229b
59ea70b
ee18b49
ce511aa
201e9c5
1d64db9
3ac420b
851dd43
085ebd0
c8cfbb1
8880629
9b17478
1c3311f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| from vllm.logger import init_logger | ||
| from vllm.lora.layers.base import BaseLayerWithLoRA | ||
| from vllm.model_executor.layers.activation import ReLUSquaredActivation | ||
| from vllm.model_executor.layers.fusion.mm_input_norm import FusedMMInputNorm | ||
| from vllm.model_executor.layers.layernorm import RMSNorm | ||
| from vllm.model_executor.model_loader.weight_utils import default_weight_loader | ||
| from vllm.model_executor.models.interfaces import ( | ||
|
|
@@ -934,6 +935,7 @@ class NemotronH_Nano_VL_V2( | |
| SupportsMultiModalPruning, | ||
| SupportsLoRA, | ||
| ): | ||
| supports_mm_device_do_normalize = True | ||
| requires_sequential_video_encoding = True | ||
| """Temporarily needed for dynamic res video w/ conv3d, doesn't support bs>1 yet""" | ||
|
|
||
|
|
@@ -992,6 +994,15 @@ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): | |
| assert isinstance(llm_dtype, torch.dtype) | ||
| self.llm_dtype = llm_dtype | ||
| with self._mark_tower_model(vllm_config, {"image", "video", "audio"}): | ||
| self.input_norm: FusedMMInputNorm | None = ( | ||
| FusedMMInputNorm( | ||
| image_mean=config.norm_mean, | ||
| image_std=config.norm_std, | ||
| rescale_factor=1.0 / 255.0, | ||
| ) | ||
| if multimodal_config.mm_device_do_normalize | ||
| else None | ||
| ) | ||
| self.vision_model = self.get_vit_model_from_radio_config(config).to( | ||
| llm_dtype | ||
| ) | ||
|
|
@@ -1099,10 +1110,23 @@ def pixel_shuffle_dynamic_res( | |
|
|
||
| return x | ||
|
|
||
| def _normalize_pixel_values(self, pixel_values: torch.Tensor) -> torch.Tensor: | ||
| if self.input_norm is None: | ||
| return pixel_values | ||
| shape = pixel_values.shape | ||
| if pixel_values.ndim == 4: | ||
| flattened = pixel_values.reshape(shape[0], -1) | ||
| else: | ||
| assert pixel_values.ndim == 3 | ||
| flattened = pixel_values.reshape(-1, shape[-1]) | ||
| normalized = self.input_norm(flattened, self.llm_dtype) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you prove this produces the same dtype we had before post-norm?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or I guess the question is - this seems to change when the norm happens at least, no? So the accuracy evals you will run will prove whether or not that changes anything
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both paths emit |
||
| return normalized.reshape(shape) | ||
|
|
||
| def extract_feature_dynamic( | ||
| self, pixel_values: torch.Tensor, imgs_sizes: list[tuple[int, int]] | ||
| ): | ||
| """Dynamic resolution extract_feature for images.""" | ||
| pixel_values = self._normalize_pixel_values(pixel_values) | ||
| _, vit_embeds = self.vision_model(pixel_values, imgs_sizes=imgs_sizes) | ||
| vit_embeds = vit_embeds.to(dtype=torch.bfloat16) | ||
| if self.vision_final_layernorm is not None: | ||
|
|
@@ -1134,6 +1158,7 @@ def extract_feature( | |
| vit_embeds_list = [] | ||
| for i in range(0, N, micro_batch_size): | ||
| chunk = pixel_values[i : i + micro_batch_size] | ||
| chunk = self._normalize_pixel_values(chunk) | ||
| if num_frames is not None and T > 1: | ||
| _, vit_embeds = self.vision_model(chunk, num_frames=chunk.shape[0]) | ||
| else: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
registry.pymaps four architectures toNemotronH_Nano_VL_V2(L542-545), includingNemotronH_Super_Omni_Reasoning_V3, so the Super checkpoints also get device normalization from this PR. Could additional rows be added for super models as well?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like
NemotronH_Super_Omni_Reasoning_V3has no models in HF? I added the other two.