Repository navigation
[None][feat] Add per-encoder attention backend selection for multimodal models #20802
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
base: main
Are you sure you want to change the base?
Changes from all commits
80465bb
0501751
f40c386
accf0d1
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 |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| # limitations under the License. | ||
|
|
||
| import contextlib | ||
| import copy | ||
| import errno | ||
| import json | ||
| import os | ||
|
|
@@ -22,7 +23,7 @@ | |
| from dataclasses import dataclass, field | ||
| from pathlib import Path | ||
| from typing import (TYPE_CHECKING, Any, Dict, Generic, Iterator, List, Optional, | ||
| Sequence, Tuple, TypeVar) | ||
| Sequence, Tuple, TypeVar, get_args) | ||
|
|
||
| import filelock | ||
| import torch | ||
|
|
@@ -39,14 +40,17 @@ | |
| torch_dtype_to_binding) | ||
| from tensorrt_llm.bindings import LayerType as LayerTypeCpp | ||
| from tensorrt_llm.functional import AllReduceStrategy | ||
| # yapf: disable | ||
| # Callers import MoeLoadBalancerConfig from this module. | ||
| from tensorrt_llm.llmapi.llm_args import MoeLoadBalancerConfig # noqa: F401 | ||
| from tensorrt_llm.llmapi.llm_args import (CSA2SparseAttentionConfig, | ||
| DeepSeekSparseAttentionConfig, | ||
| DeepSeekV4SparseAttentionConfig, | ||
| EncoderAttentionBackend, | ||
| KvCacheConfig, | ||
| MoeLoadBalancerConfigType, | ||
| MultimodalConfig) | ||
| # yapf: enable | ||
| from tensorrt_llm.logger import logger | ||
| from tensorrt_llm.mapping import Mapping | ||
| from tensorrt_llm.models.modeling_utils import QuantConfig | ||
|
|
@@ -370,6 +374,57 @@ class ModelConfig(Generic[TConfig]): | |
| # implementation. Set by the model loader, read by `_resolve_class`. | ||
| modeling_v2_target: Optional[type] = None | ||
|
|
||
| def get_encoder_attention_backend(self, encoder: str, default: str) -> str: | ||
| """Return the attention backend selected for a logical encoder. | ||
|
|
||
| Args: | ||
| encoder: Logical encoder name, such as ``"vision"`` or ``"audio"``. | ||
| default: Backend the model uses for this encoder when | ||
| ``multimodal_config.encoder_attention_backend`` omits it. | ||
|
|
||
| Returns: | ||
| The backend from ``multimodal_config.encoder_attention_backend``, | ||
| or ``default`` when the encoder has no entry. | ||
|
|
||
| Raises: | ||
| ValueError: If the selected backend is not a public encoder backend. | ||
| """ | ||
| overrides = (self.multimodal_config.encoder_attention_backend | ||
| if self.multimodal_config is not None else None) | ||
| if not overrides or encoder not in overrides: | ||
| return default | ||
| backend = overrides[encoder] | ||
| backends = get_args(EncoderAttentionBackend) | ||
| if backend not in backends: | ||
| raise ValueError( | ||
| f"Invalid attention backend {backend!r} for the {encoder} encoder; " | ||
| f"expected one of {', '.join(backends)}") | ||
| return backend | ||
|
|
||
| def with_encoder_attention_backend(self, | ||
| encoder: str, | ||
| default: str | None = None | ||
| ) -> "ModelConfig": | ||
| """Return a config whose ``attn_backend`` is the encoder's backend. | ||
|
|
||
| Args: | ||
| encoder: Logical encoder name, such as ``"vision"`` or ``"audio"``. | ||
| default: Backend the model uses for this encoder when no override | ||
| is set. ``None`` means the decoder's ``attn_backend``. | ||
|
|
||
| Returns: | ||
| ``self`` when the selected backend equals ``attn_backend``; | ||
| otherwise a shallow copy with only ``attn_backend`` changed, so the | ||
| decoder config is never modified. | ||
| """ | ||
| backend = self.get_encoder_attention_backend( | ||
| encoder, self.attn_backend if default is None else default) | ||
| if backend == self.attn_backend: | ||
| return self | ||
| config = copy.copy(self) | ||
| object.__setattr__(config, "attn_backend", backend) | ||
|
Collaborator
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. Out of curiosity, why can't we call |
||
| return config | ||
|
|
||
| @property | ||
| def kv_cache_layer_specs(self) -> Optional[Tuple[KVCacheLayerSpec, ...]]: | ||
| """Return model-provided per-layer KV-cache geometry, if any.""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,8 @@ def get_preferred_media_io_kwargs(self) -> Dict[str, Dict[str, Any]]: | |
| Cosmos3InputProcessor, model_type="cosmos3_omni", placeholder_metadata=PLACEHOLDER_METADATA | ||
| ) | ||
| class Cosmos3Model(Qwen3VLModel): | ||
| supported_encoder_attention_backends = Qwen3VLModel.supported_encoder_attention_backends | ||
|
Collaborator
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. Maybe stupid question - without this line, does |
||
|
|
||
| def __init__(self, model_config: ModelConfig[PretrainedConfig], *args, **kwargs): | ||
| omni_config = model_config.pretrained_config | ||
| if omni_config is None: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -192,6 +192,8 @@ class Exaone4_5_VisionModel(Qwen2VisionModelBase): | |
| ), | ||
| ) | ||
| class Exaone4_5_ForConditionalGeneration(Qwen2VLModelBase): | ||
| supported_encoder_attention_backends = Qwen2VLModelBase.supported_encoder_attention_backends | ||
|
Collaborator
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. Similar question re inheritance. |
||
|
|
||
| def __init__( | ||
| self, | ||
| model_config: ModelConfig[PretrainedConfig], | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -647,6 +647,7 @@ def __init__( | |
| vlm_base_model: Optional[type[nn.Module]] = None, | ||
| ) -> None: | ||
| super().__init__() | ||
| model_config = model_config.with_encoder_attention_backend("vision") | ||
| self.model_config = model_config | ||
| self.model_dtype = _text_dtype(model_config) | ||
| # The tower is excluded from checkpoint quantization (BF16 in | ||
|
|
@@ -1028,6 +1029,8 @@ class Glm5NextVLM(MultimodalModelMixin, PreTrainedModel): | |
| requests bypass the vision tower, which runs full attention without KV cache. | ||
| """ | ||
|
|
||
| supported_encoder_attention_backends = {"vision": frozenset({"TRTLLM"})} | ||
|
Collaborator
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. Out of curiosity, why do we use |
||
|
|
||
| _supports_flash_attn = True | ||
| _supports_sdpa = True | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.